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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
module Daffm where
import qualified Brick.Main as M
import qualified Brick.Types as T
import qualified Brick.Widgets.List as L
import Control.Monad (forM)
import Control.Monad.State (MonadIO (liftIO), get, gets, modify)
import Daffm.Attrs (appAttrMap)
import Daffm.Types (AppState (..), FileInfo (..), FileType (..))
import Daffm.View (appView)
import Data.Char (toLower)
import Data.List (sortBy)
import Data.Maybe (fromMaybe)
import Data.Vector ((!?))
import qualified Data.Vector as Vec
import qualified Graphics.Vty as V
import System.Directory (listDirectory, makeAbsolute, setCurrentDirectory)
import System.FilePath (takeDirectory)
import qualified System.PosixCompat as Posix
openSelectedFile :: T.EventM () AppState ()
openSelectedFile = do
state <- get
let indexM = L.listSelected . stateFiles $ state
let files = L.listElements . stateFiles $ state
let fileM = indexM >>= (files !?)
case fileM of
Just (FileInfo {filePath, fileType = Directory}) -> do
let parentDir = stateCwd state
nextState <- liftIO $ loadDirInAppState filePath parentDir state
modify (const nextState)
pure ()
Just (FileInfo {filePath, fileType}) -> do
liftIO . putStrLn $ "Opening " <> show fileType <> ": " <> filePath
pure ()
Nothing -> pure ()
pure ()
goBackToParentDir :: T.EventM () AppState ()
goBackToParentDir = do
state <- get
let dir = stateParentDir state
let parentDir = takeDirectory dir
nextState <- liftIO $ loadDirInAppState dir parentDir state
modify (const nextState)
appEvent :: T.BrickEvent () e -> T.EventM () AppState ()
appEvent (T.VtyEvent e) =
case e of
V.EvKey V.KEsc [] -> M.halt
V.EvKey (V.KChar 'q') [] -> M.halt
V.EvKey (V.KChar 'l') [] -> openSelectedFile
V.EvKey (V.KChar 'h') [] -> goBackToParentDir
V.EvKey V.KEnter [] -> openSelectedFile
V.EvKey V.KBS [] -> goBackToParentDir
ev -> do
files <- gets stateFiles
newFiles <- T.nestEventM' files (L.handleListEventVi L.handleListEvent ev)
modify (\appState -> appState {stateFiles = newFiles})
appEvent _ = pure ()
app :: M.App AppState e ()
app =
M.App
{ M.appDraw = appView,
M.appChooseCursor = M.showFirstCursor,
M.appHandleEvent = appEvent,
M.appStartEvent = pure (),
M.appAttrMap = const appAttrMap
}
fileTypeFromStatus :: Posix.FileStatus -> Maybe FileType
fileTypeFromStatus s =
if
| Posix.isBlockDevice s -> Just BlockDevice
| Posix.isCharacterDevice s -> Just CharacterDevice
| Posix.isNamedPipe s -> Just NamedPipe
| Posix.isRegularFile s -> Just RegularFile
| Posix.isDirectory s -> Just Directory
| Posix.isSocket s -> Just UnixSocket
| Posix.isSymbolicLink s -> Just SymbolicLink
| otherwise -> Nothing
getFileInfo :: FilePath -> IO FileInfo
getFileInfo name = do
path <- makeAbsolute name
stat <- Posix.getSymbolicLinkStatus path
pure $
FileInfo
{ filePath = path,
fileName = name,
fileSize = Posix.fileSize stat,
fileType = fromMaybe RegularFile $ fileTypeFromStatus stat
}
fileSorter :: FileInfo -> FileInfo -> Ordering
fileSorter (FileInfo {fileType = Directory, fileName = fa}) (FileInfo {fileType = Directory, fileName = fb}) =
compare (toLower <$> fa) (toLower <$> fb)
fileSorter (FileInfo {fileType = Directory}) _ = LT
fileSorter _ (FileInfo {fileType = Directory}) = GT
fileSorter (FileInfo {fileName = fa}) (FileInfo {fileName = fb}) =
compare (toLower <$> fa) (toLower <$> fb)
listFilesInDir :: FilePath -> IO [FileInfo]
listFilesInDir dir = do
files <- listDirectory dir
sortBy fileSorter <$> forM files getFileInfo
loadDirInAppState :: FilePath -> FilePath -> AppState -> IO AppState
loadDirInAppState dir parentDir appState = do
setCurrentDirectory dir
files <- listFilesInDir dir
pure $
appState
{ stateFiles = L.list () (Vec.fromList files) 1,
stateCwd = dir,
stateParentDir = parentDir
}
mkEmptyAppState :: AppState
mkEmptyAppState =
AppState
{ stateFiles = L.list () (Vec.fromList []) 1,
stateCwd = "",
stateParentDir = ""
}
|