module Unison.Codebase.Editor.HandleInput.Names (handleNames) where

import Control.Monad (when)
import Data.Set qualified as Set
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.NamesUtils qualified as Cli
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch qualified as Branch
import Unison.Codebase.Branch.Names qualified as Branch
import Unison.Codebase.Editor.HandleInput.Global qualified as Global
import Unison.Codebase.Editor.Input (ErrorMessageOrName, RawQuery)
import Unison.Codebase.Editor.Output (Output (..))
import Unison.HashQualifiedPrime qualified as HQ'
import Unison.Name (Name)
import Unison.NamesWithHistory qualified as Names
import Unison.PrettyPrintEnv qualified as PPE
import Unison.PrettyPrintEnv.Names qualified as PPE
import Unison.PrettyPrintEnvDecl qualified as PPED
import Unison.PrettyPrintEnvDecl.Names qualified as PPED
import Unison.Reference (Reference)
import Unison.Referent (Referent)
import Unison.Util.Pretty qualified as P

-- | Handles a single @NamesI@ input query returning terms that match a given name.
--
-- Parameters:
--
-- * @global :: Bool@
-- ** If @True@, search all projects and branches.
-- ** If @False@, search only the current branch.
--
-- * @query :: (RawQuery, ErrorMessageOrName)@
-- ** The first member is the raw @nameQuery@ being handled.
-- ** The second member is the parsed @nameQuery@ that is either an error message
--    to be printed or a name that can be looked up in the codebase.
handleNames ::
  Bool ->
  (RawQuery, ErrorMessageOrName) ->
  Cli ()
handleNames :: Bool -> (RawQuery, ErrorMessageOrName) -> Cli ()
handleNames Bool
_ (RawQuery
nameQuery, Left Pretty ColorText
errMsg) = do
  Output -> Cli ()
Cli.respond (Output -> Cli ()) -> Output -> Cli ()
forall a b. (a -> b) -> a -> b
$
    Pretty ColorText -> Output
PrintMessage (Pretty ColorText -> Output) -> Pretty ColorText -> Output
forall a b. (a -> b) -> a -> b
$
      [Pretty ColorText] -> Pretty ColorText
forall (f :: * -> *) s.
(Foldable f, IsString s) =>
f (Pretty s) -> Pretty s
P.lines [Pretty ColorText
prettyNameQuery, Pretty ColorText
errMsg]
  where
    prettyNameQuery :: Pretty ColorText
prettyNameQuery =
      Pretty ColorText -> Pretty ColorText
P.red (Pretty ColorText -> Pretty ColorText
P.bold (Pretty ColorText -> Pretty ColorText)
-> Pretty ColorText -> Pretty ColorText
forall a b. (a -> b) -> a -> b
$ RawQuery -> Pretty ColorText
forall s. IsString s => RawQuery -> Pretty s
P.string RawQuery
nameQuery) Pretty ColorText -> Pretty ColorText -> Pretty ColorText
forall a. Semigroup a => a -> a -> a
<> Pretty ColorText
":"
handleNames Bool
global (RawQuery
nameQuery, Right HashQualified Name
query) = do
  Int
hqLength <- Transaction Int -> Cli Int
forall a. Transaction a -> Cli a
Cli.runTransaction Transaction Int
Codebase.hashLength
  let searchNames :: Names
-> Cli
     ([(Referent, [HashQualified Name])],
      [(Reference, [HashQualified Name])])
searchNames Names
names = do
        let pped :: PrettyPrintEnvDecl
pped = Namer -> Suffixifier -> PrettyPrintEnvDecl
PPED.makePPED (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)
            unsuffixifiedPPE :: PrettyPrintEnv
unsuffixifiedPPE = PrettyPrintEnvDecl -> PrettyPrintEnv
PPED.unsuffixifiedPPE PrettyPrintEnvDecl
pped
            terms :: Set Referent
terms = SearchType -> HashQualified Name -> Names -> Set Referent
Names.lookupHQTerm SearchType
Names.IncludeSuffixes HashQualified Name
query Names
names
            types :: Set Reference
types = SearchType -> HashQualified Name -> Names -> Set Reference
Names.lookupHQType SearchType
Names.IncludeSuffixes HashQualified Name
query Names
names
            terms' :: [(Referent, [HQ'.HashQualified Name])]
            terms' :: [(Referent, [HashQualified Name])]
terms' = (Referent -> (Referent, [HashQualified Name]))
-> [Referent] -> [(Referent, [HashQualified Name])]
forall a b. (a -> b) -> [a] -> [b]
map (\Referent
r -> (Referent
r, PrettyPrintEnv -> Referent -> [HashQualified Name]
PPE.allTermNames PrettyPrintEnv
unsuffixifiedPPE Referent
r)) (Set Referent -> [Referent]
forall a. Set a -> [a]
Set.toList Set Referent
terms)
            types' :: [(Reference, [HQ'.HashQualified Name])]
            types' :: [(Reference, [HashQualified Name])]
types' = (Reference -> (Reference, [HashQualified Name]))
-> [Reference] -> [(Reference, [HashQualified Name])]
forall a b. (a -> b) -> [a] -> [b]
map (\Reference
r -> (Reference
r, PrettyPrintEnv -> Reference -> [HashQualified Name]
PPE.allTypeNames PrettyPrintEnv
unsuffixifiedPPE Reference
r)) (Set Reference -> [Reference]
forall a. Set a -> [a]
Set.toList Set Reference
types)
        ([(Referent, [HashQualified Name])],
 [(Reference, [HashQualified Name])])
-> Cli
     ([(Referent, [HashQualified Name])],
      [(Reference, [HashQualified Name])])
forall a. a -> Cli a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([(Referent, [HashQualified Name])]
terms', [(Reference, [HashQualified Name])]
types')
  if Bool
global
    then do
      ((ProjectAndBranch ProjectName ProjectBranchName,
  ProjectAndBranch ProjectId ProjectBranchId)
 -> Branch IO -> Cli ())
-> Cli ()
forall r.
Monoid r =>
((ProjectAndBranch ProjectName ProjectBranchName,
  ProjectAndBranch ProjectId ProjectBranchId)
 -> Branch IO -> Cli r)
-> Cli r
Global.forAllProjectBranches \(ProjectAndBranch ProjectName ProjectBranchName
projBranchNames, ProjectAndBranch ProjectId ProjectBranchId
_ids) Branch IO
branch -> do
        let names :: Names
names = Branch0 IO -> Names
forall (m :: * -> *). Branch0 m -> Names
Branch.toNames (Branch0 IO -> Names)
-> (Branch IO -> Branch0 IO) -> Branch IO -> Names
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Branch IO -> Branch0 IO
forall (m :: * -> *). Branch m -> Branch0 m
Branch.head (Branch IO -> Names) -> Branch IO -> Names
forall a b. (a -> b) -> a -> b
$ Branch IO
branch
        ([(Referent, [HashQualified Name])]
terms, [(Reference, [HashQualified Name])]
types) <- Names
-> Cli
     ([(Referent, [HashQualified Name])],
      [(Reference, [HashQualified Name])])
searchNames Names
names
        Bool -> Cli () -> Cli ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not ([(Referent, [HashQualified Name])] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Referent, [HashQualified Name])]
terms) Bool -> Bool -> Bool
|| Bool -> Bool
not ([(Reference, [HashQualified Name])] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Reference, [HashQualified Name])]
types)) do
          Output -> Cli ()
Cli.respond (Output -> Cli ()) -> Output -> Cli ()
forall a b. (a -> b) -> a -> b
$ RawQuery
-> ProjectAndBranch ProjectName ProjectBranchName
-> Int
-> [(Reference, [HashQualified Name])]
-> [(Referent, [HashQualified Name])]
-> Output
GlobalListNames RawQuery
nameQuery ProjectAndBranch ProjectName ProjectBranchName
projBranchNames Int
hqLength [(Reference, [HashQualified Name])]
types [(Referent, [HashQualified Name])]
terms
    else do
      Names
names <- Cli Names
Cli.currentNames
      ([(Referent, [HashQualified Name])]
terms, [(Reference, [HashQualified Name])]
types) <- Names
-> Cli
     ([(Referent, [HashQualified Name])],
      [(Reference, [HashQualified Name])])
searchNames Names
names
      Output -> Cli ()
Cli.respond (Output -> Cli ()) -> Output -> Cli ()
forall a b. (a -> b) -> a -> b
$ RawQuery
-> Int
-> [(Reference, [HashQualified Name])]
-> [(Referent, [HashQualified Name])]
-> Output
ListNames RawQuery
nameQuery Int
hqLength [(Reference, [HashQualified Name])]
types [(Referent, [HashQualified Name])]
terms