Skip to content

Commit

Permalink
lenses all over the place and a little bit of refactoring/renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
pasqu4le committed Jun 7, 2018
1 parent b8bb0ab commit a243817
Show file tree
Hide file tree
Showing 10 changed files with 499 additions and 358 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ $ cabal install
```

## Features
Clifm is a [brick](https://github.com/jtdaugherty/brick) application, that in turn builds upon [vty](https://github.com/jtdaugherty/vty). As such it supports a large number of terminals, but not on Windows, handles windows resizing and more.
Clifm is a [brick](https://github.com/jtdaugherty/brick) application, that in turn builds upon [vty](https://github.com/jtdaugherty/vty). As such it supports a large number of terminals (not on Windows), handles resizing and more.

If your terminal supports a mouse you can use it to change Tab/Pane, click a button on the bottom, change your selection or open it (double-click), but only using the keyboard you can perform every possible action. This is the list of all the keybindings:
If your terminal supports a mouse you can use it to change Tab/Pane, click a button on the bottom, change your selection or open it (double-click), but using the keyboard you can perform every possible action. This is the list of all the keybindings:

#### Bottom menu
- L: open Se**l**ection menu
Expand Down Expand Up @@ -113,11 +113,13 @@ Complete explanation from [Brick.Themes](https://hackage.haskell.org/package/bri
> Attribute names with multiple components (e.g. attr1 <> attr2) can be referenced in customization files by separating the names with a dot. For example, the attribute name "list" <> "selected" can be referenced by using the string "list.selected".
#### Threads for directory size computation
Directory size is calculated by visiting a directory tree to sum it's files sizes (using [conduit](http://hackage.haskell.org/package/conduit)) and it may take a while. For this reason directories size will be calculated in different threads.
Directory size is calculated by visiting a directory tree to sum it's files sizes (using [conduit](http://hackage.haskell.org/package/conduit)) and it may take a while. For this reason these will be calculated in different threads.

You can limit how many of these threads to have by using `--thread-num` or `-n`, for example: `clifm -n 8`.
You can limit how many of these threads to have at the same time by using `--thread-num` or `-n`, for example: `clifm -n 8`.

You are likely to have the best results with as many threads as your processor's cores. The default limit is set to 4.

## TODOs
- wide use of lenses
Right now nothing is planned.

Suggestions and requests are always welcome, if you have any or you find a bug please [open a new issue](https://github.com/pasqu4le/clifm/issues/new).
11 changes: 6 additions & 5 deletions clifm.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name: clifm
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.4.2.0
version: 0.5.0.0

-- A short (one-line) description of the package.
synopsis: Command Line Interface File Manager
Expand Down Expand Up @@ -73,16 +73,17 @@ executable clifm
build-depends: base >=4.11 && <4.12,
directory >=1.3 && <1.4,
optparse-applicative >=0.14 && <0.15,
brick >=0.36 && <0.37,
brick >=0.36 && <0.38,
filepath >=1.4 && <1.5,
vty >= 5.17 && <6,
vty >= 5.21 && <6,
vector >= 0.12 && <0.13,
time >=1.8 && <1.10,
time >=1.8 && <1.9,
process >=1.6 && <1.7,
pointedlist >= 0.6 && <0.7,
byteunits >= 0.4 && <0.5,
conduit >= 1.3 && <1.4,
containers >= 0.5 && < 0.6
containers >= 0.5 && < 0.6,
lens >= 4.16 && < 4.17

-- Directories containing source files.
hs-source-dirs: src
Expand Down
3 changes: 1 addition & 2 deletions src/Commons.hs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
module Commons where

import Data.Monoid ((<>))
import Brick.Widgets.Core (withDefAttr, str)
import Brick.Types (Widget)
import Brick.Themes (Theme, newTheme)
import Brick.AttrMap (AttrName, AttrMap, attrName, attrMap)
import Graphics.Vty (Key(..), defAttr, withStyle, underline, black, yellow, white, blue, red)
import Graphics.Vty (Key(..), withStyle, underline, black, yellow, white, blue, red)
import Brick.Util (on, fg, bg)
import Brick.BChan (BChan)
import Brick.Widgets.Edit (editFocusedAttr)
Expand Down
2 changes: 1 addition & 1 deletion src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ runUI options = do

app :: AttrMap -> App Mngr.State (ThreadEvent Tab.Tab) Name
app atrm = App {
appDraw = Mngr.drawUi,
appDraw = Mngr.render,
appStartEvent = return,
appHandleEvent = Mngr.handleEvent,
appAttrMap = const atrm,
Expand Down
117 changes: 77 additions & 40 deletions src/Widgets/Entry.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module Widgets.Entry where
import Commons

import Control.Lens
import Data.Maybe (fromMaybe)
import Data.Time.Clock (UTCTime)
import Data.Time.Format (formatTime, defaultTimeLocale)
import Control.Exception (try, SomeException)
Expand All @@ -12,18 +14,18 @@ import Brick.Types (Widget)
import Brick.Widgets.Core (vLimit, hBox, str, fill)
import Data.ByteUnits (ByteValue(..), ByteUnit(Bytes), getShortHand, getAppropriateUnits)

data Entry = Dir {name :: String, path :: FilePath, info :: Info} |
File {name :: String, path :: FilePath, info :: Info} deriving (Ord)
data Info = Info {size :: Size, perms :: Maybe Permissions, times :: Maybe (UTCTime, UTCTime)} deriving (Show, Eq, Ord)
data Entry = Dir {_name :: String, _path :: FilePath, _info :: Info} |
File {_name :: String, _path :: FilePath, _info :: Info} deriving (Ord)
data Info = Info {_size :: Size, _perms :: Maybe Permissions, _times :: Maybe (UTCTime, UTCTime)} deriving (Show, Eq, Ord)
data Size = Waiting | Calculating | Known Integer | Unknown | Avoided deriving (Show, Eq)

instance Show Entry where
show Dir {name = n} = "+ " ++ n
show File {name = n} = "- " ++ n
show Dir {_name = n} = "+ " ++ n
show File {_name = n} = "- " ++ n

instance Eq Entry where
Dir {path = p1} == Dir {path = p2} = p1 == p2
File {path = p1} == File {path = p2} = p1 == p2
Dir {_path = p1} == Dir {_path = p2} = p1 == p2
File {_path = p1} == File {_path = p2} = p1 == p2
_ == _ = False

instance Ord Size where
Expand All @@ -32,7 +34,41 @@ instance Ord Size where
compare _ (Known _) = LT
compare _ _ = EQ

-- creation functions
-- lenses
name :: Lens' Entry String
name = lens _name (\entry n -> entry {_name = n})

path :: Lens' Entry FilePath
path = lens _path (\entry p -> entry {_path = p})

info :: Lens' Entry Info
info = lens _info (\entry i -> entry {_info = i})

size :: Lens' Entry Size
size = info.infoSize

perms :: Lens' Entry (Maybe Permissions)
perms = info.infoPerms

times :: Lens' Entry (Maybe (UTCTime, UTCTime))
times = info.infoTimes

accessTime :: Traversal' Entry UTCTime
accessTime = times._Just._1

modifTime :: Traversal' Entry UTCTime
modifTime = times._Just._2

infoSize :: Lens' Info Size
infoSize = lens _size (\entry s -> entry {_size = s})

infoPerms :: Lens' Info (Maybe Permissions)
infoPerms = lens _perms (\entry p -> entry {_perms = p})

infoTimes :: Lens' Info (Maybe (UTCTime, UTCTime))
infoTimes = lens _times (\entry t -> entry {_times = t})

-- creation
make :: FilePath -> IO Entry
make filePath = do
isFile <- doesFileExist filePath
Expand All @@ -43,29 +79,29 @@ makeInfo :: FilePath -> Bool -> IO Info
makeInfo filePath isFile = do
enSize <- getEntrySize filePath isFile
enPerms <- toMaybe <$> try (getPermissions filePath)
enTimes <- toMaybe <$> try (getEntryTimes filePath)
enTimes <- toMaybe <$> try (getTimes filePath)
return $ Info enSize enPerms enTimes

getEntryTimes :: FilePath -> IO (UTCTime, UTCTime)
getEntryTimes filePath = do
accessTime <- getAccessTime filePath
modifTime <- getModificationTime filePath
return (accessTime, modifTime)
getTimes :: FilePath -> IO (UTCTime, UTCTime)
getTimes filePath = do
accTime <- getAccessTime filePath
modTime <- getModificationTime filePath
return (accTime, modTime)

makeBackDir :: FilePath -> IO Entry
makeBackDir filePath = do
enPerms <- toMaybe <$> try (getPermissions filePath)
enTimes <- toMaybe <$> try (getEntryTimes filePath)
enTimes <- toMaybe <$> try (getTimes filePath)
return $ Dir ".." filePath (Info Avoided enPerms enTimes)

-- rendering functions
-- rendering
render :: Bool -> Entry -> Widget Name
render _ en = let enInfo = info en in vLimit 1 $ hBox [
str $ show en,
render _ entry = vLimit 1 $ hBox [
str $ show entry,
fill ' ',
str $ shortSize enInfo,
renderPerms $ perms enInfo,
renderTime (times enInfo) False
views size (str . shortSize) entry,
views perms renderPerms entry,
renderModTime $ preview modifTime entry
]

renderPerms :: Maybe Permissions -> Widget Name
Expand All @@ -78,12 +114,23 @@ renderPerms (Just p) = str [
if searchable p then 's' else '-'
]

renderTime :: Maybe (UTCTime, UTCTime) -> Bool -> Widget Name
renderTime Nothing _ = str " -----------------"
renderTime (Just tms) sel = str . format $ (if sel then fst else snd) tms
where format = formatTime defaultTimeLocale " %R %b %e %Y"
renderModTime :: Maybe UTCTime -> Widget Name
renderModTime modTime = str $ case modTime of
Just mtm -> formatTime defaultTimeLocale " %R %b %e %Y" mtm
_ -> " -----------------"

-- utility
isDir :: Entry -> Bool
isDir Dir {} = True
isDir _ = False

isFile :: Entry -> Bool
isFile File {} = True
isFile _ = False

isWaiting :: Entry -> Bool
isWaiting entry = entry ^. size == Waiting

-- utility functions
toMaybe :: Either SomeException b -> Maybe b
toMaybe = either (const Nothing) Just

Expand All @@ -94,27 +141,17 @@ isReadable :: Entry -> Bool
isReadable = hasPermission readable

hasPermission :: (Permissions -> Bool) -> Entry -> Bool
hasPermission prop en = case perms $ info en of
Just enPerms -> prop enPerms
_ -> False
hasPermission p = maybe False p . view perms

shortSize :: Info -> String
shortSize enInfo = case size enInfo of
shortSize :: Size -> String
shortSize sz = case sz of
Known enSize -> getShortHand . getAppropriateUnits $ ByteValue (fromInteger enSize) Bytes
Unknown -> "???"
Calculating -> "..."
Waiting -> "..."
_ -> ""

notifySize :: FilePath -> Size -> Entry -> Entry
notifySize p s entry
| p == path entry = entry {info = updateSize s $ info entry}
| otherwise = entry

updateSize :: Size -> Info -> Info
updateSize s info = info {size = s}

-- directory size function
-- directory size
getEntrySize :: FilePath -> Bool -> IO Size
getEntrySize filePath isFile
| isFile = toSizeResult <$> try (getFileSize filePath)
Expand Down
Loading

0 comments on commit a243817

Please sign in to comment.