module Unison.Codebase.Editor.HandleInput.Dependents
  ( handleDependents,
  )
where

import Data.Bifoldable (bifoldMap, binull)
import Data.Set qualified as Set
import U.Codebase.Sqlite.Operations qualified as Operations
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.Branch qualified as Branch
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.LabeledDependency qualified as LD
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 qualified as Reference
import Unison.Referent qualified as Referent
import Unison.Syntax.HashQualifiedPrime qualified as HQ' (toText)
import Unison.Util.Defns (Defns (..), DefnsF)
import Unison.Util.Set qualified as Set

handleDependents :: HQ.HashQualified Name -> Cli ()
handleDependents :: HashQualified Name -> Cli ()
handleDependents HashQualified Name
hq = do
  DefnsF Set Referent Reference
refs <- HashQualified Name -> Cli (DefnsF Set Referent Reference)
resolveHQName HashQualified Name
hq

  Bool -> Cli () -> Cli ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (DefnsF Set Referent Reference -> Bool
forall (t :: * -> * -> *) a b. Bifoldable t => t a b -> Bool
binull DefnsF Set Referent Reference
refs) do
    Output -> Cli ()
forall a. Output -> Cli a
Cli.returnEarly (HashQualified Name -> Output
LabeledReferenceNotFound HashQualified Name
hq)

  Branch0 IO
namespace <- Cli (Branch0 IO)
Cli.getCurrentProjectRoot0
  let ppe :: PrettyPrintEnv
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)

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

  DefnsF Set Id Id
dependents <- do
    Transaction (DefnsF Set Id Id) -> Cli (DefnsF Set Id Id)
forall a. Transaction a -> Cli a
Cli.runTransaction do
      Set Id -> Set Reference -> Transaction (DefnsF Set Id Id)
Operations.directDependentsWithinScope
        ( Set Id -> Set Id -> Set Id
forall a. Ord a => Set a -> Set a -> Set a
Set.union
            ((Reference -> Maybe Id) -> Set Reference -> Set Id
forall b a. Ord b => (a -> Maybe b) -> Set a -> Set b
Set.mapMaybe Reference -> Maybe Id
Reference.toId (Branch0 IO -> Set Reference
forall (m :: * -> *). Branch0 m -> Set Reference
Branch.deepTypeReferences Branch0 IO
namespaceWithoutLibdeps))
            ((Referent -> Maybe Id) -> Set Referent -> Set Id
forall b a. Ord b => (a -> Maybe b) -> Set a -> Set b
Set.mapMaybe Referent -> Maybe Id
Referent.toTermReferenceId (Branch0 IO -> Set Referent
forall (m :: * -> *). Branch0 m -> Set Referent
Branch.deepReferents Branch0 IO
namespaceWithoutLibdeps))
        )
        ((Set Referent -> Set Reference)
-> (Set Reference -> Set Reference)
-> DefnsF Set Referent Reference
-> Set Reference
forall m a b. Monoid m => (a -> m) -> (b -> m) -> Defns a b -> m
forall (p :: * -> * -> *) m a b.
(Bifoldable p, Monoid m) =>
(a -> m) -> (b -> m) -> p a b -> m
bifoldMap ((Referent -> Reference) -> Set Referent -> Set Reference
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map Referent -> Reference
Referent.toReference) Set Reference -> Set Reference
forall a. a -> a
id DefnsF Set Referent Reference
refs)

  let dependentNames ::
        DefnsF
          []
          (HQ'.HashQualified Name, HQ'.HashQualified Name)
          (HQ'.HashQualified Name, HQ'.HashQualified Name)
      dependentNames :: DefnsF
  []
  (HashQualified Name, HashQualified Name)
  (HashQualified Name, HashQualified Name)
dependentNames =
        (Set Id -> [(HashQualified Name, HashQualified Name)])
-> (Set Id -> [(HashQualified Name, HashQualified Name)])
-> DefnsF Set Id Id
-> 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
          ((Id -> [(HashQualified Name, HashQualified Name)])
-> Set Id -> [(HashQualified Name, HashQualified Name)]
forall {a} {b}.
(a -> [(HashQualified Name, b)])
-> Set a -> [(HashQualified Name, b)]
f (Id -> Referent
Referent.fromTermReferenceId (Id -> Referent)
-> (Referent -> [(HashQualified Name, HashQualified Name)])
-> Id
-> [(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
ppeWithoutLibdeps))
          ((Id -> [(HashQualified Name, HashQualified Name)])
-> Set Id -> [(HashQualified Name, HashQualified Name)]
forall {a} {b}.
(a -> [(HashQualified Name, b)])
-> Set a -> [(HashQualified Name, b)]
f (Id -> Reference
Reference.fromId (Id -> Reference)
-> (Reference -> [(HashQualified Name, HashQualified Name)])
-> Id
-> [(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
-> Reference -> [(HashQualified Name, HashQualified Name)]
PPE.typeNames PrettyPrintEnv
ppeWithoutLibdeps))
          DefnsF Set Id Id
dependents
        where
          f :: (a -> [(HashQualified Name, b)])
-> Set a -> [(HashQualified Name, b)]
f a -> [(HashQualified Name, b)]
g =
            Set a -> [a]
forall a. Set a -> [a]
Set.toList
              (Set a -> [a])
-> ([a] -> [(HashQualified Name, b)])
-> Set a
-> [(HashQualified Name, b)]
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (a -> Maybe (HashQualified Name, b))
-> [a] -> [(HashQualified Name, b)]
forall a b. (a -> Maybe b) -> [a] -> [b]
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
mapMaybe (a -> [(HashQualified Name, b)]
g (a -> [(HashQualified Name, b)])
-> ([(HashQualified Name, b)] -> Maybe (HashQualified Name, b))
-> a
-> Maybe (HashQualified Name, b)
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> [(HashQualified Name, b)] -> Maybe (HashQualified Name, b)
forall a. [a] -> Maybe a
listToMaybe)
              ([a] -> [(HashQualified Name, b)])
-> ([(HashQualified Name, b)] -> [(HashQualified Name, b)])
-> [a]
-> [(HashQualified Name, b)]
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> ((HashQualified Name, b) -> Text)
-> [(HashQualified Name, b)] -> [(HashQualified Name, b)]
forall a. (a -> Text) -> [a] -> [a]
Name.sortByText ((HashQualified Name, b) -> HashQualified Name
forall a b. (a, b) -> a
fst ((HashQualified Name, b) -> HashQualified Name)
-> (HashQualified Name -> Text) -> (HashQualified Name, b) -> 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)

  -- Set numbered args
  (DefnsF
  []
  (HashQualified Name, HashQualified Name)
  (HashQualified Name, HashQualified Name)
dependentNames.types [(HashQualified Name, HashQualified Name)]
-> [(HashQualified Name, HashQualified Name)]
-> [(HashQualified Name, HashQualified Name)]
forall a. [a] -> [a] -> [a]
++ DefnsF
  []
  (HashQualified Name, HashQualified Name)
  (HashQualified Name, HashQualified Name)
dependentNames.terms)
    [(HashQualified Name, HashQualified Name)]
-> ([(HashQualified Name, HashQualified Name)]
    -> [StructuredArgument])
-> [StructuredArgument]
forall a b. a -> (a -> b) -> b
& ((HashQualified Name, HashQualified Name) -> StructuredArgument)
-> [(HashQualified Name, HashQualified Name)]
-> [StructuredArgument]
forall a b. (a -> b) -> [a] -> [b]
map (HashQualified Name -> StructuredArgument
SA.HashQualified (HashQualified Name -> StructuredArgument)
-> ((HashQualified Name, HashQualified Name) -> HashQualified Name)
-> (HashQualified Name, HashQualified Name)
-> StructuredArgument
forall b c a. (b -> c) -> (a -> b) -> a -> c
. 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 a. (b -> c) -> (a -> b) -> a -> c
. (HashQualified Name, HashQualified Name) -> HashQualified Name
forall a b. (a, b) -> a
fst)
    [StructuredArgument] -> ([StructuredArgument] -> Cli ()) -> Cli ()
forall a b. a -> (a -> b) -> b
& [StructuredArgument] -> Cli ()
Cli.setNumberedArgs

  let lds :: Set LabeledDependency
lds = (Set Referent -> Set LabeledDependency)
-> (Set Reference -> Set LabeledDependency)
-> DefnsF Set Referent Reference
-> Set LabeledDependency
forall m a b. Monoid m => (a -> m) -> (b -> m) -> Defns a b -> m
forall (p :: * -> * -> *) m a b.
(Bifoldable p, Monoid m) =>
(a -> m) -> (b -> m) -> p a b -> m
bifoldMap ((Referent -> LabeledDependency)
-> Set Referent -> Set LabeledDependency
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map Referent -> LabeledDependency
LD.referent) ((Reference -> LabeledDependency)
-> Set Reference -> Set LabeledDependency
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map Reference -> LabeledDependency
LD.typeRef) DefnsF Set Referent Reference
refs
  Output -> Cli ()
Cli.respond (PrettyPrintEnv
-> Set LabeledDependency
-> DefnsF
     []
     (HashQualified Name, HashQualified Name)
     (HashQualified Name, HashQualified Name)
-> Output
ListDependents PrettyPrintEnv
ppe Set LabeledDependency
lds DefnsF
  []
  (HashQualified Name, HashQualified Name)
  (HashQualified Name, HashQualified Name)
dependentNames)