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

module Unison.CommandLine.InputPattern
  ( InputPattern (..),
    ParameterDescription,
    ParameterType (..),
    Parameter,
    TrailingParameters (..),
    Parameters (..),
    Argument,
    Arguments,
    noParams,
    foldParamsWithM,
    paramType,
    FZFResolver (..),
    Visibility (..),

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

import Control.Lens
import Data.List.Extra qualified as List
import Data.List.NonEmpty (NonEmpty (..))
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

data Visibility = Hidden | Visible
  deriving (Int -> Visibility -> ShowS
[Visibility] -> ShowS
Visibility -> String
(Int -> Visibility -> ShowS)
-> (Visibility -> String)
-> ([Visibility] -> ShowS)
-> Show Visibility
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Visibility -> ShowS
showsPrec :: Int -> Visibility -> ShowS
$cshow :: Visibility -> String
show :: Visibility -> String
$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]

-- | This should fit grammatically into sentences like “I was expecting an argument for the <paramDesc>”.
--   E.g. “namespace to merge”, “definition to delete”, “remote target to push to” etc.
type ParameterDescription = Text

data InputPattern = InputPattern
  { InputPattern -> String
patternName :: String,
    InputPattern -> [String]
aliases :: [String],
    -- | Allow hiding certain commands when debugging or work-in-progress
    InputPattern -> Visibility
visibility :: Visibility,
    InputPattern -> Parameters
params :: Parameters,
    InputPattern -> Pretty ColorText
help :: P.Pretty CT.ColorText,
    -- | Parse the arguments and return either an error message or a command `Input`.
    --
    --   The input list is always a valid length for the pattern. It may be necessary to have a catch-all case for
    --   coverage, but the implementation can assume that, say, a `OnePlus` parameter will always be provided at least
    --   one argument.
    --
    --  __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 warning 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 ParameterType = ParameterType
  { ParameterType -> String
typeName :: String,
    -- | Generate completion suggestions for this parameter type
    ParameterType
-> forall (m :: * -> *) v a.
   MonadIO m =>
   String
   -> 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 a parameter is marked as required, but no argument is provided, the fuzzy finder will be triggered if
    -- available.
    ParameterType -> Maybe FZFResolver
fzfResolver :: Maybe FZFResolver,
    ParameterType -> Bool
isStructured :: Bool
  }

type Parameter = (ParameterDescription, ParameterType)

data TrailingParameters
  = -- | Optional args followed by a possibly-empty catch-all
    Optional [Parameter] (Maybe Parameter)
  | -- | A catch-all that requires at least one value
    OnePlus Parameter

-- | The `Parameters` for an `InputPattern` are roughly
--
-- > [required …] ([optional …] [catchAll] | NonEmpty catchAll)
data Parameters = Parameters {Parameters -> [Parameter]
requiredParams :: [Parameter], Parameters -> TrailingParameters
trailingParams :: TrailingParameters}

-- | This is the parameter structure for a pattern that doesn’t accept any arguments.
noParams :: Parameters
noParams :: Parameters
noParams = [Parameter] -> TrailingParameters -> Parameters
Parameters [] (TrailingParameters -> Parameters)
-> TrailingParameters -> Parameters
forall a b. (a -> b) -> a -> b
$ [Parameter] -> Maybe Parameter -> TrailingParameters
Optional [] Maybe Parameter
forall a. Maybe a
Nothing

-- | Applies concrete arguments to a set of `Parameters`.
foldParamsWithM ::
  (Monad m) =>
  -- | Each step needs to return a new incremental result, but can also return additional arguments to apply in later
  --   steps. This allows for the expansion of an argument to multiple arguments, as with numbered arg ranges.
  (state -> Parameter -> arg -> m (state, [arg])) ->
  -- | The initial state.
  state ->
  Parameters ->
  [arg] ->
  -- | If too many arguments are provided, it returns `Left`, with the arguments that couldn’t be assigned to a
  --   parameter. Otherwise, it returns a tuple of the `Parameters` that could still be applied to additional arguments
  --   (e.g., via fuzzy completion) and the final result. If the returned `Parameters` has remaining required arguments,
  --   they must either be provided somehow (e.g., another call to this function or fuzzy completion) or result in a
  --   “not enough arguments” error.
  m (Either (NonEmpty arg) (state, Parameters))
foldParamsWithM :: forall (m :: * -> *) state arg.
Monad m =>
(state -> Parameter -> arg -> m (state, [arg]))
-> state
-> Parameters
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
foldParamsWithM state -> Parameter -> arg -> m (state, [arg])
fn state
z Parameters {[Parameter]
$sel:requiredParams:Parameters :: Parameters -> [Parameter]
requiredParams :: [Parameter]
requiredParams, TrailingParameters
$sel:trailingParams:Parameters :: Parameters -> TrailingParameters
trailingParams :: TrailingParameters
trailingParams} = state
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
foldRequiredArgs state
z [Parameter]
requiredParams
  where
    foldRequiredArgs :: state
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
foldRequiredArgs state
res = (([Parameter], [arg])
 -> m (Either (NonEmpty arg) (state, Parameters)))
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
forall a b c. ((a, b) -> c) -> a -> b -> c
curry \case
      ([], [arg]
as) -> case TrailingParameters
trailingParams of
        Optional [Parameter]
optParams Maybe Parameter
zeroPlus -> state
-> Maybe Parameter
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
foldOptionalArgs state
res Maybe Parameter
zeroPlus [Parameter]
optParams [arg]
as
        OnePlus Parameter
param -> case [arg]
as of
          [] -> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either (NonEmpty arg) (state, Parameters)
 -> m (Either (NonEmpty arg) (state, Parameters)))
-> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ (state, Parameters) -> Either (NonEmpty arg) (state, Parameters)
forall a. a -> Either (NonEmpty arg) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (state
res, [Parameter] -> TrailingParameters -> Parameters
Parameters [] (TrailingParameters -> Parameters)
-> TrailingParameters -> Parameters
forall a b. (a -> b) -> a -> b
$ Parameter -> TrailingParameters
OnePlus Parameter
param)
          arg
a : [arg]
args -> state
-> Parameter
-> NonEmpty arg
-> m (Either (NonEmpty arg) (state, Parameters))
foldCatchallArg state
res Parameter
param (NonEmpty arg -> m (Either (NonEmpty arg) (state, Parameters)))
-> NonEmpty arg -> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ arg
a arg -> [arg] -> NonEmpty arg
forall a. a -> [a] -> NonEmpty a
:| [arg]
args
      ([Parameter]
ps, []) -> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either (NonEmpty arg) (state, Parameters)
 -> m (Either (NonEmpty arg) (state, Parameters)))
-> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ (state, Parameters) -> Either (NonEmpty arg) (state, Parameters)
forall a. a -> Either (NonEmpty arg) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (state
res, [Parameter] -> TrailingParameters -> Parameters
Parameters [Parameter]
ps TrailingParameters
trailingParams)
      (Parameter
p : [Parameter]
ps, arg
a : [arg]
as) -> do
        (state
res', [arg]
extraArgs) <- state -> Parameter -> arg -> m (state, [arg])
fn state
res Parameter
p arg
a
        state
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
foldRequiredArgs state
res' [Parameter]
ps ([arg] -> m (Either (NonEmpty arg) (state, Parameters)))
-> [arg] -> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ [arg]
extraArgs [arg] -> [arg] -> [arg]
forall a. Semigroup a => a -> a -> a
<> [arg]
as
    foldOptionalArgs :: state
-> Maybe Parameter
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
foldOptionalArgs state
res Maybe Parameter
zp = (([Parameter], [arg])
 -> m (Either (NonEmpty arg) (state, Parameters)))
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
forall a b c. ((a, b) -> c) -> a -> b -> c
curry \case
      ([Parameter]
ps, []) -> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either (NonEmpty arg) (state, Parameters)
 -> m (Either (NonEmpty arg) (state, Parameters)))
-> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ (state, Parameters) -> Either (NonEmpty arg) (state, Parameters)
forall a. a -> Either (NonEmpty arg) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (state
res, [Parameter] -> TrailingParameters -> Parameters
Parameters [] (TrailingParameters -> Parameters)
-> TrailingParameters -> Parameters
forall a b. (a -> b) -> a -> b
$ [Parameter] -> Maybe Parameter -> TrailingParameters
Optional [Parameter]
ps Maybe Parameter
zp)
      ([], arg
a : [arg]
as) -> (NonEmpty arg -> m (Either (NonEmpty arg) (state, Parameters)))
-> (Parameter
    -> NonEmpty arg -> m (Either (NonEmpty arg) (state, Parameters)))
-> Maybe Parameter
-> NonEmpty arg
-> m (Either (NonEmpty arg) (state, Parameters))
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either (NonEmpty arg) (state, Parameters)
 -> m (Either (NonEmpty arg) (state, Parameters)))
-> (NonEmpty arg -> Either (NonEmpty arg) (state, Parameters))
-> NonEmpty arg
-> m (Either (NonEmpty arg) (state, Parameters))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty arg -> Either (NonEmpty arg) (state, Parameters)
forall a b. a -> Either a b
Left) (state
-> Parameter
-> NonEmpty arg
-> m (Either (NonEmpty arg) (state, Parameters))
foldCatchallArg state
res) Maybe Parameter
zp (NonEmpty arg -> m (Either (NonEmpty arg) (state, Parameters)))
-> NonEmpty arg -> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ arg
a arg -> [arg] -> NonEmpty arg
forall a. a -> [a] -> NonEmpty a
:| [arg]
as
      (Parameter
p : [Parameter]
ps, arg
a : [arg]
as) -> do
        (state
res', [arg]
extraArgs) <- state -> Parameter -> arg -> m (state, [arg])
fn state
res Parameter
p arg
a
        state
-> Maybe Parameter
-> [Parameter]
-> [arg]
-> m (Either (NonEmpty arg) (state, Parameters))
foldOptionalArgs state
res' Maybe Parameter
zp [Parameter]
ps ([arg] -> m (Either (NonEmpty arg) (state, Parameters)))
-> [arg] -> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ [arg]
extraArgs [arg] -> [arg] -> [arg]
forall a. Semigroup a => a -> a -> a
<> [arg]
as
    foldCatchallArg :: state
-> Parameter
-> NonEmpty arg
-> m (Either (NonEmpty arg) (state, Parameters))
foldCatchallArg state
res Parameter
p =
      let collectRemainingArgs :: state -> [arg] -> m (Either (NonEmpty arg) (state, Parameters))
collectRemainingArgs state
prevRes = \case
            [] -> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either (NonEmpty arg) (state, Parameters)
 -> m (Either (NonEmpty arg) (state, Parameters)))
-> Either (NonEmpty arg) (state, Parameters)
-> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ (state, Parameters) -> Either (NonEmpty arg) (state, Parameters)
forall a. a -> Either (NonEmpty arg) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (state
prevRes, [Parameter] -> TrailingParameters -> Parameters
Parameters [] (TrailingParameters -> Parameters)
-> (Maybe Parameter -> TrailingParameters)
-> Maybe Parameter
-> Parameters
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Parameter] -> Maybe Parameter -> TrailingParameters
Optional [] (Maybe Parameter -> Parameters) -> Maybe Parameter -> Parameters
forall a b. (a -> b) -> a -> b
$ Parameter -> Maybe Parameter
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Parameter
p)
            arg
a : [arg]
args -> do
              (state
res', [arg]
extraArgs) <- state -> Parameter -> arg -> m (state, [arg])
fn state
prevRes Parameter
p arg
a
              state -> [arg] -> m (Either (NonEmpty arg) (state, Parameters))
collectRemainingArgs state
res' ([arg] -> m (Either (NonEmpty arg) (state, Parameters)))
-> [arg] -> m (Either (NonEmpty arg) (state, Parameters))
forall a b. (a -> b) -> a -> b
$ [arg]
extraArgs [arg] -> [arg] -> [arg]
forall a. Semigroup a => a -> a -> a
<> [arg]
args
       in state -> [arg] -> m (Either (NonEmpty arg) (state, Parameters))
collectRemainingArgs state
res ([arg] -> m (Either (NonEmpty arg) (state, Parameters)))
-> (NonEmpty arg -> [arg])
-> NonEmpty arg
-> m (Either (NonEmpty arg) (state, Parameters))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty arg -> [arg]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList

paramInfo :: Parameters -> Int -> Maybe (ParameterDescription, ParameterType)
paramInfo :: Parameters -> Int -> Maybe Parameter
paramInfo Parameters {[Parameter]
$sel:requiredParams:Parameters :: Parameters -> [Parameter]
requiredParams :: [Parameter]
requiredParams, TrailingParameters
$sel:trailingParams:Parameters :: Parameters -> TrailingParameters
trailingParams :: TrailingParameters
trailingParams} Int
i =
  if Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< [Parameter] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Parameter]
requiredParams
    then Parameter -> Maybe Parameter
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Parameter -> Maybe Parameter) -> Parameter -> Maybe Parameter
forall a b. (a -> b) -> a -> b
$ [Parameter]
requiredParams [Parameter] -> Int -> Parameter
forall a. HasCallStack => [a] -> Int -> a
!! Int
i
    else case TrailingParameters
trailingParams of
      Optional [Parameter]
optParams Maybe Parameter
zeroPlus ->
        let rem :: Int
rem = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- [Parameter] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Parameter]
requiredParams
         in if Int
rem Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< [Parameter] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Parameter]
optParams
              then Parameter -> Maybe Parameter
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Parameter -> Maybe Parameter) -> Parameter -> Maybe Parameter
forall a b. (a -> b) -> a -> b
$ [Parameter]
optParams [Parameter] -> Int -> Parameter
forall a. HasCallStack => [a] -> Int -> a
!! Int
rem
              else Maybe Parameter
zeroPlus
      OnePlus Parameter
arg -> Parameter -> Maybe Parameter
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Parameter
arg

-- | `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
paramType :: Parameters -> Int -> Maybe ParameterType
paramType :: Parameters -> Int -> Maybe ParameterType
paramType Parameters
p = (Parameter -> ParameterType)
-> Maybe Parameter -> Maybe ParameterType
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Parameter -> ParameterType
forall a b. (a, b) -> b
snd (Maybe Parameter -> Maybe ParameterType)
-> (Int -> Maybe Parameter) -> Int -> Maybe ParameterType
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parameters -> Int -> Maybe Parameter
paramInfo Parameters
p

minArgs :: Parameters -> Int
minArgs :: Parameters -> Int
minArgs Parameters {[Parameter]
$sel:requiredParams:Parameters :: Parameters -> [Parameter]
requiredParams :: [Parameter]
requiredParams, TrailingParameters
$sel:trailingParams:Parameters :: Parameters -> TrailingParameters
trailingParams :: TrailingParameters
trailingParams} =
  [Parameter] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Parameter]
requiredParams Int -> Int -> Int
forall a. Num a => a -> a -> a
+ case TrailingParameters
trailingParams of
    Optional [Parameter]
_ Maybe Parameter
_ -> Int
0
    OnePlus Parameter
_ -> Int
1

maxArgs :: Parameters -> Maybe Int
maxArgs :: Parameters -> Maybe Int
maxArgs Parameters {[Parameter]
$sel:requiredParams:Parameters :: Parameters -> [Parameter]
requiredParams :: [Parameter]
requiredParams, TrailingParameters
$sel:trailingParams:Parameters :: Parameters -> TrailingParameters
trailingParams :: TrailingParameters
trailingParams} =
  case TrailingParameters
trailingParams of
    Optional [Parameter]
optParams Maybe Parameter
Nothing -> Int -> Maybe Int
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ [Parameter] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Parameter]
requiredParams Int -> Int -> Int
forall a. Num a => a -> a -> a
+ [Parameter] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Parameter]
optParams
    TrailingParameters
_ -> Maybe Int
forall a. Maybe a
Nothing

-- | 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 =>
[String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> String
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
unionSuggestions [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters String
inp Codebase m v a
codebase AuthenticatedHttpClient
httpClient ProjectPath
path = do
  [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> ([String
     -> Codebase m v a
     -> AuthenticatedHttpClient
     -> ProjectPath
     -> m [Completion]]
    -> m [Completion])
-> m [Completion]
forall a b. a -> (a -> b) -> b
& ((String
  -> Codebase m v a
  -> AuthenticatedHttpClient
  -> ProjectPath
  -> m [Completion])
 -> m [Completion])
-> [String
    -> 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 \String
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
suggester ->
    String
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
suggester String
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 =>
[String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> String
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
suggestionFallbacks [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters String
inp Codebase m v a
codebase AuthenticatedHttpClient
httpClient ProjectPath
path = [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> m [Completion]
go [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
suggesters
  where
    go :: [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> m [Completion]
go (String
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
s : [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
rest) = do
      [Completion]
suggestions <- String
-> Codebase m v a
-> AuthenticatedHttpClient
-> ProjectPath
-> m [Completion]
s String
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 [String
 -> Codebase m v a
 -> AuthenticatedHttpClient
 -> ProjectPath
 -> m [Completion]]
-> m [Completion]
go [String
 -> 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 []