module Unison.Codebase.Editor.HandleInput.Dependencies
  ( handleDependencies,
  )
where

import Control.Arrow ((***))
import Data.Bifoldable (binull)
import Data.Set qualified as Set
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.MonadUtils qualified as Cli
import Unison.Cli.NameResolutionUtils (resolveHQName)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch.Names qualified as Branch
import Unison.Codebase.Editor.Output
import Unison.Codebase.Editor.StructuredArgument qualified as SA
import Unison.HashQualified qualified as HQ
import Unison.HashQualifiedPrime qualified as HQ'
import Unison.Name (Name)
import Unison.Name qualified as Name
import Unison.Prelude
import Unison.PrettyPrintEnv qualified as PPE
import Unison.PrettyPrintEnv.Names qualified as PPE
import Unison.Reference (Reference)
import Unison.Referent qualified as Referent
import Unison.Syntax.HashQualifiedPrime qualified as HQ'
import Unison.Util.Defns (Defns (..), DefnsF, DefnsF2)

handleDependencies :: HQ.HashQualified Name -> Cli ()
handleDependencies :: HashQualified Name -> Cli ()
handleDependencies HashQualified Name
hq = do
  dependentsRefs <- HashQualified Name -> Cli (DefnsF Set Referent TypeReference)
resolveHQName HashQualified Name
hq

  when (binull dependentsRefs) do
    Cli.returnEarly (LabeledReferenceNotFound hq)

  namespace <- Cli.getCurrentProjectRoot0
  let ppe =
        let names :: Names
names = Branch0 IO -> Names
forall (m :: * -> *). Branch0 m -> Names
Branch.toNames Branch0 IO
namespace
         in Namer -> Suffixifier -> PrettyPrintEnv
PPE.makePPE (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)

  dependencies <- do
    Cli.runTransaction $ Codebase.directDependencies dependentsRefs

  let dependencyNames ::
        DefnsF
          []
          (HQ.HashQualified Name, HQ.HashQualified Name)
          (HQ.HashQualified Name, HQ.HashQualified Name)
      dependencyNames =
        (Set TypeReference -> [(HashQualified Name, HashQualified Name)])
-> (Set TypeReference
    -> [(HashQualified Name, HashQualified Name)])
-> DefnsF Set TypeReference TypeReference
-> DefnsF
     []
     (HashQualified Name, HashQualified Name)
     (HashQualified Name, HashQualified Name)
forall a b c d. (a -> b) -> (c -> d) -> Defns a c -> Defns b d
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap
          ((TypeReference -> [(HashQualified Name, HashQualified Name)])
-> Set TypeReference -> [(HashQualified Name, HashQualified Name)]
f (TypeReference -> Referent
Referent.fromTermReference (TypeReference -> Referent)
-> (Referent -> [(HashQualified Name, HashQualified Name)])
-> TypeReference
-> [(HashQualified Name, HashQualified Name)]
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> PrettyPrintEnv
-> Referent -> [(HashQualified Name, HashQualified Name)]
PPE.termNames PrettyPrintEnv
ppe))
          ((TypeReference -> [(HashQualified Name, HashQualified Name)])
-> Set TypeReference -> [(HashQualified Name, HashQualified Name)]
f (PrettyPrintEnv
-> TypeReference -> [(HashQualified Name, HashQualified Name)]
PPE.typeNames PrettyPrintEnv
ppe))
          DefnsF Set TypeReference TypeReference
dependencies
        where
          f ::
            (Reference -> [(HQ'.HashQualified Name, HQ'.HashQualified Name)]) ->
            Set Reference ->
            [(HQ.HashQualified Name, HQ.HashQualified Name)]
          f :: (TypeReference -> [(HashQualified Name, HashQualified Name)])
-> Set TypeReference -> [(HashQualified Name, HashQualified Name)]
f TypeReference -> [(HashQualified Name, HashQualified Name)]
g =
            Set TypeReference -> [TypeReference]
forall a. Set a -> [a]
Set.toList
              -- Pick the best name for a reference (with `listToMaybe`), else use the ref (if nameless)
              (Set TypeReference -> [TypeReference])
-> ([TypeReference] -> [(HashQualified Name, HashQualified Name)])
-> Set TypeReference
-> [(HashQualified Name, HashQualified Name)]
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (TypeReference
 -> Either TypeReference (HashQualified Name, HashQualified Name))
-> [TypeReference]
-> [Either TypeReference (HashQualified Name, HashQualified Name)]
forall a b. (a -> b) -> [a] -> [b]
map (\TypeReference
x -> Either TypeReference (HashQualified Name, HashQualified Name)
-> ((HashQualified Name, HashQualified Name)
    -> Either TypeReference (HashQualified Name, HashQualified Name))
-> Maybe (HashQualified Name, HashQualified Name)
-> Either TypeReference (HashQualified Name, HashQualified Name)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (TypeReference
-> Either TypeReference (HashQualified Name, HashQualified Name)
forall a b. a -> Either a b
Left TypeReference
x) (HashQualified Name, HashQualified Name)
-> Either TypeReference (HashQualified Name, HashQualified Name)
forall a b. b -> Either a b
Right ([(HashQualified Name, HashQualified Name)]
-> Maybe (HashQualified Name, HashQualified Name)
forall a. [a] -> Maybe a
listToMaybe (TypeReference -> [(HashQualified Name, HashQualified Name)]
g TypeReference
x)))
              ([TypeReference]
 -> [Either TypeReference (HashQualified Name, HashQualified Name)])
-> ([Either TypeReference (HashQualified Name, HashQualified Name)]
    -> [(HashQualified Name, HashQualified Name)])
-> [TypeReference]
-> [(HashQualified Name, HashQualified Name)]
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> [Either TypeReference (HashQualified Name, HashQualified Name)]
-> ([TypeReference], [(HashQualified Name, HashQualified Name)])
forall a b. [Either a b] -> ([a], [b])
partitionEithers
              -- Sort the named references alphabetically, then stick the hash-only ones at the end
              ([Either TypeReference (HashQualified Name, HashQualified Name)]
 -> ([TypeReference], [(HashQualified Name, HashQualified Name)]))
-> (([TypeReference], [(HashQualified Name, HashQualified Name)])
    -> [(HashQualified Name, HashQualified Name)])
-> [Either TypeReference (HashQualified Name, HashQualified Name)]
-> [(HashQualified Name, HashQualified Name)]
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> ([TypeReference], [(HashQualified Name, HashQualified Name)])
-> [(HashQualified Name, HashQualified Name)]
h

          h ::
            ([Reference], [(HQ'.HashQualified Name, HQ'.HashQualified Name)]) ->
            [(HQ.HashQualified Name, HQ.HashQualified Name)]
          h :: ([TypeReference], [(HashQualified Name, HashQualified Name)])
-> [(HashQualified Name, HashQualified Name)]
h ([TypeReference]
nameless, [(HashQualified Name, HashQualified Name)]
named) =
            [[(HashQualified Name, HashQualified Name)]]
-> [(HashQualified Name, HashQualified Name)]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
              [ [(HashQualified Name, HashQualified Name)]
named
                  [(HashQualified Name, HashQualified Name)]
-> ([(HashQualified Name, HashQualified Name)]
    -> [(HashQualified Name, HashQualified Name)])
-> [(HashQualified Name, HashQualified Name)]
forall a b. a -> (a -> b) -> b
& ((HashQualified Name, HashQualified Name) -> Text)
-> [(HashQualified Name, HashQualified Name)]
-> [(HashQualified Name, HashQualified Name)]
forall a. (a -> Text) -> [a] -> [a]
Name.sortByText ((HashQualified Name, HashQualified Name) -> HashQualified Name
forall a b. (a, b) -> a
fst ((HashQualified Name, HashQualified Name) -> HashQualified Name)
-> (HashQualified Name -> Text)
-> (HashQualified Name, HashQualified Name)
-> Text
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> HashQualified Name -> Text
HQ'.toText)
                  [(HashQualified Name, HashQualified Name)]
-> ([(HashQualified Name, HashQualified Name)]
    -> [(HashQualified Name, HashQualified Name)])
-> [(HashQualified Name, HashQualified Name)]
forall a b. a -> (a -> b) -> b
& ((HashQualified Name, HashQualified Name)
 -> (HashQualified Name, HashQualified Name))
-> [(HashQualified Name, HashQualified Name)]
-> [(HashQualified Name, HashQualified Name)]
forall a b. (a -> b) -> [a] -> [b]
map (HashQualified Name -> HashQualified Name
forall n. HashQualified n -> HashQualified n
HQ'.toHQ (HashQualified Name -> HashQualified Name)
-> (HashQualified Name -> HashQualified Name)
-> (HashQualified Name, HashQualified Name)
-> (HashQualified Name, HashQualified Name)
forall b c b' c'. (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** HashQualified Name -> HashQualified Name
forall n. HashQualified n -> HashQualified n
HQ'.toHQ),
                [TypeReference]
nameless
                  [TypeReference]
-> ([TypeReference] -> [(HashQualified Name, HashQualified Name)])
-> [(HashQualified Name, HashQualified Name)]
forall a b. a -> (a -> b) -> b
& (TypeReference -> (HashQualified Name, HashQualified Name))
-> [TypeReference] -> [(HashQualified Name, HashQualified Name)]
forall a b. (a -> b) -> [a] -> [b]
map (\TypeReference
x -> let y :: HashQualified Name
y = TypeReference -> HashQualified Name
HQ.fromReference TypeReference
x in (HashQualified Name
y, HashQualified Name
y))
              ]

  -- Set numbered args
  (dependencyNames.types ++ dependencyNames.terms)
    & map (SA.HashQualified . fst)
    & Cli.setNumberedArgs

  let dependentsNames :: DefnsF2 Set HQ.HashQualified Name Name
      dependentsNames =
        (Set Referent -> Set (HashQualified Name))
-> (Set TypeReference -> Set (HashQualified Name))
-> DefnsF Set Referent TypeReference
-> DefnsF2 Set HashQualified Name Name
forall a b c d. (a -> b) -> (c -> d) -> Defns a c -> Defns b d
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap
          ((Referent -> HashQualified Name)
-> Set Referent -> Set (HashQualified Name)
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map (PrettyPrintEnv -> Referent -> HashQualified Name
PPE.termNameOrHashOnly PrettyPrintEnv
ppe))
          ((TypeReference -> HashQualified Name)
-> Set TypeReference -> Set (HashQualified Name)
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map (PrettyPrintEnv -> TypeReference -> HashQualified Name
PPE.typeNameOrHashOnly PrettyPrintEnv
ppe))
          DefnsF Set Referent TypeReference
dependentsRefs

  Cli.respond (ListDependencies dependentsNames dependencyNames)