packages feed

butcher 1.2.0.0 → 1.2.1.0

raw patch · 5 files changed

+146/−7 lines, 5 filesdep ~microlensdep ~microlens-thPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: microlens, microlens-th

API changes (from Hackage documentation)

+ UI.Butcher.Monadic.Interactive: interactiveHelpDoc :: String -> CommandDesc () -> String -> Int -> Doc
+ UI.Butcher.Monadic.Interactive: partDescStrings :: PartDesc -> [String]
+ UI.Butcher.Monadic.Interactive: simpleCompletion :: String -> CommandDesc () -> String -> String
- UI.Butcher.Monadic: cmd_out :: forall out_ag6I. Lens' (CommandDesc out_ag6I) (Maybe out_ag6I)
+ UI.Butcher.Monadic: cmd_out :: forall out_ag6K. Lens' (CommandDesc out_ag6K) (Maybe out_ag6K)
- UI.Butcher.Monadic.Types: cmd_out :: forall out_ag6I. Lens' (CommandDesc out_ag6I) (Maybe out_ag6I)
+ UI.Butcher.Monadic.Types: cmd_out :: forall out_ag6K. Lens' (CommandDesc out_ag6K) (Maybe out_ag6K)

Files

ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for butcher +## 1.2.1.0  -- November 2017++* Fix bug in 'ppUsageWithHelp'+* some utilities for interactive usage in new module+  `UI.Butcher.Monadic.Interactive`+ ## 1.2.0.0  -- October 2017  * Rename some `Monadic.Param.*`, deprecate old versions.
butcher.cabal view
@@ -1,5 +1,5 @@ name:                butcher-version:             1.2.0.0+version:             1.2.1.0 synopsis:            Chops a command or program invocation into digestable pieces. description:         See the <https://github.com/lspitzner/butcher/blob/master/README.md README> (it is properly formatted on github). license:             BSD3@@ -37,6 +37,7 @@                        UI.Butcher.Monadic.Flag                        UI.Butcher.Monadic.Pretty                        UI.Butcher.Monadic.IO+                       UI.Butcher.Monadic.Interactive                        UI.Butcher.Monadic.BuiltinCommands   other-modules:       UI.Butcher.Monadic.Internal.Types                        UI.Butcher.Monadic.Internal.Core
+ src/UI/Butcher/Monadic/Interactive.hs view
@@ -0,0 +1,132 @@+-- | Utilities when writing interactive programs that interpret commands,+-- e.g. a REPL.+module UI.Butcher.Monadic.Interactive+  ( simpleCompletion+  , interactiveHelpDoc+  , partDescStrings+  )+where++++#include "prelude.inc"++import qualified Text.PrettyPrint as PP++import           UI.Butcher.Monadic.Internal.Types+import           UI.Butcher.Monadic.Internal.Core+import           UI.Butcher.Monadic.Pretty++++-- | Derives a potential completion from a given input string and a given+-- 'CommandDesc'. Considers potential subcommands and where available the+-- completion info present in 'PartDesc's.+simpleCompletion+  :: String         -- ^ input string+  -> CommandDesc () -- ^ CommandDesc obtained on that input string+  -> String         -- ^ "remaining" input after the last successfully parsed+                    -- subcommand. See 'UI.Butcher.Monadic.runCmdParserExt'.+  -> String         -- ^ completion, i.e. a string that might be appended+                    -- to the current prompt when user presses tab.+simpleCompletion line cdesc pcRest =+  List.drop (List.length lastWord) $ case choices of+    [] -> ""+    (c1:cr) ->+      case+          filter (\s -> List.all (s`isPrefixOf`) cr) $ reverse $ List.inits c1+        of+          []    -> ""+          (x:_) -> x+ where+  nameDesc = case _cmd_mParent cdesc of+    Nothing                        -> cdesc+    Just (_, parent) | null pcRest -> parent+    Just{}                         -> cdesc+  lastWord = reverse $ takeWhile (not . Char.isSpace) $ reverse $ line+  choices :: [String]+  choices = join+    [ [ r+      | (Just r, _) <- Data.Foldable.toList (_cmd_children nameDesc)+      , lastWord `isPrefixOf` r+      ]+    , [ s+      | s <- partDescStrings =<< _cmd_parts nameDesc+      , lastWord `isPrefixOf` s+      ]+    ]+++-- | Produces a 'PP.Doc' as a hint for the user during interactive command+-- input. Takes the current (incomplete) prompt line into account. For example+-- when you have commands (among others) \'config set-email\' and+-- \'config get-email\', then on empty prompt there will be an item \'config\';+-- on the partial prompt \'config \' the help doc will contain the+-- \'set-email\' and \'get-email\' items.+interactiveHelpDoc+  :: String         -- ^ input string+  -> CommandDesc () -- ^ CommandDesc obtained on that input string+  -> String         -- ^ "remaining" input after the last successfully parsed+                    -- subcommand. See 'UI.Butcher.Monadic.runCmdParserExt'.+  -> Int            -- ^ max length of help text+  -> PP.Doc+interactiveHelpDoc cmdline desc pcRest maxLines = if+  | null cmdline             -> helpStrShort+  | List.last cmdline == ' ' -> helpStrShort+  | otherwise                -> helpStr+ where+  helpStr = if List.length optionLines > maxLines+    then+      PP.fcat $ List.intersperse (PP.text "|") $ PP.text . fst <$> optionLines+    else PP.vcat $ optionLines <&> \case+      (s, "") -> PP.text s+      (s, h ) -> PP.text s PP.<> PP.text h+   where+    nameDesc = case _cmd_mParent desc of+      Nothing                        -> desc+      Just (_, parent) | null pcRest -> parent+      Just{}                         -> desc++    lastWord = reverse $ takeWhile (not . Char.isSpace) $ reverse $ cmdline+    optionLines :: [(String, String)]+    optionLines = -- a list of potential words that make sense, given+                    -- the current input.+                  join+      [ [ (s, e)+        | (Just s, c) <- Data.Foldable.toList (_cmd_children nameDesc)+        , lastWord `isPrefixOf` s+        , let e = join $ join+                [ [ " ARGS" | not $ null $ _cmd_parts c ]+                , [ " CMDS" | not $ null $ _cmd_children c ]+                , [ ": " ++ show h | Just h <- [_cmd_help c] ]+                ]+        ]+      , [ (s, "")+        | s <- partDescStrings =<< _cmd_parts nameDesc+        , lastWord `isPrefixOf` s+        ]+      ]+  helpStrShort = ppUsageWithHelp desc+++-- | Obtains a list of "expected"/potential strings for a command part+-- described in the 'PartDesc'. In constrast to the 'simpleCompletion'+-- function this function does not take into account any current input, and+-- consequently the output elements can in general not be appended to partial+-- input to form valid input.+partDescStrings :: PartDesc -> [String]+partDescStrings = \case+  PartLiteral  s      -> [s]+  PartVariable _      -> []+  -- TODO: we could handle seq of optional and such much better+  PartOptional x      -> partDescStrings x+  PartAlts     alts   -> alts >>= partDescStrings+  PartSeq      []     -> []+  PartSeq      (x:_)  -> partDescStrings x+  PartDefault    _  x -> partDescStrings x+  PartSuggestion ss x -> [ s | s <- ss ] ++ partDescStrings x+  PartRedirect   _  x -> partDescStrings x+  PartReorder xs      -> xs >>= partDescStrings+  PartMany    x       -> partDescStrings x+  PartWithHelp _h x   -> partDescStrings x -- TODO: handle help+
src/UI/Butcher/Monadic/Internal/Types.hs view
@@ -104,7 +104,7 @@  -- | A representation/description of a command parser built via the -- 'CmdParser' monad. Can be transformed into a pretty Doc to display--- as usage/help via 'ppUsage' and related functions.+-- as usage/help via 'UI.Butcher.Monadic.Pretty.ppUsage' and related functions. -- -- Note that there is the '_cmd_out' accessor that contains @Maybe out@ which -- might be useful after successful parsing.
src/UI/Butcher/Monadic/Pretty.hs view
@@ -56,9 +56,9 @@  -- | ppUsage exampleDesc yields: ----- > playground [--short] NAME [version | help]+-- > example [--short] NAME [version | help] ppUsage :: CommandDesc a -> PP.Doc-ppUsage (CommandDesc mParent _help _syn parts out children) = pparents mParent+ppUsage (CommandDesc mParent _syn _help parts out children) = pparents mParent   <+> PP.sep [PP.fsep partDocs, subsDoc]  where   pparents :: Maybe (Maybe String, CommandDesc out) -> PP.Doc@@ -79,12 +79,12 @@  -- | ppUsageWithHelp exampleDesc yields: ----- > playground [--short] NAME--- >            [version | help]: a simple butcher example program+-- > example [--short] NAME+-- >         [version | help]: a simple butcher example program -- -- And yes, the line break is not optimal in this instance with default print. ppUsageWithHelp :: CommandDesc a -> PP.Doc-ppUsageWithHelp (CommandDesc mParent help _syn parts out children) =+ppUsageWithHelp (CommandDesc mParent _syn help parts out children) =   pparents mParent <+> PP.fsep (partDocs ++ [subsDoc]) PP.<> helpDoc  where   pparents :: Maybe (Maybe String, CommandDesc out) -> PP.Doc