packages feed

brick-filetree (empty) → 0.1.0.0

raw patch · 11 files changed

+469/−0 lines, 11 filesdep +basedep +brickdep +brick-filetreesetup-changed

Dependencies added: base, brick, brick-filetree, comonad, containers, directory, directory-tree, filepath, free, vector, vty

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for brick-filetree++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Chris Penner (c) 2019++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Chris Penner nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# brick-filetree
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ brick-filetree.cabal view
@@ -0,0 +1,72 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 3c8791fcfeb7862a7b511beda8cdf4e80ecdfeb8abcfb818c4f44436894c0b96++name:           brick-filetree+version:        0.1.0.0+description:    Please see the README on GitHub at <https://github.com/ChrisPenner/brick-filetree#readme>+homepage:       https://github.com/ChrisPenner/brick-filetree#readme+bug-reports:    https://github.com/ChrisPenner/brick-filetree/issues+author:         Chris Penner+maintainer:     christopher.penner@gmail.com+copyright:      Chris Penner+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://github.com/ChrisPenner/brick-filetree++library+  exposed-modules:+      Brick.Widgets.FileTree+      Brick.Widgets.FileTree.Internal.Actions+      Brick.Widgets.FileTree.Internal.Render+      Brick.Widgets.FileTree.Internal.Types+      Lib+  other-modules:+      Paths_brick_filetree+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , brick+    , comonad+    , containers+    , directory+    , directory-tree+    , filepath+    , free+    , vector+    , vty+  default-language: Haskell2010++test-suite brick-filetree-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_brick_filetree+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , brick+    , brick-filetree+    , comonad+    , containers+    , directory+    , directory-tree+    , filepath+    , free+    , vector+    , vty+  default-language: Haskell2010
+ src/Brick/Widgets/FileTree.hs view
@@ -0,0 +1,29 @@+module Brick.Widgets.FileTree+  ( FileTree+  , newFileTree+  , toggleSelection+  , moveUp+  , moveDown+  , pageDown+  , pageUp+  , moveToTop+  , moveToBottom+  , ascendDir+  , descendDir+  , getCurrentFilePath+  , getCurrentDir+  , getFlagged+  , toggleSelectionVisible+  , renderFileTree+  , renderSelection+  , selectedItemAttr+  , titleAttr+  , dirAttr+  , fileAttr+  , errorAttr+  )+where++import           Brick.Widgets.FileTree.Internal.Types+import           Brick.Widgets.FileTree.Internal.Actions+import           Brick.Widgets.FileTree.Internal.Render
+ src/Brick/Widgets/FileTree/Internal/Actions.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+module Brick.Widgets.FileTree.Internal.Actions+  ( moveUp+  , moveDown+  , pageUp+  , pageDown+  , moveToTop+  , moveToBottom+  , ascendDir+  , descendDir+  , getCurrentFilePath+  , getCurrentDir+  , toggleSelection+  , getFlagged+  , toggleSelectionVisible+  ) where++import qualified Graphics.Vty.Input as V+import Brick.Main+import Brick.Types+import Brick.Widgets.List+import Data.Foldable+import Control.Comonad.Cofree as CF+import qualified Data.Sequence as Seq+import qualified Data.Set as S+import Brick.Widgets.FileTree.Internal.Types+import Brick.Widgets.FileTree.Internal.Render+import Control.Monad.IO.Class+import Control.Comonad+import Data.Maybe++overCurrentList+  :: (List String SubTree -> EventM String (List String SubTree))+  -> FileTree+  -> EventM String FileTree+overCurrentList f fz@(FZ { context = x :< lst }) = do+  newLst <- f lst+  return fz { context = x :< newLst }++pressKey :: V.Key -> (FileTree -> EventM String FileTree)+pressKey k = overCurrentList (handleListEvent (V.EvKey k []))++moveDown :: FileTree -> EventM String FileTree+moveDown = pressKey V.KDown++moveUp :: FileTree -> EventM String FileTree+moveUp = pressKey V.KUp++pageDown :: FileTree -> EventM String FileTree+pageDown = pressKey V.KPageDown++pageUp :: FileTree -> EventM String FileTree+pageUp = pressKey V.KPageDown++moveToTop :: FileTree -> EventM String FileTree+moveToTop = pressKey V.KHome++moveToBottom :: FileTree -> EventM String FileTree+moveToBottom = pressKey V.KEnd++ascendDir :: FileTree -> EventM String FileTree+ascendDir (FZ { parents = Seq.Empty, context = tree@((extract -> path -> p)), selection, ..})+  = do+    fz <- liftIO $ buildParent p tree+    return $ fz { selection = selection }+ascendDir (FZ { parents = (ps Seq.:|> (f :< pList)), context, ..}) = do+  invalidateCacheEntry (cacheKey f)+  return+    $ FZ {parents = ps, context = (f :< listModify (const context) pList), ..}++descendDir :: FileTree -> EventM String FileTree+descendDir fz@(FZ { parents, context = (f :< children), ..}) = do+  invalidateCacheEntry (cacheKey f)+  return $ case listSelectedElement children of+    Nothing -> fz+    Just (_, nextChildren@(FC { kind = Dir } :< _)) -> FZ+      { parents = (parents Seq.|> (f :< children))+      , context = nextChildren+      , ..+      }+    Just _ -> fz++getCurrentFilePath :: FileTree -> Maybe FilePath+getCurrentFilePath (FZ { context = unwrap -> children }) =+  case listSelectedElement children of+    Nothing                            -> Nothing+    Just (_, FC { kind = Error } :< _) -> Nothing+    Just (_, fc :< _                 ) -> Just (path fc)++getCurrentDir :: FileTree -> FilePath+getCurrentDir (FZ { context = extract -> path -> p }) = p++toggleSelection :: FileTree -> EventM String FileTree+toggleSelection fz@(FZ { context = (fc :< lst), selection, ..}) = do+  invalidateCacheEntry selectionCacheKey+  return . fromMaybe fz $ do+    ((selectedContext@FC { selected = isSelected, path }) :< rest) <- snd+      <$> listSelectedElement lst+    let newSelection = if isSelected+          then S.delete path selection+          else S.insert path selection+    let newList = listModify+          (const (selectedContext { selected = not isSelected } :< rest))+          lst+    return $ FZ {context = (fc :< newList), selection = newSelection, ..}++getFlagged :: FileTree -> [FilePath]+getFlagged = toList . selection++toggleSelectionVisible :: FileTree -> FileTree+toggleSelectionVisible fz@(FZ { config }) =+  fz { config = config { showSelection = not $ showSelection config } }
+ src/Brick/Widgets/FileTree/Internal/Render.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE OverloadedStrings #-}+module Brick.Widgets.FileTree.Internal.Render+  ( selectedItemAttr+  , titleAttr+  , dirAttr+  , fileAttr+  , errorAttr+  , cacheKey+  , renderHeader+  , renderFileTree+  , selectionCacheKey+  , renderSelection+  )+where++import           Brick.Widgets.FileTree.Internal.Types++import           Data.Foldable+import           Brick.Widgets.Core+import           Brick.Widgets.Border+import           Brick.Types+import           Brick.AttrMap+import           Brick.Widgets.List+import           Control.Comonad.Cofree        as CF+import           Control.Comonad+import           Data.Bool+import qualified Data.Sequence                 as S++selectedItemAttr, titleAttr, dirAttr, fileAttr, errorAttr :: AttrName+selectedItemAttr = "selectedItemAttr"+titleAttr = "titleAttr"+dirAttr = "dirAttr"+fileAttr = "fileAttr"+errorAttr = "errorAttr"++cacheKey :: FileContext -> String+cacheKey = path++renderHeader :: SubTree -> Widget String+renderHeader ((path -> p) :< _) = withAttr titleAttr (str p) <=> hBorder++renderFileTree :: FileTree -> Widget String+renderFileTree fz@(FZ { parents, context, config }) =+  (   renderHeader context+  <=> (renderParents parents <+> renderNode context <+> previewW)+  <=> selectionW+  )+ where+  selectionW = if showSelection config then renderSelection fz else emptyWidget+  previewW   = if previewDir config then renderPreview context else emptyWidget++renderPreview :: SubTree -> Widget String+renderPreview (_ :< lst) = do+  case listSelectedElement lst of+    Just (_, node@(FC { kind = Dir } :< _)) -> vBorder <+> renderNode node+    _ -> emptyWidget++selectionCacheKey :: String+selectionCacheKey = "delve!selection"++renderSelection :: FileTree -> Widget String+renderSelection (FZ { selection })+  | null selection+  = emptyWidget+  | otherwise+  = let selectionsW =+          cached selectionCacheKey+            . vBox+            . fmap (withAttr selectedItemAttr . str)+            . toList+            $ selection+    in  hBorder <=> withAttr titleAttr (str "Selected") <=> selectionsW++renderParents :: S.Seq SubTree -> Widget String+renderParents S.Empty                    = emptyWidget+renderParents parents@(_ S.:|> (p :< _)) = cached+  (cacheKey p)+  (hBox . toList $ (renderParent <$> S.drop ind parents))+ where+  len = S.length parents+  ind = max 0 (len - 2)++renderNode :: SubTree -> Widget String+renderNode (_ :< ls) = renderList+  (\b -> bool id (forceAttr listSelectedAttr) b . renderFileContext . extract)+  True+  ls++renderParent :: SubTree -> Widget String+renderParent = (<+> vBorder) . hLimit 20 . renderNode++renderFileContext :: FileContext -> Widget String+renderFileContext (FC { kind = File, name, selected }) =+  let (attr', modStr) =+        if selected then (selectedItemAttr, "* ") else (fileAttr, "")+  in  withAttr attr' . str $ modStr <> name+renderFileContext (FC { kind = Error, name, path }) =+  withAttr errorAttr . str $ "! " <> path <> ": " <> name+renderFileContext (FC { kind = Dir, name, selected }) =+  let (attr', modStr) =+        if selected then (selectedItemAttr, "* ") else (dirAttr, "")+  in  withAttr attr' . str $ modStr <> name <> "/"
+ src/Brick/Widgets/FileTree/Internal/Types.hs view
@@ -0,0 +1,103 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE RecordWildCards #-}+module Brick.Widgets.FileTree.Internal.Types+  ( FileKind(..)+  , FileContext(..)+  , Config(..)+  , FileTree(..)+  , SubTree+  , buildParent+  , newFileTree+  , defaultConfig+  )+where++import           Brick.Widgets.List+import qualified Data.Vector                   as V+import           Control.Comonad.Cofree        as CF+import qualified System.Directory.Tree         as FT+import qualified Data.Sequence                 as S+import           System.FilePath.Posix+import           System.Directory+import qualified Data.Set                      as S++data FileKind = Dir | File | Error++data FileContext =+  FC+    { selected :: Bool+    , path :: FilePath+    , name :: String+    , kind :: FileKind+    }++data Config =+  Config+    { showSelection :: Bool+    , previewDir :: Bool+    }++defaultConfig :: Config+defaultConfig = Config {showSelection = True, previewDir = False}++type SubTree = Cofree (GenericList String V.Vector) FileContext++data FileTree = FZ+  { parents :: S.Seq SubTree+  , selection :: S.Set FilePath+  , context :: SubTree+  , config :: Config+  }++buildParent :: FilePath -> SubTree -> IO FileTree+buildParent p child = do+  FZ { context = (c :< ls), ..} <- newFileTree (takeDirectory p)+  let newChildren = fmap (replace p child) ls+  return $ FZ {context = c :< newChildren, ..}+ where+  replace pth fc@((path -> pth') :< _) new | pth == pth' = new+                                           | otherwise   = fc++newFileTree :: FilePath -> IO FileTree+newFileTree currentDir = do+  absRoot        <- makeAbsolute (normalise currentDir)+  (_ FT.:/ tree) <- FT.buildL absRoot+  return $ convert (takeDirectory absRoot) tree++convert :: FilePath -> FT.DirTree FilePath -> FileTree+convert root tree =+  let subTree = go (normalise root) $ tree+  in  FZ+        { parents   = []+        , selection = mempty+        , config    = defaultConfig+        , context   = subTree+        }+ where+  go :: FilePath -> FT.DirTree FilePath -> SubTree+  go root' (FT.Failed { FT.name, FT.err }) =+    FC+        { name     = show err+        , path     = normalise (root' </> name)+        , selected = False+        , kind     = Error+        }+      :< list name mempty 1+  go root' (FT.File { FT.name }) =+    FC+        { name     = name+        , path     = normalise (root' </> name)+        , selected = False+        , kind     = File+        }+      :< list name mempty 1+  go root' (FT.Dir path contents) =+    FC+        { name     = path+        , path     = normalise (root' </> path)+        , kind     = Dir+        , selected = False+        }+      :< list path (V.fromList . fmap (go (root' </> path)) $ contents) 1
+ src/Lib.hs view
@@ -0,0 +1,6 @@+module Lib+    ( someFunc+    ) where++someFunc :: IO ()+someFunc = putStrLn "someFunc"
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"