module Unison.Codebase.Editor.HandleInput.TermResolution
  ( lookupTermRefs,
    lookupTermRefWithType,
    resolveCon,
    resolveTerm,
    resolveTermRef,
    resolveMainRef,
  )
where

import Control.Monad.Reader (ask)
import Control.Monad.Trans (liftIO)
import Data.Maybe (catMaybes, fromJust)
import Data.Set (fromList, toList)
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.Editor.Output (Output (..))
import Unison.Codebase.Path (hqSplitFromName')
import Unison.Codebase.Runtime qualified as Runtime
import Unison.ConstructorReference
import Unison.HashQualified qualified as HQ
import Unison.Name (Name)
import Unison.Names (Names)
import Unison.NamesWithHistory qualified as Names
import Unison.Parser.Ann (Ann)
import Unison.PrettyPrintEnv (PrettyPrintEnv)
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, pattern Con, pattern Ref)
import Unison.Symbol (Symbol)
import Unison.Type (Type)
import Unison.Typechecker qualified as Typechecker

lookupTerm :: HQ.HashQualified Name -> Names -> [Referent]
lookupTerm :: HashQualified Name -> Names -> [Referent]
lookupTerm HashQualified Name
hq Names
parseNames = Set Referent -> [Referent]
forall a. Set a -> [a]
toList (SearchType -> HashQualified Name -> Names -> Set Referent
Names.lookupHQTerm SearchType
Names.IncludeSuffixes HashQualified Name
hq Names
parseNames)

lookupCon ::
  HQ.HashQualified Name ->
  Names ->
  ([ConstructorReference], [Referent])
lookupCon :: HashQualified Name -> Names -> ([ConstructorReference], [Referent])
lookupCon HashQualified Name
hq Names
parseNames =
  [(ConstructorReference, Referent)]
-> ([ConstructorReference], [Referent])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(ConstructorReference, Referent)]
 -> ([ConstructorReference], [Referent]))
-> ([Referent] -> [(ConstructorReference, Referent)])
-> [Referent]
-> ([ConstructorReference], [Referent])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe (ConstructorReference, Referent)]
-> [(ConstructorReference, Referent)]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe (ConstructorReference, Referent)]
 -> [(ConstructorReference, Referent)])
-> ([Referent] -> [Maybe (ConstructorReference, Referent)])
-> [Referent]
-> [(ConstructorReference, Referent)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Referent -> Maybe (ConstructorReference, Referent))
-> [Referent] -> [Maybe (ConstructorReference, Referent)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Referent -> Maybe (ConstructorReference, Referent)
extract ([Referent] -> ([ConstructorReference], [Referent]))
-> [Referent] -> ([ConstructorReference], [Referent])
forall a b. (a -> b) -> a -> b
$ HashQualified Name -> Names -> [Referent]
lookupTerm HashQualified Name
hq Names
parseNames
  where
    extract :: Referent -> Maybe (ConstructorReference, Referent)
extract rt :: Referent
rt@(Con ConstructorReference
rf ConstructorType
_) = (ConstructorReference, Referent)
-> Maybe (ConstructorReference, Referent)
forall a. a -> Maybe a
Just (ConstructorReference
rf, Referent
rt)
    extract Referent
_ = Maybe (ConstructorReference, Referent)
forall a. Maybe a
Nothing

lookupTermRefs ::
  HQ.HashQualified Name -> Names -> ([Reference], [Referent])
lookupTermRefs :: HashQualified Name -> Names -> ([Reference' Text Hash], [Referent])
lookupTermRefs HashQualified Name
hq Names
parseNames =
  [(Reference' Text Hash, Referent)]
-> ([Reference' Text Hash], [Referent])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(Reference' Text Hash, Referent)]
 -> ([Reference' Text Hash], [Referent]))
-> ([Referent] -> [(Reference' Text Hash, Referent)])
-> [Referent]
-> ([Reference' Text Hash], [Referent])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe (Reference' Text Hash, Referent)]
-> [(Reference' Text Hash, Referent)]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe (Reference' Text Hash, Referent)]
 -> [(Reference' Text Hash, Referent)])
-> ([Referent] -> [Maybe (Reference' Text Hash, Referent)])
-> [Referent]
-> [(Reference' Text Hash, Referent)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Referent -> Maybe (Reference' Text Hash, Referent))
-> [Referent] -> [Maybe (Reference' Text Hash, Referent)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Referent -> Maybe (Reference' Text Hash, Referent)
extract ([Referent] -> ([Reference' Text Hash], [Referent]))
-> [Referent] -> ([Reference' Text Hash], [Referent])
forall a b. (a -> b) -> a -> b
$ HashQualified Name -> Names -> [Referent]
lookupTerm HashQualified Name
hq Names
parseNames
  where
    extract :: Referent -> Maybe (Reference' Text Hash, Referent)
extract rt :: Referent
rt@(Ref Reference' Text Hash
rf) = (Reference' Text Hash, Referent)
-> Maybe (Reference' Text Hash, Referent)
forall a. a -> Maybe a
Just (Reference' Text Hash
rf, Referent
rt)
    extract Referent
_ = Maybe (Reference' Text Hash, Referent)
forall a. Maybe a
Nothing

lookupTermRefWithType ::
  Codebase.Codebase IO Symbol Ann ->
  HQ.HashQualified Name ->
  Cli [(Reference, Type Symbol Ann)]
lookupTermRefWithType :: Codebase IO Symbol Ann
-> HashQualified Name
-> Cli [(Reference' Text Hash, Type Symbol Ann)]
lookupTermRefWithType Codebase IO Symbol Ann
codebase HashQualified Name
name = do
  Names
names <- Cli Names
Cli.currentNames
  IO [(Reference' Text Hash, Type Symbol Ann)]
-> Cli [(Reference' Text Hash, Type Symbol Ann)]
forall a. IO a -> Cli a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
    (IO [(Reference' Text Hash, Type Symbol Ann)]
 -> Cli [(Reference' Text Hash, Type Symbol Ann)])
-> (([Reference' Text Hash], [Referent])
    -> IO [(Reference' Text Hash, Type Symbol Ann)])
-> ([Reference' Text Hash], [Referent])
-> Cli [(Reference' Text Hash, Type Symbol Ann)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Codebase IO Symbol Ann
-> Transaction [(Reference' Text Hash, Type Symbol Ann)]
-> IO [(Reference' Text Hash, Type Symbol Ann)]
forall (m :: * -> *) v a b.
MonadIO m =>
Codebase m v a -> Transaction b -> m b
Codebase.runTransaction Codebase IO Symbol Ann
codebase
    (Transaction [(Reference' Text Hash, Type Symbol Ann)]
 -> IO [(Reference' Text Hash, Type Symbol Ann)])
-> (([Reference' Text Hash], [Referent])
    -> Transaction [(Reference' Text Hash, Type Symbol Ann)])
-> ([Reference' Text Hash], [Referent])
-> IO [(Reference' Text Hash, Type Symbol Ann)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Maybe (Reference' Text Hash, Type Symbol Ann)]
 -> [(Reference' Text Hash, Type Symbol Ann)])
-> Transaction [Maybe (Reference' Text Hash, Type Symbol Ann)]
-> Transaction [(Reference' Text Hash, Type Symbol Ann)]
forall a b. (a -> b) -> Transaction a -> Transaction b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Maybe (Reference' Text Hash, Type Symbol Ann)]
-> [(Reference' Text Hash, Type Symbol Ann)]
forall a. [Maybe a] -> [a]
catMaybes
    (Transaction [Maybe (Reference' Text Hash, Type Symbol Ann)]
 -> Transaction [(Reference' Text Hash, Type Symbol Ann)])
-> (([Reference' Text Hash], [Referent])
    -> Transaction [Maybe (Reference' Text Hash, Type Symbol Ann)])
-> ([Reference' Text Hash], [Referent])
-> Transaction [(Reference' Text Hash, Type Symbol Ann)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Reference' Text Hash
 -> Transaction (Maybe (Reference' Text Hash, Type Symbol Ann)))
-> [Reference' Text Hash]
-> Transaction [Maybe (Reference' Text Hash, Type Symbol Ann)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse Reference' Text Hash
-> Transaction (Maybe (Reference' Text Hash, Type Symbol Ann))
annot
    ([Reference' Text Hash]
 -> Transaction [Maybe (Reference' Text Hash, Type Symbol Ann)])
-> (([Reference' Text Hash], [Referent]) -> [Reference' Text Hash])
-> ([Reference' Text Hash], [Referent])
-> Transaction [Maybe (Reference' Text Hash, Type Symbol Ann)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Reference' Text Hash], [Referent]) -> [Reference' Text Hash]
forall a b. (a, b) -> a
fst
    (([Reference' Text Hash], [Referent])
 -> Cli [(Reference' Text Hash, Type Symbol Ann)])
-> ([Reference' Text Hash], [Referent])
-> Cli [(Reference' Text Hash, Type Symbol Ann)]
forall a b. (a -> b) -> a -> b
$ HashQualified Name -> Names -> ([Reference' Text Hash], [Referent])
lookupTermRefs HashQualified Name
name Names
names
  where
    annot :: Reference' Text Hash
-> Transaction (Maybe (Reference' Text Hash, Type Symbol Ann))
annot Reference' Text Hash
tm =
      (Type Symbol Ann -> (Reference' Text Hash, Type Symbol Ann))
-> Maybe (Type Symbol Ann)
-> Maybe (Reference' Text Hash, Type Symbol Ann)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((,) Reference' Text Hash
tm) (Maybe (Type Symbol Ann)
 -> Maybe (Reference' Text Hash, Type Symbol Ann))
-> Transaction (Maybe (Type Symbol Ann))
-> Transaction (Maybe (Reference' Text Hash, Type Symbol Ann))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Codebase IO Symbol Ann
-> Reference' Text Hash -> Transaction (Maybe (Type Symbol Ann))
forall a (m :: * -> *).
BuiltinAnnotation a =>
Codebase m Symbol a
-> Reference' Text Hash -> Transaction (Maybe (Type Symbol a))
Codebase.getTypeOfTerm Codebase IO Symbol Ann
codebase Reference' Text Hash
tm

resolveTerm :: HQ.HashQualified Name -> Cli Referent
resolveTerm :: HashQualified Name -> Cli Referent
resolveTerm HashQualified Name
name = do
  Names
names <- Cli Names
Cli.currentNames
  let pped :: PrettyPrintEnvDecl
pped = Namer -> Suffixifier -> PrettyPrintEnvDecl
PPED.makePPED (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)
  let suffixifiedPPE :: PrettyPrintEnv
suffixifiedPPE = PrettyPrintEnvDecl -> PrettyPrintEnv
PPED.suffixifiedPPE PrettyPrintEnvDecl
pped
  case HashQualified Name -> Names -> [Referent]
lookupTerm HashQualified Name
name Names
names of
    [] -> Output -> Cli Referent
forall a. Output -> Cli a
Cli.returnEarly (HQSplit' -> Output
TermNotFound (HQSplit' -> Output) -> HQSplit' -> Output
forall a b. (a -> b) -> a -> b
$ Maybe HQSplit' -> HQSplit'
forall a. HasCallStack => Maybe a -> a
fromJust Maybe HQSplit'
parsed)
      where
        parsed :: Maybe HQSplit'
parsed = Name -> HQSplit'
hqSplitFromName' (Name -> HQSplit') -> Maybe Name -> Maybe HQSplit'
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashQualified Name -> Maybe Name
forall n. HashQualified n -> Maybe n
HQ.toName HashQualified Name
name
    [Referent
rf] -> Referent -> Cli Referent
forall a. a -> Cli a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Referent
rf
    [Referent]
rfs ->
      Output -> Cli Referent
forall a. Output -> Cli a
Cli.returnEarly (PrettyPrintEnv -> HashQualified Name -> Set Referent -> Output
TermAmbiguous PrettyPrintEnv
suffixifiedPPE HashQualified Name
name ([Referent] -> Set Referent
forall a. Ord a => [a] -> Set a
fromList [Referent]
rfs))

resolveCon :: HQ.HashQualified Name -> Cli ConstructorReference
resolveCon :: HashQualified Name -> Cli ConstructorReference
resolveCon HashQualified Name
name = do
  Names
names <- Cli Names
Cli.currentNames
  let pped :: PrettyPrintEnvDecl
pped = Namer -> Suffixifier -> PrettyPrintEnvDecl
PPED.makePPED (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)
  let suffixifiedPPE :: PrettyPrintEnv
suffixifiedPPE = PrettyPrintEnvDecl -> PrettyPrintEnv
PPED.suffixifiedPPE PrettyPrintEnvDecl
pped
  case HashQualified Name -> Names -> ([ConstructorReference], [Referent])
lookupCon HashQualified Name
name Names
names of
    ([], [Referent]
_) -> Output -> Cli ConstructorReference
forall a. Output -> Cli a
Cli.returnEarly (HQSplit' -> Output
TermNotFound (HQSplit' -> Output) -> HQSplit' -> Output
forall a b. (a -> b) -> a -> b
$ Maybe HQSplit' -> HQSplit'
forall a. HasCallStack => Maybe a -> a
fromJust Maybe HQSplit'
parsed)
      where
        parsed :: Maybe HQSplit'
parsed = Name -> HQSplit'
hqSplitFromName' (Name -> HQSplit') -> Maybe Name -> Maybe HQSplit'
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashQualified Name -> Maybe Name
forall n. HashQualified n -> Maybe n
HQ.toName HashQualified Name
name
    ([ConstructorReference
co], [Referent]
_) -> ConstructorReference -> Cli ConstructorReference
forall a. a -> Cli a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ConstructorReference
co
    ([ConstructorReference]
_, [Referent]
rfts) ->
      Output -> Cli ConstructorReference
forall a. Output -> Cli a
Cli.returnEarly (PrettyPrintEnv -> HashQualified Name -> Set Referent -> Output
TermAmbiguous PrettyPrintEnv
suffixifiedPPE HashQualified Name
name ([Referent] -> Set Referent
forall a. Ord a => [a] -> Set a
fromList [Referent]
rfts))

resolveTermRef :: HQ.HashQualified Name -> Cli Reference
resolveTermRef :: HashQualified Name -> Cli (Reference' Text Hash)
resolveTermRef HashQualified Name
name = do
  Names
names <- Cli Names
Cli.currentNames
  let pped :: PrettyPrintEnvDecl
pped = Namer -> Suffixifier -> PrettyPrintEnvDecl
PPED.makePPED (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)
  let suffixifiedPPE :: PrettyPrintEnv
suffixifiedPPE = PrettyPrintEnvDecl -> PrettyPrintEnv
PPED.suffixifiedPPE PrettyPrintEnvDecl
pped
  case HashQualified Name -> Names -> ([Reference' Text Hash], [Referent])
lookupTermRefs HashQualified Name
name Names
names of
    ([], [Referent]
_) -> Output -> Cli (Reference' Text Hash)
forall a. Output -> Cli a
Cli.returnEarly (HQSplit' -> Output
TermNotFound (HQSplit' -> Output) -> HQSplit' -> Output
forall a b. (a -> b) -> a -> b
$ Maybe HQSplit' -> HQSplit'
forall a. HasCallStack => Maybe a -> a
fromJust Maybe HQSplit'
parsed)
      where
        parsed :: Maybe HQSplit'
parsed = Name -> HQSplit'
hqSplitFromName' (Name -> HQSplit') -> Maybe Name -> Maybe HQSplit'
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashQualified Name -> Maybe Name
forall n. HashQualified n -> Maybe n
HQ.toName HashQualified Name
name
    ([Reference' Text Hash
rf], [Referent]
_) -> Reference' Text Hash -> Cli (Reference' Text Hash)
forall a. a -> Cli a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Reference' Text Hash
rf
    ([Reference' Text Hash]
_, [Referent]
rfts) ->
      Output -> Cli (Reference' Text Hash)
forall a. Output -> Cli a
Cli.returnEarly (PrettyPrintEnv -> HashQualified Name -> Set Referent -> Output
TermAmbiguous PrettyPrintEnv
suffixifiedPPE HashQualified Name
name ([Referent] -> Set Referent
forall a. Ord a => [a] -> Set a
fromList [Referent]
rfts))

resolveMainRef :: HQ.HashQualified Name -> Cli (Reference, PrettyPrintEnv)
resolveMainRef :: HashQualified Name -> Cli (Reference' Text Hash, PrettyPrintEnv)
resolveMainRef HashQualified Name
main = do
  Cli.Env {Codebase IO Symbol Ann
codebase :: Codebase IO Symbol Ann
$sel:codebase:Env :: Env -> Codebase IO Symbol Ann
codebase, Runtime Symbol
runtime :: Runtime Symbol
$sel:runtime:Env :: Env -> Runtime Symbol
runtime} <- Cli Env
forall r (m :: * -> *). MonadReader r m => m r
ask
  Names
names <- Cli Names
Cli.currentNames
  let pped :: PrettyPrintEnvDecl
pped = Namer -> Suffixifier -> PrettyPrintEnvDecl
PPED.makePPED (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)
  let suffixifiedPPE :: PrettyPrintEnv
suffixifiedPPE = PrettyPrintEnvDecl -> PrettyPrintEnv
PPED.suffixifiedPPE PrettyPrintEnvDecl
pped
  let mainType :: Type Symbol Ann
mainType = Runtime Symbol -> Type Symbol Ann
forall v. Runtime v -> Type v Ann
Runtime.mainType Runtime Symbol
runtime
  Codebase IO Symbol Ann
-> HashQualified Name
-> Cli [(Reference' Text Hash, Type Symbol Ann)]
lookupTermRefWithType Codebase IO Symbol Ann
codebase HashQualified Name
main Cli [(Reference' Text Hash, Type Symbol Ann)]
-> ([(Reference' Text Hash, Type Symbol Ann)]
    -> Cli (Reference' Text Hash, PrettyPrintEnv))
-> Cli (Reference' Text Hash, PrettyPrintEnv)
forall a b. Cli a -> (a -> Cli b) -> Cli b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    [(Reference' Text Hash
rf, Type Symbol Ann
ty)]
      | Type Symbol Ann -> Type Symbol Ann -> Bool
forall v loc. Var v => Type v loc -> Type v loc -> Bool
Typechecker.fitsScheme Type Symbol Ann
ty Type Symbol Ann
mainType -> (Reference' Text Hash, PrettyPrintEnv)
-> Cli (Reference' Text Hash, PrettyPrintEnv)
forall a. a -> Cli a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Reference' Text Hash
rf, PrettyPrintEnv
suffixifiedPPE)
      | Bool
otherwise -> Output -> Cli (Reference' Text Hash, PrettyPrintEnv)
forall a. Output -> Cli a
Cli.returnEarly (Text
-> HashQualified Name
-> Type Symbol Ann
-> PrettyPrintEnv
-> [Type Symbol Ann]
-> Output
BadMainFunction Text
"main" HashQualified Name
main Type Symbol Ann
ty PrettyPrintEnv
suffixifiedPPE [Type Symbol Ann
mainType])
    [(Reference' Text Hash, Type Symbol Ann)]
_ -> Output -> Cli (Reference' Text Hash, PrettyPrintEnv)
forall a. Output -> Cli a
Cli.returnEarly (HashQualified Name -> PrettyPrintEnv -> [Type Symbol Ann] -> Output
NoMainFunction HashQualified Name
main PrettyPrintEnv
suffixifiedPPE [Type Symbol Ann
mainType])