diff --git a/src/Nixon/Command/Placeholder.hs b/src/Nixon/Command/Placeholder.hs index 8a9e208..f90c00b 100644 --- a/src/Nixon/Command/Placeholder.hs +++ b/src/Nixon/Command/Placeholder.hs @@ -29,6 +29,10 @@ data Placeholder = Placeholder name :: Text, -- | The field numbers to extract format :: PlaceholderFormat, + -- | The filter to apply + filter :: Maybe Text, + -- | If the placeholder just lists the available options + list :: Bool, -- | If the placeholder can select multiple multiple :: Bool, -- | Pre-expanded value of the placeholder diff --git a/src/Nixon/Command/Run.hs b/src/Nixon/Command/Run.hs index 0777ddf..73209c3 100644 --- a/src/Nixon/Command/Run.hs +++ b/src/Nixon/Command/Run.hs @@ -11,7 +11,9 @@ import Control.Arrow ((&&&)) import Control.Monad (foldM) import Data.Aeson (eitherDecodeStrict) import Data.Foldable (find) +import Data.Maybe (fromMaybe) import qualified Data.Text as T +import qualified Nixon.Backend.Fzf as Fzf import Nixon.Command (Command) import qualified Nixon.Command as Cmd import Nixon.Command.Find (findProjectCommands) @@ -23,7 +25,7 @@ import Nixon.Process (run_with_output) import qualified Nixon.Process import Nixon.Project (Project) import qualified Nixon.Project as Project -import Nixon.Select (Selection (..), Selector, selector_format, selector_multiple) +import Nixon.Select (Selection (..), Selector, selector_format, selector_multiple, selector_search) import qualified Nixon.Select as Select import Nixon.Types (Nixon) import Nixon.Utils (shell_to_list, toLines) @@ -55,7 +57,7 @@ resolveEnv project selector cmd args = do zipArgs :: [P.Placeholder] -> [Text] -> [(P.Placeholder, Select.SelectorOpts)] zipArgs [] args' = map ((,Select.defaults) . argOverflow) args' where - argOverflow = P.Placeholder P.Arg "arg" P.Lines False . pure + argOverflow = P.Placeholder P.Arg "arg" P.Lines Nothing False False . pure zipArgs placeholders [] = map (,Select.defaults) placeholders zipArgs (p : ps) (a : as) = (p, Select.search a) : zipArgs ps as @@ -63,16 +65,18 @@ zipArgs (p : ps) (a : as) = (p, Select.search a) : zipArgs ps as resolveEnv' :: Project -> Selector Nixon -> [(P.Placeholder, Select.SelectorOpts)] -> Nixon (Maybe (Shell Text), [Text], Nixon.Process.Env) resolveEnv' project selector = foldM resolveEach (Nothing, [], []) where - resolveEach (stdin, args', envs) (P.Placeholder envType cmdName format' multiple value, select_opts) = do + resolveEach (stdin, args', envs) (P.Placeholder envType cmdName format' search list multiple value, select_opts) = do resolved <- case value of [] -> do + let selector' = if list then const (Fzf.fzf $ Fzf.fzfFilter $ fromMaybe "" search) else selector cmd' <- assertCommand cmdName let select_opts' = select_opts { selector_format = format', - selector_multiple = Just multiple + selector_multiple = Just multiple, + selector_search = search } - resolveCmd project selector cmd' select_opts' + resolveCmd project selector' cmd' select_opts' _ -> pure value case envType of -- Standard inputs are concatenated diff --git a/src/Nixon/Config/Markdown.hs b/src/Nixon/Config/Markdown.hs index 43410f9..65b9c50 100644 --- a/src/Nixon/Config/Markdown.hs +++ b/src/Nixon/Config/Markdown.hs @@ -327,7 +327,7 @@ parseCommandPlaceholder = do P.EnvVar "" -> P.EnvVar $ fixup name P.EnvVar alias -> P.EnvVar $ fixup alias same -> same - pure $ P.Placeholder placeholderWithName name P.Lines False [] + pure $ P.Placeholder placeholderWithName name P.Lines Nothing False False [] parsePlaceholderModifiers placeholder <* P.char '}' -- | Parse the "modifiers" which affect how command placeholders are handled. @@ -358,8 +358,10 @@ parsePlaceholderModifiers placeholder = do _ <- P.many P.space *> P.char '|' *> P.many P.space p' <- P.choice - [ parsePipeFields p, - parsePipeMultiple p + [ P.try $ parsePipeFields p, + P.try $ parsePipeFilter p, + P.try $ parsePipeList p, + P.try $ parsePipeMultiple p ] _ <- P.many P.space P.option p' (parsePipeModifiers p') @@ -373,6 +375,14 @@ parsePlaceholderModifiers placeholder = do <|> (P.string "fields" *> P.many P.space *> parseFields P.Fields p) <|> (P.string "json" $> p {P.format = P.JSON}) + -- Parse `filter "some-filter"` + parsePipeFilter p = do + _ <- (P.string "filter" :: Parser String) *> P.many P.space + f <- P.between (P.char '"') (P.char '"') (P.many P.alphaNum) + pure p {P.filter = Just $ pack f} + + parsePipeList p = (P.string "list" :: Parser String) $> p {P.list = True} + parsePipeMultiple p = (P.string "multi" :: Parser String) $> p {P.multiple = True} -- Parse `command-name:1,2` diff --git a/test/Test/Nixon/Config/Markdown.hs b/test/Test/Nixon/Config/Markdown.hs index 425b53a..738728c 100644 --- a/test/Test/Nixon/Config/Markdown.hs +++ b/test/Test/Nixon/Config/Markdown.hs @@ -327,7 +327,7 @@ command_tests = describe "commands section" $ do "```" ] selector = fmap (Cmd.cmdName &&& Cmd.cmdPlaceholders) . Cfg.commands - placeholder = Placeholder Arg "placeholder" (Columns False [1]) False [] + placeholder = Placeholder Arg "placeholder" (Columns False [1]) Nothing False False [] in selector <$> result `shouldBe` Right [("hello", [placeholder])] it "detects columns with header output format" @@ -340,7 +340,7 @@ command_tests = describe "commands section" $ do "```" ] selector = fmap (Cmd.cmdName &&& Cmd.cmdPlaceholders) . Cfg.commands - placeholder = Placeholder Arg "placeholder" (Columns True [1]) False [] + placeholder = Placeholder Arg "placeholder" (Columns True [1]) Nothing False False [] in selector <$> result `shouldBe` Right [("hello", [placeholder])] it "errors on combined columns and fields output format" @@ -366,7 +366,7 @@ command_tests = describe "commands section" $ do "```" ] selector = fmap (Cmd.cmdName &&& Cmd.cmdPlaceholders) . Cfg.commands - in selector <$> result `shouldBe` Right [("hello", [Placeholder Arg "placeholder" JSON False []])] + in selector <$> result `shouldBe` Right [("hello", [Placeholder Arg "placeholder" JSON Nothing False False []])] it "detects project type" $ let result = @@ -425,7 +425,7 @@ command_tests = describe "commands section" $ do `shouldBe` Right [ ( "hello", True, - [Placeholder Arg "arg" Lines False [], Placeholder Arg "another-arg" Lines False []] + [Placeholder Arg "arg" Lines Nothing False False [], Placeholder Arg "another-arg" Lines Nothing False False []] ) ] @@ -445,7 +445,7 @@ command_tests = describe "commands section" $ do Cmd.cmdPlaceholders = placeholders } = (lang, placeholders) fmap selector . Cfg.commands <$> result - `shouldBe` Right [(Bash, [Placeholder Arg "arg" Lines False []])] + `shouldBe` Right [(Bash, [Placeholder Arg "arg" Lines Nothing False False []])] it "extracts code block placeholders" $ do let result = @@ -477,10 +477,10 @@ command_tests = describe "commands section" $ do } = (lang, placeholders) fmap selector . Cfg.commands <$> result `shouldBe` Right - [ (Bash, [Placeholder Arg "arg-one" Lines False []]), - (Bash, [Placeholder Arg "arg-two" Lines True []]), - (Bash, [Placeholder Arg "arg-three" (Fields [1, 2]) False []]), - (Bash, [Placeholder Arg "arg-four" (Fields [1, 2]) True []]) + [ (Bash, [Placeholder Arg "arg-one" Lines Nothing False False []]), + (Bash, [Placeholder Arg "arg-two" Lines Nothing False True []]), + (Bash, [Placeholder Arg "arg-three" (Fields [1, 2]) Nothing False False []]), + (Bash, [Placeholder Arg "arg-four" (Fields [1, 2]) Nothing False True []]) ] it "complains on both header & code block placeholders" $ do @@ -625,71 +625,79 @@ parse_command_name_tests = describe "parseCommandName" $ do it "parses arg part" $ do parseCommandName "cat ${arg}" - `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines False []]) + `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines Nothing False False []]) it "parses stdin arg part" $ do parseCommandName "cat <{arg}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" Lines False []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" Lines Nothing False False []]) it "parses envvar arg part" $ do parseCommandName "cat ={arg}" - `shouldBe` Right ("cat", [Placeholder (EnvVar "arg") "arg" Lines False []]) + `shouldBe` Right ("cat", [Placeholder (EnvVar "arg") "arg" Lines Nothing False False []]) it "parses envvar part with alias" $ do parseCommandName "cat FOO={bar}" - `shouldBe` Right ("cat", [Placeholder (EnvVar "FOO") "bar" Lines False []]) + `shouldBe` Right ("cat", [Placeholder (EnvVar "FOO") "bar" Lines Nothing False False []]) it "parses arg field selector" $ do parseCommandName "cat <{arg:1}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1]) False []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1]) Nothing False False []]) it "parses several arg field selectors" $ do parseCommandName "cat <{arg:1,3,5}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) False []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) Nothing False False []]) it "parses arg modifiers" $ do parseCommandName "cat ${arg:m}" - `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines True []]) + `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines Nothing False True []]) it "parses arg modifiers" $ do parseCommandName "cat ${arg | fields 1,3}" - `shouldBe` Right ("cat", [Placeholder Arg "arg" (Fields [1, 3]) False []]) + `shouldBe` Right ("cat", [Placeholder Arg "arg" (Fields [1, 3]) Nothing False False []]) it "parses arg modifiers" $ do parseCommandName "cat ${arg | multi}" - `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines True []]) + `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines Nothing False True []]) it "parses stdin arg modifiers" $ do parseCommandName "cat <{arg:m}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" Lines True []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" Lines Nothing False True []]) it "parses arg field and multiple selector" $ do parseCommandName "cat <{arg:m1,3,5}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) True []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) Nothing False True []]) it "parses arg field and multiple selector (flipped)" $ do parseCommandName "cat <{arg:1,3,5m}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) True []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) Nothing False True []]) it "parses arg field and pipe fields" $ do parseCommandName "cat <{arg | fields 1,3,5}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) False []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) Nothing False False []]) + + it "parses list modifier" $ do + parseCommandName "cat ${arg | list}" + `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines Nothing True False []]) + + it "parses filter modifier" $ do + parseCommandName "cat ${arg | filter \"filter\"}" + `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines (Just "filter") False False []]) it "parses arg field, pipe fields and pipe multiple" $ do parseCommandName "cat <{arg | fields 1,3,5 | multi}" - `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) True []]) + `shouldBe` Right ("cat", [Placeholder Stdin "arg" (Fields [1, 3, 5]) Nothing False True []]) it "parses text and placeholder part" $ do parseCommandName "cat \"${arg}\"" - `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines False []]) + `shouldBe` Right ("cat", [Placeholder Arg "arg" Lines Nothing False False []]) it "replaces '-' with '_' in env var name" $ do parseCommandName "cat \"={some-arg}\"" - `shouldBe` Right ("cat", [Placeholder (EnvVar "some_arg") "some-arg" Lines False []]) + `shouldBe` Right ("cat", [Placeholder (EnvVar "some_arg") "some-arg" Lines Nothing False False []]) it "supports '_' in env var alias" $ do parseCommandName "cat some_arg={some-arg}" - `shouldBe` Right ("cat", [Placeholder (EnvVar "some_arg") "some-arg" Lines False []]) + `shouldBe` Right ("cat", [Placeholder (EnvVar "some_arg") "some-arg" Lines Nothing False False []]) it "allows use of $ not matching '${'" $ do parseCommandName "echo $SOME_VAR" `shouldBe` Right ("echo", [])