aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Daffm/Action/Commands.hs7
-rw-r--r--lib/Daffm/Action/Core.hs49
-rw-r--r--lib/Daffm/Attrs.hs6
-rw-r--r--lib/Daffm/State.hs12
-rw-r--r--lib/Daffm/Types.hs11
-rw-r--r--lib/Daffm/View.hs14
6 files changed, 84 insertions, 15 deletions
diff --git a/lib/Daffm/Action/Commands.hs b/lib/Daffm/Action/Commands.hs
index a9cf691..35262df 100644
--- a/lib/Daffm/Action/Commands.hs
+++ b/lib/Daffm/Action/Commands.hs
@@ -9,7 +9,7 @@ import Control.Monad.IO.Class (MonadIO (liftIO))
import Daffm.Action.Cmdline
import Daffm.Action.Core
import Daffm.Types
-import Daffm.Utils (trimStart)
+import Daffm.Utils (trim, trimStart)
import Data.Bifunctor (Bifunctor (second))
import Data.Char (isSpace)
import Data.Maybe (fromMaybe)
@@ -45,6 +45,9 @@ parseCommand cmd = mkCmd . splitCmdArgs $ trimStart cmd
("cmdline-set", txt) -> Just $ CmdSetCmdline txt
("selection-toggle", _) -> Just CmdToggleSelection
("selection-clear", _) -> Just CmdClearSelection
+ ("search", term) -> Just $ CmdSearch $ trim term
+ ("search-next", _) -> Just $ CmdSearchNext 1
+ ("search-prev", _) -> Just $ CmdSearchNext (-1)
_ -> Nothing
readCommandLines' :: Text.Text -> IO [Text.Text]
@@ -83,6 +86,8 @@ processCommand CmdToggleSelection = toggleCurrentFileSelection
processCommand CmdClearSelection = clearFileSelections
processCommand CmdGoBack = goBackToParentDir
processCommand (CmdChain chain) = forM_ chain processCommand
+processCommand (CmdSearch term) = setSearchTerm term >> applySearch >> nextSearchMatch
+processCommand (CmdSearchNext change) = updateSearchIndex (+ change) >> nextSearchMatch
processCommand CmdNoop = pure ()
evaluateCommand :: Text.Text -> AppEvent ()
diff --git a/lib/Daffm/Action/Core.hs b/lib/Daffm/Action/Core.hs
index a8dad32..a726c77 100644
--- a/lib/Daffm/Action/Core.hs
+++ b/lib/Daffm/Action/Core.hs
@@ -13,6 +13,7 @@ import Daffm.Types (AppEvent, AppState (..), FileInfo (..), FilePathText, FileTy
import Data.Maybe (fromMaybe)
import qualified Data.Set as Set
import qualified Data.Text as Text
+import qualified Data.Vector as Vec
import System.Directory (getHomeDirectory)
import qualified System.Exit as Proc
import System.FilePath (takeDirectory)
@@ -24,6 +25,7 @@ modifyM f = get >>= f >>= put
loadDir :: FilePathText -> AppEvent ()
loadDir dir = do
modifyM (liftIO . (>>= filterInvalidSelections) . loadDirToState dir)
+ applySearch
reloadDir :: AppEvent ()
reloadDir = do
@@ -93,9 +95,7 @@ currentFile = do
toggleCurrentFileSelection :: AppEvent ()
toggleCurrentFileSelection = do
- currentFile >>= \case
- Just file -> modify $ toggleFileSelection (filePath file)
- Nothing -> pure ()
+ currentFile >>= maybe (pure ()) (modify . toggleFileSelection . filePath)
moveCurrent 1
clearFileSelections :: AppEvent ()
@@ -106,3 +106,46 @@ moveCurrent :: Int -> AppEvent ()
moveCurrent count = do
files <- gets stateFiles
modify $ \s -> s {stateFiles = L.listMoveBy count files}
+
+setSearchTerm :: Text.Text -> AppEvent ()
+setSearchTerm "" = modify (\st -> st {stateSearchTerm = Nothing, stateSearchIndex = 0})
+setSearchTerm term = modify (\st -> st {stateSearchTerm = Just term, stateSearchIndex = 0})
+
+applySearch :: AppEvent ()
+applySearch = get >>= apply
+ where
+ apply :: AppState -> AppEvent ()
+ apply (AppState {stateSearchTerm = Nothing}) =
+ modify
+ (\st -> st {stateSearchMatches = Vec.empty, stateSearchIndex = 0})
+ apply (AppState {stateSearchTerm = Just term, stateFiles}) = do
+ let search (_, FileInfo {fileName}) = Text.toLower term `Text.isInfixOf` Text.toLower fileName
+ let matches = Vec.map fst . Vec.filter search . Vec.indexed $ L.listElements stateFiles
+ modify
+ ( \st ->
+ st
+ { stateSearchMatches = matches,
+ stateSearchIndex = wrapSearchIndex st (stateSearchIndex st)
+ }
+ )
+
+nextSearchMatch :: AppEvent ()
+nextSearchMatch = do
+ st@(AppState {stateSearchMatches, stateFiles, stateSearchIndex}) <- get
+ let nextFiles =
+ if Vec.null stateSearchMatches
+ then stateFiles
+ else L.listMoveTo (stateSearchMatches Vec.! wrapSearchIndex st stateSearchIndex) stateFiles
+ modify (\st' -> st' {stateFiles = nextFiles})
+
+wrapSearchIndex :: AppState -> Int -> Int
+wrapSearchIndex (AppState {stateSearchMatches}) nextIndex =
+ let matchCount = length stateSearchMatches
+ in if
+ | nextIndex < 0 -> matchCount - 1
+ | nextIndex >= matchCount && matchCount /= 0 -> nextIndex `mod` matchCount
+ | otherwise -> nextIndex
+
+updateSearchIndex :: (Int -> Int) -> AppEvent ()
+updateSearchIndex upd =
+ modify (\st -> st {stateSearchIndex = wrapSearchIndex st $ upd $ stateSearchIndex st})
diff --git a/lib/Daffm/Attrs.hs b/lib/Daffm/Attrs.hs
index 77f9ee6..d7b7097 100644
--- a/lib/Daffm/Attrs.hs
+++ b/lib/Daffm/Attrs.hs
@@ -18,6 +18,9 @@ directoryAttr = listAttr <> A.attrName "directory"
directorySelectedAttr :: A.AttrName
directorySelectedAttr = listSelectedAttr <> directoryAttr
+searchMarchAttr :: A.AttrName
+searchMarchAttr = listAttr <> A.attrName "match-indicator"
+
appAttrMap :: A.AttrMap
appAttrMap =
A.attrMap
@@ -27,5 +30,6 @@ appAttrMap =
(directoryAttr, fg V.brightCyan),
(directorySelectedAttr, fg V.brightCyan),
(fileAttr, fg V.white),
- (fileSelectedAttr, fg V.white)
+ (fileSelectedAttr, fg V.white),
+ (searchMarchAttr, fg V.magenta)
]
diff --git a/lib/Daffm/State.hs b/lib/Daffm/State.hs
index 210cd8d..1429897 100644
--- a/lib/Daffm/State.hs
+++ b/lib/Daffm/State.hs
@@ -32,12 +32,16 @@ mkEmptyAppState config =
stateCwd = "",
stateKeyMap = configKeymap config,
stateOpenerScript = configOpener config,
- stateKeySequence = []
+ stateKeySequence = [],
+ stateSearchTerm = Nothing,
+ stateSearchMatches = Vec.empty,
+ stateSearchIndex = 0
}
toggleSetItem :: (Ord a) => a -> Set.Set a -> Set.Set a
-toggleSetItem val set =
- if Set.member val set then Set.delete val set else Set.insert val set
+toggleSetItem val set
+ | Set.member val set = Set.delete val set
+ | otherwise = Set.insert val set
toggleFileSelection :: FilePathText -> AppState -> AppState
toggleFileSelection path st = st {stateFileSelections = toggleSetItem path $ stateFileSelections st}
@@ -83,7 +87,7 @@ loadDirToState dir' appState@(AppState {stateCwd, stateListPositionHistory}) = d
let targetFilePosM = targetFilePathM >>= \f -> findIndex ((== f) . filePath) files
let pos = fromMaybe 0 (targetFilePosM <|> cachedPosM <|> prevDirPosM)
let list = L.listMoveTo pos $ L.list FocusMain (Vec.fromList files) 1
- pure $ appState {stateFiles = list, stateCwd = dir}
+ pure $ appState {stateFiles = list, stateCwd = dir, stateSearchIndex = 0, stateSearchMatches = Vec.empty}
False -> pure appState
fileTypeFromStatus :: Posix.FileStatus -> FileType
diff --git a/lib/Daffm/Types.hs b/lib/Daffm/Types.hs
index eb9d9a5..dc6ca36 100644
--- a/lib/Daffm/Types.hs
+++ b/lib/Daffm/Types.hs
@@ -7,6 +7,7 @@ import Control.Applicative ((<|>))
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.Text as Text
+import qualified Data.Vector as Vec
import qualified Graphics.Vty as K
import qualified Graphics.Vty as V
import System.Posix.Types (FileMode, FileOffset)
@@ -44,7 +45,10 @@ data AppState = AppState
stateKeyMap :: Keymap,
stateKeySequence :: KeySequence,
stateListPositionHistory :: Map.Map Text.Text Int,
- stateOpenerScript :: Maybe Text.Text
+ stateOpenerScript :: Maybe Text.Text,
+ stateSearchTerm :: Maybe Text.Text,
+ stateSearchMatches :: Vec.Vector Int,
+ stateSearchIndex :: Int
}
deriving (Show)
@@ -69,6 +73,8 @@ data Command
| CmdClearSelection
| CmdGoBack
| CmdChain [Command]
+ | CmdSearch Text.Text
+ | CmdSearchNext Int
| CmdNoop
deriving (Show, Eq)
@@ -109,6 +115,9 @@ defaultKeymaps =
[ ([K.KChar 'q'], CmdQuit),
([K.KChar 'r', K.KChar 'r'], CmdReload),
([K.KChar '!'], CmdSetCmdline "!"),
+ ([K.KChar '/'], CmdSetCmdline "search "),
+ ([K.KChar 'n'], CmdSearchNext 1),
+ ([K.KChar 'N'], CmdSearchNext (-1)),
([K.KChar ':'], CmdEnterCmdline),
([K.KChar 'l'], CmdOpenSelection),
([K.KChar 'h'], CmdGoBack),
diff --git a/lib/Daffm/View.hs b/lib/Daffm/View.hs
index 387fffc..081cd38 100644
--- a/lib/Daffm/View.hs
+++ b/lib/Daffm/View.hs
@@ -4,7 +4,7 @@ import Brick.Types (Widget)
import Brick.Widgets.Core (Padding (Max, Pad), emptyWidget, hBox, hLimit, padLeft, padRight, str, txt, vBox, vLimit, withAttr, (<+>))
import Brick.Widgets.Edit (renderEditor)
import qualified Brick.Widgets.List as L
-import Daffm.Attrs (directoryAttr, directorySelectedAttr, fileAttr, fileSelectedAttr)
+import Daffm.Attrs (directoryAttr, directorySelectedAttr, fileAttr, fileSelectedAttr, searchMarchAttr)
import Daffm.Keymap (showKeySequence)
import Daffm.Types (AppState (..), FileInfo (..), FileType (..), FocusTarget (..))
import Data.Int (Int64)
@@ -21,7 +21,7 @@ appView appState@(AppState {stateFiles}) = [ui]
ui = vBox [vLimit 1 header, box, vLimit 1 cmdline]
header = headerView appState
cmdline = cmdlineView appState
- box = L.renderList (fileItemView appState) True stateFiles
+ box = L.renderListWithIndex (fileItemView appState) True stateFiles
hFixed :: Int -> Widget n -> Widget n
hFixed w = hLimit w . padRight Max
@@ -29,20 +29,24 @@ hFixed w = hLimit w . padRight Max
headerView :: AppState -> Widget n
headerView (AppState {stateCwd}) = txt stateCwd
-fileItemView :: AppState -> Bool -> FileInfo -> Widget FocusTarget
-fileItemView appState sel fileInfo@(FileInfo {filePath, fileSize, fileType, fileMode}) =
+fileItemView :: AppState -> Int -> Bool -> FileInfo -> Widget FocusTarget
+fileItemView appState index sel fileInfo@(FileInfo {filePath, fileSize, fileType, fileMode}) =
hBox
[ hFixed 2 fileSelectionView,
hFixed 10 $ fileModeView fileMode,
hFixed 6 $ fileTypeView fileType,
hFixed 7 $ fileSizeView fileSize,
- fileNameView sel fileInfo
+ fileNameView sel fileInfo,
+ searchMatchIndicatorView
]
where
fileSizeView = txt . prettyFileSize . fromIntegral
fileTypeView = txt . showFileType
fileModeView = txt . showFileMode
fileSelectionView = txt $ if Set.member filePath $ stateFileSelections appState then ">" else " "
+ searchMatchIndicatorView
+ | index `Vec.elem` stateSearchMatches appState = padLeft (Pad 1) $ withAttr searchMarchAttr $ txt "*"
+ | otherwise = emptyWidget
showFileType :: FileType -> Text.Text
showFileType Directory = "dir"