module Unison.Codebase.Editor.HandleInput.DeleteNamespace
( handleDeleteNamespace,
getEndangeredDependents,
)
where
import Control.Lens hiding (from)
import Control.Lens qualified as Lens
import Control.Monad.State qualified as State
import Data.Map qualified as Map
import Data.Set qualified as Set
import Data.Set.NonEmpty (NESet)
import Data.Set.NonEmpty qualified as NESet
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.MonadUtils 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.Input
import Unison.Codebase.Editor.Output
import Unison.Codebase.Path (Path)
import Unison.Codebase.Path qualified as Path
import Unison.Codebase.ProjectPath qualified as ProjectPath
import Unison.LabeledDependency (LabeledDependency)
import Unison.LabeledDependency qualified as LD
import Unison.NameSegment qualified as NameSegment
import Unison.Names (Names)
import Unison.Names qualified as Names
import Unison.Prelude
import Unison.PrettyPrintEnv.Names qualified as PPE
import Unison.PrettyPrintEnvDecl.Names qualified as PPED
import Unison.Referent qualified as Referent
import Unison.Sqlite qualified as Sqlite
handleDeleteNamespace :: Input -> Insistence -> Maybe (Path, NameSegment.NameSegment) -> Cli ()
handleDeleteNamespace :: Input -> Insistence -> Maybe (Path, NameSegment) -> Cli ()
handleDeleteNamespace Input
input Insistence
insistence = \case
Maybe (Path, NameSegment)
Nothing -> do
LoopState
loopState <- Cli LoopState
forall s (m :: * -> *). MonadState s m => m s
State.get
if LoopState
loopState.lastInput Maybe Input -> Maybe Input -> Bool
forall a. Eq a => a -> a -> Bool
== Input -> Maybe Input
forall a. a -> Maybe a
Just Input
input Bool -> Bool -> Bool
|| Insistence
insistence Insistence -> Insistence -> Bool
forall a. Eq a => a -> a -> Bool
== Insistence
Force
then do
ProjectPathG Project ProjectBranch
pp <- Cli (ProjectPathG Project ProjectBranch)
Cli.getCurrentProjectPath
Bool
_ <- Text
-> ProjectPathG Project ProjectBranch
-> (Branch IO -> Branch IO)
-> Cli Bool
Cli.updateAt (Text
commandName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" .") ProjectPathG Project ProjectBranch
pp (Branch IO -> Branch IO -> Branch IO
forall a b. a -> b -> a
const Branch IO
forall (m :: * -> *). Branch m
Branch.empty)
Output -> Cli ()
Cli.respond Output
DeletedEverything
else Output -> Cli ()
Cli.respond Output
DeleteEverythingConfirmation
Just p :: (Path, NameSegment)
p@(Path
parentPath, NameSegment
childName) -> do
Branch IO
branch <- Path -> Cli (Branch IO)
Cli.expectBranchAtPath ((Path, NameSegment) -> Path
Path.unsplit (Path, NameSegment)
p)
let toDelete :: Names
toDelete =
Name -> Names -> Names
Names.prefix0
(Split' -> Name
Path.nameFromSplit' (Split' -> Name) -> Split' -> Name
forall a b. (a -> b) -> a -> b
$ (Path -> Path') -> (Path, NameSegment) -> Split'
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (Relative -> Path'
Path.RelativePath' (Relative -> Path') -> (Path -> Relative) -> Path -> Path'
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path -> Relative
Path.Relative) (Path, NameSegment)
p)
(Branch0 IO -> Names
forall (m :: * -> *). Branch0 m -> Names
Branch.toNames (Branch IO -> Branch0 IO
forall (m :: * -> *). Branch m -> Branch0 m
Branch.head Branch IO
branch))
Cli ()
afterDelete <- do
Branch0 IO
currentBranch <- Cli (Branch0 IO)
Cli.getCurrentProjectRoot0
let names :: Names
names = Branch0 IO -> Names
forall (m :: * -> *). Branch0 m -> Names
Branch.toNames Branch0 IO
currentBranch
namesSansLib :: Names
namesSansLib = Branch0 IO -> Names
forall (m :: * -> *). Branch0 m -> Names
Branch.toNames (Branch0 IO -> Branch0 IO
forall (m :: * -> *). Branch0 m -> Branch0 m
Branch.deleteLibdeps Branch0 IO
currentBranch)
Map LabeledDependency (NESet LabeledDependency)
endangerments <- Transaction (Map LabeledDependency (NESet LabeledDependency))
-> Cli (Map LabeledDependency (NESet LabeledDependency))
forall a. Transaction a -> Cli a
Cli.runTransaction (Names
-> Set LabeledDependency
-> Names
-> Names
-> Transaction (Map LabeledDependency (NESet LabeledDependency))
getEndangeredDependents Names
toDelete Set LabeledDependency
forall a. Set a
Set.empty Names
names Names
namesSansLib)
case (Map LabeledDependency (NESet LabeledDependency) -> Bool
forall a. Map LabeledDependency a -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null Map LabeledDependency (NESet LabeledDependency)
endangerments, Insistence
insistence) of
(Bool
True, Insistence
_) -> Cli () -> Cli (Cli ())
forall a. a -> Cli a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Output -> Cli ()
Cli.respond Output
Success)
(Bool
False, Insistence
Force) -> do
let ppeDecl :: PrettyPrintEnvDecl
ppeDecl = Namer -> Suffixifier -> PrettyPrintEnvDecl
PPED.makePPED (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)
Cli () -> Cli (Cli ())
forall a. a -> Cli a
forall (f :: * -> *) a. Applicative f => a -> f a
pure do
Output -> Cli ()
Cli.respond Output
Success
NumberedOutput -> Cli ()
Cli.respondNumbered (NumberedOutput -> Cli ()) -> NumberedOutput -> Cli ()
forall a b. (a -> b) -> a -> b
$ PrettyPrintEnvDecl
-> Map LabeledDependency (NESet LabeledDependency)
-> NumberedOutput
DeletedDespiteDependents PrettyPrintEnvDecl
ppeDecl Map LabeledDependency (NESet LabeledDependency)
endangerments
(Bool
False, Insistence
Try) -> do
let ppeDecl :: PrettyPrintEnvDecl
ppeDecl = Namer -> Suffixifier -> PrettyPrintEnvDecl
PPED.makePPED (Int -> Names -> Namer
PPE.hqNamer Int
10 Names
names) (Names -> Suffixifier
PPE.suffixifyByHash Names
names)
NumberedOutput -> Cli ()
Cli.respondNumbered (NumberedOutput -> Cli ()) -> NumberedOutput -> Cli ()
forall a b. (a -> b) -> a -> b
$ PrettyPrintEnvDecl
-> Map LabeledDependency (NESet LabeledDependency)
-> NumberedOutput
CantDeleteNamespace PrettyPrintEnvDecl
ppeDecl Map LabeledDependency (NESet LabeledDependency)
endangerments
Cli (Cli ())
forall a. Cli a
Cli.returnEarlyWithoutOutput
ProjectPathG Project ProjectBranch
parentPathAbs <- Path -> Cli (ProjectPathG Project ProjectBranch)
Cli.resolvePath Path
parentPath
let description :: Text
description = Text
commandName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> forall target source. From source target => source -> target
into @Text (ProjectPathG Project ProjectBranch
parentPathAbs ProjectPathG Project ProjectBranch
-> (ProjectPathG Project ProjectBranch
-> ProjectPathG Project ProjectBranch)
-> ProjectPathG Project ProjectBranch
forall a b. a -> (a -> b) -> b
& (Absolute -> Identity Absolute)
-> ProjectPathG Project ProjectBranch
-> Identity (ProjectPathG Project ProjectBranch)
forall p b (f :: * -> *).
Functor f =>
(Absolute -> f Absolute)
-> ProjectPathG p b -> f (ProjectPathG p b)
ProjectPath.absPath_ ((Absolute -> Identity Absolute)
-> ProjectPathG Project ProjectBranch
-> Identity (ProjectPathG Project ProjectBranch))
-> (Absolute -> Absolute)
-> ProjectPathG Project ProjectBranch
-> ProjectPathG Project ProjectBranch
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ (Absolute -> NameSegment -> Absolute
forall s a. Snoc s s a a => s -> a -> s
`Lens.snoc` NameSegment
childName))
Text
-> ProjectPathG Project ProjectBranch
-> (Branch IO -> Branch IO)
-> Cli Bool
Cli.updateAt Text
description ProjectPathG Project ProjectBranch
parentPathAbs (Path -> (Branch IO -> Branch IO) -> Branch IO -> Branch IO
forall (m :: * -> *).
Applicative m =>
Path -> (Branch m -> Branch m) -> Branch m -> Branch m
Branch.modifyAt (NameSegment -> Path
Path.singleton NameSegment
childName) \Branch IO
_ -> Branch IO
forall (m :: * -> *). Branch m
Branch.empty)
Cli ()
afterDelete
where
commandName :: Text
commandName :: Text
commandName =
case Insistence
insistence of
Insistence
Try -> Text
"delete.namespace"
Insistence
Force -> Text
"delete.namespace.force"
getEndangeredDependents ::
Names ->
Set LabeledDependency ->
Names ->
Names ->
Sqlite.Transaction (Map LabeledDependency (NESet LabeledDependency))
getEndangeredDependents :: Names
-> Set LabeledDependency
-> Names
-> Names
-> Transaction (Map LabeledDependency (NESet LabeledDependency))
getEndangeredDependents Names
targetToDelete Set LabeledDependency
otherDesiredDeletions Names
rootNames Names
rootNamesSansLib = do
let extinct :: Set LabeledDependency
extinct :: Set LabeledDependency
extinct = Names -> Set LabeledDependency
Names.labeledReferences Names
targetToDelete Set LabeledDependency
-> Set LabeledDependency -> Set LabeledDependency
forall a. Ord a => Set a -> Set a -> Set a
`Set.difference` Names -> Set LabeledDependency
refsAfterDeletingTarget Names
rootNames
let accumulateDependents :: LabeledDependency -> Sqlite.Transaction (Map LabeledDependency (Set LabeledDependency))
accumulateDependents :: LabeledDependency
-> Transaction (Map LabeledDependency (Set LabeledDependency))
accumulateDependents LabeledDependency
ld =
let ref :: Reference
ref = (Reference -> Reference)
-> (Referent -> Reference) -> LabeledDependency -> Reference
forall a.
(Reference -> a) -> (Referent -> a) -> LabeledDependency -> a
LD.fold Reference -> Reference
forall a. a -> a
id Referent -> Reference
Referent.toReference LabeledDependency
ld
in LabeledDependency
-> Set LabeledDependency
-> Map LabeledDependency (Set LabeledDependency)
forall k a. k -> a -> Map k a
Map.singleton LabeledDependency
ld (Set LabeledDependency
-> Map LabeledDependency (Set LabeledDependency))
-> (Set Reference -> Set LabeledDependency)
-> Set Reference
-> Map LabeledDependency (Set LabeledDependency)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Reference -> LabeledDependency)
-> Set Reference -> Set LabeledDependency
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map Reference -> LabeledDependency
LD.termRef (Set Reference -> Map LabeledDependency (Set LabeledDependency))
-> Transaction (Set Reference)
-> Transaction (Map LabeledDependency (Set LabeledDependency))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DependentsSelector -> Reference -> Transaction (Set Reference)
Codebase.dependents DependentsSelector
Queries.ExcludeOwnComponent Reference
ref
Map LabeledDependency (Set LabeledDependency)
allDependentsOfExtinct :: Map LabeledDependency (Set LabeledDependency) <-
(Set LabeledDependency
-> Set LabeledDependency -> Set LabeledDependency)
-> [Map LabeledDependency (Set LabeledDependency)]
-> Map LabeledDependency (Set LabeledDependency)
forall (f :: * -> *) k a.
(Foldable f, Ord k) =>
(a -> a -> a) -> f (Map k a) -> Map k a
Map.unionsWith Set LabeledDependency
-> Set LabeledDependency -> Set LabeledDependency
forall a. Semigroup a => a -> a -> a
(<>) ([Map LabeledDependency (Set LabeledDependency)]
-> Map LabeledDependency (Set LabeledDependency))
-> Transaction [Map LabeledDependency (Set LabeledDependency)]
-> Transaction (Map LabeledDependency (Set LabeledDependency))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [LabeledDependency]
-> (LabeledDependency
-> Transaction (Map LabeledDependency (Set LabeledDependency)))
-> Transaction [Map LabeledDependency (Set LabeledDependency)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for (Set LabeledDependency -> [LabeledDependency]
forall a. Set a -> [a]
Set.toList Set LabeledDependency
extinct) LabeledDependency
-> Transaction (Map LabeledDependency (Set LabeledDependency))
accumulateDependents
let extinctToEndangered :: Map LabeledDependency (NESet LabeledDependency)
extinctToEndangered :: Map LabeledDependency (NESet LabeledDependency)
extinctToEndangered =
(Set LabeledDependency -> Maybe (NESet LabeledDependency))
-> Map LabeledDependency (Set LabeledDependency)
-> Map LabeledDependency (NESet LabeledDependency)
forall a b k. (a -> Maybe b) -> Map k a -> Map k b
Map.mapMaybe
( Set LabeledDependency -> Maybe (NESet LabeledDependency)
forall a. Set a -> Maybe (NESet a)
NESet.nonEmptySet
(Set LabeledDependency -> Maybe (NESet LabeledDependency))
-> (Set LabeledDependency -> Set LabeledDependency)
-> Set LabeledDependency
-> Maybe (NESet LabeledDependency)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Set LabeledDependency
-> Set LabeledDependency -> Set LabeledDependency
forall a. Ord a => Set a -> Set a -> Set a
Set.intersection (Set LabeledDependency
-> Set LabeledDependency -> Set LabeledDependency
forall a. Ord a => Set a -> Set a -> Set a
Set.difference (Names -> Set LabeledDependency
refsAfterDeletingTarget Names
rootNamesSansLib) Set LabeledDependency
otherDesiredDeletions)
)
Map LabeledDependency (Set LabeledDependency)
allDependentsOfExtinct
Map LabeledDependency (NESet LabeledDependency)
-> Transaction (Map LabeledDependency (NESet LabeledDependency))
forall a. a -> Transaction a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Map LabeledDependency (NESet LabeledDependency)
extinctToEndangered
where
refsAfterDeletingTarget :: Names -> Set LabeledDependency
refsAfterDeletingTarget :: Names -> Set LabeledDependency
refsAfterDeletingTarget Names
names =
Names -> Set LabeledDependency
Names.labeledReferences (Names
names Names -> Names -> Names
`Names.difference` Names
targetToDelete)