oCompletion :: ExecParams -> Word -> Shell -> [String] -> IO ()
doCompletion execParams index shell _ =
    case Options.splitArguments' words of
        (_, _, []) ->
            completeExecSubcommand

        (_, indexBound, commandName : commandArguments)
          | not canCompleteCommand || index <= indexBound ->
                completeExecSubcommand
          | otherwise ->
                doCommandCompletion protocol config shell commandName
                    (index - indexBound) commandArguments
  where
    ExecParams
        { protocol
        , config = config@Config{commands}
        , commandAndItsArguments = words
        } = execParams

    completeExecSubcommand :: IO ()
    completeExecSubcommand
      | "--expression=" `List.isPrefixOf` pat = do
            -- TODO: Very similar code can be found in `config` (internal)
            -- subcommand.  Should this be somewhere in `command-wrapper-core`?
            let prefix = "--expression="

                pat' = List.drop (length prefix) pat

                envPrefix = "env:"

                hasPathPrefix = or
                    [ "./" `List.isPrefixOf` pat'
                    , "../" `List.isPrefixOf` pat'
                    , "~" `List.isPrefixOf` pat'
                    ]

            if
              | envPrefix `List.isPrefixOf` pat' ->
                    -- Syntax `env:VARIABLE` is a valid import in Dhall.
                    queryEnvironmentVariables defEnvironmentVariablesOptions
                        { word = List.drop (length envPrefix) pat'
                        , prefix = prefix <> envPrefix
                        }

              | pat' == "." || pat' == ".." ->
                    printMatching ((prefix <>) <$> ["./", "../"])

              | hasPathPrefix ->
                    -- File paths, even `~/some/path`, are valid Dhall
                    -- expressions.  However, relative paths like `foo/bar` are
                    -- not, they need to start with `./` or `../`
                    queryFileSystem defFileSystemOptions
                        { appendSlashToSingleDirectoryResult = True
                        , expandTilde = True
                        , prefix
                        , word = pat'
                        }

              | otherwise ->
                    printMatching ((prefix <>) <$> ["./", "../", "~/", "env:"])

      | hadListOrTreeOrHelp =
            pure ()
      | hadExpression, any (== "--notify") wordsBeforePattern =
            pure ()
      | hadExpression =
            printMatching ["--notify"]
      | hadNotifyOrPrint =
            printMatching commandNames
      | hadPrintCompletion =
            printMatching (printCompletionOptions <> commandNames)
      | otherwise =
            printMatching (allOptions <> commandNames)

    pat = fromMaybe (lastDef "" words) (atMay words (fromIntegral index))

    printMatching = mapM_ putStrLn . List.filter (pat `List.isPrefixOf`)

    wordsBeforePattern = List.take (fromIntegral index) words

    commandNames = Text.unpack . (name :: NamedCommand -> Text) <$> commands

    hadListOrTreeOrHelp = any
        (`elem` ["-l", "--ls", "--list", "-t", "--tree", "-h", "--help"])
        wordsBeforePattern

    hadExpression = any ("--expression=" `List.isPrefixOf`) wordsBeforePattern

    hadNotifyOrPrint = any (`elem` ["--notify", "--print"]) wordsBeforePattern

    hadPrintCompletion = any (== "--print-completion") wordsBeforePattern

    canCompleteCommand = not (hadListOrTreeOrHelp || hadExpression)

    printCompletionOptions =
        ( if any ("--index=" `List.isPrefixOf`) wordsBeforePattern
             then []
             else ["--index="]
        )
        <>  ( if any ("--shell=" `List.isPrefixOf`) wordsBeforePattern
                  then []
                  else ["--shell=" <> s | s <- ["bash", "fish", "zsh"]]
            )

    allOptions =
        [ "-l", "--ls", "--list"
        , "-t", "--tree"
        , "--print"
        , "--print-completion"
        , "--expression="
        , "--notify"
        , "-h", "--help"
        , "--"
        ]

doCompletion :: ExecParams -> Word -> Shell -> [String] -> IO ()
doCompletion execParams index shell _ =
    case Options.splitArguments' words of
        (_, _, []) ->
            completeExecSubcommand

        (_, indexBound, commandName : commandArguments)
          | not canCompleteCommand || index <= indexBound ->
                completeExecSubcommand
          | otherwise ->
                doCommandCompletion protocol config shell commandName
                    (index - indexBound) commandArguments
  where
    ExecParams
        { protocol
        , config = config@Config{commands}
        , commandAndItsArguments = words
        } = execParams

    completeExecSubcommand :: IO ()
    completeExecSubcommand
      | "--expression=" `List.isPrefixOf` pat = do
            -- TODO: Very similar code can be found in `config` (internal)
            -- subcommand.  Should this be somewhere in `command-wrapper-core`?
            let prefix = "--expression="

                pat' = List.drop (length prefix) pat

                envPrefix = "env:"

                hasPathPrefix = or
                    [ "./" `List.isPrefixOf` pat'
                    , "../" `List.isPrefixOf` pat'
                    , "~" `List.isPrefixOf` pat'
                    ]

            if
              | envPrefix `List.isPrefixOf` pat' ->
                    -- Syntax `env:VARIABLE` is a valid import in Dhall.
                    queryEnvironmentVariables defEnvironmentVariablesOptions
                        { word = List.drop (length envPrefix) pat'
                        , prefix = prefix <> envPrefix
                        }

              | pat' == "." || pat' == ".." ->
                    printMatching ((prefix <>) <$> ["./", "../"])

              | hasPathPrefix ->
                    -- File paths, even `~/some/path`, are valid Dhall
                    -- expressions.  However, relative paths like `foo/bar` are
                    -- not, they need to start with `./` or `../`
                    queryFileSystem defFileSystemOptions
                        { appendSlashToSingleDirectoryResult = True
                        , expandTilde = True
                        , prefix
                        , word = pat'
                        }

              | otherwise ->
                    printMatching ((prefix <>) <$> ["./", "../", "~/", "env:"])

      | hadListOrTreeOrHelp =
            pure ()
      | hadExpression, any (== "--notify") wordsBeforePattern =
            pure ()
      | hadExpression =
            printMatching ["--notify"]
      | hadNotifyOrPrint =
            printMatching commandNames
      | hadPrintCompletion =
            printMatching (printCompletionOptions <> commandNames)
      | otherwise =
            printMatching (allOptions <> commandNames)

    pat = fromMaybe (lastDef "" words) (atMay words (fromIntegral index))

    printMatching = mapM_ putStrLn . List.filter (pat `List.isPrefixOf`)

    wordsBeforePattern = List.take (fromIntegral index) words

    commandNames = Text.unpack . (name :: NamedCommand -> Text) <$> commands

    hadListOrTreeOrHelp = any
        (`elem` ["-l", "--ls", "--list", "-t", "--tree", "-h", "--help"])
        wordsBeforePattern

    hadExpression = any ("--expression=" `List.isPrefixOf`) wordsBeforePattern

    hadNotifyOrPrint = any (`elem` ["--notify", "--print"]) wordsBeforePattern

    hadPrintCompletion = any (== "--print-completion") wordsBeforePattern

    canCompleteCommand = not (hadListOrTreeOrHelp || hadExpression)

    printCompletionOptions =
        ( if any ("--index=" `List.isPrefixOf`) wordsBeforePattern
             then []
             else ["--index="]
        )
        <>  ( if any ("--shell=" `List.isPrefixOf`) wordsBeforePattern
                  then []
                  else ["--shell=" <> s | s <- ["bash", "fish", "zsh"]]
            )

    allOptions =
        [ "-l", "--ls", "--list"
        , "-t", "--tree"
        , "--print"
        , "--print-completion"
        , "--expression="
        , "--notify"
        , "-h", "--help"
        , "--"
        ]
doCompletion execParams index shell _ =
    case Options.splitArguments' words of
        (_, _, []) ->
            completeExecSubcommand

        (_, indexBound, commandName : commandArguments)
          | not canCompleteCommand || index <= indexBound ->
                completeExecSubcommand
          | otherwise ->
                doCommandCompletion protocol config shell commandName
                    (index - indexBound) commandArguments
  where
    ExecParams
        { protocol
        , config = config@Config{commands}
        , commandAndItsArguments = words
        } = execParams

    completeExecSubcommand :: IO ()
    completeExecSubcommand
      | "--expression=" `List.isPrefixOf` pat = do
            -- TODO: Very similar code can be found in `config` (internal)
            -- subcommand.  Should this be somewhere in `command-wrapper-core`?
            let prefix = "--expression="

                pat' = List.drop (length prefix) pat

                envPrefix = "env:"

                hasPathPrefix = or
                    [ "./" `List.isPrefixOf` pat'
                    , "../" `List.isPrefixOf` pat'
                    , "~" `List.isPrefixOf` pat'
                    ]

            if
              | envPrefix `List.isPrefixOf` pat' ->
                    -- Syntax `env:VARIABLE` is a valid import in Dhall.
                    queryEnvironmentVariables defEnvironmentVariablesOptions
                        { word = List.drop (length envPrefix) pat'
                        , prefix = prefix <> envPrefix
                        }

              | pat' == "." || pat' == ".." ->
                    printMatching ((prefix <>) <$> ["./", "../"])

              | hasPathPrefix ->
                    -- File paths, even `~/some/path`, are valid Dhall
                    -- expressions.  However, relative paths like `foo/bar` are
                    -- not, they need to start with `./` or `../`
                    queryFileSystem defFileSystemOptions
                        { appendSlashToSingleDirectoryResult = True
                        , expandTilde = True
                        , prefix
                        , word = pat'
                        }

              | otherwise ->
                    printMatching ((prefix <>) <$> ["./", "../", "~/", "env:"])

      | hadListOrTreeOrHelp =
            pure ()
      | hadExpression, any (== "--notify") wordsBeforePattern =
            pure ()
      | hadExpression =
            printMatching ["--notify"]
      | hadNotifyOrPrint =
            printMatching commandNames
      | hadPrintCompletion =
            printMatching (printCompletionOptions <> commandNames)
      | otherwise =
            printMatching (allOptions <> commandNames)

    pat = fromMaybe (lastDef "" words) (atMay words (fromIntegral index))

    printMatching = mapM_ putStrLn . List.filter (pat `List.isPrefixOf`)

    wordsBeforePattern = List.take (fromIntegral index) words

    commandNames = Text.unpack . (name :: NamedCommand -> Text) <$> commands

    hadListOrTreeOrHelp = any
        (`elem` ["-l", "--ls", "--list", "-t", "--tree", "-h", "--help"])
        wordsBeforePattern

    hadExpression = any ("--expression=" `List.isPrefixOf`) wordsBeforePattern

    hadNotifyOrPrint = any (`elem` ["--notify", "--print"]) wordsBeforePattern

    hadPrintCompletion = any (== "--print-completion") wordsBeforePattern

    canCompleteCommand = not (hadListOrTreeOrHelp || hadExpression)

    printCompletionOptions =
        ( if any ("--index=" `List.isPrefixOf`) wordsBeforePattern
             then []
             else ["--index="]
        )
        <>  ( if any ("--shell=" `List.isPrefixOf`) wordsBeforePattern
                  then []
                  else ["--shell=" <> s | s <- ["bash", "fish", "zsh"]]
            )

    allOptions =
        [ "-l", "--ls", "--list"
        , "-t", "--tree"
        , "--print"
        , "--print-completion"
        , "--expression="
        , "--notify"
        , "-h", "--help"
        , "--"
        ]