packages feed

opt-env-conf-test 0.0.0.3 → 0.0.0.4

raw patch · 50 files changed

+1941/−308 lines, 50 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- OptEnvConf.Test: pureGoldenNixOptions :: FilePath -> Parser a -> GoldenTest Text
+ OptEnvConf.Test: pureGoldenNixOptions :: HasCallStack => FilePath -> Parser a -> GoldenTest Text

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Changelog +## [0.0.0.4] - 2026-02-25++### Changed++* `pureGoldenNixOptions` now prepends a generated file comment pointing to the test that generated each file+ ## [0.0.0.3] - 2025-11-20  ### Added
opt-env-conf-test.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.36.1.+-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack  name:           opt-env-conf-test-version:        0.0.0.3+version:        0.0.0.4 synopsis:       A testing companion package for opt-env-conf homepage:       https://github.com/NorfairKing/opt-env-conf#readme bug-reports:    https://github.com/NorfairKing/opt-env-conf/issues
src/OptEnvConf/Test.hs view
@@ -38,10 +38,12 @@ where  import Data.Aeson as JSON+import Data.Char (isDigit) import Data.Maybe import Data.Text (Text) import qualified Data.Text as T-import GHC.Stack (HasCallStack, withFrozenCallStack)+import GHC.Stack (CallStack, HasCallStack, getCallStack, withFrozenCallStack)+import qualified GHC.Stack as Stack import OptEnvConf import OptEnvConf.Args import OptEnvConf.Completion@@ -106,7 +108,8 @@   let arg = fromMaybe "" $ listToMaybe $ drop ix ws   let completions = pureCompletionQuery p ix ws   evaluatedCompletions <- evalCompletions arg completions-  evaluatedCompletions `shouldBe` expected+  let toStringCompletion c = c {completionSuggestion = completionResultValue (completionSuggestion c)}+  map toStringCompletion evaluatedCompletions `shouldBe` expected  parserCompletionDescriptionTest :: Parser a -> Int -> [String] -> [String] -> IO () parserCompletionDescriptionTest p ix ws descriptions = do@@ -136,7 +139,42 @@   specify "produces the nix options as before" $     pureGoldenNixOptions path parser -pureGoldenNixOptions :: FilePath -> Parser a -> GoldenTest Text+pureGoldenNixOptions :: (HasCallStack) => FilePath -> Parser a -> GoldenTest Text pureGoldenNixOptions path parser =   pureGoldenTextFile path $-    renderParserNixOptions parser+    generatedFileComment Stack.callStack <> renderParserNixOptions parser++generatedFileComment :: CallStack -> T.Text+generatedFileComment cs =+  let loc = case getCallStack cs of+        (_, srcLoc) : _ ->+          packageName (Stack.srcLocPackage srcLoc)+            <> ":"+            <> Stack.srcLocFile srcLoc+            <> ":"+            <> show (Stack.srcLocStartLine srcLoc)+        [] -> "unknown"+   in T.pack $+        unlines $+          map+            ("# " <>)+            [ "DO NOT EDIT THIS FILE DIRECTLY",+              "This file was generated by",+              loc,+              "To update this it, run the test with --golden-reset"+            ]++-- | Extract just the package name from a GHC package identifier.+--+-- GHC package identifiers look like: @name-version-hash-unitid@+-- This function returns just @name@.+packageName :: String -> String+packageName = go+  where+    go [] = []+    go ('-' : rest)+      | isVersion version = []+      where+        version = takeWhile (\c -> isDigit c || c == '.') rest+    go (c : rest) = c : go rest+    isVersion v = not (null v) && elem '.' v && all (\c -> isDigit c || c == '.') v
test/OptEnvConf/APISpec.hs view
@@ -112,8 +112,7 @@         parserDocs p    it "renders the Nix options the same way" $-    pureGoldenTextFile ("test_resources/docs/" <> dir <> "/nix-options.nix") $-      renderParserNixOptions p+    pureGoldenNixOptions ("test_resources/docs/" <> dir <> "/nix-options.nix") p  sameHelpParser :: Parser (Either Int String, Bool) sameHelpParser =
test/OptEnvConf/CompleterSpec.hs view
@@ -27,6 +27,9 @@         -- File in dir in dir         exampleFile3 <- resolveFile deeperDir "gold.txt"         writeFile (fromAbsFile exampleFile3) ""+        -- YAML file+        yamlFile <- resolveFile tdir "config.yaml"+        writeFile (fromAbsFile yamlFile) ""         -- Hidden file         hiddenFile <- resolveFile tdir ".hidden.txt"         writeFile (fromAbsFile hiddenFile) ""@@ -43,37 +46,192 @@       )     $ do       describe "filePath" $ do-        let c :: (HasCallStack) => String -> [String] -> TestDef '[Path Abs Dir] ()+        let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] ()             c s l =               withFrozenCallStack $-                itWithOuter (unwords ["can complete", show s, "to", show l]) $ \tdir ->+                itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir ->                   withCurrentDir tdir $                     unCompleter filePath s `shouldReturn` l -        c "" ["foo.txt", "bar/"]-        c "f" ["foo.txt"]-        c "b" ["bar/"]-        c "bar" ["bar/quux.txt", "bar/", "bar/deep/"]+        c "" [fileR "foo.txt", fileR "config.yaml", dirR "bar/"]+        c "f" [fileR "foo.txt"]+        c "b" [dirR "bar/"]+        c "bar" [fileR "bar/quux.txt", dirR "bar/", dirR "bar/deep/"]+        c "c" [fileR "config.yaml"]         c "q" []-        c "." [".hidden.txt", ".hidden/"]-        c "./" ["./foo.txt", "./bar/"]-        c "././" ["././foo.txt", "././bar/"]-        c "./." ["./.hidden.txt", "./.hidden/"]-        c "./bar" ["./bar/quux.txt", "./bar/", "./bar/deep/"]+        c "." [fileR ".hidden.txt", dirR ".hidden/"]+        c "./" [fileR "./foo.txt", fileR "./config.yaml", dirR "./bar/"]+        c "././" [fileR "././foo.txt", fileR "././config.yaml", dirR "././bar/"]+        c "./." [fileR "./.hidden.txt", dirR "./.hidden/"]+        c "./bar" [fileR "./bar/quux.txt", dirR "./bar/", dirR "./bar/deep/"] +        -- Deeper nesting.+        -- Directories end in /, files do not.  This convention is how+        -- shells decide whether to append a trailing space after a+        -- completion.+        c "bar/" [fileR "bar/quux.txt", dirR "bar/deep/"]+        c "bar/d" [dirR "bar/deep/"]+        c "bar/deep" [fileR "bar/deep/gold.txt", dirR "bar/deep/"]+        c "bar/deep/" [fileR "bar/deep/gold.txt"]+        c "bar/q" [fileR "bar/quux.txt"]++        -- Parent directory paths (..)+        -- These tests run from inside bar/ so that .. refers to the+        -- test root directory.+        itWithOuter "can complete \"..\" to parent directory contents" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter filePath ".."+            results+              `shouldBe` [ fileR "../foo.txt",+                           fileR "../config.yaml",+                           dirR "../bar/"+                         ]+        itWithOuter "can complete \"../\" to parent directory contents" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter filePath "../"+            results+              `shouldBe` [ fileR "../foo.txt",+                           fileR "../config.yaml",+                           dirR "../bar/"+                         ]+        itWithOuter "can complete \"../f\" to matching parent files" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter filePath "../f"+            results+              `shouldBe` [fileR "../foo.txt"]+        itWithOuter "can complete \"../bar/\" to sibling dir contents" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter filePath "../bar/"+            results+              `shouldBe` [ fileR "../bar/quux.txt",+                           dirR "../bar/deep/"+                         ]++        -- Parent directory in the middle of a path+        itWithOuter "can complete \"bar/../\" to current dir contents" $ \tdir ->+          withCurrentDir tdir $ do+            results <- unCompleter filePath "bar/../"+            results+              `shouldBe` [ fileR "bar/../foo.txt",+                           fileR "bar/../config.yaml",+                           dirR "bar/../bar/"+                         ]+        itWithOuter "can complete \"bar/../f\" to matching files" $ \tdir ->+          withCurrentDir tdir $ do+            results <- unCompleter filePath "bar/../f"+            results+              `shouldBe` [fileR "bar/../foo.txt"]+        itWithOuter "can complete \"../bar/deep/../\" to bar/ contents" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter filePath "../bar/deep/../"+            results+              `shouldBe` [ fileR "../bar/deep/../quux.txt",+                           dirR "../bar/deep/../deep/"+                         ]++        -- Absolute paths+        itWithOuter "can complete absolute paths" $ \tdir ->+          withCurrentDir tdir $ do+            let absPrefix = fromAbsDir tdir+            results <- unCompleter filePath absPrefix+            results+              `shouldBe` [ fileR (absPrefix <> "foo.txt"),+                           fileR (absPrefix <> "config.yaml"),+                           dirR (absPrefix <> "bar/")+                         ]+       describe "directoryPath" $ do-        let c :: (HasCallStack) => String -> [String] -> TestDef '[Path Abs Dir] ()+        let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] ()             c s l = withFrozenCallStack $-              itWithOuter (unwords ["can complete", show s, "to", show l]) $ \tdir ->+              itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir ->                 withCurrentDir tdir $                   unCompleter directoryPath s `shouldReturn` l -        c "" ["bar/"]-        c "b" ["bar/"]+        c "" [dirR "bar/"]+        c "b" [dirR "bar/"]         c "f" []-        c "." [".hidden/"]-        c "./" ["./bar/"]-        c "./." ["./.hidden/"]-        c "././" ["././bar/"]-        c "./." ["./.hidden/"]-        c "./bar" ["./bar/", "./bar/deep/"]+        c "." [dirR ".hidden/"]+        c "./" [dirR "./bar/"]+        c "./." [dirR "./.hidden/"]+        c "././" [dirR "././bar/"]+        c "./." [dirR "./.hidden/"]+        c "./bar" [dirR "./bar/", dirR "./bar/deep/"]++        -- Deeper nesting.+        -- Only directories are returned, never files.+        c "bar/" [dirR "bar/", dirR "bar/deep/"]+        c "bar/d" [dirR "bar/deep/"]+        c "bar/deep" [dirR "bar/deep/"]++        -- Parent directory paths (..)+        itWithOuter "can complete \"..\" to parent directories" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter directoryPath ".."+            results+              `shouldBe` [dirR "../bar/"]+        itWithOuter "can complete \"../\" to parent directories" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter directoryPath "../"+            results+              `shouldBe` [dirR "../bar/"]+        itWithOuter "can complete \"../b\" to matching parent directories" $ \tdir -> do+          subdir <- resolveDir tdir "bar"+          withCurrentDir subdir $ do+            results <- unCompleter directoryPath "../b"+            results+              `shouldBe` [dirR "../bar/"]++        -- Parent directory in the middle of a path+        itWithOuter "can complete \"bar/../\" to current dir directories" $ \tdir ->+          withCurrentDir tdir $ do+            results <- unCompleter directoryPath "bar/../"+            results+              `shouldBe` [dirR "bar/../bar/"]+        itWithOuter "can complete \"bar/../b\" to matching directories" $ \tdir ->+          withCurrentDir tdir $ do+            results <- unCompleter directoryPath "bar/../b"+            results+              `shouldBe` [dirR "bar/../bar/"]++      describe "filePathWithExtension" $ do+        let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] ()+            c s l = withFrozenCallStack $+              itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir ->+                withCurrentDir tdir $+                  unCompleter (filePathWithExtension ".yaml") s `shouldReturn` l++        c "" [fileR "config.yaml", dirR "bar/"]+        c "c" [fileR "config.yaml"]+        c "b" [dirR "bar/"]+        c "bar" [dirR "bar/", dirR "bar/deep/"]++      describe "filePathWithExtensions" $ do+        let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] ()+            c s l = withFrozenCallStack $+              itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir ->+                withCurrentDir tdir $+                  unCompleter (filePathWithExtensions [".txt", ".yaml"]) s `shouldReturn` l++        c "" [fileR "foo.txt", fileR "config.yaml", dirR "bar/"]+        c "bar/" [fileR "bar/quux.txt", dirR "bar/deep/"]++fileR :: String -> CompletionResult+fileR s =+  CompletionResult+    { completionResultValue = s,+      completionResultFinality = CompletionFinal+    }++dirR :: String -> CompletionResult+dirR s =+  CompletionResult+    { completionResultValue = s,+      completionResultFinality = CompletionNotFinal+    }
test/OptEnvConf/CompletionSpec.hs view
@@ -4,236 +4,1543 @@ module OptEnvConf.CompletionSpec (spec) where  import Control.Applicative-import OptEnvConf.Completer-import OptEnvConf.Completion-import OptEnvConf.Parser-import OptEnvConf.Setting-import OptEnvConf.Test-import Path-import Test.Syd--spec :: Spec-spec = do-  describe "bash" $ do-    it "produces the same bash completion script" $-      pureGoldenStringFile "test_resources/completion/bash-completion-script.bash" $-        bashCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"-  describe "zsh" $ do-    it "produces the same zsh completion script" $-      pureGoldenStringFile "test_resources/completion/zsh-completion-script.zsh" $-        zshCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"-  describe "fish" $ do-    it "produces the same fish completion script" $-      pureGoldenStringFile "test_resources/completion/fish-completion-script.fish" $-        fishCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"--  describe "pureCompletionQuery" $ do-    it "can complete a switch from nothing" $-      parserCompletionTest-        (setting [switch (), short 'e', long "example"])-        0-        []-        ["--example"] -- Only the long version-    it "can complete a short switch from a single dash" $-      parserCompletionTest-        (setting [switch (), short 'e'])-        0-        ["-"]-        ["-e"]--    it "can complete a long switch from a single dash" $-      parserCompletionTest-        (setting [switch (), long "example"])-        0-        ["-"]-        ["--example"]--    it "can complete a long switch from a double dash" $-      parserCompletionTest-        (setting [switch (), long "example"])-        0-        ["--"]-        ["--example"]--    it "can complete a short option's dashed" $-      parserCompletionTest-        (setting [option, short 'e', completer $ listCompleter ["hi"]])-        0-        []-        ["-e"]--    it "can complete a long option's dashed" $-      parserCompletionTest-        (setting [option, long "example", completer $ listCompleter ["hi"]])-        0-        []-        ["--example"]--    it "can complete a short option with a separate arg" $-      parserCompletionTest-        (setting [option, short 'e', completer $ listCompleter ["hi"]])-        1-        ["-e"]-        ["hi"]--    -- Don't think we want to support this.-    -- pending "can complete a short option in short-hand mode.--    it "can complete a long option" $-      parserCompletionTest-        (setting [option, long "example", completer $ listCompleter ["hi"]])-        1-        ["--example"]-        ["hi"]--    -- Don't think we want to support this-    -- pending "can complete a long option with equals sign"--    it "can complete both switches of a tuple" $-      parserCompletionTest-        ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])-        0-        []-        ["--foo", "--bar"]--    it "can complete both switches of a tuple, with a prefix" $-      parserCompletionTest-        ((,) <$> setting [switch (), long "bar"] <*> setting [switch (), long "baz"])-        0-        ["--b"]-        ["--bar", "--baz"]--    it "can complete both switches of an either" $-      parserCompletionTest-        (setting [switch (), long "foo"] <|> setting [switch (), long "bar"])-        0-        []-        ["--foo", "--bar"]--    it "can complete both switches of an either wrapped in optionals" $-      parserCompletionTest-        (optional (setting [switch (), long "foo"]) <|> optional (setting [switch (), long "bar"]))-        0-        []-        ["--foo", "--bar"]--    describe "commands" $ do-      let p =-            commands-              [ command "foo" "1" $ pure (),-                command "bar" "2" $ pure (),-                command "baz" "3" $ pure ()-              ]--      it "can complete a command argument" $-        parserCompletionTest-          p-          0-          []-          [Completion "foo" (Just "1"), Completion "bar" (Just "2"), Completion "baz" (Just "3")]--      it "can complete a command argument when it's been partially provided" $-        parserCompletionTest-          p-          0-          ["b"]-          [Completion "bar" (Just "2"), Completion "baz" (Just "3")]--    describe "completion after a command" $ do-      it "can complete a command with a switch" $-        parserCompletionTest-          (commands [command "foo" "1" $ setting [help "ex", switch (), short 'e', long "example"]])-          1-          ["foo"]-          [Completion "--example" (Just "ex")] -- Only the long version-      it "can complete a command's short switch" $-        parserCompletionTest-          (commands [command "foo" "1" $ setting [switch (), short 'e']])-          1-          ["foo", "-"]-          ["-e"]--      it "can complete a command's long switch from a single dash" $-        parserCompletionTest-          (commands [command "foo" "1" $ setting [switch (), long "example"]])-          1-          ["foo", "-"]-          ["--example"]--      it "can complete a command's long switch from a double dash" $-        parserCompletionTest-          (commands [command "foo" "1" $ setting [switch (), long "example"]])-          1-          ["foo", "--"]-          ["--example"]--      it "can complete a command's short option" $-        parserCompletionTest-          (commands [command "foo" "1" $ setting [option, short 'e', completer $ listCompleter ["hi"]]])-          2-          ["foo", "-e"]-          ["hi"]--      it "can complete a command's long option" $-        parserCompletionTest-          (commands [command "foo" "1" $ setting [option, long "example", completer $ listCompleter ["hi"]]])-          2-          ["foo", "--example"]-          ["hi"]--    it "can complete a file argument" $-      parserCompletionDescriptionTest-        (filePathSetting [help "file arg", argument])-        0-        []-        ["file arg"]--    it "can complete a file option" $-      parserCompletionDescriptionTest-        (filePathSetting [help "file arg", option, long "file"])-        1-        ["--file"]-        ["file arg"]--    it "can complete a directory argument" $-      parserCompletionDescriptionTest-        (directoryPathSetting [help "dir arg", argument])-        0-        []-        ["dir arg"]--    it "can complete a directory option" $-      parserCompletionDescriptionTest-        (directoryPathSetting [help "dir arg", option, long "file"])-        1-        ["--file"]-        ["dir arg"]--    it "no longer suggests a switch that has already been parsed" $-      parserCompletionTest-        ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])-        1-        ["--foo"]-        ["--bar"]--    it "no longer suggests an option that has already been parsed" $-      parserCompletionTest-        ( (,,)-            <$> setting [option, reader (str :: Reader String), long "foo"]-            <*> setting [option, reader (str :: Reader String), long "bar"]-            <*> setting [switch (), long "quux"]-        )-        2-        ["--foo", "foo"]-        ["--bar", "--quux"]--    it "no longer suggests an argument that has already been parsed" $-      parserCompletionDescriptionTest-        ( (,,)-            <$> setting [argument, reader (str :: Reader String), help "hi", completer $ listCompleter ["hi"]]-            <*> setting [argument, reader (str :: Reader String), help "ho", completer $ listCompleter ["ho"]]-            <*> setting [switch (), long "bar", help "hu"]-        )-        1-        ["foo"]-        ["ho", "hu"]+import qualified Data.List.NonEmpty as NE+import Data.Maybe+import OptEnvConf.Completer+import OptEnvConf.Completion+import OptEnvConf.Parser+import OptEnvConf.Setting+import OptEnvConf.Test+import Path+import Path.IO+import Test.Syd++spec :: Spec+spec = do+  describe "bash" $ do+    it "produces the same bash completion script" $+      pureGoldenStringFile "test_resources/completion/bash-completion-script.bash" $+        bashCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"+  describe "zsh" $ do+    it "produces the same zsh completion script" $+      pureGoldenStringFile "test_resources/completion/zsh-completion-script.zsh" $+        zshCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"+  describe "fish" $ do+    it "produces the same fish completion script" $+      pureGoldenStringFile "test_resources/completion/fish-completion-script.fish" $+        fishCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"++  describe "pureCompletionQuery" $ do+    it "can complete a switch from nothing" $+      parserCompletionTest+        (setting [switch (), short 'e', long "example"])+        0+        []+        ["--example"] -- Only the long version+    it "can complete a short switch from a single dash" $+      parserCompletionTest+        (setting [switch (), short 'e'])+        0+        ["-"]+        ["-e"]++    it "can complete a long switch from a single dash" $+      parserCompletionTest+        (setting [switch (), long "example"])+        0+        ["-"]+        ["--example"]++    it "can complete a long switch from a double dash" $+      parserCompletionTest+        (setting [switch (), long "example"])+        0+        ["--"]+        ["--example"]++    it "can complete a short option's dashed" $+      parserCompletionTest+        (setting [option, short 'e', completer $ listCompleter ["hi"]])+        0+        []+        ["-e"]++    it "can complete a long option's dashed" $+      parserCompletionTest+        (setting [option, long "example", completer $ listCompleter ["hi"]])+        0+        []+        ["--example"]++    it "can complete a short option with a separate arg" $+      parserCompletionTest+        (setting [option, short 'e', completer $ listCompleter ["hi"]])+        1+        ["-e"]+        ["hi"]++    -- Don't think we want to support this.+    -- pending "can complete a short option in short-hand mode.++    it "can complete a long option" $+      parserCompletionTest+        (setting [option, long "example", completer $ listCompleter ["hi"]])+        1+        ["--example"]+        ["hi"]++    -- Don't think we want to support this+    -- pending "can complete a long option with equals sign"++    it "can complete both switches of a tuple" $+      parserCompletionTest+        ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])+        0+        []+        [ "--foo",+          "--bar"+        ]++    it "can complete both switches of a tuple, with a prefix" $+      parserCompletionTest+        ((,) <$> setting [switch (), long "bar"] <*> setting [switch (), long "baz"])+        0+        ["--b"]+        [ "--bar",+          "--baz"+        ]++    it "can complete both switches of an either" $+      parserCompletionTest+        (setting [switch (), long "foo"] <|> setting [switch (), long "bar"])+        0+        []+        [ "--foo",+          "--bar"+        ]++    it "can complete both switches of an either wrapped in optionals" $+      parserCompletionTest+        (optional (setting [switch (), long "foo"]) <|> optional (setting [switch (), long "bar"]))+        0+        []+        [ "--foo",+          "--bar"+        ]++    describe "commands" $ do+      let p =+            commands+              [ command "foo" "1" $ pure (),+                command "bar" "2" $ pure (),+                command "baz" "3" $ pure ()+              ]++      it "can complete a command argument" $+        parserCompletionTest+          p+          0+          []+          [ Completion "foo" (Just "1"),+            Completion "bar" (Just "2"),+            Completion "baz" (Just "3")+          ]++      it "can complete a command argument when it's been partially provided" $+        parserCompletionTest+          p+          0+          ["b"]+          [ Completion "bar" (Just "2"),+            Completion "baz" (Just "3")+          ]++    describe "completion after a command" $ do+      it "can complete a command with a switch" $+        parserCompletionTest+          (commands [command "foo" "1" $ setting [help "ex", switch (), short 'e', long "example"]])+          1+          ["foo"]+          [Completion "--example" (Just "ex")] -- Only the long version+      it "can complete a command's short switch" $+        parserCompletionTest+          (commands [command "foo" "1" $ setting [switch (), short 'e']])+          1+          ["foo", "-"]+          ["-e"]++      it "can complete a command's long switch from a single dash" $+        parserCompletionTest+          (commands [command "foo" "1" $ setting [switch (), long "example"]])+          1+          ["foo", "-"]+          ["--example"]++      it "can complete a command's long switch from a double dash" $+        parserCompletionTest+          (commands [command "foo" "1" $ setting [switch (), long "example"]])+          1+          ["foo", "--"]+          ["--example"]++      it "can complete a command's short option" $+        parserCompletionTest+          (commands [command "foo" "1" $ setting [option, short 'e', completer $ listCompleter ["hi"]]])+          2+          ["foo", "-e"]+          ["hi"]++      it "can complete a command's long option" $+        parserCompletionTest+          (commands [command "foo" "1" $ setting [option, long "example", completer $ listCompleter ["hi"]]])+          2+          ["foo", "--example"]+          ["hi"]++    it "can complete a file argument" $+      parserCompletionDescriptionTest+        (filePathSetting [help "file arg", argument])+        0+        []+        ["file arg"]++    it "can complete a file option" $+      parserCompletionDescriptionTest+        (filePathSetting [help "file arg", option, long "file"])+        1+        ["--file"]+        ["file arg"]++    it "can complete a directory argument" $+      parserCompletionDescriptionTest+        (directoryPathSetting [help "dir arg", argument])+        0+        []+        ["dir arg"]++    it "can complete a directory option" $+      parserCompletionDescriptionTest+        (directoryPathSetting [help "dir arg", option, long "file"])+        1+        ["--file"]+        ["dir arg"]++    it "no longer suggests a switch that has already been parsed" $+      parserCompletionTest+        ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])+        1+        ["--foo"]+        ["--bar"]++    it "no longer suggests an option that has already been parsed" $+      parserCompletionTest+        ( (,,)+            <$> setting [option, reader (str :: Reader String), long "foo"]+            <*> setting [option, reader (str :: Reader String), long "bar"]+            <*> setting [switch (), long "quux"]+        )+        2+        ["--foo", "foo"]+        [ "--bar",+          "--quux"+        ]++    it "no longer suggests an argument that has already been parsed" $+      parserCompletionDescriptionTest+        ( (,,)+            <$> setting [argument, reader (str :: Reader String), help "hi", completer $ listCompleter ["hi"]]+            <*> setting [argument, reader (str :: Reader String), help "ho", completer $ listCompleter ["ho"]]+            <*> setting [switch (), long "bar", help "hu"]+        )+        1+        ["foo"]+        [ "ho",+          "hu"+        ]++    describe "commands with a default command" $ do+      let p =+            commands+              [ command "foo" "1" $ pure (),+                command "bar" "2" $ pure (),+                command "baz" "3" $ pure (),+                defaultCommand "bar"+              ]++      it "still lists all commands when there is a default" $+        parserCompletionTest+          p+          0+          []+          [ Completion "foo" (Just "1"),+            Completion "bar" (Just "2"),+            Completion "baz" (Just "3")+          ]++      it "still filters commands by prefix when there is a default" $+        parserCompletionTest+          p+          0+          ["b"]+          [ Completion "bar" (Just "2"),+            Completion "baz" (Just "3")+          ]++      it "completes the default command's switch when no command is given" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $ setting [switch (), long "example"],+                command "bar" "2" $ pure (),+                defaultCommand "foo"+              ]+          )+          0+          ["--"]+          ["--example"]++      it "completes both commands and the default command's switch" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $ setting [switch (), long "example"],+                command "bar" "2" $ pure (),+                defaultCommand "foo"+              ]+          )+          0+          []+          [ Completion "foo" (Just "1"),+            Completion "bar" (Just "2"),+            "--example"+          ]++      it "completes the default command's option when no command is given" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $ setting [option, long "example", completer $ listCompleter ["hi"]],+                command "bar" "2" $ pure (),+                defaultCommand "foo"+              ]+          )+          0+          ["--"]+          ["--example"]++      it "completes the default command's option value when no command is given" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $ setting [option, long "example", reader (str :: Reader String), completer $ listCompleter ["hi"]],+                command "bar" "2" $ pure "bar",+                defaultCommand "foo"+              ]+          )+          1+          ["--example"]+          ["hi"]++      it "completes the default command's argument when no command is given" $+        parserCompletionDescriptionTest+          ( commands+              [ command "foo" "1" $ setting [argument, reader (str :: Reader String), help "arg help", completer $ listCompleter ["val"]],+                command "bar" "2" $ pure "bar",+                defaultCommand "foo"+              ]+          )+          0+          []+          -- Should include the default command's argument completer along with command names+          [ "1",+            "2",+            "arg help"+          ]++      it "completes inside the default command after consuming its switch" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $+                  (,)+                    <$> setting [switch (), long "first"]+                    <*> setting [switch (), long "second"],+                command "bar" "2" $ pure ((), ()),+                defaultCommand "foo"+              ]+          )+          1+          ["--first"]+          ["--second"]++      it "completes inside the default command after consuming its option" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $+                  (,)+                    <$> setting [option, reader (str :: Reader String), long "name"]+                    <*> setting [switch (), long "verbose"],+                command "bar" "2" $ pure ("bar", ()),+                defaultCommand "foo"+              ]+          )+          2+          ["--name", "hello"]+          ["--verbose"]++    describe "global options with commands and a default" $ do+      it "completes global options and commands together" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> commands+                [ command "foo" "1" $ pure (),+                  command "bar" "2" $ pure (),+                  defaultCommand "foo"+                ]+          )+          0+          []+          [ "--verbose",+            Completion "foo" (Just "1"),+            Completion "bar" (Just "2")+          ]++      it "completes commands after a global option" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> commands+                [ command "foo" "1" $ pure (),+                  command "bar" "2" $ pure (),+                  defaultCommand "foo"+                ]+          )+          1+          ["--verbose"]+          [ Completion "foo" (Just "1"),+            Completion "bar" (Just "2")+          ]++      it "completes the default command's options after a global option" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> commands+                [ command "foo" "1" $ setting [switch (), long "example"],+                  command "bar" "2" $ pure (),+                  defaultCommand "foo"+                ]+          )+          1+          ["--verbose"]+          [ Completion "foo" (Just "1"),+            Completion "bar" (Just "2"),+            "--example"+          ]++    describe "many" $ do+      it "can complete repeated switches" $+        parserCompletionTest+          (length <$> many (setting [switch (), long "verbose", short 'v']))+          0+          []+          ["--verbose"]++      it "can still complete after consuming one" $+        parserCompletionTest+          (length <$> many (setting [switch (), long "verbose", short 'v']))+          1+          ["--verbose"]+          ["--verbose"]++      it "can still complete after consuming two" $+        parserCompletionTest+          (length <$> many (setting [switch (), long "verbose", short 'v']))+          2+          ["-v", "-v"]+          ["--verbose"]++      it "can complete repeated options" $+        parserCompletionTest+          ( many+              ( setting+                  [ option,+                    reader (str :: Reader String),+                    long "include",+                    completer $ listCompleter ["foo", "bar"]+                  ]+              )+          )+          1+          ["--include"]+          [ "foo",+            "bar"+          ]++      it "can complete repeated options after one has already been parsed" $+        parserCompletionTest+          ( many+              ( setting+                  [ option,+                    reader (str :: Reader String),+                    long "include",+                    completer $ listCompleter ["foo", "bar"]+                  ]+              )+          )+          3+          [ "--include",+            "foo",+            "--include"+          ]+          [ "foo",+            "bar"+          ]++    describe "some" $ do+      it "can complete at least one switch" $+        parserCompletionTest+          (NE.length <$> someNonEmpty (setting [switch (), long "verbose", short 'v']))+          0+          []+          ["--verbose"]++      it "can still complete after consuming one" $+        parserCompletionTest+          (NE.length <$> someNonEmpty (setting [switch (), long "verbose", short 'v']))+          1+          ["--verbose"]+          ["--verbose"]++    describe "allOrNothing" $ do+      it "completes normally inside allOrNothing" $+        parserCompletionTest+          ( allOrNothing $+              (,)+                <$> setting [option, reader (str :: Reader String), long "host"]+                <*> setting [option, reader (str :: Reader String), long "port"]+          )+          0+          []+          [ "--host",+            "--port"+          ]++      it "still suggests the remaining option after one is consumed" $+        parserCompletionTest+          ( allOrNothing $+              (,)+                <$> setting [option, reader (str :: Reader String), long "host"]+                <*> setting [option, reader (str :: Reader String), long "port"]+          )+          2+          ["--host", "localhost"]+          ["--port"]++    describe "check" $ do+      it "completes through a checkMapEither" $+        parserCompletionTest+          ( checkMapEither+              (\s -> if null s then Left "empty" else Right s)+              (setting [argument, reader (str :: Reader String), completer $ listCompleter ["hello"]])+          )+          0+          []+          ["hello"]++      it "completes through a mapIO" $+        parserCompletionTest+          ( mapIO pure $+              setting [switch (), long "example"]+          )+          0+          []+          ["--example"]++    describe "hidden settings" $ do+      it "does not suggest hidden switches" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "visible"]+              <*> setting [switch (), long "secret", hidden]+          )+          0+          []+          ["--visible"]++      it "does not suggest hidden options" $+        parserCompletionTest+          ( (,)+              <$> setting [option, reader (str :: Reader String), long "visible"]+              <*> setting [option, reader (str :: Reader String), long "secret", hidden]+          )+          0+          []+          ["--visible"]++    describe "settings with only env or conf" $ do+      it "does not error on env-only settings combined with arg settings" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> setting [reader (str :: Reader String), env "MY_VAR"]+          )+          0+          []+          ["--verbose"]++    describe "enableDisableSwitch" $ do+      -- enableDisableSwitch uses a visible "dummy" with the+      -- (enable|disable)- prefix for documentation, while the actual+      -- --enable-feature and --disable-feature switches are hidden.+      it "completes the documented dummy flag" $+        parserCompletionTest+          ( enableDisableSwitch+              [ help "enable feature",+                name "feature",+                value False+              ]+          )+          0+          []+          [Completion "--(enable|disable)-feature" (Just "enable feature")]++      it "filters the documented flag by prefix" $+        parserCompletionTest+          ( enableDisableSwitch+              [ help "enable feature",+                name "feature",+                value False+              ]+          )+          0+          ["--(e"]+          [Completion "--(enable|disable)-feature" (Just "enable feature")]++    describe "choice" $ do+      it "completes across all choices" $+        parserCompletionTest+          ( choice+              [ setting [switch (), long "alpha"],+                setting [switch (), long "beta"],+                setting [switch (), long "gamma"]+              ]+          )+          0+          []+          [ "--alpha",+            "--beta",+            "--gamma"+          ]++    describe "withDefault" $ do+      it "completes the underlying parser" $+        parserCompletionTest+          (withDefault "default" $ setting [option, reader (str :: Reader String), long "name", completer $ listCompleter ["alice", "bob"]])+          1+          ["--name"]+          [ "alice",+            "bob"+          ]++      it "still suggests the option when it has a default" $+        parserCompletionTest+          (withDefault "default" $ setting [option, reader (str :: Reader String), long "name"])+          0+          []+          ["--name"]++    describe "nested commands" $ do+      let p =+            commands+              [ command "top" "top-level" $+                  commands+                    [ command "sub-a" "sub a" $ pure (),+                      command "sub-b" "sub b" $ pure ()+                    ],+                command "other" "other" $ pure ()+              ]++      it "completes top-level commands" $+        parserCompletionTest+          p+          0+          []+          [ Completion "top" (Just "top-level"),+            Completion "other" (Just "other")+          ]++      it "completes sub-commands after selecting a top-level command" $+        parserCompletionTest+          p+          1+          ["top"]+          [ Completion "sub-a" (Just "sub a"),+            Completion "sub-b" (Just "sub b")+          ]++      it "filters sub-commands by prefix" $+        parserCompletionTest+          p+          1+          ["top", "sub-a"]+          [Completion "sub-a" (Just "sub a")]++      it "completes nested default commands" $+        parserCompletionTest+          ( commands+              [ command "top" "top-level" $+                  commands+                    [ command "sub-a" "sub a" $ setting [switch (), long "flag"],+                      command "sub-b" "sub b" $ pure (),+                      defaultCommand "sub-a"+                    ],+                command "other" "other" $ pure ()+              ]+          )+          1+          ["top"]+          [ Completion "sub-a" (Just "sub a"),+            Completion "sub-b" (Just "sub b"),+            "--flag"+          ]++    describe "commands merged via alternative" $ do+      it "completes commands from both sides of an alternative" $+        parserCompletionTest+          ( commands [command "foo" "1" $ pure ()]+              <|> commands [command "bar" "2" $ pure ()]+          )+          0+          []+          [ Completion "foo" (Just "1"),+            Completion "bar" (Just "2")+          ]++    describe "arguments after double dash" $ do+      -- A bare -- is consumed as the argument value "--" when it's+      -- the only argument remaining, so completion sees nothing left.+      it "consumes -- as the argument value" $+        parserCompletionTest+          (setting [argument, reader (str :: Reader String), completer $ listCompleter ["file1", "file2"]])+          1+          ["--"]+          []++      -- After --, the completion should suggest positional arguments+      -- rather than switches. Currently it does not: the switch is+      -- still suggested because the completion engine does not+      -- account for -- separating options from arguments.+      it "suggests the switch even after -- (known bug)" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file1", "file2"]]+          )+          2+          ["--", "fi"]+          ["--verbose"]++    describe "mixed arguments and options" $ do+      it "can complete an option after a positional argument" $+        parserCompletionTest+          ( (,)+              <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["input"]]+              <*> setting [switch (), long "verbose"]+          )+          1+          ["input"]+          ["--verbose"]++      it "can complete a positional after an option" $+        parserCompletionDescriptionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> setting [argument, reader (str :: Reader String), help "file", completer $ listCompleter ["output"]]+          )+          1+          ["--verbose"]+          ["file"]++    describe "multiple arguments" $ do+      -- When no args have been typed, both argument completers fire+      -- because the completion engine doesn't know which one will be+      -- consumed first. This is the documented TODO in Completion.hs.+      it "completes both arguments when none have been typed" $+        parserCompletionTest+          ( (,)+              <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["src"]]+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dst"]]+          )+          0+          []+          [ "src",+            "dst"+          ]++      it "completes the second argument after the first" $+        parserCompletionTest+          ( (,)+              <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["src"]]+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dst"]]+          )+          1+          ["something"]+          ["dst"]++    describe "empty parser" $ do+      it "produces no completions for empty" $+        parserCompletionTest+          (empty :: Parser ())+          0+          []+          []++    describe "pure parser" $ do+      it "produces no completions for pure" $+        parserCompletionTest+          (pure () :: Parser ())+          0+          []+          []++    describe "select" $ do+      -- Select uses andCompletions: both branches contribute because+      -- the completion engine cannot know at completion time whether+      -- the first parser will return Left or Right.+      it "completes through a select" $+        parserCompletionTest+          ( select+              (Left <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["hello"]])+              (const <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["world"]])+          )+          0+          []+          [ "hello",+            "world"+          ]++    describe "withConfig" $ do+      it "completes the main parser through withConfig" $+        parserCompletionTest+          ( withConfig (pure Nothing) $+              setting [switch (), long "example"]+          )+          0+          []+          ["--example"]++      it "completes the config file option together with the main parser" $+        parserCompletionTest+          ( withConfig+              (Nothing <$ setting [option, reader (str :: Reader String), long "config-file", completer $ listCompleter ["config.yaml"]])+              (setting [switch (), long "verbose"])+          )+          0+          []+          -- Main parser completions come first (p2), then config parser completions (p1)+          [ "--verbose",+            "--config-file"+          ]++      it "completes the config file value along with main parser suggestions" $+        parserCompletionTest+          ( withConfig+              (Nothing <$ setting [option, reader (str :: Reader String), long "config-file", completer $ listCompleter ["config.yaml"]])+              (setting [switch (), long "verbose"])+          )+          1+          ["--config-file"]+          -- The main parser (p2) still suggests --verbose since it hasn't+          -- consumed anything, and the config parser (p1) offers the completer.+          [ "--verbose",+            "config.yaml"+          ]++      it "completes the main parser after the config option is consumed" $+        parserCompletionTest+          ( withConfig+              (Nothing <$ setting [option, reader (str :: Reader String), long "config-file", completer $ listCompleter ["config.yaml"]])+              (setting [switch (), long "verbose"])+          )+          2+          ["--config-file", "config.yaml"]+          ["--verbose"]++    describe "optional" $ do+      it "completes through an optional parser" $+        parserCompletionTest+          (optional $ setting [switch (), long "verbose"])+          0+          []+          ["--verbose"]++      it "completes an optional with an applicative" $+        parserCompletionTest+          ( (,)+              <$> optional (setting [option, reader (str :: Reader String), long "name", completer $ listCompleter ["alice"]])+              <*> setting [switch (), long "verbose"]+          )+          0+          []+          [ "--name",+            "--verbose"+          ]++      it "completes the second part when the optional is skipped" $+        parserCompletionTest+          ( (,)+              <$> optional (setting [option, reader (str :: Reader String), long "name"])+              <*> setting [switch (), long "verbose"]+          )+          0+          []+          [ "--name",+            "--verbose"+          ]++    describe "many with other parsers" $ do+      it "completes both many and a following switch" $+        parserCompletionTest+          ( (,)+              <$> many (setting [option, reader (str :: Reader String), long "include", completer $ listCompleter ["foo"]])+              <*> setting [switch (), long "verbose"]+          )+          0+          []+          [ "--include",+            "--verbose"+          ]++      it "completes both after consuming one many-option" $+        parserCompletionTest+          ( (,)+              <$> many (setting [option, reader (str :: Reader String), long "include", completer $ listCompleter ["foo"]])+              <*> setting [switch (), long "verbose"]+          )+          2+          ["--include", "foo"]+          [ "--include",+            "--verbose"+          ]++      it "completes many switches combined with a command" $+        parserCompletionTest+          ( (,)+              <$> many (setting [switch (), long "verbose", short 'v'])+              <*> commands+                [ command "run" "run it" $ pure (),+                  command "build" "build it" $ pure ()+                ]+          )+          0+          []+          [ "--verbose",+            Completion "run" (Just "run it"),+            Completion "build" (Just "build it")+          ]++      it "completes commands after consuming many switches" $+        parserCompletionTest+          ( (,)+              <$> many (setting [switch (), long "verbose", short 'v'])+              <*> commands+                [ command "run" "run it" $ pure (),+                  command "build" "build it" $ pure ()+                ]+          )+          2+          ["-v", "-v"]+          [ "--verbose",+            Completion "run" (Just "run it"),+            Completion "build" (Just "build it")+          ]++    describe "folded short switches" $ do+      it "can complete after a folded short switch is consumed" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), short 'v']+              <*> setting [switch (), short 'n']+          )+          1+          ["-v"]+          ["-n"]++      it "no longer suggests a switch consumed via folding" $+        parserCompletionTest+          ( (,,)+              <$> setting [switch (), short 'v']+              <*> setting [switch (), short 'n']+              <*> setting [switch (), short 'x']+          )+          1+          ["-vn"]+          ["-x"]++    describe "short option shorthand" $ do+      it "completes after a short option consumed in shorthand" $+        parserCompletionTest+          ( (,)+              <$> setting [option, reader (str :: Reader String), short 'f']+              <*> setting [switch (), long "verbose"]+          )+          1+          ["-ffoo.txt"]+          ["--verbose"]++    describe "deeply nested parsers" $ do+      it "completes through optional + many + applicative" $+        parserCompletionTest+          ( (,,)+              <$> optional (setting [option, reader (str :: Reader String), long "config"])+              <*> many (setting [switch (), long "verbose", short 'v'])+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file.txt"]]+          )+          0+          []+          [ "--config",+            "--verbose",+            "file.txt"+          ]++      it "completes correctly after all optional+many consumed" $+        parserCompletionTest+          ( (,,)+              <$> optional (setting [option, reader (str :: Reader String), long "config"])+              <*> many (setting [switch (), long "verbose", short 'v'])+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file.txt"]]+          )+          3+          [ "--config",+            "foo",+            "--verbose"+          ]+          [ "--verbose",+            "file.txt"+          ]++    describe "commands with options inside" $ do+      it "completes a command's many options" $+        parserCompletionTest+          ( commands+              [ command "run" "run it" $+                  many (setting [option, reader (str :: Reader String), long "arg", completer $ listCompleter ["val"]])+              ]+          )+          2+          ["run", "--arg"]+          ["val"]++      it "completes a command's many options after one is consumed" $+        parserCompletionTest+          ( commands+              [ command "run" "run it" $+                  many (setting [option, reader (str :: Reader String), long "arg", completer $ listCompleter ["val"]])+              ]+          )+          3+          [ "run",+            "--arg",+            "val"+          ]+          ["--arg"]++    describe "multiple commands with shared options" $ do+      it "completes options in the selected command only" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $ setting [switch (), long "alpha"],+                command "bar" "2" $ setting [switch (), long "beta"]+              ]+          )+          1+          ["foo"]+          ["--alpha"]++      it "does not leak options from other commands" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $ setting [switch (), long "alpha"],+                command "bar" "2" $ setting [switch (), long "beta"]+              ]+          )+          1+          ["bar"]+          ["--beta"]++    describe "alternative branch failures" $ do+      -- A parser that is not detected as isEmpty but still fails during+      -- completion (andCompletions fails because the inner empty fails).+      -- This avoids the (<|>) optimization that removes empty alternatives.+      let failingP = (empty :: Parser ()) *> pure ()+      it "restores state when first branch fails in an alternative" $+        parserCompletionTest+          (failingP <|> setting [switch (), long "foo"])+          0+          []+          ["--foo"]++      it "returns first branch completions when second branch fails" $+        parserCompletionTest+          (setting [switch (), long "foo"] <|> failingP)+          0+          []+          ["--foo"]++      it "returns no completions when both alternatives fail" $+        parserCompletionTest+          (failingP <|> failingP)+          0+          []+          []++    describe "applicative branch failures" $ do+      it "returns nothing when first applicative branch fails" $+        parserCompletionTest+          ((,) <$> ((empty :: Parser ()) *> pure ()) <*> setting [switch (), long "foo"])+          0+          []+          []++      it "returns nothing when second applicative branch fails" $+        parserCompletionTest+          ((,) <$> setting [switch (), long "foo"] <*> ((empty :: Parser String) *> pure ("" :: String)))+          0+          []+          []++    describe "many edge cases" $ do+      it "handles many where the inner parser fails" $+        parserCompletionTest+          (many ((empty :: Parser ()) *> pure ()))+          0+          []+          []++      it "returns first iteration completions when recursive call fails" $+        parserCompletionTest+          (many (commands [command "foo" "1" $ pure ()]))+          1+          ["foo"]+          [Completion "foo" (Just "1")]++    describe "command not found" $ do+      it "returns no completions for an unknown command" $+        parserCompletionTest+          (commands [command "foo" "1" $ pure (), command "bar" "2" $ pure ()])+          1+          ["unknown"]+          []++    describe "argument completer at end" $ do+      it "offers the argument completer at the end after a switch is consumed" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file"]]+          )+          1+          ["--verbose"]+          ["file"]++      it "does not offer option completer when not at end of args" $+        parserCompletionTest+          ( (,,)+              <$> setting [switch (), long "other"]+              <*> setting [option, reader (str :: Reader String), long "name", completer $ listCompleter ["val"]]+              <*> setting [switch (), long "extra"]+          )+          3+          [ "--name",+            "--other",+            "--extra"+          ]+          []++    describe "option with different dashed in args" $ do+      it "does not suggest an option when a different dashed is in the args" $+        parserCompletionTest+          ( (,)+              <$> setting [option, reader (str :: Reader String), long "output", completer $ listCompleter ["file.txt"]]+              <*> setting [option, reader (str :: Reader String), long "input", completer $ listCompleter ["data.csv"]]+          )+          1+          ["--input"]+          ["data.csv"]++    describe "default command parser failure" $ do+      let emptyS = empty :: Parser String+      it "falls back to explicit commands when default command parser fails" $+        parserCompletionTest+          ( commands+              [ command "foo" "1" $ (,) <$> setting [argument, reader (str :: Reader String)] <*> emptyS,+                command "bar" "2" $ pure ("", ""),+                defaultCommand "foo"+              ]+          )+          0+          []+          [ Completion "foo" (Just "1"),+            Completion "bar" (Just "2")+          ]++    describe "prefix filtering" $ do+      it "filters switches by typed prefix" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> setting [switch (), long "version"]+          )+          0+          ["--verb"]+          ["--verbose"]++      it "returns nothing when prefix matches nothing" $+        parserCompletionTest+          (setting [switch (), long "verbose"])+          0+          ["--xyz"]+          []++      it "filters commands by typed prefix" $+        parserCompletionDescriptionTest+          ( commands+              [ command "generate" "gen" $ pure (),+                command "get" "getter" $ pure (),+                command "build" "builder" $ pure ()+              ]+          )+          0+          ["ge"]+          [ "gen",+            "getter"+          ]++    describe "argument completion with dashed values" $ do+      -- When a dashed-looking value is provided as a positional argument,+      -- the completion engine treats it as a potential switch (first+      -- consumeArgument possibility) instead of consuming it.  This+      -- causes the argument's completer to fire even though a value was+      -- already provided.+      it "should not offer the argument completer after a dashed value is consumed" $+        parserCompletionTest+          (setting [argument, reader (str :: Reader String), completer $ listCompleter ["file"]])+          1+          ["--foo"]+          []++      -- Same issue in a tuple: the first argument's completer fires+      -- alongside the second's, even though the first argument already+      -- has a value.+      it "should only complete the second argument after a dashed first argument" $+        parserCompletionTest+          ( (,)+              <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file"]]+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dest"]]+          )+          1+          ["--foo"]+          ["dest"]++    describe "partial option value completion" $ do+      it "filters option values by typed prefix" $+        parserCompletionTest+          ( setting+              [ option,+                reader (str :: Reader String),+                long "name",+                completer $ listCompleter ["alice", "bob"]+              ]+          )+          1+          ["--name", "al"]+          ["alice"]++      it "returns all option values when prefix is empty" $+        parserCompletionTest+          ( setting+              [ option,+                reader (str :: Reader String),+                long "name",+                completer $ listCompleter ["alice", "bob"]+              ]+          )+          1+          ["--name", ""]+          [ "alice",+            "bob"+          ]++    describe "partial argument completion" $ do+      it "filters argument values by typed prefix" $+        parserCompletionTest+          ( setting+              [ argument,+                reader (str :: Reader String),+                completer $ listCompleter ["file1", "file2", "data"]+              ]+          )+          0+          ["fi"]+          [ "file1",+            "file2"+          ]++      it "filters argument values by a different prefix" $+        parserCompletionTest+          ( setting+              [ argument,+                reader (str :: Reader String),+                completer $ listCompleter ["file1", "file2", "data"]+              ]+          )+          0+          ["da"]+          ["data"]++    describe "partial command name with inner option completion" $ do+      it "completes a command's options after selecting by prefix" $+        parserCompletionTest+          ( commands+              [ command "deploy" "deploy it" $ setting [switch (), long "force"],+                command "debug" "debug it" $ pure ()+              ]+          )+          1+          ["deploy"]+          ["--force"]++    describe "completion at a middle index" $ do+      it "completes the value of an option at its value position with a matching prefix" $+        parserCompletionTest+          ( (,)+              <$> setting+                [ option,+                  reader (str :: Reader String),+                  long "foo",+                  completer $ listCompleter ["val1", "val2"]+                ]+              <*> setting [switch (), long "bar"]+          )+          1+          ["--foo", "v", "--bar"]+          [ "val1",+            "val2"+          ]++      it "filters completions when cursor word narrows the match" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "alpha"]+              <*> setting [switch (), long "beta"]+          )+          0+          ["--alpha", "--beta"]+          ["--alpha"]++    describe "partial long option prefix with multiple matches" $ do+      it "filters options by a short prefix matching multiple" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> setting [switch (), long "version"]+          )+          0+          ["--ver"]+          [ "--verbose",+            "--version"+          ]++      it "filters options by a longer prefix matching one" $+        parserCompletionTest+          ( (,)+              <$> setting [switch (), long "verbose"]+              <*> setting [switch (), long "version"]+          )+          0+          ["--verb"]+          ["--verbose"]++    describe "partial argument completion after consuming earlier args" $ do+      it "filters the second argument's completer by prefix after the first is consumed" $+        parserCompletionTest+          ( (,)+              <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["src"]]+              <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dst", "data"]]+          )+          1+          ["src", "ds"]+          ["dst"]++    describe "commands with partial option value inside" $ do+      it "filters a command's option values by prefix" $+        parserCompletionTest+          ( commands+              [ command "run" "run it" $+                  setting+                    [ option,+                      reader (str :: Reader String),+                      long "output",+                      completer $ listCompleter ["file.txt", "final.out", "data.csv"]+                    ]+              ]+          )+          2+          ["run", "--output", "fi"]+          [ "file.txt",+            "final.out"+          ]++    describe "many with partial values" $ do+      it "filters many option values by prefix" $+        parserCompletionTest+          ( many+              ( setting+                  [ option,+                    reader (str :: Reader String),+                    long "include",+                    completer $ listCompleter ["foo", "bar"]+                  ]+              )+          )+          1+          ["--include", "fo"]+          ["foo"]++      it "filters many option values by prefix after one is consumed" $+        parserCompletionTest+          ( many+              ( setting+                  [ option,+                    reader (str :: Reader String),+                    long "include",+                    completer $ listCompleter ["foo", "bar"]+                  ]+              )+          )+          3+          ["--include", "foo", "--include", "ba"]+          ["bar"]++  sequential . doNotRandomiseExecutionOrder+    $ aroundAll+      ( \func -> withSystemTempDir "completion-spec" $ \tdir -> do+          exampleFile <- resolveFile tdir "example.txt"+          writeFile (fromAbsFile exampleFile) ""+          exampleDir <- resolveDir tdir "exampledir"+          createDir exampleDir+          nested <- resolveFile exampleDir "nested.txt"+          writeFile (fromAbsFile nested) ""+          func tdir+      )+    $ do+      let evalQuery p ix ws tdir = withCurrentDir tdir $ do+            let arg = fromMaybe "" $ listToMaybe $ drop ix ws+            let completions = pureCompletionQuery p ix ws+            evalCompletions arg completions+          suggestionValues = map (completionResultValue . completionSuggestion)++      describe "file and directory completion" $ do+        describe "filePathSetting" $ do+          itWithOuter "completes files and directories for a file argument" $ \tdir -> do+            results <- evalQuery (filePathSetting [help "f", argument]) 0 [] tdir+            suggestionValues results `shouldBe` ["example.txt", "exampledir/"]++          itWithOuter "completes files and directories after a file option's dashed" $ \tdir -> do+            results <- evalQuery (filePathSetting [help "f", option, long "file"]) 1 ["--file"] tdir+            suggestionValues results `shouldBe` ["example.txt", "exampledir/"]++          itWithOuter "filters by prefix" $ \tdir -> do+            results <- evalQuery (filePathSetting [help "f", argument]) 0 ["exampledi"] tdir+            suggestionValues results `shouldBe` ["exampledir/"]++          -- Directories end in /, files do not.  This convention is how+          -- shells decide whether to append a trailing space after a+          -- completion.  The exact values are checked by+          -- "completes files and directories for a file argument" above;+          -- this test re-states the invariant explicitly.+          itWithOuter "directories end in / and files do not" $ \tdir -> do+            results <- evalQuery (filePathSetting [help "f", argument]) 0 [] tdir+            suggestionValues results `shouldBe` ["example.txt", "exampledir/"]++          itWithOuter "files are final and directories are not final" $ \tdir -> do+            results <- evalQuery (filePathSetting [help "f", argument]) 0 [] tdir+            map (completionResultFinality . completionSuggestion) results+              `shouldBe` [CompletionFinal, CompletionNotFinal]++          itWithOuter "carries the description through to file completions" $ \tdir -> do+            results <- evalQuery (filePathSetting [help "my file", argument]) 0 [] tdir+            map completionDescription results `shouldBe` [Just "my file", Just "my file"]++        describe "directoryPathSetting" $ do+          itWithOuter "completes only directories for a directory argument" $ \tdir -> do+            results <- evalQuery (directoryPathSetting [help "d", argument]) 0 [] tdir+            suggestionValues results `shouldBe` ["exampledir/"]++          itWithOuter "completes only directories after a directory option's dashed" $ \tdir -> do+            results <- evalQuery (directoryPathSetting [help "d", option, long "dir"]) 1 ["--dir"] tdir+            suggestionValues results `shouldBe` ["exampledir/"]++          itWithOuter "never includes files" $ \tdir -> do+            results <- evalQuery (directoryPathSetting [help "d", argument]) 0 [] tdir+            suggestionValues results `shouldBe` ["exampledir/"]++          itWithOuter "directories are not final" $ \tdir -> do+            results <- evalQuery (directoryPathSetting [help "d", argument]) 0 [] tdir+            map (completionResultFinality . completionSuggestion) results+              `shouldBe` [CompletionNotFinal]++          itWithOuter "carries the description through to directory completions" $ \tdir -> do+            results <- evalQuery (directoryPathSetting [help "my dir", argument]) 0 [] tdir+            map completionDescription results `shouldBe` [Just "my dir"]++        describe "combined with other settings" $ do+          itWithOuter "includes file completions alongside other options" $ \tdir -> do+            let parser =+                  (,)+                    <$> filePathSetting [help "f", option, long "file"]+                    <*> setting [switch (), long "verbose"]+            results <- evalQuery parser 1 ["--file"] tdir+            suggestionValues results `shouldBe` ["example.txt", "exampledir/", "--verbose"]++          itWithOuter "includes directory completions alongside other options" $ \tdir -> do+            let parser =+                  (,)+                    <$> directoryPathSetting [help "d", option, long "dir"]+                    <*> setting [switch (), long "verbose"]+            results <- evalQuery parser 1 ["--dir"] tdir+            suggestionValues results `shouldBe` ["exampledir/", "--verbose"]++          -- After typing the dashed of an option that takes a value, the+          -- cursor is in the value position.  The option's completer+          -- should fire first, then other options should follow.+          --+          -- Shell sends index pointing at the empty cursor word:+          --   exe --dir <tab> -> index=1, words=["--dir", ""]+          itWithOuter "completes directory option value first, then other options" $ \tdir -> do+            let parser =+                  (,)+                    <$> directoryPathSetting [help "d", option, long "dir"]+                    <*> setting [switch (), long "verbose"]+            results <- evalQuery parser 1 ["--dir", ""] tdir+            suggestionValues results `shouldBe` ["exampledir/", "--verbose"]++          itWithOuter "completes file option value first, then other options" $ \tdir -> do+            let parser =+                  (,)+                    <$> filePathSetting [help "f", option, long "file"]+                    <*> setting [switch (), long "verbose"]+            results <- evalQuery parser 1 ["--file", ""] tdir+            suggestionValues results `shouldBe` ["example.txt", "exampledir/", "--verbose"]++          -- A default command with an argument combined with a+          -- directory option causes the directory completer not to fire+          -- when completing the option's value.+          itWithOuter "completes directory option value alongside default command with argument" $ \tdir -> do+            let parser =+                  (,)+                    <$> commands+                      [ command "file" "f" $+                          setting [help "f", argument, reader (str :: Reader String)],+                        defaultCommand "file"+                      ]+                    <*> directoryPathSetting [help "a", option, long "archive-dir"]+            results <- evalQuery parser 1 ["--archive-dir", ""] tdir+            suggestionValues results `shouldSatisfy` elem "exampledir/"
test/OptEnvConf/RunSpec.hs view
@@ -8,6 +8,7 @@ import Autodocodec import Control.Applicative import Control.Concurrent+import Control.Monad (void) import Data.Aeson as JSON (Object, Value (Null), toJSON) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KeyMap@@ -25,6 +26,7 @@ import qualified OptEnvConf.EnvMap as EnvMap import OptEnvConf.EnvMap.Gen () import OptEnvConf.Error+import qualified System.Timeout as Timeout import Test.Syd import Test.Syd.Validity import Text.Colour@@ -129,6 +131,45 @@               let expected = NE.toList ls               shouldParse p args e mConf expected +      -- Regression test for an exponential-time bug.+      --+      -- A parser shaped like+      --+      --     choice [some argument, many --filter] <*> many --opt1 <*> many --opt2 <*> ...+      --+      -- combined with input that has both several @--opt1 VAL@ pairs and at+      -- least one @--opt2 VAL@ pair scaled super-linearly: in the broken+      -- version, every added @--opt2@ multiplied total parse time by ~3x.+      --+      -- The original report came from a sydtest mutation suite invocation+      -- with 6 @--mutation@ + 6 @--mutation-suite-exe@ flags taking+      -- > 30 seconds of CPU just to parse settings. With the parser shape+      -- below and 6 @--opt1@ + 6 @--opt2@ pairs the broken version takes+      -- many minutes; the fixed version completes in milliseconds.+      it "parses many-many input in bounded time (regression: no exponential blowup)" $ do+        let parser :: Parser ()+            parser =+              void $+                (,,)+                  <$> choice+                    [ some (setting [reader str, argument, help "F", metavar "F"] :: Parser String),+                      many (setting [reader str, option, long "filter", help "F", metavar "F"])+                    ]+                  <*> many (setting [reader str, option, long "opt1", help "x", metavar "X"] :: Parser String)+                  <*> many (setting [reader str, option, long "opt2", help "x", metavar "X"] :: Parser String)+        let args =+              parseArgs $+                concat (replicate 6 ["--opt1", "/v"])+                  ++ concat (replicate 6 ["--opt2", "/v"])+        mResult <-+          Timeout.timeout+            (500 * 1000)+            (runParserOn allCapabilities Nothing parser args EnvMap.empty Nothing)+        case mResult of+          Nothing -> expectationFailure "parser took longer than 0.5 seconds; likely exponential blowup"+          Just (Left errs) -> expectationFailure $ T.unpack $ renderChunksText With24BitColours $ renderErrors errs+          Just (Right ()) -> pure ()+     describe "MapIO" $ do       it "can run an IO action on the result of a parser" $         forAllValid $ \e ->@@ -526,16 +567,15 @@        -- Unfolding short options as well       argParseSpecs-        ( (,)-            <$> ( length-                    <$> many-                      ( setting-                          [ help "verbosity",-                            switch (),-                            short 'v'-                          ]-                      )+        ( ( (,) . length+              <$> many+                ( setting+                    [ help "verbosity",+                      switch (),+                      short 'v'+                    ]                 )+          )             <*> setting               [ reader str,                 option,@@ -650,8 +690,9 @@         verbosityArgsExamples       argParseSpecs         ( swap-            <$> ( (,)-                    <$> (length <$> many (setting [switch (), short 'v', long "verbose"]))+            <$> ( ( (,) . length+                      <$> many (setting [switch (), short 'v', long "verbose"])+                  )                     <*> many (setting [reader str, argument])                 ) ::             Parser ([String], Int)
test_resources/completion/fish-completion-script.fish view
@@ -8,10 +8,11 @@       set tmpline $tmpline --completion-word $arg     end     for opt in (/usr/bin/example-executable $tmpline)-      if test -d $opt-        echo -E "$opt/"+      set -l val (string split \t -- $opt)[1]+      if test -d $val+        echo -E "$val/"       else-        echo -E "$opt"+        echo -E "$val"       end     end end
test_resources/completion/zsh-completion-script.zsh view
@@ -22,11 +22,18 @@      if [[ $word[1] == "-" ]]; then        local desc=("$parts[1] ($parts[2])")        compadd -d desc -- $parts[1]+     elif [[ $parts[3] == 'N' ]]; then+       local desc=($(print -f  "%-019s -- %s" $parts[1] $parts[2]))+       compadd -f -l -S '' -d desc -- $parts[1]      else        local desc=($(print -f  "%-019s -- %s" $parts[1] $parts[2]))-       compadd -l -d desc -- $parts[1]+       compadd -f -l -d desc -- $parts[1]      fi   else-    compadd -f -- $word+    if [[ $parts[3] == 'N' ]]; then+      compadd -f -S '' -- $parts[1]+    else+      compadd -f -- $parts[1]+    fi   fi done
test_resources/docs/big-config/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:25+# To update this it, run the test with --golden-reset { lib }: {   big = lib.mkOption {
test_resources/docs/empty/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:21+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/enable-disable-optional/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:30+# To update this it, run the test with --golden-reset { lib }: {   example = lib.mkOption {
test_resources/docs/enable-disable/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:28+# To update this it, run the test with --golden-reset { lib }: {   example = lib.mkOption {
test_resources/docs/greet/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:37+# To update this it, run the test with --golden-reset { lib }: {   greeting = lib.mkOption {
test_resources/docs/hidden/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:27+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/many-args/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:22+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/optional/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:24+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/same-help/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:33+# To update this it, run the test with --golden-reset { lib }: {   int = lib.mkOption {
test_resources/docs/secret/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:35+# To update this it, run the test with --golden-reset { lib }: {   first-secret-file = lib.mkOption {
test_resources/docs/some-args/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:23+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/sub-commands/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:39+# To update this it, run the test with --golden-reset { lib }: {   name = lib.mkOption {
test_resources/docs/sub-settings/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:26+# To update this it, run the test with --golden-reset { lib }: {   foo = lib.mkOption {
test_resources/docs/sum-type/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:34+# To update this it, run the test with --golden-reset { lib }: {   sum-type = lib.mkOption {
test_resources/docs/three-commands/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:38+# To update this it, run the test with --golden-reset { lib }: {   enable = lib.mkOption {
test_resources/docs/verbose/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:32+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/with-default/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:36+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/yes-no-optional/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:31+# To update this it, run the test with --golden-reset { lib }: {   example = lib.mkOption {
test_resources/docs/yes-no/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:29+# To update this it, run the test with --golden-reset { lib }: {   example = lib.mkOption {
test_resources/error/all-or-nothing-relevant.txt view
@@ -1,5 +1,5 @@ Missing option: [37m--bar[m [34mThis one will not exist[m You are seeing this error because at least one, but not all, of the settings in an allOrNothing (or subSettings) parser have been defined. The following settings have been parsed:-[36mtest/OptEnvConf/ErrorSpec.hs:153:21 in main:OptEnvConf.ErrorSpec[m+[36mtest/OptEnvConf/ErrorSpec.hs:153:21 in opt-env-conf-test:OptEnvConf.ErrorSpec[m Hit the 'empty' case of the Parser type, this should not happen.
test_resources/error/all-or-nothing.txt view
@@ -1,4 +1,4 @@ Missing option: [37m--bar[m [34mThis one will not exist[m You are seeing this error because at least one, but not all, of the settings in an allOrNothing (or subSettings) parser have been defined. The following settings have been parsed:-[36mtest/OptEnvConf/ErrorSpec.hs:141:19 in main:OptEnvConf.ErrorSpec[m+[36mtest/OptEnvConf/ErrorSpec.hs:141:19 in opt-env-conf-test:OptEnvConf.ErrorSpec[m
test_resources/help/toplevel-pure-with-invalid-port.txt view
@@ -1,4 +1,4 @@-[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mFILE_PATH[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND+[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND  example program description @@ -37,8 +37,8 @@       [33m<string>[m    [34mSecret key[m-  option: [37m--payment-secret-key[m [33mFILE_PATH[m-  env: [37mPAYMENT_SECRET_KEY[m [33mFILE_PATH[m+  option: [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m+  env: [37mPAYMENT_SECRET_KEY[m [33mSECRET_KEY_FILE[m   config:     [37mpayment.secret-key[m: # [32mor null[m       [33m<string>[m
test_resources/help/toplevel-pure.txt view
@@ -1,4 +1,4 @@-[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mFILE_PATH[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND+[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND  example program description @@ -37,8 +37,8 @@       [33m<string>[m    [34mSecret key[m-  option: [37m--payment-secret-key[m [33mFILE_PATH[m-  env: [37mPAYMENT_SECRET_KEY[m [33mFILE_PATH[m+  option: [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m+  env: [37mPAYMENT_SECRET_KEY[m [33mSECRET_KEY_FILE[m   config:     [37mpayment.secret-key[m: # [32mor null[m       [33m<string>[m
test_resources/help/wrong-command.txt view
@@ -1,4 +1,4 @@-[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mFILE_PATH[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND+[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND  example program description @@ -37,8 +37,8 @@       [33m<string>[m    [34mSecret key[m-  option: [37m--payment-secret-key[m [33mFILE_PATH[m-  env: [37mPAYMENT_SECRET_KEY[m [33mFILE_PATH[m+  option: [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m+  env: [37mPAYMENT_SECRET_KEY[m [33mSECRET_KEY_FILE[m   config:     [37mpayment.secret-key[m: # [32mor null[m       [33m<string>[m
test_resources/lint/config-without-load.txt view
@@ -3,4 +3,4 @@   [31mInvalid Setting:[m   [33mconf[m or [33mname[m was called with no way to load configuration.   You can load configuration with [33mwithConfig[m, or explicitly not load any configuration with [33mwithoutConfig[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:124:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:124:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/dash-in-long.txt view
@@ -4,4 +4,4 @@   [33mlong[m may not start with a '-'.   Found [33mlong[m "--switch".   Try [33mlong[m "switch" instead.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:33:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:33:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/dash-in-short.txt view
@@ -3,4 +3,4 @@   [31mInvalid Setting:[m   [33mshort[m may not contain a '-'.   Found [33mshort[m '-'.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:25:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:25:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/empty-setting.txt view
@@ -3,4 +3,4 @@   [31mInvalid Setting:[m   This [33msetting[m parses nothing.   Add an [33margument[m, [33mswitch[m, [33moption[m, [33menv[m, [33mconf[m, or [33mvalue[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:19:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:19:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-commands.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33mcommands[m was called with an empty list.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:121:6 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:121:6 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-dashed-for-option.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33moption[m has no [33mlong[m or [33mshort[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:67:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:67:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-dashed-for-switch.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33mswitch[m has no [33mlong[m or [33mshort[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:98:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:98:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-metavar-for-argument.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33margument[m has no [33mmetavar[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:49:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:49:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-metavar-for-env.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33menv[m or [33mname[m has no [33mmetavar[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:112:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:112:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-metavar-for-option.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33moption[m or [33mname[m has no [33mmetavar[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:88:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:88:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-option-or-switch-for-dashed.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33mlong[m or [33mshort[m has no [33moption[m or [33mswitch[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:77:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:77:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-reader-for-argument.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33margument[m has no [33mreader[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:41:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:41:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-reader-for-env.txt view
@@ -2,8 +2,8 @@      [31mInvalid Setting:[m   [33menv[m or [33mname[m has no [33mreader[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in opt-env-conf-test:OptEnvConf.LintSpec[m      [31mInvalid Setting:[m   [33menv[m or [33mname[m has no [33mmetavar[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-reader-for-option.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33moption[m or [33mname[m has no [33mreader[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:58:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:58:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/null-setting.txt view
@@ -2,9 +2,9 @@      [31mInvalid Setting:[m   missing [33mhelp[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in opt-env-conf-test:OptEnvConf.LintSpec[m      [31mInvalid Setting:[m   This [33msetting[m parses nothing.   Add an [33margument[m, [33mswitch[m, [33moption[m, [33menv[m, [33mconf[m, or [33mvalue[m.-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/unknown-default-command.txt view
@@ -2,4 +2,4 @@      [31mInvalid Setting:[m   [33mdefaultCommand[m was called with an unknown command: [35mb[m-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:185:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:185:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/unreadable-example.txt view
@@ -3,4 +3,4 @@   [31mInvalid Setting:[m   [33mexample[m was called with an example that none of the [33mreader[ms succeed in reading.   Example: NaN-  Defined at: [36mtest/OptEnvConf/LintSpec.hs:132:7 in main:OptEnvConf.LintSpec[m+  Defined at: [36mtest/OptEnvConf/LintSpec.hs:132:7 in opt-env-conf-test:OptEnvConf.LintSpec[m