aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2025-10-03 17:30:44 +0530
committerAkshay Nair <phenax5@gmail.com>2025-10-03 17:30:44 +0530
commit4d669ba5d5858e47b8d5723aae89b75481a2df2f (patch)
tree8a939aa17cf850a74b51637a8a7e1a1846e3a02a
parenta3d5295ee745ab851c28d0a53147cbad65a7805f (diff)
downloaddaffm-4d669ba5d5858e47b8d5723aae89b75481a2df2f.tar.gz
daffm-4d669ba5d5858e47b8d5723aae89b75481a2df2f.zip
Add multifile selection/marking
-rw-r--r--lib/Daffm/Action/Core.hs52
-rw-r--r--lib/Daffm/Event.hs7
-rw-r--r--lib/Daffm/State.hs9
-rw-r--r--lib/Daffm/Types.hs2
-rw-r--r--lib/Daffm/View.hs11
-rw-r--r--notes.org2
6 files changed, 62 insertions, 21 deletions
diff --git a/lib/Daffm/Action/Core.hs b/lib/Daffm/Action/Core.hs
index 48792c4..9fed688 100644
--- a/lib/Daffm/Action/Core.hs
+++ b/lib/Daffm/Action/Core.hs
@@ -5,10 +5,11 @@ module Daffm.Action.Core where
import Brick (suspendAndResume')
import qualified Brick.Widgets.List as L
-import Control.Monad.State (MonadIO (liftIO), MonadState, get, gets, put)
-import Daffm.State (loadDirInAppState)
+import Control.Monad.State (MonadIO (liftIO), MonadState, get, gets, modify, put)
+import Daffm.State (loadDirInAppState, toggleFileSelection)
import Daffm.Types (AppEvent, AppState (..), FileInfo (..), FileType (..))
-import Data.Vector ((!?))
+import qualified Data.Set as Set
+import System.Directory (getHomeDirectory)
import System.FilePath (takeDirectory)
import System.Process (callProcess)
@@ -25,21 +26,44 @@ goBackToParentDir = do
dir <- gets stateParentDir
modifyM (liftIO . loadDirInAppState dir (takeDirectory dir))
+goHome :: AppEvent ()
+goHome = do
+ dir <- liftIO getHomeDirectory
+ modifyM (liftIO . loadDirInAppState dir (takeDirectory dir))
+
openSelectedFile :: AppEvent ()
openSelectedFile = do
- appState <- get
- let indexM = L.listSelected $ stateFiles appState
- let files = L.listElements $ stateFiles appState
- case indexM >>= (files !?) of
- Just file -> openFile appState file
+ fileM <- currentFile
+ case fileM of
+ Just file -> openFile file
Nothing -> pure ()
- pure ()
-openFile :: AppState -> FileInfo -> AppEvent ()
-openFile appState (FileInfo {filePath, fileType = Directory}) = do
- modifyM (liftIO . loadDirInAppState filePath (stateCwd appState))
-openFile _appState (FileInfo {filePath, fileType}) = do
+openFile :: FileInfo -> AppEvent ()
+openFile (FileInfo {filePath, fileType = Directory}) = do
+ (AppState {stateCwd}) <- get
+ modifyM (liftIO . loadDirInAppState filePath stateCwd)
+openFile (FileInfo {filePath, fileType}) = do
suspendAndResume' $ do
putStrLn $ "Opening " <> show fileType <> ": " <> filePath
callProcess "nvim" [filePath]
- pure ()
+
+currentFile :: AppEvent (Maybe FileInfo)
+currentFile = do
+ gets (fmap snd . L.listSelectedElement . stateFiles)
+
+toggleCurrentFileSelection :: AppEvent ()
+toggleCurrentFileSelection = do
+ fileM <- currentFile
+ case fileM of
+ Just file -> modify $ toggleFileSelection (filePath file)
+ Nothing -> pure ()
+ moveCurrent 1
+
+clearFileSelections :: AppEvent ()
+clearFileSelections =
+ modify $ \s -> s {stateFileSelections = Set.empty}
+
+moveCurrent :: Int -> AppEvent ()
+moveCurrent count = do
+ files <- gets stateFiles
+ modify $ \s -> s {stateFiles = L.listMoveBy count files}
diff --git a/lib/Daffm/Event.hs b/lib/Daffm/Event.hs
index 8eaf960..247719b 100644
--- a/lib/Daffm/Event.hs
+++ b/lib/Daffm/Event.hs
@@ -5,8 +5,8 @@ import qualified Brick.Types as T
import qualified Brick.Widgets.Edit as Editor
import qualified Brick.Widgets.List as L
import Control.Monad.State (gets, modify)
-import Daffm.Action.Cmdline (enterCmdline, evaluateCommand, leaveCmdline, runCmdline, setCmdlineText)
-import Daffm.Action.Core (goBackToParentDir, openSelectedFile, reloadDir)
+import Daffm.Action.Cmdline
+import Daffm.Action.Core
import Daffm.State (cacheDirPosition)
import Daffm.Types (AppEvent, AppState (..), FocusTarget (..))
import qualified Graphics.Vty as V
@@ -19,10 +19,13 @@ appEvent brickevent@(T.VtyEvent event) = do
(FocusMain, V.EvKey (V.KChar 'h') []) -> goBackToParentDir
(FocusMain, V.EvKey V.KEnter []) -> openSelectedFile
(FocusMain, V.EvKey V.KBS []) -> goBackToParentDir
+ (FocusMain, V.EvKey (V.KChar '~') []) -> goHome
(FocusMain, V.EvKey (V.KChar ':') []) -> enterCmdline
(FocusMain, V.EvKey (V.KChar '!') []) -> setCmdlineText "!" >> enterCmdline
(FocusMain, V.EvKey (V.KChar 'q') []) -> M.halt
(FocusMain, V.EvKey (V.KChar 'r') [V.MCtrl]) -> reloadDir
+ (FocusMain, V.EvKey (V.KChar 'v') []) -> toggleCurrentFileSelection
+ (FocusMain, V.EvKey (V.KChar 'C') []) -> clearFileSelections
-- Just for testing
(FocusMain, V.EvKey (V.KChar 'p') [V.MCtrl]) -> evaluateCommand "!!chafa -f kitty %"
(FocusCmdline, V.EvKey V.KEsc []) -> leaveCmdline
diff --git a/lib/Daffm/State.hs b/lib/Daffm/State.hs
index 3990ac0..ead5209 100644
--- a/lib/Daffm/State.hs
+++ b/lib/Daffm/State.hs
@@ -9,6 +9,7 @@ import Data.Char (toLower)
import Data.List (findIndex, sortBy)
import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
import qualified Data.Text.Zipper.Generic as Zipper
import qualified Data.Vector as Vec
import System.Directory (listDirectory, makeAbsolute, setCurrentDirectory)
@@ -24,10 +25,18 @@ mkEmptyAppState =
stateCmdlineEditor = mkEditor "",
stateFocusTarget = FocusMain,
stateListPositionCache = Map.empty,
+ stateFileSelections = Set.empty,
stateCwd = "",
stateParentDir = ""
}
+toggleSetItem :: (Ord a) => a -> Set.Set a -> Set.Set a
+toggleSetItem val set =
+ if val `Set.member` set then Set.delete val set else Set.insert val set
+
+toggleFileSelection :: FilePath -> AppState -> AppState
+toggleFileSelection path st = st {stateFileSelections = toggleSetItem path $ stateFileSelections st}
+
loadDirInAppState :: FilePath -> FilePath -> AppState -> IO AppState
loadDirInAppState dir parentDir appState@(AppState {stateCwd, stateListPositionCache}) = do
setCurrentDirectory dir
diff --git a/lib/Daffm/Types.hs b/lib/Daffm/Types.hs
index 14444f4..882a9de 100644
--- a/lib/Daffm/Types.hs
+++ b/lib/Daffm/Types.hs
@@ -4,6 +4,7 @@ import Brick (EventM)
import qualified Brick.Widgets.Edit as Editor
import qualified Brick.Widgets.List as L
import qualified Data.Map as Map
+import qualified Data.Set as Set
import System.Posix.Types (FileMode, FileOffset)
data FileType
@@ -31,6 +32,7 @@ data FocusTarget = FocusCmdline | FocusMain deriving (Show, Eq, Ord)
data AppState = AppState
{ stateFiles :: L.List FocusTarget FileInfo,
stateCmdlineEditor :: CmdlineEditor,
+ stateFileSelections :: Set.Set FilePath,
stateFocusTarget :: FocusTarget,
stateCwd :: FilePath,
stateListPositionCache :: Map.Map String Int,
diff --git a/lib/Daffm/View.hs b/lib/Daffm/View.hs
index 906fe26..9e0294d 100644
--- a/lib/Daffm/View.hs
+++ b/lib/Daffm/View.hs
@@ -7,6 +7,7 @@ import qualified Brick.Widgets.List as L
import Daffm.Attrs (directoryAttr, directorySelectedAttr, fileAttr, fileSelectedAttr)
import Daffm.Types (AppState (..), FileInfo (..), FileType (..), FocusTarget (..))
import Data.Int (Int64)
+import qualified Data.Set as Set
import qualified Data.Vector as Vec
import System.Posix.Types (FileMode)
import qualified System.PosixCompat as Posix
@@ -18,7 +19,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 True stateFiles
+ box = L.renderList (fileItemView appState) True stateFiles
hFixed :: Int -> Widget n -> Widget n
hFixed w = hLimit w . padRight Max
@@ -26,10 +27,11 @@ hFixed w = hLimit w . padRight Max
headerView :: AppState -> Widget n
headerView (AppState {stateCwd}) = str stateCwd
-fileItemView :: Bool -> FileInfo -> Widget FocusTarget
-fileItemView sel fileInfo@(FileInfo {fileSize, fileType, fileMode}) =
+fileItemView :: AppState -> Bool -> FileInfo -> Widget FocusTarget
+fileItemView appState sel fileInfo@(FileInfo {filePath, fileSize, fileType, fileMode}) =
hBox
- [ hFixed 10 $ fileModeView fileMode,
+ [ hFixed 2 fileSelectionView,
+ hFixed 10 $ fileModeView fileMode,
hFixed 6 $ fileTypeView fileType,
hFixed 7 $ fileSizeView fileSize,
fileNameView sel fileInfo
@@ -38,6 +40,7 @@ fileItemView sel fileInfo@(FileInfo {fileSize, fileType, fileMode}) =
fileSizeView = str . prettyFileSize . fromIntegral
fileTypeView = str . showFileType
fileModeView = str . showFileMode
+ fileSelectionView = str $ if Set.member filePath $ stateFileSelections appState then ">" else " "
showFileType :: FileType -> String
showFileType Directory = "dir"
diff --git a/notes.org b/notes.org
index 4ea0ace..62eee77 100644
--- a/notes.org
+++ b/notes.org
@@ -6,13 +6,13 @@
- [X] Command substitution %
- [X] Command substitution %d:cwd
- [X] Show file permissions
+- [ ] select multiple files
- [ ] Commands
- [ ] Error handling
- [ ] Cli arg parsing
** Later
- [ ] handle on open (for external integrations)
- [ ] Cmdline history
-- [ ] select multiple files
- [ ] Command substitution %s:selections
- [ ] cd into dir symlinks
- [ ] copy/paste across instances