aboutsummaryrefslogtreecommitdiff
path: root/lib/Daffm/Action/Cmdline.hs
blob: 64977aa1e869df1614be7d7405937788257e5afd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
module Daffm.Action.Cmdline where

import Brick (suspendAndResume')
import qualified Brick.Widgets.Edit as Editor
import qualified Brick.Widgets.List as L
import Control.Monad (unless, void)
import Control.Monad.State (get, gets, modify)
import Daffm.Action.Core (reloadDir)
import Daffm.Types (AppEvent, AppState (..), FileInfo (..), FocusTarget (..))
import Data.Char (isSpace)
import Data.List (dropWhileEnd)
import qualified Data.Set as Set
import qualified Data.Text as Text
import qualified Data.Text.Zipper as Z
import qualified Data.Text.Zipper as Zipper
import System.Process (callCommand, callProcess)

leaveCmdline :: AppEvent ()
leaveCmdline = clearCmdline >> modify (\st -> st {stateFocusTarget = FocusMain})

enterCmdline :: AppEvent ()
enterCmdline = modify (\st -> st {stateFocusTarget = FocusCmdline})

setCmdlineText :: String -> AppEvent ()
setCmdlineText text =
  applyCmdlineEdit (const $ Z.stringZipper [text] (Just 1))

clearCmdline :: AppEvent ()
clearCmdline = applyCmdlineEdit Z.clearZipper

runCmdline :: AppEvent ()
runCmdline = do
  cmd <- gets (trimCmd . Editor.getEditContents . stateCmdlineEditor)
  evaluateCommand cmd
  leaveCmdline
  where
    trimCmd = dropWhile isSpace . dropWhileEnd isSpace . unlines

evaluateCommand :: String -> AppEvent ()
evaluateCommand ('!' : '!' : cmd) = do
  cmd' <- Text.unpack <$> cmdSubstitutions (Text.pack cmd)
  suspendAndResume' $ do
    callCommand cmd'
    putStrLn "Press any key to continue" >> void getChar
  reloadDir
evaluateCommand ('!' : cmd) = do
  cmd' <- Text.unpack <$> cmdSubstitutions (Text.pack cmd)
  suspendAndResume' $ callCommand cmd'
  reloadDir
evaluateCommand "delete" = do
  (AppState {stateFileSelections, stateFiles}) <- get
  let files =
        if Set.null stateFileSelections
          then maybe [] ((: []) . filePath . snd) $ L.listSelectedElement stateFiles
          else Set.elems stateFileSelections
  unless (null files) $ do
    suspendAndResume' $ callProcess "rm" ("-rfi" : files)
  reloadDir
evaluateCommand _cmd = pure ()

cmdSubstitutions :: Text.Text -> AppEvent Text.Text
cmdSubstitutions cmd = do
  (AppState {stateFiles, stateCwd, stateFileSelections}) <- get
  let file = maybe "" (filePath . snd) . L.listSelectedElement $ stateFiles
  let escape = (\s -> "'" <> s <> "'") . Text.replace "'" "\\'"
  let selections = Text.unwords . map (escape . Text.pack) $ Set.elems stateFileSelections
  -- TODO: Escaping %
  let subst =
        Text.replace "%" (Text.pack file)
          . Text.replace "%d" (Text.pack stateCwd)
          . Text.replace "%s" selections
  pure . subst $ cmd

applyCmdlineEdit :: (Zipper.TextZipper String -> Zipper.TextZipper String) -> AppEvent ()
applyCmdlineEdit zipper = do
  editor <- gets stateCmdlineEditor
  let editor' = Editor.applyEdit zipper editor
  modify (\s -> s {stateCmdlineEditor = editor'})