mirror of
https://github.com/utdemir/nix-tree.git
synced 2024-08-16 10:10:38 +03:00
parent
c494717450
commit
3bb55ba770
@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
* feat: Ability to yank selected store path to clipboard (shortcut: 'y')
|
||||
|
||||
## 0.1.6 - 2021-03-12
|
||||
|
||||
* feat: Support non standard Nix store locations
|
||||
|
@ -41,6 +41,7 @@ common common-options
|
||||
StorePath
|
||||
App
|
||||
InvertedIndex
|
||||
Clipboard
|
||||
Paths_nix_tree
|
||||
autogen-modules: Paths_nix_tree
|
||||
mixins: base hiding (Prelude)
|
||||
|
46
src/App.hs
46
src/App.hs
@ -5,6 +5,7 @@ import qualified Brick.BChan as B
|
||||
import qualified Brick.Widgets.Border as B
|
||||
import qualified Brick.Widgets.Center as B
|
||||
import qualified Brick.Widgets.List as B
|
||||
import qualified Clipboard
|
||||
import qualified Data.List.NonEmpty as NE
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Sequence as S
|
||||
@ -28,8 +29,10 @@ data Widgets
|
||||
| WidgetWhyDependsViewport
|
||||
deriving (Show, Eq, Ord)
|
||||
|
||||
data Notice = Notice Text Text
|
||||
|
||||
data Modal s
|
||||
= ModalHelp
|
||||
= ModalNotice Notice
|
||||
| ModalWhyDepends (B.GenericList Widgets Seq (NonEmpty (Path s)))
|
||||
| ModalSearch Text Text (B.GenericList Widgets Seq (Path s))
|
||||
|
||||
@ -177,9 +180,9 @@ app =
|
||||
{ B.appDraw = \env@AppEnv {aeOpenModal} ->
|
||||
[ case aeOpenModal of
|
||||
Nothing -> B.emptyWidget
|
||||
Just ModalHelp -> renderHelpModal
|
||||
Just (ModalWhyDepends l) -> renderWhyDependsModal l
|
||||
Just (ModalSearch l r xs) -> renderSearchModal l r xs,
|
||||
Just (ModalSearch l r xs) -> renderSearchModal l r xs
|
||||
Just (ModalNotice notice) -> renderNotice notice,
|
||||
renderMainScreen env
|
||||
],
|
||||
B.appChooseCursor = \_ -> const Nothing,
|
||||
@ -190,12 +193,17 @@ app =
|
||||
| k `elem` [V.KChar 'q', V.KEsc] ->
|
||||
B.halt s
|
||||
(B.VtyEvent (V.EvKey (V.KChar '?') []), Nothing) ->
|
||||
B.continue s {aeOpenModal = Just ModalHelp}
|
||||
B.continue s {aeOpenModal = Just (ModalNotice helpNotice)}
|
||||
(B.VtyEvent (V.EvKey (V.KChar 'w') []), Nothing) -> do
|
||||
B.hScrollToBeginning (B.viewportScroll WidgetWhyDependsViewport)
|
||||
B.continue $ showWhyDepends s
|
||||
(B.VtyEvent (V.EvKey (V.KChar '/') []), Nothing) ->
|
||||
B.continue $ showAndUpdateSearch "" "" s
|
||||
(B.VtyEvent (V.EvKey (V.KChar 'y') []), Nothing) -> do
|
||||
liftIO (yankToClipboard $ spName (selectedPath s))
|
||||
>>= \case
|
||||
Right () -> B.continue s
|
||||
Left n -> B.continue s {aeOpenModal = Just (ModalNotice n)}
|
||||
(B.VtyEvent (V.EvKey (V.KChar 's') []), Nothing) ->
|
||||
B.continue $
|
||||
s
|
||||
@ -283,8 +291,8 @@ app =
|
||||
selectPath
|
||||
(shortestPathTo (aeActualStoreEnv s) (spName path))
|
||||
closed
|
||||
-- help modal
|
||||
(B.VtyEvent (V.EvKey k []), Just ModalHelp)
|
||||
-- notices
|
||||
(B.VtyEvent (V.EvKey k []), Just (ModalNotice _))
|
||||
| k `elem` [V.KChar 'q', V.KEsc] ->
|
||||
B.continue s {aeOpenModal = Nothing}
|
||||
-- handle our events
|
||||
@ -314,6 +322,20 @@ app =
|
||||
]
|
||||
)
|
||||
|
||||
yankToClipboard :: StoreName s -> IO (Either Notice ())
|
||||
yankToClipboard p =
|
||||
Clipboard.copy (toS $ storeNameToPath p)
|
||||
<&> \case
|
||||
Right () -> Right ()
|
||||
Left errs ->
|
||||
Left $
|
||||
Notice
|
||||
"Error"
|
||||
( T.intercalate "\n" $
|
||||
"Cannot copy to clipboard: " :
|
||||
map (" " <>) errs
|
||||
)
|
||||
|
||||
renderMainScreen :: AppEnv s -> B.Widget Widgets
|
||||
renderMainScreen env@AppEnv {aePrevPane, aeCurrPane, aeNextPane} =
|
||||
(B.joinBorders . B.border)
|
||||
@ -373,15 +395,19 @@ helpText =
|
||||
T.intercalate
|
||||
"\n"
|
||||
[ "hjkl/Arrow Keys : Navigate",
|
||||
"q/Esc: : Quit / close modal",
|
||||
"w : Open why-depends mode",
|
||||
"/ : Open search mode",
|
||||
"s : Change sort order",
|
||||
"? : Show help"
|
||||
"y : Yank selected path to clipboard",
|
||||
"? : Show help",
|
||||
"q/Esc: : Quit / close modal"
|
||||
]
|
||||
|
||||
renderHelpModal :: B.Widget a
|
||||
renderHelpModal = renderModal "Help" (B.txt helpText)
|
||||
helpNotice :: Notice
|
||||
helpNotice = Notice "Help" helpText
|
||||
|
||||
renderNotice :: Notice -> B.Widget a
|
||||
renderNotice (Notice title txt) = renderModal title (B.txt txt)
|
||||
|
||||
renderWhyDependsModal ::
|
||||
B.GenericList Widgets Seq (NonEmpty (Path s)) ->
|
||||
|
51
src/Clipboard.hs
Normal file
51
src/Clipboard.hs
Normal file
@ -0,0 +1,51 @@
|
||||
module Clipboard
|
||||
( copy,
|
||||
)
|
||||
where
|
||||
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified System.Process.Typed as P
|
||||
|
||||
cmds :: [(Text, [Text])]
|
||||
cmds =
|
||||
[ ("xsel", ["-i", "-b"]),
|
||||
("xclip", ["-selection", "clipboard"]),
|
||||
("wl-copy", []),
|
||||
("pbcopy", [])
|
||||
]
|
||||
|
||||
runCmd :: Text -> (Text, [Text]) -> IO (Either Text ())
|
||||
runCmd txt (cmd, args) =
|
||||
P.proc (toS cmd) (map toS args)
|
||||
& P.setStdin (P.byteStringInput $ toUtf8Lazy txt)
|
||||
& P.readProcess
|
||||
& try
|
||||
<&> \case
|
||||
(Right (ExitSuccess, _, _)) -> Right ()
|
||||
(Right (ExitFailure e, out, err)) ->
|
||||
Left $
|
||||
"Running " <> show (cmd, args) <> " "
|
||||
<> "failed with exit code "
|
||||
<> show e
|
||||
<> ", "
|
||||
<> "stdout: "
|
||||
<> decodeUtf8 (BL.toStrict out)
|
||||
<> ", "
|
||||
<> "stderr: "
|
||||
<> decodeUtf8 (BL.toStrict err)
|
||||
<> "."
|
||||
(Left (ex :: SomeException)) ->
|
||||
Left $
|
||||
"Running " <> show (cmd, args) <> " "
|
||||
<> "failed with exception: "
|
||||
<> show ex
|
||||
<> "."
|
||||
|
||||
copy :: Text -> IO (Either [Text] ())
|
||||
copy txt = go cmds []
|
||||
where
|
||||
go [] errs = return $ Left errs
|
||||
go (x : xs) errs =
|
||||
runCmd txt x >>= \case
|
||||
Right () -> return $ Right ()
|
||||
Left err -> go xs (err : errs)
|
Loading…
Reference in New Issue
Block a user