{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ViewPatterns #-}

module Unison.CommandLine.InputPattern
  ( InputPattern (..),
    Argument,
    ArgumentType (..),
    ArgumentDescription,
    Arguments,
    argType,
    FZFResolver (..),
    IsOptional (..),
    Visibility (..),

    -- * Currently Unused
    minArgs,
    maxArgs,
    unionSuggestions,
    suggestionFallbacks,
  )
where

import Control.Lens
import Data.List.Extra qualified as List
import System.Console.Haskeline qualified as Line
import Unison.Auth.HTTPClient (AuthenticatedHttpClient)
import Unison.Codebase (Codebase)
import Unison.Codebase.Editor.Input (Input (..))
import Unison.Codebase.Editor.StructuredArgument (StructuredArgument)
import Unison.Codebase.ProjectPath qualified as PP
import Unison.CommandLine.FZFResolvers (FZFResolver (..))
import Unison.Prelude
import Unison.Util.ColorText qualified as CT
import Unison.Util.Monoid (foldMapM)
import Unison.Util.Pretty qualified as P

-- InputPatterns accept some fixed number of Required arguments of various
-- types, followed by a variable number of a single type of argument.
data IsOptional
  = Required -- 1, at the start
  | Optional -- 0 or 1, at the end
  | ZeroPlus -- 0 or more, at the end
  | OnePlus -- 1 or more, at the end
  deriving (Int -> IsOptional -> ShowS
[IsOptional] -> ShowS
IsOptional -> [Char]
(Int -> IsOptional -> ShowS)
-> (IsOptional -> [Char])
-> ([IsOptional] -> ShowS)
-> Show IsOptional
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> IsOptional -> ShowS
showsPrec :: Int -> IsOptional -> ShowS
$cshow :: IsOptional -> [Char]
show :: IsOptional -> [Char]
$cshowList :: [IsOptional] -> ShowS
showList :: [IsOptional] -> ShowS
Show, IsOptional -> IsOptional -> Bool
(IsOptional -> IsOptional -> Bool)
-> (IsOptional -> IsOptional -> Bool) -> Eq IsOptional
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: IsOptional -> IsOptional -> Bool
== :: IsOptional -> IsOptional -> Bool
$c/= :: IsOptional -> IsOptional -> Bool
/= :: IsOptional -> IsOptional -> Bool
Eq)

data Visibility = Hidden | Visible
  deriving (Int -> Visibility -> ShowS
[Visibility] -> ShowS
Visibility -> [Char]
(Int -> Visibility -> ShowS)
-> (Visibility -> [Char])
-> ([Visibility] -> ShowS)
-> Show Visibility
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Visibility -> ShowS
showsPrec :: Int -> Visibility -> ShowS
$cshow :: Visibility -> [Char]
show :: Visibility -> [Char]
$cshowList :: [Visibility] -> ShowS
showList :: [Visibility] -> ShowS
Show, Visibility -> Visibility -> Bool
(Visibility -> Visibility -> Bool)
-> (Visibility -> Visibility -> Bool) -> Eq Visibility
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Visibility -> Visibility -> Bool
== :: Visibility -> Visibility -> Bool
$c/= :: Visibility -> Visibility -> Bool
/= :: Visibility -> Visibility -> Bool
Eq, Eq Visibility
Eq Visibility =>
(Visibility -> Visibility -> Ordering)
-> (Visibility -> Visibility -> Bool)
-> (Visibility -> Visibility -> Bool)
-> (Visibility -> Visibility -> Bool)
-> (Visibility -> Visibility -> Bool)
-> (Visibility -> Visibility -> Visibility)
-> (Visibility -> Visibility -> Visibility)
-> Ord Visibility
Visibility -> Visibility -> Bool
Visibility -> Visibility -> Ordering
Visibility -> Visibility -> Visibility
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Visibility -> Visibility -> Ordering
compare :: Visibility -> Visibility -> Ordering
$c< :: Visibility -> Visibility -> Bool
< :: Visibility -> Visibility -> Bool
$c<= :: Visibility -> Visibility -> Bool
<= :: Visibility -> Visibility -> Bool
$c> :: Visibility -> Visibility -> Bool
> :: Visibility -> Visibility -> Bool
$c>= :: Visibility -> Visibility -> Bool
>= :: Visibility -> Visibility -> Bool
$cmax :: Visibility -> Visibility -> Visibility
max :: Visibility -> Visibility -> Visibility
$cmin :: Visibility -> Visibility -> Visibility
min :: Visibility -> Visibility -> Visibility
Ord)

-- | An argument to a command is either a string provided by the user which
-- needs to be parsed or a numbered argument that doesn’t need to be parsed, as
-- we’ve preserved its representation (although the numbered argument could
-- still be of the wrong type, which should result in an error).
type Argument = Either String StructuredArgument

type Arguments = [Argument]

-- | Argument description
-- It should fit grammatically into sentences like "I was expecting an argument for the <argDesc>"
-- e.g. "namespace to merge", "definition to delete", "remote target to push to" etc.
type ArgumentDescription = Text

data InputPattern = InputPattern
  { InputPattern -> [Char]
patternName :: String,
    InputPattern -> [[Char]]
aliases :: [String],
    InputPattern -> Visibility
visibility :: Visibility, -- Allow hiding certain commands when debugging or work-in-progress
    InputPattern -> [(ArgumentDescription, IsOptional, ArgumentType)]
args :: [(ArgumentDescription, IsOptional, ArgumentType)],
    InputPattern -> Pretty ColorText
help :: P.Pretty CT.ColorText,
    -- | Parse the arguments and return either an error message or a command `Input`.
    --
    --  __NB__: This function should return `Left` only on failure. For commands (like `help`) that simply produce
    --          formatted output, use `pure . Input.CreateMessage`. The failure output should be fully formatted (using
    --         `wrap`, etc.), but shouldn’t include any general error components like a warninng flag or the full help
    --          message, and shouldn’t plan for the context it is being output to (e.g., don’t `P.indentN` the entire
    --          message).
    InputPattern -> Arguments -> Either (Pretty ColorText) Input
parse ::
      Arguments ->
      Either (P.Pretty CT.ColorText) Input
  }

data ArgumentType = ArgumentType
  { ArgumentType -> [Char]
typeName :: String,
    -- | Generate completion suggestions for this argument type
    ArgumentType
-> forall (m :: * -> *) v a.
   MonadIO m =>
   [Char]
   -> Codebase m v a
   -> AuthenticatedHttpClient
   -> ProjectPath
   -> m [Completion]
suggestions ::
      forall m v a.
      (MonadIO m) =>
      String ->
      Codebase m v a ->
      AuthenticatedHttpClient ->
      PP.ProjectPath ->
      m [Line.Completion],
    -- | If an argument is marked as required, but not provided, the fuzzy finder will be triggered if
    -- available.
    ArgumentType -> Maybe FZFResolver
fzfResolver :: Maybe FZFResolver
  }

instance Show ArgumentType where
  show :: ArgumentType -> [Char]
show ArgumentType
at = [Char]
"ArgumentType " [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> ArgumentType -> [Char]
typeName ArgumentType
at

-- `argType` gets called when the user tries to autocomplete an `i`th argument (zero-indexed).
-- todo: would be nice if we could alert the user if they try to autocomplete
-- past the end.  It would also be nice if
argInfo :: InputPattern -> Int -> Maybe (ArgumentDescription, ArgumentType)
argInfo :: InputPattern -> Int -> Maybe (ArgumentDescription, ArgumentType)
argInfo InputPattern {[(ArgumentDescription, IsOptional, ArgumentType)]
$sel:args:InputPattern :: InputPattern -> [(ArgumentDescription, IsOptional, ArgumentType)]
args :: [(ArgumentDescription, IsOptional, ArgumentType)]
args, [Char]
$sel:patternName:InputPattern :: InputPattern -> [Char]
patternName :: [Char]
patternName} Int
i = (Int, [(ArgumentDescription, IsOptional, ArgumentType)])
-> Maybe (ArgumentDescription, ArgumentType)
go (Int
i, [(ArgumentDescription, IsOptional, ArgumentType)]
args)
  where
    -- Strategy: all of these input patterns take some number of arguments.
    -- If it takes no arguments, then don't autocomplete.
    go :: (Int, [(Text, IsOptional, ArgumentType)]) -> Maybe (ArgumentDescription, ArgumentType)
    go :: (Int, [(ArgumentDescription, IsOptional, ArgumentType)])
-> Maybe (ArgumentDescription, ArgumentType)
go (Int
_, []) = Maybe (ArgumentDescription, ArgumentType)
forall a. Maybe a
Nothing
    -- If requesting the 0th of >=1 arguments, return it.
    go (Int
0, (ArgumentDescription
argName, IsOptional
_, ArgumentType
t) : [(ArgumentDescription, IsOptional, ArgumentType)]
_) = (ArgumentDescription, ArgumentType)
-> Maybe (ArgumentDescription, ArgumentType)
forall a. a -> Maybe a
Just (ArgumentDescription
argName, ArgumentType
t)
    -- Vararg parameters should appear at the end of the arg list, and work for
    -- any later argument number.
    go (Int
_, [(ArgumentDescription
argName, IsOptional
ZeroPlus, ArgumentType
t)]) = (ArgumentDescription, ArgumentType)
-> Maybe (ArgumentDescription, ArgumentType)
forall a. a -> Maybe a
Just (ArgumentDescription
argName, ArgumentType
t)
    go (Int
_, [(ArgumentDescription
argName, IsOptional
OnePlus, ArgumentType
t)]) = (ArgumentDescription, ArgumentType)
-> Maybe (ArgumentDescription, ArgumentType)
forall a. a -> Maybe a
Just (ArgumentDescription
argName, ArgumentType
t)
    -- If requesting a later parameter, decrement and drop one.
    go (Int
n, (ArgumentDescription
_argName, IsOptional
o, ArgumentType
_) : [(ArgumentDescription, IsOptional, ArgumentType)]
argTypes)
      | IsOptional
o IsOptional -> IsOptional -> Bool
forall a. Eq a => a -> a -> Bool
== IsOptional
Optional Bool -> Bool -> Bool
|| IsOptional
o IsOptional -> IsOptional -> Bool
forall a. Eq a => a -> a -> Bool
== IsOptional
Required = (Int, [(ArgumentDescription, IsOptional, ArgumentType)])
-> Maybe (ArgumentDescription, ArgumentType)
go (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1, [(ArgumentDescription, IsOptional, ArgumentType)]
argTypes)
    -- The argument list spec is invalid if something follows a vararg
    go (Int, [(ArgumentDescription, IsOptional, ArgumentType)])
args =
      [Char] -> Maybe (ArgumentDescription, ArgumentType)
forall a. HasCallStack => [Char] -> a
error ([Char] -> Maybe (ArgumentDescription, ArgumentType))
-> [Char] -> Maybe (ArgumentDescription, ArgumentType)
forall a b. (a -> b) -> a -> b
$
        [Char]
"Input pattern "
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> ShowS
forall a. Show a => a -> [Char]
show [Char]
patternName
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
" has an invalid argument list: "
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> (Int, [(ArgumentDescription, IsOptional, ArgumentType)]) -> [Char]
forall a. Show a => a -> [Char]
show (Int, [(ArgumentDescription, IsOptional, ArgumentType)])
args

-- `argType` gets called when the user tries to autocomplete an `i`th argument (zero-indexed).
-- todo: would be nice if we could alert the user if they try to autocomplete
-- past the end.  It would also be nice if
argType :: InputPattern -> Int -> Maybe ArgumentType
argType :: InputPattern -> Int -> Maybe ArgumentType
argType InputPattern
ip Int
i = (ArgumentDescription, ArgumentType) -> ArgumentType
forall a b. (a, b) -> b
snd ((ArgumentDescription, ArgumentType) -> ArgumentType)
-> Maybe (ArgumentDescription, ArgumentType) -> Maybe ArgumentType
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (InputPattern -> Int -> Maybe (ArgumentDescription, ArgumentType)
argInfo InputPattern
ip Int
i)

minArgs :: InputPattern -> Int
minArgs :: InputPattern -> Int
minArgs (InputPattern {[(ArgumentDescription, IsOptional, ArgumentType)]
$sel:args:InputPattern :: InputPattern -> [(ArgumentDescription, IsOptional, ArgumentType)]
args :: [(ArgumentDescription, IsOptional, ArgumentType)]
args, [Char]
$sel:patternName:InputPattern :: InputPattern -> [Char]
patternName :: [Char]
patternName}) =
  [IsOptional] -> Int
go ([(ArgumentDescription, IsOptional, ArgumentType)]
args [(ArgumentDescription, IsOptional, ArgumentType)]
-> Getting
     (Endo [IsOptional])
     [(ArgumentDescription, IsOptional, ArgumentType)]
     IsOptional
-> [IsOptional]
forall s a. s -> Getting (Endo [a]) s a -> [a]
^.. ((ArgumentDescription, IsOptional, ArgumentType)
 -> Const
      (Endo [IsOptional])
      (ArgumentDescription, IsOptional, ArgumentType))
-> [(ArgumentDescription, IsOptional, ArgumentType)]
-> Const
     (Endo [IsOptional])
     [(ArgumentDescription, IsOptional, ArgumentType)]
forall (f :: * -> *) a. Foldable f => IndexedFold Int (f a) a
IndexedFold
  Int
  [(ArgumentDescription, IsOptional, ArgumentType)]
  (ArgumentDescription, IsOptional, ArgumentType)
folded (((ArgumentDescription, IsOptional, ArgumentType)
  -> Const
       (Endo [IsOptional])
       (ArgumentDescription, IsOptional, ArgumentType))
 -> [(ArgumentDescription, IsOptional, ArgumentType)]
 -> Const
      (Endo [IsOptional])
      [(ArgumentDescription, IsOptional, ArgumentType)])
-> ((IsOptional -> Const (Endo [IsOptional]) IsOptional)
    -> (ArgumentDescription, IsOptional, ArgumentType)
    -> Const
         (Endo [IsOptional])
         (ArgumentDescription, IsOptional, ArgumentType))
-> Getting
     (Endo [IsOptional])
     [(ArgumentDescription, IsOptional, ArgumentType)]
     IsOptional
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (IsOptional -> Const (Endo [IsOptional]) IsOptional)
-> (ArgumentDescription, IsOptional, ArgumentType)
-> Const
     (Endo [IsOptional]) (ArgumentDescription, IsOptional, ArgumentType)
forall s t a b. Field2 s t a b => Lens s t a b
Lens
  (ArgumentDescription, IsOptional, ArgumentType)
  (ArgumentDescription, IsOptional, ArgumentType)
  IsOptional
  IsOptional
_2)
  where
    go :: [IsOptional] -> Int
go [] = Int
0
    go (IsOptional
Required : [IsOptional]
argTypes) = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ [IsOptional] -> Int
go [IsOptional]
argTypes
    go [IsOptional
_] = Int
0
    go [IsOptional]
_ =
      [Char] -> Int
forall a. HasCallStack => [Char] -> a
error ([Char] -> Int) -> [Char] -> Int
forall a b. (a -> b) -> a -> b
$
        [Char]
"Invalid args for InputPattern ("
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> ShowS
forall a. Show a => a -> [Char]
show [Char]
patternName
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
"): "
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [(ArgumentDescription, IsOptional, ArgumentType)] -> [Char]
forall a. Show a => a -> [Char]
show [(ArgumentDescription, IsOptional, ArgumentType)]
args

maxArgs :: InputPattern -> Maybe Int
maxArgs :: InputPattern -> Maybe Int
maxArgs (InputPattern {[(ArgumentDescription, IsOptional, ArgumentType)]
$sel:args:InputPattern :: InputPattern -> [(ArgumentDescription, IsOptional, ArgumentType)]
args :: [(ArgumentDescription, IsOptional, ArgumentType)]
args, [Char]
$sel:patternName:InputPattern :: InputPattern -> [Char]
patternName :: [Char]
patternName}) = [IsOptional] -> Maybe Int
go [IsOptional]
argTypes
  where
    argTypes :: [IsOptional]
argTypes = [(ArgumentDescription, IsOptional, ArgumentType)]
args [(ArgumentDescription, IsOptional, ArgumentType)]
-> Getting
     (Endo [IsOptional])
     [(ArgumentDescription, IsOptional, ArgumentType)]
     IsOptional
-> [IsOptional]
forall s a. s -> Getting (Endo [a]) s a -> [a]
^.. ((ArgumentDescription, IsOptional, ArgumentType)
 -> Const
      (Endo [IsOptional])
      (ArgumentDescription, IsOptional, ArgumentType))
-> [(ArgumentDescription, IsOptional, ArgumentType)]
-> Const
     (Endo [IsOptional])
     [(ArgumentDescription, IsOptional, ArgumentType)]
forall (f :: * -> *) a. Foldable f => IndexedFold Int (f a) a
IndexedFold
  Int
  [(ArgumentDescription, IsOptional, ArgumentType)]
  (ArgumentDescription, IsOptional, ArgumentType)
folded (((ArgumentDescription, IsOptional, ArgumentType)
  -> Const
       (Endo [IsOptional])
       (ArgumentDescription, IsOptional, ArgumentType))
 -> [(ArgumentDescription, IsOptional, ArgumentType)]
 -> Const
      (Endo [IsOptional])
      [(ArgumentDescription, IsOptional, ArgumentType)])
-> ((IsOptional -> Const (Endo [IsOptional]) IsOptional)
    -> (ArgumentDescription, IsOptional, ArgumentType)
    -> Const
         (Endo [IsOptional])
         (ArgumentDescription, IsOptional, ArgumentType))
-> Getting
     (Endo [IsOptional])
     [(ArgumentDescription, IsOptional, ArgumentType)]
     IsOptional
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (IsOptional -> Const (Endo [IsOptional]) IsOptional)
-> (ArgumentDescription, IsOptional, ArgumentType)
-> Const
     (Endo [IsOptional]) (ArgumentDescription, IsOptional, ArgumentType)
forall s t a b. Field2 s t a b => Lens s t a b
Lens
  (ArgumentDescription, IsOptional, ArgumentType)
  (ArgumentDescription, IsOptional, ArgumentType)
  IsOptional
  IsOptional
_2
    go :: [IsOptional] -> Maybe Int
go [] = Int -> Maybe Int
forall a. a -> Maybe a
Just Int
0
    go (IsOptional
Required : [IsOptional]
argTypes) = (Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+) (Int -> Int) -> Maybe Int -> Maybe Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [IsOptional] -> Maybe Int
go [IsOptional]
argTypes
    go [IsOptional
Optional] = Int -> Maybe Int
forall a. a -> Maybe a
Just Int
0
    go [IsOptional
_] = Maybe Int
forall a. Maybe a
Nothing
    go [IsOptional]
_ =
      [Char] -> Maybe Int
forall a. HasCallStack => [Char] -> a
error ([Char] -> Maybe Int) -> [Char] -> Maybe Int
forall a b. (a -> b) -> a -> b
$
        [Char]
"Invalid args for InputPattern ("
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> ShowS
forall a. Show a => a -> [Char]
show [Char]
patternName
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
"): "
          [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [IsOptional] -> [Char]
forall a. Show a => a -> [Char]
show [IsOptional]
argTypes

-- | Union suggestions from all possible completions
unionSuggestions ::
  forall m v a.
  (MonadIO m) =>
  [ ( String ->
      Codebase m v a ->
      AuthenticatedHttpClient ->
      PP.ProjectPath ->
      m [Line.Completion]
    )
  ] ->
  ( String ->
    Codebase m v a ->
    AuthenticatedHttpClient ->
    PP.ProjectPath ->
    m [Line.Completion]
  )
unionSuggestions :: forall (m :: * -> *) v a.
MonadIO m =>
[[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> [Char]
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
unionSuggestions [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters [Char]
inp Codebase m v a
codebase AuthenticatedHttpClient
httpClient ProjectPath
path = do
  [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> ([[Char]
     -> Codebase m v a
     -> AuthenticatedHttpClient
     -> ProjectPath
     -> m [Completion]]
    -> m [Completion])
-> m [Completion]
forall a b. a -> (a -> b) -> b
& (([Char]
  -> Codebase m v a
  -> AuthenticatedHttpClient
  -> ProjectPath
  -> m [Completion])
 -> m [Completion])
-> [[Char]
    -> Codebase m v a
    -> AuthenticatedHttpClient
    -> ProjectPath
    -> m [Completion]]
-> m [Completion]
forall (m :: * -> *) (f :: * -> *) b a.
(Monad m, Foldable f, Monoid b) =>
(a -> m b) -> f a -> m b
foldMapM \[Char]
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
suggester ->
    [Char]
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
suggester [Char]
inp Codebase m v a
codebase AuthenticatedHttpClient
httpClient ProjectPath
path
      m [Completion]
-> (m [Completion] -> m [Completion]) -> m [Completion]
forall a b. a -> (a -> b) -> b
& ([Completion] -> [Completion]) -> m [Completion] -> m [Completion]
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Completion] -> [Completion]
forall a. Ord a => [a] -> [a]
List.nubOrd

-- | Try the first completer, if it returns no suggestions, try the second, etc.
suggestionFallbacks ::
  forall m v a.
  (MonadIO m) =>
  [ ( String ->
      Codebase m v a ->
      AuthenticatedHttpClient ->
      PP.ProjectPath ->
      m [Line.Completion]
    )
  ] ->
  ( String ->
    Codebase m v a ->
    AuthenticatedHttpClient ->
    PP.ProjectPath ->
    m [Line.Completion]
  )
suggestionFallbacks :: forall (m :: * -> *) v a.
MonadIO m =>
[[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> [Char]
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
suggestionFallbacks [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters [Char]
inp Codebase m v a
codebase AuthenticatedHttpClient
httpClient ProjectPath
path = [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> m [Completion]
go [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters
  where
    go :: [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> m [Completion]
go ([Char]
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
s : [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
rest) = do
      [Completion]
suggestions <- [Char]
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
s [Char]
inp Codebase m v a
codebase AuthenticatedHttpClient
httpClient ProjectPath
path
      if [Completion] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Completion]
suggestions
        then [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> m [Completion]
go [[Char]
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
rest
        else [Completion] -> m [Completion]
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure [Completion]
suggestions
    go [] = [Completion] -> m [Completion]
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure []