diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index 9949ef7a42..9637f85c1a 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -88,7 +88,7 @@ import Unison.Codebase.Editor.HandleInput.ShowDefinition (showDefinitions) import Unison.Codebase.Editor.HandleInput.TermResolution (resolveMainRef) import Unison.Codebase.Editor.HandleInput.Tests qualified as Tests import Unison.Codebase.Editor.HandleInput.UI (openUI) -import Unison.Codebase.Editor.HandleInput.Update (doSlurpAdds, handleUpdate) +import Unison.Codebase.Editor.HandleInput.Update (doSlurpAdds) import Unison.Codebase.Editor.HandleInput.Update2 (handleUpdate2) import Unison.Codebase.Editor.HandleInput.Upgrade (handleUpgrade) import Unison.Codebase.Editor.Input @@ -733,15 +733,7 @@ loop e = do currentNames <- Branch.toNames <$> Cli.getCurrentBranch0 let sr = Slurp.slurpFile uf vars Slurp.AddOp currentNames previewResponse sourceName sr uf - UpdateI optionalPatch requestedNames -> handleUpdate input optionalPatch requestedNames Update2I -> handleUpdate2 - PreviewUpdateI requestedNames -> do - (sourceName, _) <- Cli.expectLatestFile - uf <- Cli.expectLatestTypecheckedFile - let vars = Set.map Name.toVar requestedNames - currentNames <- Branch.toNames <$> Cli.getCurrentBranch0 - let sr = Slurp.slurpFile uf vars Slurp.UpdateOp currentNames - previewResponse sourceName sr uf TodoI patchPath branchPath' -> do patch <- Cli.getPatchAt (fromMaybe Cli.defaultPatchPath patchPath) branchPath <- Cli.resolvePath' branchPath' @@ -1053,13 +1045,6 @@ inputDescription input = DeleteTarget'ProjectBranch _ -> wat DeleteTarget'Project _ -> wat AddI _selection -> pure "add" - UpdateI p0 _selection -> do - p <- - case p0 of - NoPatch -> pure ".nopatch" - DefaultPatch -> (" " <>) <$> ps' Cli.defaultPatchPath - UsePatch p0 -> (" " <>) <$> ps' p0 - pure ("update.old" <> p) Update2I -> pure ("update") UndoI {} -> pure "undo" ExecuteI s args -> pure ("execute " <> Text.unwords (HQ.toText s : fmap Text.pack args)) @@ -1120,7 +1105,6 @@ inputDescription input = PopBranchI {} -> wat PreviewAddI {} -> wat PreviewMergeLocalBranchI {} -> wat - PreviewUpdateI {} -> wat ProjectCreateI {} -> wat ProjectRenameI {} -> wat ProjectSwitchI {} -> wat diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update.hs index b6bb301056..aaa532fb7c 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update.hs @@ -1,580 +1,24 @@ module Unison.Codebase.Editor.HandleInput.Update - ( handleUpdate, - doSlurpAdds, + ( doSlurpAdds, ) where -import Control.Lens -import Control.Monad.Reader (ask) -import Data.Foldable qualified as Foldable -import Data.List qualified as List -import Data.Map qualified as Map -import Data.Set qualified as Set -import Data.Set.NonEmpty qualified as NESet -import U.Codebase.Sqlite.Queries qualified as Queries -import Unison.ABT qualified as ABT -import Unison.Cli.Monad (Cli) -import Unison.Cli.Monad qualified as Cli -import Unison.Cli.MonadUtils qualified as Cli -import Unison.Cli.NamesUtils qualified as Cli -import Unison.Cli.PrettyPrintUtils qualified as Cli -import Unison.Cli.TypeCheck (computeTypecheckingEnvironment) -import Unison.Codebase qualified as Codebase import Unison.Codebase.Branch (Branch0) import Unison.Codebase.Branch qualified as Branch import Unison.Codebase.BranchUtil qualified as BranchUtil -import Unison.Codebase.Editor.Input -import Unison.Codebase.Editor.Output -import Unison.Codebase.Editor.Propagate qualified as Propagate -import Unison.Codebase.Editor.Slurp qualified as Slurp import Unison.Codebase.Editor.SlurpComponent (SlurpComponent (..)) import Unison.Codebase.Editor.SlurpComponent qualified as SC -import Unison.Codebase.Editor.SlurpResult (SlurpResult (..)) -import Unison.Codebase.Editor.SlurpResult qualified as Slurp -import Unison.Codebase.Patch (Patch (..)) -import Unison.Codebase.Patch qualified as Patch import Unison.Codebase.Path (Path) import Unison.Codebase.Path qualified as Path -import Unison.Codebase.TermEdit qualified as TermEdit -import Unison.Codebase.TypeEdit qualified as TypeEdit -import Unison.DataDeclaration (Decl) -import Unison.FileParsers qualified as FileParsers -import Unison.Hash (Hash) -import Unison.Name (Name) -import Unison.Names (Names) import Unison.Names qualified as Names import Unison.Parser.Ann (Ann (..)) import Unison.Prelude -import Unison.PrettyPrintEnvDecl qualified as PPE hiding (biasTo) -import Unison.Reference (Reference, TermReference, TermReferenceId, TypeReference, TypeReferenceId) -import Unison.Reference qualified as Reference -import Unison.Referent (Referent) -import Unison.Referent qualified as Referent -import Unison.Result qualified as Result -import Unison.Sqlite qualified as Sqlite import Unison.Symbol (Symbol) -import Unison.Syntax.Name qualified as Name (toVar, unsafeParseVar) -import Unison.Term (Term) -import Unison.Term qualified as Term -import Unison.Type (Type) -import Unison.Type qualified as Type -import Unison.Typechecker qualified as Typechecker -import Unison.UnisonFile (TypecheckedUnisonFile, UnisonFile) +import Unison.Syntax.Name qualified as Name (unsafeParseVar) +import Unison.UnisonFile (TypecheckedUnisonFile) import Unison.UnisonFile qualified as UF import Unison.UnisonFile.Names qualified as UF -import Unison.Util.Map qualified as Map (remap, upsert) -import Unison.Util.Monoid (foldMapM) -import Unison.Util.Relation qualified as R -import Unison.Util.Set qualified as Set import Unison.Var qualified as Var -import Unison.WatchKind (WatchKind) - --- | Handle an @update@ command. -handleUpdate :: Input -> OptionalPatch -> Set Name -> Cli () -handleUpdate input optionalPatch requestedNames = do - Cli.Env {codebase} <- ask - currentPath' <- Cli.getCurrentPath - let patchPath = - case optionalPatch of - NoPatch -> Nothing - DefaultPatch -> Just Cli.defaultPatchPath - UsePatch p -> Just p - currentCodebaseNames <- Cli.currentNames - sr <- getSlurpResultForUpdate requestedNames currentCodebaseNames - let addsAndUpdates :: SlurpComponent - addsAndUpdates = Slurp.updates sr <> Slurp.adds sr - fileNames :: Names - fileNames = UF.typecheckedToNames (Slurp.originalFile sr) - -- todo: display some error if typeEdits or termEdits itself contains a loop - typeEdits :: [(Name, Reference, Reference)] - typeEdits = do - v <- Set.toList (SC.types (updates sr)) - let n = Name.unsafeParseVar v - let oldRefs0 = Names.typesNamed currentCodebaseNames n - let newRefs = Names.typesNamed fileNames n - case (,) <$> NESet.nonEmptySet oldRefs0 <*> Set.asSingleton newRefs of - Nothing -> error (reportBug "E722145" ("bad (old,new) names: " ++ show (oldRefs0, newRefs))) - Just (oldRefs, newRef) -> do - oldRef <- Foldable.toList oldRefs - [(n, oldRef, newRef)] - hashTerms :: Map Reference (Type Symbol Ann) - hashTerms = Map.fromList (toList hashTerms0) - where - hashTerms0 = (\(_ann, r, _wk, _tm, typ) -> (r, typ)) <$> UF.hashTerms (Slurp.originalFile sr) - termEdits :: [(Name, Reference, Reference)] - termEdits = do - v <- Set.toList (SC.terms (updates sr)) - let n = Name.unsafeParseVar v - let oldRefs0 = Names.refTermsNamed currentCodebaseNames n - let newRefs = Names.refTermsNamed fileNames n - case (,) <$> NESet.nonEmptySet oldRefs0 <*> Set.asSingleton newRefs of - Nothing -> error (reportBug "E936103" ("bad (old,new) names: " ++ show (oldRefs0, newRefs))) - Just (oldRefs, newRef) -> do - oldRef <- Foldable.toList oldRefs - [(n, oldRef, newRef)] - termDeprecations :: [(Name, Referent)] - termDeprecations = - [ (n, r) - | (_, oldTypeRef, _) <- typeEdits, - (n, r) <- Names.constructorsForType oldTypeRef currentCodebaseNames - ] - patchOps <- for patchPath \patchPath -> do - ye'ol'Patch <- Cli.getPatchAt patchPath - -- If `uf` updates a -> a', we want to replace all (a0 -> a) in patch - -- with (a0 -> a') in patch'. - -- So for all (a0 -> a) in patch, for all (a -> a') in `uf`, - -- we must know the type of a0, a, a'. - let -- we need: - -- all of the `old` references from the `new` edits, - -- plus all of the `old` references for edits from patch we're replacing - collectOldForTyping :: [(Reference, Reference)] -> Patch -> Set Reference - collectOldForTyping new old = foldl' f mempty (new ++ fromOld) - where - f acc (r, _r') = Set.insert r acc - newLHS = Set.fromList . fmap fst $ new - fromOld :: [(Reference, Reference)] - fromOld = - [ (r, r') | (r, TermEdit.Replace r' _) <- R.toList . Patch._termEdits $ old, Set.member r' newLHS - ] - neededTypes = collectOldForTyping (map (\(_, old, new) -> (old, new)) termEdits) ye'ol'Patch - - allTypes :: Map Reference (Type v Ann) <- - (liftIO . Codebase.runTransaction codebase) do - fmap Map.fromList . for (toList neededTypes) $ \r -> - (r,) . fromMaybe (Type.builtin External "unknown type") - <$> Codebase.getTypeOfTerm codebase r - - let typing r1 r2 = case (Map.lookup r1 allTypes, Map.lookup r2 hashTerms) of - (Just t1, Just t2) - | Typechecker.isEqual t1 t2 -> TermEdit.Same - | Typechecker.isSubtype t1 t2 -> TermEdit.Subtype - | otherwise -> TermEdit.Different - e -> - error $ - "compiler bug: typing map not constructed properly\n" - <> "typing " - <> show r1 - <> " " - <> show r2 - <> " : " - <> show e - - updatePatch :: Patch -> Patch - updatePatch p = foldl' step2 p' termEdits - where - p' = foldl' step1 p typeEdits - step1 p (_, r, r') = Patch.updateType r (TypeEdit.Replace r') p - step2 p (_, r, r') = Patch.updateTerm typing r (TermEdit.Replace r' (typing r r')) p - (p, seg) = Path.toAbsoluteSplit currentPath' patchPath - updatePatches :: (Monad m) => Branch0 m -> m (Branch0 m) - updatePatches = Branch.modifyPatches seg updatePatch - pure (updatePatch ye'ol'Patch, updatePatches, p) - - when (Slurp.hasAddsOrUpdates sr) $ do - -- take a look at the `updates` from the SlurpResult - -- and make a patch diff to record a replacement from the old to new references - Cli.stepManyAtMNoSync - ( [ ( Path.unabsolute currentPath', - pure . doSlurpUpdates typeEdits termEdits termDeprecations - ), - ( Path.unabsolute currentPath', - pure . doSlurpAdds addsAndUpdates (Slurp.originalFile sr) - ) - ] - ++ case patchOps of - Nothing -> [] - Just (_, update, p) -> [(Path.unabsolute p, update)] - ) - Cli.runTransaction - . Codebase.addDefsToCodebase codebase - . Slurp.filterUnisonFile sr - $ Slurp.originalFile sr - let codebaseAndFileNames = UF.addNamesFromTypeCheckedUnisonFile (Slurp.originalFile sr) currentCodebaseNames - pped <- Cli.prettyPrintEnvDeclFromNames codebaseAndFileNames - let suffixifiedPPE = PPE.suffixifiedPPE pped - Cli.respond $ SlurpOutput input suffixifiedPPE sr - whenJust patchOps \(updatedPatch, _, _) -> - void $ propagatePatchNoSync updatedPatch currentPath' - Cli.syncRoot case patchPath of - Nothing -> "update.nopatch" - Just p -> - p - & Path.unsplit' - & Path.resolve @_ @_ @Path.Absolute currentPath' - & tShow - -getSlurpResultForUpdate :: Set Name -> Names -> Cli SlurpResult -getSlurpResultForUpdate requestedNames slurpCheckNames = do - let slurp :: TypecheckedUnisonFile Symbol Ann -> SlurpResult - slurp file = - Slurp.slurpFile file (Set.map Name.toVar requestedNames) Slurp.UpdateOp slurpCheckNames - - let termRefToNames :: TermReferenceId -> Set Symbol - termRefToNames = - Set.map Name.toVar . Names.namesForReferent slurpCheckNames . Referent.fromTermReferenceId - - let nameToTermRefs :: Symbol -> Set TermReference - nameToTermRefs = Names.refTermsNamed slurpCheckNames . Name.unsafeParseVar - - slurp1 <- do - Cli.Env {codebase} <- ask - unisonFile0 <- Cli.expectLatestTypecheckedFile - - -- Here, we identify whether there are any "implicit terms", which are terms that should be brought into the latest - -- typechecked Unsion file and re-typechecked, which also re-discovers components. - -- - -- The running example here will be one in which the current namespace already has the following terms stored. - -- (For simplicity, we will ignore the limitation that mutually recursive definitions must be top-level arrows; - -- pretend this is Haskell's let). - -- - -- ping = pong + 1 (reference = #pingpong.ping) - -- pong = ping + 2 (reference = #pingpong.pong) - -- wham = pong + 3 (reference = #wham) - -- - -- The user re-defines "ping" in their scratch file thus: - -- - -- ping = wham + 4 (reference = #newping) - -- - -- Note that, pre-update, we had two components [ping,pong] and [wham]. But after the update, since the new `ping` - -- refers to the old `wham`, which refers to the old `pong`, which refers to the old `ping`, we really want to end - -- up with a single component in the end, [ping,pong,wham]. - - -- First, compute an initial slurp, which will identify the initial set of definitions we are updating ({"ping"}). - -- Also, this will be the slurp that we fall back on, in case re-typechecking another Unison file with implicit - -- terms in it fails. - let slurp0 = slurp unisonFile0 - - -- Grab some interim info out of the original slurp. - -- - -- Running example: - -- - -- "ping" => (#newping, Nothing, <#wham + 4>, ) - let nameToInterimInfo :: Map Symbol (Ann, TermReferenceId, Maybe WatchKind, Term Symbol Ann, Type Symbol Ann) - nameToInterimInfo = - UF.hashTermsId (Slurp.originalFile slurp0) - - -- Get the set of names that are being updated. - -- - -- Running example: - -- - -- { "ping" } - let namesBeingUpdated :: Set Symbol - namesBeingUpdated = - SC.terms (Slurp.updates slurp0) - - -- Associate each such name with the set of old (already in the codebase) and new (in the scratch file) references - -- that it's associated with. - -- - -- Running example: - -- - -- "ping" => ({ #pingpong.ping }, #newping) - let updatedNameToRefs :: Map Symbol (Set TermReference, TermReferenceId) - updatedNameToRefs = - Map.fromSet - ( \name -> - case Map.lookup name nameToInterimInfo of - Nothing -> error (reportBug "E798907" "no interim ref for name") - Just (_, interimRef, _, _, _) -> (nameToTermRefs name, interimRef) - ) - namesBeingUpdated - - -- Identify all of the implicit terms, which are: - -- - -- - Both: - -- - Either: - -- - (A) A component-mate of a term that's being updated. - -- - Both: - -- - (B) A dependent of a term that's being updated. - -- - (C) A dependency of the new term. - -- - (D) Not being updated. - -- - -- FIXME Additionally, we have one more temporary requirement. - -- - -- - (E) The term has at least one unambiguous (unconflicted) name in the codebase. - -- - -- This works around two fixable issues: - -- - -- 1. If the term has no names in the namespace, then we can't successfully put it into the Unison file anyway and - -- forward it through the typecheck + slurp process, because slurping currently assumes that it can freely - -- convert back-and-forth between vars and names. - -- - -- 2. If the term has only ambiguous/conflicted names, then putting one of them in the Unison file and proceeding - -- to do an update would have the undesirable side-effect of resolving the conflict. - -- - -- FIXME don't bother for type-changing updates - -- - -- In our running example, the full list of component-mates (A) of the terms being updated is: - -- - -- [ #pingpong.ping, #pingpong.pong ] - -- - -- And because #pingpong.ping is being updated, due to (D), only #pingpong.pong remains. - -- - -- Furthermore, #wham is both a dependent of #pingpong (B), and a dependency of #newping, so it too is an implicit - -- term. - -- - -- Running example: - -- - -- #pingpong.pong => (<#pingpong.ping + 2>, "pong") - -- #wham => (<#pingpong.pong + 3>, "wham") - implicitTerms :: Map TermReferenceId (Term Symbol Ann, Symbol) <- - liftIO do - Codebase.withConnection codebase \conn -> - Sqlite.runTransaction conn do - -- Running example: - -- - -- #pingpong => #newping - let oldHashToInterimHash :: Map Hash Hash - oldHashToInterimHash = - updatedNameToRefs & foldMap \(oldRefs, interimRef) -> - let interimHash = Reference.idToHash interimRef - in Map.fromList $ - oldRefs - & Set.toList - & mapMaybe Reference.toHash - & map (,interimHash) - - let hashToImplicitTerms :: Hash -> Sqlite.Transaction (Map TermReferenceId (Term Symbol Ann, Symbol)) - hashToImplicitTerms hash = do - -- Running example (for `oldHash` iteration): - -- - -- [ (<#pingpong.pong + 1>, ), - -- (<#pingpong.ping + 2>, ) - -- ] - terms <- Codebase.unsafeGetTermComponent codebase hash - let keep :: TermReferenceId -> Maybe Symbol - keep ref = - if notBeingUpdated names - then Foldable.find nameIsUnconflicted names -- (E) - else Nothing - where - -- (D) After getting the entire component of `oldHash`, which has at least one member being - -- updated, we want to keep only the members that are *not* being updated (i.e. those who have - -- no name that is being updated). - -- - -- Running example, first time through (processing #pingpong.ping): - -- - -- Set.disjoint { "ping" } { "ping" } is false, so don't add to the map. - -- - -- Running example, second time through (processing #pingpong.pong): - -- - -- Set.disjoint { "ping" } { "pong" } is true, so add - -- #pingpong.pong => (<#pingpong.ping + 2>, { "pong" })) to the map. - notBeingUpdated = Set.disjoint namesBeingUpdated - nameIsUnconflicted name = Set.size (nameToTermRefs name) == 1 - names = termRefToNames ref - pure $ - terms - -- Running example: - -- - -- [ (#pingpong.ping, (<#pingpong.pong + 1>, )), - -- (#pingpong.pong, (<#pingpong.ping + 2>, )) - -- ] - & Reference.componentFor hash - & List.foldl' - ( \acc (ref, (term, _typ)) -> - case keep ref of - Nothing -> acc - Just name -> Map.insert ref (term, name) acc - ) - Map.empty - - if Map.null oldHashToInterimHash - then pure Map.empty - else do - Sqlite.savepoint do - -- Compute the actual interim decl/term components in the latest typechecked file. These aren't quite - -- given in the unison file structure itself - in the `topLevelComponents'` field we have the - -- components in some arbitrary order (I *think*), each tagged with its stringy name, and in the - -- `hashTermsId` field we have all of the individual terms organized by reference. - let interimDeclComponents :: [(Hash, [Decl Symbol Ann])] - interimDeclComponents = - decls UF.dataDeclarationsId' Right ++ decls UF.effectDeclarationsId' Left - where - decls :: - (TypecheckedUnisonFile Symbol Ann -> Map Symbol (TypeReferenceId, decl)) -> - (decl -> Decl Symbol Ann) -> - [(Hash, [Decl Symbol Ann])] - decls project inject = - slurp0 - & Slurp.originalFile - & project - & Map.elems - & recomponentize - & over (mapped . _2 . mapped) inject - interimTermComponents :: [(Hash, [(Term Symbol Ann, Type Symbol Ann)])] - interimTermComponents = - nameToInterimInfo - & Map.elems - & map (\(_ann, ref, _wk, term, typ) -> (ref, (term, typ))) - & componentize - & uncomponentize - - -- Insert each interim component into the codebase proper. Note: this relies on the codebase interface - -- being smart enough to handle out-of-order components (i.e. inserting a dependent before a - -- dependency). That's currently how the codebase interface works, but maybe in the future it'll grow - -- a precondition that components can only be inserted after their dependencies. - for_ interimDeclComponents \(hash, decls) -> Codebase.putTypeDeclarationComponent codebase hash decls - for_ interimTermComponents \(hash, terms) -> Codebase.putTermComponent codebase hash terms - - terms <- - let interimHashes :: Set Hash - interimHashes = Set.fromList (map fst interimTermComponents) - in Map.toList oldHashToInterimHash & foldMapM \(oldHash, interimHash) -> do - hashes <- - Queries.loadObjectIdForAnyHash oldHash >>= \case - -- better would be to short-circuit all the way to the user and say, actually we can't - -- perform this update at all, due to some intervening delete (e.g. some sort of - -- hard-reset or garbage collection on the codebase) - Nothing -> pure Set.empty - Just oldOid -> do - interimOid <- Queries.expectObjectIdForPrimaryHash interimHash - betweenOids <- Queries.getDependenciesBetweenTerms interimOid oldOid - (Set.\\ interimHashes) <$> Set.traverse Queries.expectPrimaryHashByObjectId betweenOids - foldMapM hashToImplicitTerms (oldHash : Set.toList hashes) - pure (Left terms) -- left = rollback to savepoint - if Map.null implicitTerms - then pure slurp0 - else do - -- We found some implicit terms, so it's time to: - -- - -- 1. Reconstruct a new unison file out of the latest typechecked unison file, plus all of the implicit terms, - -- taking care to adjust their references to each other, so that the proper components are discovered. - -- - -- 2. Re-typecheck, and if it that succeeds, use the resulting typechecked unison file and slurp. - - -- Remap the references contained in each implicit term, then add back in the slurped interim terms and unhash - -- the collection. The unhashing process will invent a fresh variable name for each term. - -- - -- Running example: - -- - -- #newping => ("fresh1", <"fresh3" + 4>) - -- #pingpong.pong => ("fresh2", <"fresh1" + 2>) - -- #wham => ("fresh3", <"fresh2" + 3>) - let refToGeneratedNameAndTerm :: Map TermReferenceId (Symbol, Term Symbol Ann) - refToGeneratedNameAndTerm = - -- Running example: - -- - -- #pingpong.pong => (<#pingpong.ping + 2>, { "pong" }) - -- #wham => (<#pingpong.pong + 3>, { "wham" }) - implicitTerms - -- Running example: - -- - -- #pingpong.pong => <#newping + 2> - -- #wham => <#pingpong.pong + 3> - & Map.map (\(term, _names) -> rewrite term) - -- Running example: - -- - -- #newping => <#wham + 4> - -- #pingpong.pong => <#newping + 2> - -- #wham => <#pingpong.pong + 3> - & Map.union interimRefToTerm - & Term.unhashComponent - where - -- Running example: - -- - -- #newping => <#wham + 4> - interimRefToTerm :: Map TermReferenceId (Term Symbol Ann) - interimRefToTerm = - Map.remap (\(_var, (_ann, ref, _wk, term, _typ)) -> (ref, term)) nameToInterimInfo - -- Running example: apply the following reference mapping everwhere in a term: - -- - -- #pingpong.ping -> #newping - -- ref -> ref - rewrite :: Term Symbol Ann -> Term Symbol Ann - rewrite = - rewriteTermReferences (foldMap toMapping updatedNameToRefs) - where - toMapping :: - (Set TermReference, TermReferenceId) -> - Map TermReference TermReferenceId - toMapping (oldRefs, interimRef) = - foldMap (\oldRef -> Map.singleton oldRef interimRef) oldRefs - - let unisonFile :: UnisonFile Symbol Ann - unisonFile = - UF.UnisonFileId - { dataDeclarationsId = UF.dataDeclarationsId' (Slurp.originalFile slurp0), - effectDeclarationsId = UF.effectDeclarationsId' (Slurp.originalFile slurp0), - -- Running example: - -- - -- fresh1 = fresh3 + 4 - -- fresh2 = fresh1 + 2 - -- fresh3 = fresh2 + 3 - terms = - Map.fromList $ Map.elems refToGeneratedNameAndTerm <&> \(v, term) -> (v, (External, term)), - -- In the context of this update, whatever watches were in the latest typechecked Unison file are - -- irrelevant, so we don't need to copy them over. - watches = Map.empty - } - typecheckingEnv <- - liftIO do - Codebase.runTransaction codebase do - computeTypecheckingEnvironment FileParsers.ShouldUseTndr'No codebase [] unisonFile - case Result.result (FileParsers.synthesizeFile typecheckingEnv unisonFile) of - Just file0 -> do - -- Map each name generated by unhashing back to the name it should have in the Unison file we're going to - -- typecheck. - -- - -- Running example: - -- - -- "fresh1" -> "ping" - -- "fresh2" -> "pong" - -- "fresh3" -> "wham" - let generatedNameToName :: Map Symbol Symbol - generatedNameToName = - refToGeneratedNameAndTerm & Map.remap \(ref, (generatedName, _term)) -> - ( generatedName, - case Map.lookup ref interimRefToName of - Just name -> name - Nothing -> - case Map.lookup ref implicitTerms of - Just (_term, name) -> name - Nothing -> error (reportBug "E836680" "ref not interim nor implicit") - ) - where - -- Associate each term name being updated with its interim reference. - -- - -- Running example: - -- - -- #newping => "ping" - interimRefToName :: Map TermReferenceId Symbol - interimRefToName = - Map.remap (\(name, (_ann, ref, _wk, _term, _typ)) -> (ref, name)) nameToInterimInfo - - let renameTerm :: - (Symbol, Ann, Term Symbol Ann, Type Symbol Ann) -> - (Symbol, Ann, Term Symbol Ann, Type Symbol Ann) - renameTerm (generatedName, ann, term, typ) = - ( case Map.lookup generatedName generatedNameToName of - Just name -> name - Nothing -> error (reportBug "E440546" "no name for generated name"), - ann, - ABT.renames generatedNameToName term, - typ - ) - - let file1 :: TypecheckedUnisonFile Symbol Ann - file1 = - UF.typecheckedUnisonFile - (file0 ^. #dataDeclarationsId') - (file0 ^. #effectDeclarationsId') - ((file0 ^. #topLevelComponents') & over (mapped . mapped) renameTerm) - ((file0 ^. #watchComponents) & over (mapped . _2 . mapped) renameTerm) - - pure (slurp file1) - _ -> pure slurp0 - - pure slurp1 - -rewriteTermReferences :: (Ord v) => Map TermReference TermReferenceId -> Term v a -> Term v a -rewriteTermReferences mapping = - ABT.rebuildUp \term -> - case term of - Term.Ref ref0 -> - case Map.lookup ref0 mapping of - Nothing -> term - Just ref1 -> Term.Ref (Reference.fromId ref1) - _ -> term -- updates the namespace for adding `slurp` doSlurpAdds :: @@ -615,62 +59,3 @@ doSlurpAdds slurp uf = Branch.batchUpdates (typeActions <> termActions) <> ": " <> show wha errorMissingVar v = error $ "expected to find " ++ show v ++ " in " ++ show uf - -doSlurpUpdates :: - (Monad m) => - [(Name, TypeReference, TypeReference)] -> - [(Name, TermReference, TermReference)] -> - [(Name, Referent)] -> - (Branch0 m -> Branch0 m) -doSlurpUpdates typeEdits termEdits deprecated b0 = - Branch.batchUpdates (typeActions <> termActions <> deprecateActions) b0 - where - typeActions = join . map doType $ typeEdits - termActions = join . map doTerm $ termEdits - deprecateActions = join . map doDeprecate $ deprecated - where - doDeprecate (n, r) = [BranchUtil.makeDeleteTermName (Path.splitFromName n) r] - - doType :: (Name, TypeReference, TypeReference) -> [(Path, Branch0 m -> Branch0 m)] - doType (n, old, new) = - let split = Path.splitFromName n - in [ BranchUtil.makeDeleteTypeName split old, - BranchUtil.makeAddTypeName split new - ] - doTerm :: (Name, TermReference, TermReference) -> [(Path, Branch0 m -> Branch0 m)] - doTerm (n, old, new) = - [ BranchUtil.makeDeleteTermName split (Referent.Ref old), - BranchUtil.makeAddTermName split (Referent.Ref new) - ] - where - split = Path.splitFromName n - --- Returns True if the operation changed the namespace, False otherwise. -propagatePatchNoSync :: Patch -> Path.Absolute -> Cli Bool -propagatePatchNoSync patch scopePath = - Cli.time "propagatePatchNoSync" do - Cli.stepAtNoSync' (Path.unabsolute scopePath, Propagate.propagateAndApply patch) - -recomponentize :: [(Reference.Id, a)] -> [(Hash, [a])] -recomponentize = - uncomponentize . componentize - --- Misc. helper: convert a component in listy-form to mappy-form. -componentize :: [(Reference.Id, a)] -> Map Hash (Map Reference.Pos a) -componentize = - foldl' step Map.empty - where - step :: Map Hash (Map Reference.Pos a) -> (Reference.Id, a) -> Map Hash (Map Reference.Pos a) - step acc (Reference.Id hash pos, x) = - Map.upsert - ( \case - Nothing -> Map.singleton pos x - Just acc1 -> Map.insert pos x acc1 - ) - hash - acc - --- Misc. helper: convert a component in mappy-form to listy-form. -uncomponentize :: Map Hash (Map Reference.Pos a) -> [(Hash, [a])] -uncomponentize = - over (mapped . _2) Map.elems . Map.toList diff --git a/unison-cli/src/Unison/Codebase/Editor/Input.hs b/unison-cli/src/Unison/Codebase/Editor/Input.hs index 8edb5317cb..6f471072fc 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Input.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Input.hs @@ -147,9 +147,7 @@ data Input | ClearI | AddI (Set Name) | PreviewAddI (Set Name) - | UpdateI OptionalPatch (Set Name) | Update2I - | PreviewUpdateI (Set Name) | TodoI (Maybe PatchPath) Path' | UndoI | -- First `Maybe Int` is cap on number of results, if any diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index 78cb3d0914..6d164c6df4 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -82,7 +82,6 @@ module Unison.CommandLine.InputPatterns names, namespaceDependencies, previewAdd, - previewUpdate, printVersion, projectCreate, projectCreateEmptyInputPattern, @@ -114,8 +113,6 @@ module Unison.CommandLine.InputPatterns up, update, updateBuiltins, - updateOld, - updateOldNoPatch, upgrade, upgradeCommitInputPattern, view, @@ -829,85 +826,6 @@ update = _ -> Left $ I.help update } -updateOldNoPatch :: InputPattern -updateOldNoPatch = - InputPattern - "update.old.nopatch" - [] - I.Visible - [("definition", ZeroPlus, noCompletionsArg)] - ( P.wrap - ( makeExample' updateOldNoPatch - <> "works like" - <> P.group (makeExample' updateOld <> ",") - <> "except it doesn't add a patch entry for any updates. " - <> "Use this when you want to make changes to definitions without " - <> "pushing those changes to dependents beyond your codebase. " - <> "An example is when updating docs, or when updating a term you " - <> "just added." - ) - <> P.wrapColumn2 - [ ( makeExample' updateOldNoPatch, - "updates all definitions in the .u file." - ), - ( makeExample updateOldNoPatch ["foo", "bar"], - "updates `foo`, `bar`, and their dependents from the .u file." - ) - ] - ) - $ fmap (Input.UpdateI Input.NoPatch . Set.fromList) . traverse handleNameArg - -updateOld :: InputPattern -updateOld = - InputPattern - "update.old" - [] - I.Visible - [("patch", Optional, patchArg), ("definition", ZeroPlus, noCompletionsArg)] - ( P.wrap - ( makeExample' updateOld - <> "works like" - <> P.group (makeExample' add <> ",") - <> "except that if a definition in the file has the same name as an" - <> "existing definition, the name gets updated to point to the new" - <> "definition. If the old definition has any dependents, `update` will" - <> "add those dependents to a refactoring session, specified by an" - <> "optional patch." - ) - <> P.wrapColumn2 - [ ( makeExample' updateOld, - "adds all definitions in the .u file, noting replacements in the" - <> "default patch for the current namespace." - ), - ( makeExample updateOld [""], - "adds all definitions in the .u file, noting replacements in the" - <> "specified patch." - ), - ( makeExample updateOld ["", "foo", "bar"], - "adds `foo`, `bar`, and their dependents from the .u file, noting" - <> "any replacements into the specified patch." - ) - ] - ) - \case - patchStr : ws -> - Input.UpdateI . Input.UsePatch <$> handleSplit'Arg patchStr <*> fmap Set.fromList (traverse handleNameArg ws) - [] -> Right $ Input.UpdateI Input.DefaultPatch mempty - -previewUpdate :: InputPattern -previewUpdate = - InputPattern - "update.old.preview" - [] - I.Visible - [("definition", ZeroPlus, noCompletionsArg)] - ( "`update.old.preview` previews updates to the codebase from the most " - <> "recently typechecked file. This command only displays cached " - <> "typechecking results. Use `load` to reparse & typecheck the file if " - <> "the context has changed." - ) - $ fmap (Input.PreviewUpdateI . Set.fromList) . traverse handleNameArg - view :: InputPattern view = InputPattern @@ -3296,7 +3214,6 @@ validInputs = names True, -- names.global namespaceDependencies, previewAdd, - previewUpdate, printVersion, projectCreate, projectCreateEmptyInputPattern, @@ -3327,8 +3244,6 @@ validInputs = up, update, updateBuiltins, - updateOld, - updateOldNoPatch, upgrade, upgradeCommitInputPattern, view, diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 84da282537..65c6b3024f 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -953,7 +953,6 @@ notifyUser dir = \case SlurpOutput input ppe s -> let isPast = case input of Input.AddI {} -> True - Input.UpdateI {} -> True Input.SaveExecuteResultI {} -> True _ -> False in pure $ SlurpResult.pretty isPast ppe s diff --git a/unison-src/transcripts/ability-term-conflicts-on-update.md b/unison-src/transcripts/ability-term-conflicts-on-update.md index 04810a4939..96b31624f5 100644 --- a/unison-src/transcripts/ability-term-conflicts-on-update.md +++ b/unison-src/transcripts/ability-term-conflicts-on-update.md @@ -33,14 +33,13 @@ thing : '{Channels} () thing _ = send 1 ``` -These should fail with a term/ctor conflict since we exclude the ability from the update. +We should be able to update everything at once. -```ucm:error -.ns> update.old patch Channels.send -.ns> update.old patch thing +```ucm +.ns> update ``` -If however, `Channels.send` and `thing` _depend_ on `Channels`, updating them should succeed since it pulls in the ability as a dependency. +If `Channels.send` and `thing` _depend_ on `Channels`, updating them should succeed since it pulls in the ability as a dependency. ```unison unique ability Channels where @@ -53,17 +52,10 @@ thing : '{Channels} () thing _ = send 1 ``` -These updates should succeed since `Channels` is a dependency. - -```ucm -.ns> update.old.preview patch Channels.send -.ns> update.old.preview patch thing -``` - -We should also be able to successfully update the whole thing. +We should be able to successfully update the whole thing. ```ucm -.ns> update.old +.ns> update ``` # Constructor-term conflict diff --git a/unison-src/transcripts/ability-term-conflicts-on-update.output.md b/unison-src/transcripts/ability-term-conflicts-on-update.output.md index 901446e8d4..bb12fd97d7 100644 --- a/unison-src/transcripts/ability-term-conflicts-on-update.output.md +++ b/unison-src/transcripts/ability-term-conflicts-on-update.output.md @@ -65,31 +65,18 @@ thing _ = send 1 ability Channels ``` -These should fail with a term/ctor conflict since we exclude the ability from the update. +We should be able to update everything at once. ```ucm -.ns> update.old patch Channels.send +.ns> update - x These definitions failed: - - Reason - term/ctor collision Channels.send : a -> () - - Tip: Use `help filestatus` to learn more. + Okay, I'm searching the branch for code that needs to be + updated... -.ns> update.old patch thing - - ⍟ I've added these definitions: - - Channels.send : a -> () - thing : '{Channels} () - - ⍟ I've updated these names to your new definition: - - ability Channels + Done. ``` -If however, `Channels.send` and `thing` _depend_ on `Channels`, updating them should succeed since it pulls in the ability as a dependency. +If `Channels.send` and `thing` _depend_ on `Channels`, updating them should succeed since it pulls in the ability as a dependency. ```unison unique ability Channels where @@ -119,48 +106,15 @@ thing _ = send 1 thing : '{Channels} () ``` -These updates should succeed since `Channels` is a dependency. +We should be able to successfully update the whole thing. ```ucm -.ns> update.old.preview patch Channels.send - - I found and typechecked these definitions in scratch.u. If you - do an `add` or `update`, here's how your codebase would - change: - - ⊡ Previously added definitions will be ignored: Channels - - ⍟ These names already exist. You can `update` them to your - new definition: - - Channels.send : a ->{Channels} () - -.ns> update.old.preview patch thing - - I found and typechecked these definitions in scratch.u. If you - do an `add` or `update`, here's how your codebase would - change: - - ⊡ Previously added definitions will be ignored: Channels - - ⍟ These names already exist. You can `update` them to your - new definition: - - Channels.send : a ->{Channels} () - thing : '{Channels} () +.ns> update -``` -We should also be able to successfully update the whole thing. - -```ucm -.ns> update.old + Okay, I'm searching the branch for code that needs to be + updated... - ⊡ Ignored previously added definitions: Channels - - ⍟ I've updated these names to your new definition: - - Channels.send : a ->{Channels} () - thing : '{Channels} () + Done. ``` # Constructor-term conflict diff --git a/unison-src/transcripts/cycle-update-3.md b/unison-src/transcripts/cycle-update-3.md index dfcd87305e..2bb9608433 100644 --- a/unison-src/transcripts/cycle-update-3.md +++ b/unison-src/transcripts/cycle-update-3.md @@ -21,7 +21,12 @@ ping : Nat ping = 3 ``` -```ucm -.> update.old +Updating only part of a cycle should bring the rest of the cycle into scope: + +```ucm:error +.> update +``` + +``` .> view ping pong ``` diff --git a/unison-src/transcripts/cycle-update-3.output.md b/unison-src/transcripts/cycle-update-3.output.md index 7a0a499dbc..36e7d16e5c 100644 --- a/unison-src/transcripts/cycle-update-3.output.md +++ b/unison-src/transcripts/cycle-update-3.output.md @@ -50,21 +50,33 @@ ping = 3 ping : Nat ``` +Updating only part of a cycle should bring the rest of the cycle into scope: + ```ucm -.> update.old +.> update - ⍟ I've updated these names to your new definition: - - ping : Nat + Okay, I'm searching the branch for code that needs to be + updated... -.> view ping pong + That's done. Now I'm making sure everything typechecks... - ping : Nat - ping = 3 - - pong : 'Nat - pong _ = - use Nat + - !#4t465jk908.1 + 2 + Typechecking failed. I've updated your scratch file with the + definitions that need fixing. Once the file is compiling, try + `update` again. ``` +```unison:added-by-ucm scratch.u +pong : 'Nat +pong _ = + use Nat + + !ping + 2 + +ping : Nat +ping = 3 +``` + +``` +.> view ping pong + +``` + diff --git a/unison-src/transcripts/cycle-update-4.md b/unison-src/transcripts/cycle-update-4.md index d2bf98f690..bd67c8d937 100644 --- a/unison-src/transcripts/cycle-update-4.md +++ b/unison-src/transcripts/cycle-update-4.md @@ -25,6 +25,6 @@ clang _ = !pong + 3 ``` ```ucm -.> update.old ping +.> update .> view ping pong clang ``` diff --git a/unison-src/transcripts/cycle-update-4.output.md b/unison-src/transcripts/cycle-update-4.output.md index fd525176bf..fb42994ad1 100644 --- a/unison-src/transcripts/cycle-update-4.output.md +++ b/unison-src/transcripts/cycle-update-4.output.md @@ -58,16 +58,16 @@ clang _ = !pong + 3 ``` ```ucm -.> update.old ping +.> update - ⍟ I've added these definitions: - - clang : 'Nat - - ⍟ I've updated these names to your new definition: - - ping : 'Nat - pong : 'Nat + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. .> view ping pong clang diff --git a/unison-src/transcripts/cycle-update-5.md b/unison-src/transcripts/cycle-update-5.md index c09a93c3d7..66908419bb 100644 --- a/unison-src/transcripts/cycle-update-5.md +++ b/unison-src/transcripts/cycle-update-5.md @@ -25,7 +25,7 @@ inner.ping _ = !pong + 3 ``` ```ucm -.inner> update.old +.inner> update .> view inner.ping ``` diff --git a/unison-src/transcripts/cycle-update-5.output.md b/unison-src/transcripts/cycle-update-5.output.md index 3e3361f70c..ecdf196b8b 100644 --- a/unison-src/transcripts/cycle-update-5.output.md +++ b/unison-src/transcripts/cycle-update-5.output.md @@ -54,11 +54,12 @@ inner.ping _ = !pong + 3 ``` ```ucm -.inner> update.old +.inner> update - ⍟ I've added these definitions: - - inner.ping : '##Nat + Okay, I'm searching the branch for code that needs to be + updated... + + Done. .> view inner.ping diff --git a/unison-src/transcripts/diff-namespace.md b/unison-src/transcripts/diff-namespace.md index 5e938a79a5..11308f273b 100644 --- a/unison-src/transcripts/diff-namespace.md +++ b/unison-src/transcripts/diff-namespace.md @@ -44,6 +44,7 @@ structural ability X a1 a2 where x : () ``` ```ucm +.ns1> builtins.merge .ns1> add .ns1> alias.term fromJust fromJust' .ns1> alias.term helloWorld helloWorld2 @@ -72,6 +73,7 @@ fromJust = "asldkfjasldkfj" ```unison:hide fromJust = 99 b = "oog" +bdependent = b d = 4 e = 5 f = 6 @@ -79,7 +81,7 @@ unique type Y a b = Y a b ``` ```ucm -.ns2> update.old +.ns2> update .> diff.namespace ns1 ns2 .> alias.term ns2.d ns2.d' .> alias.type ns2.A ns2.A' @@ -96,44 +98,10 @@ unique type Y a b = Y a b bdependent = "banana" ``` ```ucm -.ns3> update.old +.ns3> update .> diff.namespace ns2 ns3 ``` - -## Two different auto-propagated changes creating a name conflict -Currently, the auto-propagated name-conflicted definitions are not explicitly -shown, only their also-conflicted dependency is shown. -```unison:hide -a = 333 -b = a + 1 -``` -```ucm -.nsx> add -.> fork nsx nsy -.> fork nsx nsz -``` -```unison:hide -a = 444 -``` -```ucm -.nsy> update.old -``` -```unison:hide -a = 555 -``` -```ucm -.nsz> update.old -.> merge.old nsy nsw -``` -```ucm:error -.> merge.old nsz nsw -``` -```ucm -.> diff.namespace nsx nsw -.nsw> view a b -``` - ## Should be able to diff a namespace hash from history. ```unison diff --git a/unison-src/transcripts/diff-namespace.output.md b/unison-src/transcripts/diff-namespace.output.md index cacb9d1fc4..06ed8fa14a 100644 --- a/unison-src/transcripts/diff-namespace.output.md +++ b/unison-src/transcripts/diff-namespace.output.md @@ -113,17 +113,21 @@ structural ability X a1 a2 where x : () ```ucm ☝️ The namespace .ns1 is empty. +.ns1> builtins.merge + + Done. + .ns1> add ⍟ I've added these definitions: structural type A a structural ability X a1 a2 - b : ##Nat - bdependent : ##Nat - c : ##Nat - fromJust : ##Nat - helloWorld : ##Text + b : Nat + bdependent : Nat + c : Nat + fromJust : Nat + helloWorld : Text .ns1> alias.term fromJust fromJust' @@ -189,6 +193,7 @@ fromJust = "asldkfjasldkfj" ```unison fromJust = 99 b = "oog" +bdependent = b d = 4 e = 5 f = 6 @@ -196,20 +201,12 @@ unique type Y a b = Y a b ``` ```ucm -.ns2> update.old +.ns2> update - ⍟ I've added these definitions: - - type Y a b - d : ##Nat - e : ##Nat - f : ##Nat - - ⍟ I've updated these names to your new definition: - - b : ##Text - fromJust : ##Nat - (The old definition was also named fromJust'.) + Okay, I'm searching the branch for code that needs to be + updated... + + Done. .> diff.namespace ns1 ns2 @@ -226,9 +223,9 @@ unique type Y a b = Y a b ↓ 5. b : Text - 6. fromJust' : Nat + 6. bdependent : Nat ↓ - 7. fromJust' : Nat + 7. bdependent : Text Added definitions: @@ -238,7 +235,11 @@ unique type Y a b = Y a b 11. e : Nat 12. f : Nat - 13. patch patch (added 2 updates) + Name changes: + + Original Changes + 13. fromJust' ┐ 14. fromJust#gjmq673r1v (removed) + 15. fromJust#gjmq673r1v ┘ .> alias.term ns2.d ns2.d' @@ -267,9 +268,9 @@ unique type Y a b = Y a b ↓ 5. b : Text - 6. fromJust' : Nat + 6. bdependent : Nat ↓ - 7. fromJust' : Nat + 7. bdependent : Text Added definitions: @@ -280,14 +281,15 @@ unique type Y a b = Y a b 12. e : Nat 13. f : Nat - 14. patch patch (added 2 updates) - Name changes: - Original Changes - 15. A 16. A' (added) + Original Changes + 14. A 15. A' (added) - 17. X 18. X' (added) + 16. X 17. X' (added) + + 18. fromJust' ┐ 19. fromJust#gjmq673r1v (removed) + 20. fromJust#gjmq673r1v ┘ .> alias.type ns1.X ns1.X2 @@ -309,12 +311,12 @@ unique type Y a b = Y a b Name changes: - Original Changes - 1. ns2.fromJust ┐ 2. ns2.fromJust' (removed) - 3. ns2.fromJust' │ - 4. ns2.yoohoo │ - 5. ns3.fromJust │ - 6. ns3.fromJust' ┘ + Original Changes + 1. ns1.fromJust' ┐ 2. ns2.fromJust' (removed) + 3. ns2.fromJust' │ + 4. ns2.yoohoo │ + 5. ns3.fromJust' │ + 6. ns1.fromJust#gjmq673r1v ┘ Tip: You can use `undo` or `reflog` to undo this change. @@ -323,8 +325,8 @@ unique type Y a b = Y a b Name changes: Original Changes - 1. fromJust ┐ 2. yoohoo (added) - 3. fromJust' ┘ 4. fromJust' (removed) + 1. fromJust' 2. yoohoo (added) + 3. fromJust' (removed) ``` ```unison @@ -332,162 +334,26 @@ bdependent = "banana" ``` ```ucm -.ns3> update.old +.ns3> update - ⍟ I've updated these names to your new definition: - - bdependent : ##Text + Okay, I'm searching the branch for code that needs to be + updated... + + Done. .> diff.namespace ns2 ns3 Updates: - 1. bdependent : Nat + 1. bdependent : Text ↓ 2. bdependent : Text - 3. patch patch (added 1 updates) - Name changes: - Original Changes - 4. fromJust ┐ 5. fromJust' (added) - 6. yoohoo ┘ 7. yoohoo (removed) - -``` -## Two different auto-propagated changes creating a name conflict -Currently, the auto-propagated name-conflicted definitions are not explicitly -shown, only their also-conflicted dependency is shown. -```unison -a = 333 -b = a + 1 -``` - -```ucm - ☝️ The namespace .nsx is empty. - -.nsx> add - - ⍟ I've added these definitions: - - a : ##Nat - b : ##Nat - -.> fork nsx nsy - - Done. - -.> fork nsx nsz - - Done. - -``` -```unison -a = 444 -``` - -```ucm -.nsy> update.old - - ⍟ I've updated these names to your new definition: - - a : ##Nat - -``` -```unison -a = 555 -``` - -```ucm -.nsz> update.old - - ⍟ I've updated these names to your new definition: - - a : ##Nat - -.> merge.old nsy nsw - - Here's what's changed in nsw after the merge: - - Added definitions: - - 1. a : Nat - 2. b : Nat - - 3. patch patch (added 1 updates) - - Tip: You can use `todo` to see if this generated any work to - do in this namespace and `test` to run the tests. Or you - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -``` -```ucm -.> merge.old nsz nsw - - Here's what's changed in nsw after the merge: - - New name conflicts: - - 1. a#mdl4vqtu00 : Nat - ↓ - 2. ┌ a#mdl4vqtu00 : Nat - 3. └ a#vrs8gtkl2t : Nat - - 4. b#unkqhuu66p : Nat - ↓ - 5. ┌ b#aapqletas7 : Nat - 6. └ b#unkqhuu66p : Nat - - Updates: - - 7. patch patch (added 1 updates) - - Tip: You can use `todo` to see if this generated any work to - do in this namespace and `test` to run the tests. Or you - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - - I tried to auto-apply the patch, but couldn't because it - contained contradictory entries. - -``` -```ucm -.> diff.namespace nsx nsw - - New name conflicts: - - 1. a#uiiiv8a86s : Nat - ↓ - 2. ┌ a#mdl4vqtu00 : Nat - 3. └ a#vrs8gtkl2t : Nat - - 4. b#lhigeb1let : Nat - ↓ - 5. ┌ b#aapqletas7 : Nat - 6. └ b#unkqhuu66p : Nat - - Added definitions: - - 7. patch patch (added 2 updates) - -.nsw> view a b - - a#mdl4vqtu00 : ##Nat - a#mdl4vqtu00 = 444 - - a#vrs8gtkl2t : ##Nat - a#vrs8gtkl2t = 555 - - b#aapqletas7 : ##Nat - b#aapqletas7 = ##Nat.+ a#vrs8gtkl2t 1 - - b#unkqhuu66p : ##Nat - b#unkqhuu66p = ##Nat.+ a#mdl4vqtu00 1 + Original Changes + 3. yoohoo 4. fromJust' (added) + 5. yoohoo (removed) ``` ## Should be able to diff a namespace hash from history. @@ -506,7 +372,9 @@ x = 1 ⍟ These new definitions are ok to `add`: - x : ##Nat + x : Nat + (also named ns1.fromJust , ns1.fromJust' , ns3.fromJust' + , and ns2.yoohoo) ``` ```ucm diff --git a/unison-src/transcripts/fix2254.md b/unison-src/transcripts/fix2254.md index 3b6dd15e6c..01823ebc8d 100644 --- a/unison-src/transcripts/fix2254.md +++ b/unison-src/transcripts/fix2254.md @@ -56,7 +56,7 @@ unique type A a b c d Let's do the update now, and verify that the definitions all look good and there's nothing `todo`: ```ucm -.a2> update.old +.a2> update .a2> view A NeedsA f f2 f3 g .a2> todo ``` @@ -91,6 +91,6 @@ And checking that after updating this record, there's nothing `todo`: ```ucm .> fork a3 a4 -.a4> update.old +.a4> update .a4> todo ``` diff --git a/unison-src/transcripts/fix2254.output.md b/unison-src/transcripts/fix2254.output.md index 61af269b2c..903ee46bf2 100644 --- a/unison-src/transcripts/fix2254.output.md +++ b/unison-src/transcripts/fix2254.output.md @@ -64,24 +64,29 @@ unique type A a b c d Let's do the update now, and verify that the definitions all look good and there's nothing `todo`: ```ucm -.a2> update.old +.a2> update - ⍟ I've updated these names to your new definition: - - type A a b c d + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. .a2> view A NeedsA f f2 f3 g type A a b c d - = B b + = A a | D d | E a d + | B b | C c - | A a structural type NeedsA a b - = Zoink Text - | NeedsA (A a b Nat Nat) + = NeedsA (A a b Nat Nat) + | Zoink Text f : A Nat Nat Nat Nat -> Nat f = cases @@ -193,23 +198,16 @@ And checking that after updating this record, there's nothing `todo`: Done. -.a4> update.old +.a4> update - ⍟ I've added these definitions: - - Rec.tres : Rec -> Text - Rec.tres.modify : (Text ->{g} Text) -> Rec ->{g} Rec - Rec.tres.set : Text -> Rec -> Rec - - ⍟ I've updated these names to your new definition: - - structural type Rec - Rec.dos : Rec -> Nat - Rec.dos.modify : (Nat ->{g} Nat) -> Rec ->{g} Rec - Rec.dos.set : Nat -> Rec -> Rec - Rec.uno : Rec -> Nat - Rec.uno.modify : (Nat ->{g} Nat) -> Rec ->{g} Rec - Rec.uno.set : Nat -> Rec -> Rec + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. .a4> todo diff --git a/unison-src/transcripts/fix4172.md b/unison-src/transcripts/fix4172.md index 2c7d6c3b14..1fbca711f6 100644 --- a/unison-src/transcripts/fix4172.md +++ b/unison-src/transcripts/fix4172.md @@ -26,6 +26,6 @@ bool = false ``` ```ucm:error -.> update.old +.> update .> test ``` diff --git a/unison-src/transcripts/fix4172.output.md b/unison-src/transcripts/fix4172.output.md index 59a5d83b87..71f20ca144 100644 --- a/unison-src/transcripts/fix4172.output.md +++ b/unison-src/transcripts/fix4172.output.md @@ -75,11 +75,16 @@ bool = false ``` ```ucm -.> update.old +.> update - ⍟ I've updated these names to your new definition: - - bool : Boolean + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. .> test diff --git a/unison-src/transcripts/propagate.md b/unison-src/transcripts/propagate.md index cc80ef885c..e29e3c3f94 100644 --- a/unison-src/transcripts/propagate.md +++ b/unison-src/transcripts/propagate.md @@ -30,7 +30,7 @@ unique type Foo = Foo | Bar and update the codebase to use the new type `Foo`... ```ucm -.subpath> update.old +.subpath> update ``` ... it should automatically propagate the type to `fooToInt`. @@ -68,7 +68,7 @@ preserve.someTerm _ = None Update... ```ucm -.subpath> update.old +.subpath> update ``` Now the type of `someTerm` should be `Optional x -> Optional x` and the @@ -85,23 +85,23 @@ Cleaning up a bit... ```ucm .> delete.namespace subpath -.subpath.lib> builtins.merge +.subpath.one.lib> builtins.merge ``` Now, we make two terms, where one depends on the other. ```unison -one.someTerm : Optional foo -> Optional foo -one.someTerm x = x +someTerm : Optional foo -> Optional foo +someTerm x = x -one.otherTerm : Optional baz -> Optional baz -one.otherTerm y = someTerm y +otherTerm : Optional baz -> Optional baz +otherTerm y = someTerm y ``` We'll make two copies of this namespace. ```ucm -.subpath> add +.subpath.one> add .subpath> fork one two ``` @@ -115,7 +115,7 @@ someTerm _ = None ... in one of the namespaces... ```ucm -.subpath.one> update.old +.subpath.one> update ``` The other namespace should be left alone. diff --git a/unison-src/transcripts/propagate.output.md b/unison-src/transcripts/propagate.output.md index 5f0b72bb35..57bbbd4c1d 100644 --- a/unison-src/transcripts/propagate.output.md +++ b/unison-src/transcripts/propagate.output.md @@ -75,11 +75,16 @@ unique type Foo = Foo | Bar and update the codebase to use the new type `Foo`... ```ucm -.subpath> update.old +.subpath> update - ⍟ I've updated these names to your new definition: - - type Foo + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. ``` ... it should automatically propagate the type to `fooToInt`. @@ -153,11 +158,16 @@ preserve.someTerm _ = None Update... ```ucm -.subpath> update.old +.subpath> update - ⍟ I've updated these names to your new definition: - - preserve.someTerm : Optional x -> Optional x + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. ``` Now the type of `someTerm` should be `Optional x -> Optional x` and the @@ -184,9 +194,9 @@ Cleaning up a bit... Done. - ☝️ The namespace .subpath.lib is empty. + ☝️ The namespace .subpath.one.lib is empty. -.subpath.lib> builtins.merge +.subpath.one.lib> builtins.merge Done. @@ -194,11 +204,11 @@ Cleaning up a bit... Now, we make two terms, where one depends on the other. ```unison -one.someTerm : Optional foo -> Optional foo -one.someTerm x = x +someTerm : Optional foo -> Optional foo +someTerm x = x -one.otherTerm : Optional baz -> Optional baz -one.otherTerm y = someTerm y +otherTerm : Optional baz -> Optional baz +otherTerm y = someTerm y ``` ```ucm @@ -211,19 +221,19 @@ one.otherTerm y = someTerm y ⍟ These new definitions are ok to `add`: - one.otherTerm : Optional baz -> Optional baz - one.someTerm : Optional foo -> Optional foo + otherTerm : Optional baz -> Optional baz + someTerm : Optional foo -> Optional foo ``` We'll make two copies of this namespace. ```ucm -.subpath> add +.subpath.one> add ⍟ I've added these definitions: - one.otherTerm : Optional baz -> Optional baz - one.someTerm : Optional foo -> Optional foo + otherTerm : Optional baz -> Optional baz + someTerm : Optional foo -> Optional foo .subpath> fork one two @@ -253,11 +263,16 @@ someTerm _ = None ... in one of the namespaces... ```ucm -.subpath.one> update.old +.subpath.one> update - ⍟ I've updated these names to your new definition: - - someTerm : #nirp5os0q6 x -> #nirp5os0q6 x + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. ``` The other namespace should be left alone. diff --git a/unison-src/transcripts/sum-type-update-conflicts.md b/unison-src/transcripts/sum-type-update-conflicts.md index ce29931852..bd75e1851b 100644 --- a/unison-src/transcripts/sum-type-update-conflicts.md +++ b/unison-src/transcripts/sum-type-update-conflicts.md @@ -32,5 +32,5 @@ This update should succeed since the conflicted constructor is removed in the same update that the new term is being added. ```ucm -.ns> update.old +.ns> update ``` diff --git a/unison-src/transcripts/sum-type-update-conflicts.output.md b/unison-src/transcripts/sum-type-update-conflicts.output.md index fc45a547bf..592ea563b3 100644 --- a/unison-src/transcripts/sum-type-update-conflicts.output.md +++ b/unison-src/transcripts/sum-type-update-conflicts.output.md @@ -67,16 +67,15 @@ This update should succeed since the conflicted constructor is removed in the same update that the new term is being added. ```ucm -.ns> update.old +.ns> update - ⍟ I've added these definitions: - - X.x : Text - dependsOnX : Nat - - ⍟ I've updated these names to your new definition: - - structural type X - (The old definition was also named builtin.Unit.) + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Everything typechecks, so I'm saving the results... + + Done. ``` diff --git a/unison-src/transcripts/tab-completion.md b/unison-src/transcripts/tab-completion.md index a5c7b090ec..5f6e0558bc 100644 --- a/unison-src/transcripts/tab-completion.md +++ b/unison-src/transcripts/tab-completion.md @@ -61,7 +61,7 @@ add b = b ``` ```ucm -.> update.old +.> update .> debug.tab-complete delete.type Foo .> debug.tab-complete delete.term add ``` diff --git a/unison-src/transcripts/tab-completion.output.md b/unison-src/transcripts/tab-completion.output.md index 34ce96db90..0a93031d94 100644 --- a/unison-src/transcripts/tab-completion.output.md +++ b/unison-src/transcripts/tab-completion.output.md @@ -153,12 +153,12 @@ add b = b ``` ```ucm -.> update.old +.> update - ⍟ I've added these definitions: - - type Foo - add : a -> a + Okay, I'm searching the branch for code that needs to be + updated... + + Done. .> debug.tab-complete delete.type Foo diff --git a/unison-src/transcripts/todo.md b/unison-src/transcripts/todo.md index 39fece2f61..d3723aef96 100644 --- a/unison-src/transcripts/todo.md +++ b/unison-src/transcripts/todo.md @@ -28,7 +28,7 @@ type MyType = MyType Text ``` ```ucm:error -.simple> update.old +.simple> update .simple> todo ``` @@ -58,7 +58,7 @@ type MyType = MyType Nat ``` ```ucm:hide -.mergeA> update.old +.mergeA> update ``` ```unison:hide @@ -67,7 +67,7 @@ type MyType = MyType Int ``` ```ucm:hide -.mergeB> update.old +.mergeB> update ``` ```ucm:error @@ -94,7 +94,7 @@ foo = 802 ``` ```ucm -.lhs> update.old +.lhs> update ``` ```unison @@ -106,7 +106,7 @@ oldfoo = 801 .lhs> todo ``` -## A type-changing update to one element of a cycle, which doesn't propagate to the other +## A type-changing update to one element of a cycle ```ucm:hide .cycle2> builtins.merge @@ -130,10 +130,21 @@ odd = cases even = 17 ``` -```ucm -.cycle2> update.old -``` +Updating should bring the other half into scope. ```ucm:error +.cycle2> update +``` + +We can manually break the cycle: + +```unison +odd = 22 + +even = 17 +``` + +```ucm +.cycle2> update .cycle2> todo ``` diff --git a/unison-src/transcripts/todo.output.md b/unison-src/transcripts/todo.output.md index b0a9d69c6d..2538ef06b8 100644 --- a/unison-src/transcripts/todo.output.md +++ b/unison-src/transcripts/todo.output.md @@ -20,31 +20,41 @@ type MyType = MyType Text ``` ```ucm -.simple> update.old +.simple> update - ⍟ I've updated these names to your new definition: - - type MyType - x : Int + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Typechecking failed. I've updated your scratch file with the + definitions that need fixing. Once the file is compiling, try + `update` again. .simple> todo - 🚧 - - The namespace has 2 transitive dependent(s) left to upgrade. - Your edit frontier is the dependents of these definitions: - - type #vijug0om28 - #gjmq673r1v : Nat - - I recommend working on them in the following order: - - 1. useMyType : Nat - 2. useX : Nat - + ✅ + No conflicts or edits in progress. + +``` +```unison:added-by-ucm scratch.u +useMyType : Nat +useMyType = + use Nat + + (MyType a) = MyType 1 + a + 10 + +useX : Nat +useX = + use Nat + + x + 10 +x = -1 + +type MyType = MyType Text ``` + ## A merge with conflicting updates. ```unison @@ -74,24 +84,20 @@ type MyType = MyType Int New name conflicts: - 1. type MyType#ig1g2ka7lv - ↓ - 2. ┌ type MyType#8c6f40i3tj - 3. └ type MyType#ig1g2ka7lv + 1. type MyType#5287s21ais + ↓ + 2. ┌ type MyType#5287s21ais + 3. └ type MyType#m6mdqhqcr1 - 4. MyType.MyType#ig1g2ka7lv#0 : Nat -> MyType#ig1g2ka7lv - ↓ - 5. ┌ MyType.MyType#8c6f40i3tj#0 : Int -> MyType#8c6f40i3tj - 6. └ MyType.MyType#ig1g2ka7lv#0 : Nat -> MyType#ig1g2ka7lv + 4. MyType.MyType#5287s21ais#0 : Nat -> MyType#5287s21ais + ↓ + 5. ┌ MyType.MyType#5287s21ais#0 : Nat -> MyType#5287s21ais + 6. └ MyType.MyType#m6mdqhqcr1#0 : Int -> MyType#m6mdqhqcr1 - 7. x#dcgdua2lj6 : Nat - ↓ - 8. ┌ x#dcgdua2lj6 : Nat - 9. └ x#f3lgjvjqoo : Nat - - Updates: - - 10. patch patch (added 2 updates) + 7. x#dcgdua2lj6 : Nat + ↓ + 8. ┌ x#dcgdua2lj6 : Nat + 9. └ x#f3lgjvjqoo : Nat Tip: You can use `todo` to see if this generated any work to do in this namespace and `test` to run the tests. Or you @@ -100,31 +106,23 @@ type MyType = MyType Int Applying changes from patch... - I tried to auto-apply the patch, but couldn't because it - contained contradictory entries. - .mergeA> todo ❓ - These definitions were edited differently in namespaces that - have been merged into this one. You'll have to tell me what to - use as the new definition: - - The type 1. #8h7qq3ougl was replaced with - 2. MyType#8c6f40i3tj - 3. MyType#ig1g2ka7lv - The term 4. #gjmq673r1v was replaced with - 5. x#dcgdua2lj6 - 6. x#f3lgjvjqoo - ❓ + The type MyType has conflicting definitions: + 1. MyType#5287s21ais + 2. MyType#m6mdqhqcr1 The term MyType.MyType has conflicting definitions: - 7. MyType.MyType#8c6f40i3tj#0 - 8. MyType.MyType#ig1g2ka7lv#0 + 3. MyType.MyType#5287s21ais#0 + 4. MyType.MyType#m6mdqhqcr1#0 + The term x has conflicting definitions: + 5. x#dcgdua2lj6 + 6. x#f3lgjvjqoo Tip: This occurs when merging branches that both independently - introduce the same name. Use `move.term` or `delete.term` + introduce the same name. Use `move.type` or `delete.type` to resolve the conflicts. ``` @@ -174,11 +172,12 @@ foo = 802 ``` ```ucm -.lhs> update.old +.lhs> update - ⍟ I've updated these names to your new definition: - - foo : Nat + Okay, I'm searching the branch for code that needs to be + updated... + + Done. ``` ```unison @@ -212,7 +211,7 @@ oldfoo = 801 No conflicts or edits in progress. ``` -## A type-changing update to one element of a cycle, which doesn't propagate to the other +## A type-changing update to one element of a cycle ```unison even = cases @@ -265,28 +264,65 @@ even = 17 even : Nat ``` +Updating should bring the other half into scope. + +```ucm +.cycle2> update + + Okay, I'm searching the branch for code that needs to be + updated... + + That's done. Now I'm making sure everything typechecks... + + Typechecking failed. I've updated your scratch file with the + definitions that need fixing. Once the file is compiling, try + `update` again. + +``` +```unison:added-by-ucm scratch.u +odd : Nat -> Boolean +odd = cases + 0 -> false + n -> even (Nat.drop 1 n) + +even = 17 +``` + +We can manually break the cycle: + +```unison +odd = 22 + +even = 17 +``` + ```ucm -.cycle2> update.old - ⍟ I've updated these names to your new definition: + Loading changes detected in scratch.u. + + I found and typechecked these definitions in scratch.u. If you + do an `add` or `update`, here's how your codebase would + change: - even : Nat + ⍟ These names already exist. You can `update` them to your + new definition: + + even : Nat + odd : Nat ``` ```ucm +.cycle2> update + + Okay, I'm searching the branch for code that needs to be + updated... + + Done. + .cycle2> todo - 🚧 - - The namespace has 1 transitive dependent(s) left to upgrade. - Your edit frontier is the dependents of these definitions: - - #kkohl7ba1e : Nat -> Boolean - - I recommend working on them in the following order: - - 1. odd : Nat -> Boolean - + ✅ + No conflicts or edits in progress. ```