Skip to content

Commit

Permalink
Merge pull request unisonweb#5466 from unisonweb/24-10-16-edit-depend…
Browse files Browse the repository at this point in the history
…ents

feat: add `edit.dependents`
  • Loading branch information
aryairani authored Dec 2, 2024
2 parents 99baffd + 6c2a6e9 commit ce0e9af
Show file tree
Hide file tree
Showing 19 changed files with 816 additions and 137 deletions.
6 changes: 6 additions & 0 deletions lib/unison-prelude/src/Unison/Util/Set.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Unison.Util.Set
( asSingleton,
difference1,
intersects,
mapMaybe,
symmetricDifference,
Unison.Util.Set.traverse,
Expand Down Expand Up @@ -29,6 +30,11 @@ difference1 xs ys =
where
zs = Set.difference xs ys

-- | Get whether two sets intersect.
intersects :: (Ord a) => Set a -> Set a -> Bool
intersects xs ys =
not (Set.disjoint xs ys)

symmetricDifference :: (Ord a) => Set a -> Set a -> Set a
symmetricDifference a b = (a `Set.difference` b) `Set.union` (b `Set.difference` a)

Expand Down
20 changes: 17 additions & 3 deletions parser-typechecker/src/Unison/Codebase.hs
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,28 @@ termsOfTypeByReference c r =
. Set.map (fmap Reference.DerivedId)
<$> termsOfTypeImpl c r

filterTermsByReferentHavingType :: (Var v) => Codebase m v a -> Type v a -> Set Referent.Referent -> Sqlite.Transaction (Set Referent.Referent)
filterTermsByReferentHavingType ::
(Var v) =>
Codebase m v a ->
Type v a ->
Set Referent.Referent ->
Sqlite.Transaction (Set Referent.Referent)
filterTermsByReferentHavingType c ty = filterTermsByReferentHavingTypeByReference c $ Hashing.typeToReference ty

filterTermsByReferenceIdHavingType :: (Var v) => Codebase m v a -> Type v a -> Set TermReferenceId -> Sqlite.Transaction (Set TermReferenceId)
filterTermsByReferenceIdHavingType ::
(Var v) =>
Codebase m v a ->
Type v a ->
Set TermReferenceId ->
Sqlite.Transaction (Set TermReferenceId)
filterTermsByReferenceIdHavingType c ty = filterTermsByReferenceIdHavingTypeImpl c (Hashing.typeToReference ty)

-- | Find the subset of `tms` which match the exact type `r` points to.
filterTermsByReferentHavingTypeByReference :: Codebase m v a -> TypeReference -> Set Referent.Referent -> Sqlite.Transaction (Set Referent.Referent)
filterTermsByReferentHavingTypeByReference ::
Codebase m v a ->
TypeReference ->
Set Referent.Referent ->
Sqlite.Transaction (Set Referent.Referent)
filterTermsByReferentHavingTypeByReference c r tms = do
let (builtins, derived) = partitionEithers . map p $ Set.toList tms
let builtins' =
Expand Down
22 changes: 14 additions & 8 deletions unison-cli/src/Unison/Cli/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -431,15 +431,21 @@ respondNumbered output = do
setNumberedArgs args

-- | Perform a Cli action with access to a console region, which is closed upon completion.
--
-- (In transcripts, this just outputs messages as normal).
withRespondRegion :: ((Output -> Cli ()) -> Cli a) -> Cli a
withRespondRegion action =
with_ Console.Regions.displayConsoleRegions do
with (Console.Regions.withConsoleRegion Console.Regions.Linear) \region ->
action \output ->
liftIO do
string <- (OutputMessages.notifyUser "." output)
width <- PrettyTerminal.getAvailableWidth
Console.Regions.setConsoleRegion region (Pretty.toANSI width (Pretty.border 2 string))
withRespondRegion action = do
env <- ask
case env.isTranscriptTest of
False ->
with_ Console.Regions.displayConsoleRegions do
with (Console.Regions.withConsoleRegion Console.Regions.Linear) \region ->
action \output ->
liftIO do
string <- (OutputMessages.notifyUser "." output)
width <- PrettyTerminal.getAvailableWidth
Console.Regions.setConsoleRegion region (Pretty.toANSI width (Pretty.border 2 string))
True -> action respond

-- | Updates the numbered args, but only if the new args are non-empty.
setNumberedArgs :: NumberedArgs -> Cli ()
Expand Down
51 changes: 51 additions & 0 deletions unison-cli/src/Unison/Cli/NameResolutionUtils.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
-- | Utilities related to resolving names to things.
module Unison.Cli.NameResolutionUtils
( resolveHQName,
resolveHQToLabeledDependencies,
)
where

import Control.Monad.Reader (ask)
import Data.Bifoldable (bifoldMap)
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.HashQualified qualified as HQ
import Unison.LabeledDependency (LabeledDependency)
import Unison.LabeledDependency qualified as LD
import Unison.Name (Name)
import Unison.Name qualified as Name
import Unison.Names qualified as Names
import Unison.Prelude
import Unison.Reference (TypeReference)
import Unison.Referent (Referent)
import Unison.Server.NameSearch.Sqlite qualified as Sqlite
import Unison.ShortHash (ShortHash)
import Unison.Util.Defns (Defns (..), DefnsF)

resolveHQName :: HQ.HashQualified Name -> Cli (DefnsF Set Referent TypeReference)
resolveHQName = \case
HQ.NameOnly name -> do
names <- Cli.currentNames
pure
Defns
{ terms = Name.searchByRankedSuffix name names.terms,
types = Name.searchByRankedSuffix name names.types
}
-- rationale: the hash should be unique enough that the name never helps
-- mitchell says: that seems wrong
HQ.HashQualified _n hash -> resolveHashOnly hash
HQ.HashOnly hash -> resolveHashOnly hash
where
resolveHashOnly :: ShortHash -> Cli (DefnsF Set Referent TypeReference)
resolveHashOnly hash = do
env <- ask
Cli.runTransaction do
terms <- Sqlite.termReferentsByShortHash env.codebase hash
types <- Sqlite.typeReferencesByShortHash hash
pure Defns {terms, types}

resolveHQToLabeledDependencies :: HQ.HashQualified Name -> Cli (Set LabeledDependency)
resolveHQToLabeledDependencies =
fmap (bifoldMap (Set.map LD.referent) (Set.map LD.typeRef)) . resolveHQName
66 changes: 5 additions & 61 deletions unison-cli/src/Unison/Codebase/Editor/HandleInput.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import U.Codebase.Branch.Diff qualified as V2Branch.Diff
import U.Codebase.Causal qualified as V2Causal
import U.Codebase.HashTags (CausalHash (..))
import U.Codebase.Reflog qualified as Reflog
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.ABT qualified as ABT
import Unison.Builtin qualified as Builtin
import Unison.Builtin.Terms qualified as Builtin
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.MonadUtils (getCurrentProjectBranch)
import Unison.Cli.MonadUtils qualified as Cli
import Unison.Cli.NameResolutionUtils (resolveHQToLabeledDependencies)
import Unison.Cli.NamesUtils qualified as Cli
import Unison.Cli.ProjectUtils qualified as ProjectUtils
import Unison.Codebase qualified as Codebase
Expand All @@ -58,7 +58,9 @@ import Unison.Codebase.Editor.HandleInput.DebugSynhashTerm (handleDebugSynhashTe
import Unison.Codebase.Editor.HandleInput.DeleteBranch (handleDeleteBranch)
import Unison.Codebase.Editor.HandleInput.DeleteNamespace (getEndangeredDependents, handleDeleteNamespace)
import Unison.Codebase.Editor.HandleInput.DeleteProject (handleDeleteProject)
import Unison.Codebase.Editor.HandleInput.Dependents (handleDependents)
import Unison.Codebase.Editor.HandleInput.EditNamespace (handleEditNamespace)
import Unison.Codebase.Editor.HandleInput.EditDependents (handleEditDependents)
import Unison.Codebase.Editor.HandleInput.FindAndReplace (handleStructuredFindI, handleStructuredFindReplaceI, handleTextFindI)
import Unison.Codebase.Editor.HandleInput.FormatFile qualified as Format
import Unison.Codebase.Editor.HandleInput.Global qualified as Global
Expand Down Expand Up @@ -845,6 +847,7 @@ loop e = do
UpgradeCommitI -> handleCommitUpgrade
LibInstallI remind libdep -> handleInstallLib remind libdep
DebugSynhashTermI name -> handleDebugSynhashTerm name
EditDependentsI name -> handleEditDependents name

inputDescription :: Input -> Cli Text
inputDescription input =
Expand Down Expand Up @@ -987,6 +990,7 @@ inputDescription input =
DisplayI {} -> wat
DocsI {} -> wat
DocsToHtmlI {} -> wat
EditDependentsI {} -> wat
FindI {} -> wat
FindShallowI {} -> wat
HistoryI {} -> wat
Expand Down Expand Up @@ -1191,66 +1195,6 @@ handleDependencies hq = do
Cli.setNumberedArgs . map SA.HashQualified $ types <> terms
Cli.respond $ ListDependencies suffixifiedPPE lds types terms

handleDependents :: HQ.HashQualified Name -> Cli ()
handleDependents hq = do
-- todo: add flag to handle transitive efficiently
lds <- resolveHQToLabeledDependencies hq
-- Use an unsuffixified PPE here, so we display full names (relative to the current path),
-- rather than the shortest possible unambiguous name.
names <- Cli.currentNames
let pped = PPED.makePPED (PPE.hqNamer 10 names) (PPE.suffixifyByHash names)
let fqppe = PPE.unsuffixifiedPPE pped
let ppe = PPE.suffixifiedPPE pped
when (null lds) do
Cli.returnEarly (LabeledReferenceNotFound hq)

results <- for (toList lds) \ld -> do
-- The full set of dependent references, any number of which may not have names in the current namespace.
dependents <-
let tp = Codebase.dependents Queries.ExcludeOwnComponent
tm = \case
Referent.Ref r -> Codebase.dependents Queries.ExcludeOwnComponent r
Referent.Con (ConstructorReference r _cid) _ct ->
Codebase.dependents Queries.ExcludeOwnComponent r
in Cli.runTransaction (LD.fold tp tm ld)
let -- True is term names, False is type names
results :: [(Bool, HQ.HashQualified Name, Reference)]
results = do
r <- Set.toList dependents
Just (isTerm, hq) <- [(True,) <$> PPE.terms fqppe (Referent.Ref r), (False,) <$> PPE.types fqppe r]
fullName <- [HQ'.toName hq]
guard (not (Name.beginsWithSegment fullName NameSegment.libSegment))
Just shortName <- pure $ PPE.terms ppe (Referent.Ref r) <|> PPE.types ppe r
pure (isTerm, HQ'.toHQ shortName, r)
pure results
let sort = fmap fst . nubOrdOn snd . Name.sortByText (HQ.toText . fst)
let types = sort [(n, r) | (False, n, r) <- join results]
let terms = sort [(n, r) | (True, n, r) <- join results]
Cli.setNumberedArgs . map SA.HashQualified $ types <> terms
Cli.respond (ListDependents ppe lds types terms)

-- todo: compare to `getHQTerms` / `getHQTypes`. Is one universally better?
resolveHQToLabeledDependencies :: HQ.HashQualified Name -> Cli (Set LabeledDependency)
resolveHQToLabeledDependencies = \case
HQ.NameOnly n -> do
names <- Cli.currentNames
let terms, types :: Set LabeledDependency
terms = Set.map LD.referent . Name.searchBySuffix n $ Names.terms names
types = Set.map LD.typeRef . Name.searchBySuffix n $ Names.types names
pure $ terms <> types
-- rationale: the hash should be unique enough that the name never helps
HQ.HashQualified _n sh -> resolveHashOnly sh
HQ.HashOnly sh -> resolveHashOnly sh
where
resolveHashOnly sh = do
Cli.Env {codebase} <- ask
(terms, types) <-
Cli.runTransaction do
terms <- Backend.termReferentsByShortHash codebase sh
types <- Backend.typeReferencesByShortHash sh
pure (terms, types)
pure $ Set.map LD.referent terms <> Set.map LD.typeRef types

doDisplay :: OutputLocation -> Names -> Term Symbol () -> Cli ()
doDisplay outputLoc names tm = do
Cli.Env {codebase} <- ask
Expand Down
68 changes: 68 additions & 0 deletions unison-cli/src/Unison/Codebase/Editor/HandleInput/Dependents.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module Unison.Codebase.Editor.HandleInput.Dependents
( handleDependents,
)
where

import Data.Set qualified as Set
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.NameResolutionUtils (resolveHQToLabeledDependencies)
import Unison.Cli.NamesUtils qualified as Cli
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Editor.Output
import Unison.Codebase.Editor.StructuredArgument qualified as SA
import Unison.ConstructorReference (GConstructorReference (..))
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.NameSegment qualified as NameSegment
import Unison.Prelude
import Unison.PrettyPrintEnv qualified as PPE
import Unison.PrettyPrintEnv.Names qualified as PPE
import Unison.PrettyPrintEnvDecl qualified as PPE hiding (biasTo, empty)
import Unison.PrettyPrintEnvDecl.Names qualified as PPED
import Unison.Reference (Reference)
import Unison.Referent qualified as Referent
import Unison.Syntax.HashQualified qualified as HQ (toText)
import Unison.Util.List (nubOrdOn)

handleDependents :: HQ.HashQualified Name -> Cli ()
handleDependents hq = do
-- todo: add flag to handle transitive efficiently
lds <- resolveHQToLabeledDependencies hq
-- Use an unsuffixified PPE here, so we display full names (relative to the current path),
-- rather than the shortest possible unambiguous name.
names <- Cli.currentNames
let pped = PPED.makePPED (PPE.hqNamer 10 names) (PPE.suffixifyByHash names)
let fqppe = PPE.unsuffixifiedPPE pped
let ppe = PPE.suffixifiedPPE pped
when (null lds) do
Cli.returnEarly (LabeledReferenceNotFound hq)

results <- for (toList lds) \ld -> do
-- The full set of dependent references, any number of which may not have names in the current namespace.
dependents <-
let tp = Codebase.dependents Queries.ExcludeOwnComponent
tm = \case
Referent.Ref r -> Codebase.dependents Queries.ExcludeOwnComponent r
Referent.Con (ConstructorReference r _cid) _ct ->
Codebase.dependents Queries.ExcludeOwnComponent r
in Cli.runTransaction (LD.fold tp tm ld)
let -- True is term names, False is type names
results :: [(Bool, HQ.HashQualified Name, Reference)]
results = do
r <- Set.toList dependents
Just (isTerm, hq) <- [(True,) <$> PPE.terms fqppe (Referent.Ref r), (False,) <$> PPE.types fqppe r]
fullName <- [HQ'.toName hq]
guard (not (Name.beginsWithSegment fullName NameSegment.libSegment))
Just shortName <- pure $ PPE.terms ppe (Referent.Ref r) <|> PPE.types ppe r
pure (isTerm, HQ'.toHQ shortName, r)
pure results
let sort = fmap fst . nubOrdOn snd . Name.sortByText (HQ.toText . fst)
let types = sort [(n, r) | (False, n, r) <- join results]
let terms = sort [(n, r) | (True, n, r) <- join results]
Cli.setNumberedArgs . map SA.HashQualified $ types <> terms
Cli.respond (ListDependents ppe lds types terms)
Loading

0 comments on commit ce0e9af

Please sign in to comment.