From ce9110be46b7a92a46c78bf68f8536536bdaa16a Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 20 May 2024 14:21:06 -0400 Subject: [PATCH 01/81] Add `merge.commit` command --- unison-cli/src/Unison/Cli/ProjectUtils.hs | 14 +- .../src/Unison/Codebase/Editor/HandleInput.hs | 3 + .../Editor/HandleInput/CommitMerge.hs | 57 +++ .../Editor/HandleInput/CommitUpgrade.hs | 2 + .../Codebase/Editor/HandleInput/Merge2.hs | 471 +++++++++--------- .../src/Unison/Codebase/Editor/Input.hs | 1 + .../src/Unison/Codebase/Editor/Output.hs | 2 + .../src/Unison/CommandLine/InputPatterns.hs | 14 + .../src/Unison/CommandLine/OutputMessages.hs | 2 + unison-cli/unison-cli.cabal | 1 + unison-src/transcripts/merge.md | 66 +++ unison-src/transcripts/merge.output.md | 90 ++++ 12 files changed, 487 insertions(+), 236 deletions(-) create mode 100644 unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs diff --git a/unison-cli/src/Unison/Cli/ProjectUtils.hs b/unison-cli/src/Unison/Cli/ProjectUtils.hs index 66eb87414c..8ffbf752de 100644 --- a/unison-cli/src/Unison/Cli/ProjectUtils.hs +++ b/unison-cli/src/Unison/Cli/ProjectUtils.hs @@ -46,7 +46,8 @@ module Unison.Cli.ProjectUtils findTemporaryBranchName, expectLatestReleaseBranchName, - -- * Upgrade branch utils + -- * Merge/upgrade branch utils + getMergeBranchParent, getUpgradeBranchParent, ) where @@ -407,6 +408,17 @@ expectLatestReleaseBranchName remoteProject = Nothing -> Cli.returnEarly (Output.ProjectHasNoReleases remoteProject.projectName) Just semver -> pure (UnsafeProjectBranchName ("releases/" <> into @Text semver)) +-- | @getMergeBranchParent branch@ returns the parent branch of a "merge" branch. +-- +-- When a merge fails, we put you on a branch called `merge--into-`. That's a "merge" branch. It's not +-- currently distinguished in the database, so we first just switch on whether its name begins with "merge-". If it +-- does, then we get the branch's parent, which should exist, but perhaps wouldn't if the user had manually made a +-- parentless branch called "merge-whatever" for whatever reason. +getMergeBranchParent :: Sqlite.ProjectBranch -> Maybe ProjectBranchId +getMergeBranchParent branch = do + guard ("merge-" `Text.isPrefixOf` into @Text branch.name) + branch.parentBranchId + -- | @getUpgradeBranchParent branch@ returns the parent branch of an "upgrade" branch. -- -- When an upgrade fails, we put you on a branch called `upgrade--to-`. That's an "upgrade" branch. It's not diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index 376f521a86..64598461a9 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -57,6 +57,7 @@ import Unison.Codebase.Editor.HandleInput.AuthLogin (authLogin) import Unison.Codebase.Editor.HandleInput.Branch (handleBranch) import Unison.Codebase.Editor.HandleInput.BranchRename (handleBranchRename) import Unison.Codebase.Editor.HandleInput.Branches (handleBranches) +import Unison.Codebase.Editor.HandleInput.CommitMerge (handleCommitMerge) import Unison.Codebase.Editor.HandleInput.CommitUpgrade (handleCommitUpgrade) import Unison.Codebase.Editor.HandleInput.DebugDefinition qualified as DebugDefinition import Unison.Codebase.Editor.HandleInput.DebugFoldRanges qualified as DebugFoldRanges @@ -428,6 +429,7 @@ loop e = do then Success else BranchEmpty branchEmpty MergeI branch -> handleMerge branch + MergeCommitI -> handleCommitMerge MergeLocalBranchI src0 dest0 mergeMode -> do description <- inputDescription input src0 <- ProjectUtils.expectLooseCodeOrProjectBranch src0 @@ -1385,6 +1387,7 @@ inputDescription input = ListEditsI {} -> wat LoadI {} -> wat MergeI {} -> wat + MergeCommitI {} -> wat NamesI {} -> wat NamespaceDependenciesI {} -> wat PopBranchI {} -> wat diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs new file mode 100644 index 0000000000..4f8abd347f --- /dev/null +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs @@ -0,0 +1,57 @@ +-- | @merge.commit@ handler. +module Unison.Codebase.Editor.HandleInput.CommitMerge + ( handleCommitMerge, + ) +where + +import U.Codebase.Sqlite.Project qualified +import U.Codebase.Sqlite.Queries qualified as Queries +import Unison.Cli.Monad (Cli) +import Unison.Cli.Monad qualified as Cli +import Unison.Cli.ProjectUtils qualified as ProjectUtils +import Unison.Codebase.Editor.HandleInput.DeleteBranch qualified as DeleteBranch +import Unison.Codebase.Editor.HandleInput.Merge2 qualified as Merge +import Unison.Codebase.Editor.HandleInput.ProjectSwitch qualified as ProjectSwitch +import Unison.Codebase.Editor.HandleInput.Update2 qualified as Update +import Unison.Codebase.Editor.Output qualified as Output +import Unison.Merge.TwoWay (TwoWay (..)) +import Unison.Prelude +import Unison.Project (ProjectAndBranch (..)) + +-- Note: this implementation is similar to `upgrade.commit`. + +handleCommitMerge :: Cli () +handleCommitMerge = do + (mergeProjectAndBranch, _path) <- ProjectUtils.expectCurrentProjectBranch + + -- Assert that this is a "merge" branch and get its parent, which is the branch we were on when we ran `merge`. + + parentBranchId <- + ProjectUtils.getMergeBranchParent mergeProjectAndBranch.branch + & onNothing (Cli.returnEarly Output.NoMergeInProgress) + parentBranch <- + Cli.runTransaction do + Queries.expectProjectBranch mergeProjectAndBranch.project.projectId parentBranchId + + let parentProjectAndBranch = + ProjectAndBranch mergeProjectAndBranch.project parentBranch + + -- Run `update` + + Update.handleUpdate2 + + -- Switch to the parent + + ProjectSwitch.switchToProjectBranch (ProjectUtils.justTheIds parentProjectAndBranch) + + -- Merge the merge branch into the parent + + Merge.doMergeLocalBranch + TwoWay + { alice = parentProjectAndBranch, + bob = mergeProjectAndBranch + } + + -- Delete the merge branch + + DeleteBranch.doDeleteProjectBranch mergeProjectAndBranch diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs index 901dada1e4..6717bcbbc2 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs @@ -18,6 +18,8 @@ import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Prelude import Unison.Project (ProjectAndBranch (..)) +-- Note: this implementation is similar to `merge.commit`. + handleCommitUpgrade :: Cli () handleCommitUpgrade = do (upgradeProjectAndBranch, _path) <- ProjectUtils.expectCurrentProjectBranch diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs index 265cd9d06c..fa3ed37fea 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs @@ -199,241 +199,242 @@ doMerge info = do Cli.Env {codebase} <- ask - Cli.label \done -> do - -- If alice == bob, or LCA == bob (so alice is ahead of bob), then we are done. - when (info.alice.causalHash == info.bob.causalHash || info.lca.causalHash == Just info.bob.causalHash) do - Cli.respond (Output.MergeAlreadyUpToDate2 mergeSourceAndTarget) - done () - - -- Otherwise, if LCA == alice (so alice is behind bob), then we could fast forward to bob, so we're done. - when (info.lca.causalHash == Just info.alice.causalHash) do - bobBranch <- liftIO (Codebase.expectBranchForHash codebase info.bob.causalHash) - _ <- Cli.updateAt info.description alicePath (\_aliceBranch -> bobBranch) - Cli.respond (Output.MergeSuccessFastForward mergeSourceAndTarget) - done () - - -- Create a bunch of cached database lookup functions - db <- makeMergeDatabase codebase - - -- Load Alice/Bob/LCA causals - causals <- Cli.runTransaction do - traverse - Operations.expectCausalBranchByCausalHash - TwoOrThreeWay - { alice = info.alice.causalHash, - bob = info.bob.causalHash, - lca = info.lca.causalHash - } - - liftIO (debugFunctions.debugCausals causals) - - -- Load Alice/Bob/LCA branches - branches <- - Cli.runTransaction do - alice <- causals.alice.value - bob <- causals.bob.value - lca <- for causals.lca \causal -> causal.value - pure TwoOrThreeWay {lca, alice, bob} - - -- Assert that neither Alice nor Bob have defns in lib - for_ [(mergeTarget, branches.alice), (mergeSource, branches.bob)] \(who, branch) -> do - libdeps <- - case Map.lookup NameSegment.libSegment branch.children of - Nothing -> pure V2.Branch.empty - Just libdeps -> Cli.runTransaction libdeps.value - when (not (Map.null libdeps.terms) || not (Map.null libdeps.types)) do - Cli.returnEarly (Output.MergeDefnsInLib who) - - -- Load Alice/Bob/LCA definitions and decl name lookups - (defns3, declNameLookups3) <- do - let load = \case - Nothing -> - pure - ( Nametree {value = Defns Map.empty Map.empty, children = Map.empty}, - DeclNameLookup Map.empty Map.empty - ) - Just (who, branch) -> do - defns <- - Cli.runTransaction (loadNamespaceDefinitions (referent2to1 db) branch) & onLeftM \conflictedName -> - Cli.returnEarly case conflictedName of - ConflictedName'Term name refs -> Output.MergeConflictedTermName name refs - ConflictedName'Type name refs -> Output.MergeConflictedTypeName name refs - declNameLookup <- - Cli.runTransaction (checkDeclCoherency db.loadDeclNumConstructors defns) & onLeftM \err -> - Cli.returnEarly case err of - IncoherentDeclReason'ConstructorAlias name1 name2 -> - Output.MergeConstructorAlias who name1 name2 - IncoherentDeclReason'MissingConstructorName name -> Output.MergeMissingConstructorName who name - IncoherentDeclReason'NestedDeclAlias shorterName longerName -> - Output.MergeNestedDeclAlias who shorterName longerName - IncoherentDeclReason'StrayConstructor name -> Output.MergeStrayConstructor who name - pure (defns, declNameLookup) - - (aliceDefns0, aliceDeclNameLookup) <- load (Just (Just mergeTarget, branches.alice)) - (bobDefns0, bobDeclNameLookup) <- load (Just (Just mergeSource, branches.bob)) - (lcaDefns0, lcaDeclNameLookup) <- load ((Nothing,) <$> branches.lca) - - let flatten defns = Defns (flattenNametree (view #terms) defns) (flattenNametree (view #types) defns) - let defns3 = flatten <$> ThreeWay {alice = aliceDefns0, bob = bobDefns0, lca = lcaDefns0} - let declNameLookups3 = ThreeWay {alice = aliceDeclNameLookup, bob = bobDeclNameLookup, lca = lcaDeclNameLookup} - - pure (defns3, declNameLookups3) - - let defns = ThreeWay.forgetLca defns3 - let declNameLookups = ThreeWay.forgetLca declNameLookups3 - - liftIO (debugFunctions.debugDefns defns3 declNameLookups3) - - -- Diff LCA->Alice and LCA->Bob - diffs <- Cli.runTransaction (Merge.nameBasedNamespaceDiff db declNameLookups3 defns3) - - liftIO (debugFunctions.debugDiffs diffs) - - -- Bail early if it looks like we can't proceed with the merge, because Alice or Bob has one or more conflicted alias - for_ ((,) <$> TwoWay mergeTarget mergeSource <*> diffs) \(who, diff) -> - whenJust (findConflictedAlias defns3.lca diff) \(name1, name2) -> - Cli.returnEarly (Output.MergeConflictedAliases who name1 name2) - - -- Combine the LCA->Alice and LCA->Bob diffs together - let diff = combineDiffs diffs - - liftIO (debugFunctions.debugCombinedDiff diff) - - -- Partition the combined diff into the conflicted things and the unconflicted things - (conflicts, unconflicts) <- - partitionCombinedDiffs defns declNameLookups diff & onLeft \name -> - Cli.returnEarly (Output.MergeConflictInvolvingBuiltin name) - - liftIO (debugFunctions.debugPartitionedDiff conflicts unconflicts) - - -- Identify the unconflicted dependents we need to pull into the Unison file (either first for typechecking, if there - -- aren't conflicts, or else for manual conflict resolution without a typechecking step, if there are) - dependents <- Cli.runTransaction (identifyDependents defns conflicts unconflicts) - - liftIO (debugFunctions.debugDependents dependents) - - let stageOne :: DefnsF (Map Name) Referent TypeReference - stageOne = - makeStageOne - declNameLookups - conflicts - unconflicts - dependents - (bimap BiMultimap.range BiMultimap.range defns3.lca) - - liftIO (debugFunctions.debugStageOne stageOne) - - -- Load and merge Alice's and Bob's libdeps - mergedLibdeps <- - Cli.runTransaction do - libdeps <- loadLibdeps branches - libdepsToBranch0 db (Merge.mergeLibdeps getTwoFreshNames libdeps) - - -- Make PPE for Alice that contains all of Alice's names, but suffixified against her names + Bob's names - let mkPpes :: TwoWay Names -> Names -> TwoWay PrettyPrintEnvDecl - mkPpes defnsNames libdepsNames = - defnsNames <&> \names -> PPED.makePPED (PPE.namer (names <> libdepsNames)) suffixifier - where - suffixifier = PPE.suffixifyByName (fold defnsNames <> libdepsNames) - let ppes = mkPpes (defnsToNames <$> defns) (Branch.toNames mergedLibdeps) - - hydratedThings <- do - Cli.runTransaction do - for ((,) <$> conflicts <*> dependents) \(conflicts1, dependents1) -> - let hydrate = hydrateDefns (Codebase.unsafeGetTermComponent codebase) Operations.expectDeclComponent - in (,) <$> hydrate conflicts1 <*> hydrate dependents1 - - let (renderedConflicts, renderedDependents) = - let honk declNameLookup ppe defns = - let (types, accessorNames) = - Writer.runWriter $ - defns.types & Map.traverseWithKey \name (ref, typ) -> - renderTypeBinding - -- Sort of a hack; since the decl printer looks in the PPE for names of constructors, - -- we just delete all term names out and add back the constructors... - -- probably no need to wipe out the suffixified side but we do it anyway - (setPpedToConstructorNames declNameLookup name ref ppe) - name - ref - typ - terms = - defns.terms & Map.mapMaybeWithKey \name (term, typ) -> - if Set.member name accessorNames - then Nothing - else Just (renderTermBinding ppe.suffixifiedPPE name term typ) - in Defns {terms, types} - in unzip $ - ( \declNameLookup (conflicts, dependents) ppe -> - let honk1 = honk declNameLookup ppe - in (honk1 conflicts, honk1 dependents) - ) - <$> declNameLookups - <*> hydratedThings - <*> ppes - - let prettyUnisonFile = - makePrettyUnisonFile - TwoWay - { alice = into @Text aliceBranchNames, - bob = - case info.bob.source of - MergeSource'LocalProjectBranch bobBranchNames -> into @Text bobBranchNames - MergeSource'RemoteProjectBranch bobBranchNames - | aliceBranchNames == bobBranchNames -> "remote " <> into @Text bobBranchNames - | otherwise -> into @Text bobBranchNames - MergeSource'RemoteLooseCode info -> - case Path.toName info.path of - Nothing -> "" - Just name -> Name.toText name - MergeSource'RemoteGitRepo info -> - case Path.toName info.path of - Nothing -> "" - Just name -> Name.toText name - } - renderedConflicts - renderedDependents - - let stageOneBranch = defnsAndLibdepsToBranch0 codebase stageOne mergedLibdeps - - maybeTypecheckedUnisonFile <- - let thisMergeHasConflicts = - -- Eh, they'd either both be null, or neither, but just check both maps anyway - not (defnsAreEmpty conflicts.alice) || not (defnsAreEmpty conflicts.bob) - in if thisMergeHasConflicts - then pure Nothing - else do - currentPath <- Cli.getCurrentPath - parsingEnv <- makeParsingEnv currentPath (Branch.toNames stageOneBranch) - prettyParseTypecheck2 prettyUnisonFile parsingEnv <&> eitherToMaybe - - let parents = - (\causal -> (causal.causalHash, Codebase.expectBranchForHash codebase causal.causalHash)) <$> causals - - case maybeTypecheckedUnisonFile of - Nothing -> do - Cli.Env {writeSource} <- ask - _temporaryBranchId <- - HandleInput.Branch.doCreateBranch' - (Branch.mergeNode stageOneBranch parents.alice parents.bob) - Nothing - info.alice.projectAndBranch.project - (findTemporaryBranchName info.alice.projectAndBranch.project.projectId mergeSourceAndTarget) - info.description - scratchFilePath <- - Cli.getLatestFile <&> \case - Nothing -> "scratch.u" - Just (file, _) -> file - liftIO $ writeSource (Text.pack scratchFilePath) (Text.pack $ Pretty.toPlain 80 prettyUnisonFile) - Cli.respond (Output.MergeFailure scratchFilePath mergeSourceAndTarget) - Just tuf -> do - Cli.runTransaction (Codebase.addDefsToCodebase codebase tuf) - let stageTwoBranch = Branch.batchUpdates (typecheckedUnisonFileToBranchAdds tuf) stageOneBranch - _ <- - Cli.updateAt - info.description - alicePath - (\_aliceBranch -> Branch.mergeNode stageTwoBranch parents.alice parents.bob) - Cli.respond (Output.MergeSuccess mergeSourceAndTarget) + finalOutput <- + Cli.label \done -> do + -- If alice == bob, or LCA == bob (so alice is ahead of bob), then we are done. + when (info.alice.causalHash == info.bob.causalHash || info.lca.causalHash == Just info.bob.causalHash) do + done (Output.MergeAlreadyUpToDate2 mergeSourceAndTarget) + + -- Otherwise, if LCA == alice (so alice is behind bob), then we could fast forward to bob, so we're done. + when (info.lca.causalHash == Just info.alice.causalHash) do + bobBranch <- liftIO (Codebase.expectBranchForHash codebase info.bob.causalHash) + _ <- Cli.updateAt info.description alicePath (\_aliceBranch -> bobBranch) + done (Output.MergeSuccessFastForward mergeSourceAndTarget) + + -- Create a bunch of cached database lookup functions + db <- makeMergeDatabase codebase + + -- Load Alice/Bob/LCA causals + causals <- Cli.runTransaction do + traverse + Operations.expectCausalBranchByCausalHash + TwoOrThreeWay + { alice = info.alice.causalHash, + bob = info.bob.causalHash, + lca = info.lca.causalHash + } + + liftIO (debugFunctions.debugCausals causals) + + -- Load Alice/Bob/LCA branches + branches <- + Cli.runTransaction do + alice <- causals.alice.value + bob <- causals.bob.value + lca <- for causals.lca \causal -> causal.value + pure TwoOrThreeWay {lca, alice, bob} + + -- Assert that neither Alice nor Bob have defns in lib + for_ [(mergeTarget, branches.alice), (mergeSource, branches.bob)] \(who, branch) -> do + libdeps <- + case Map.lookup NameSegment.libSegment branch.children of + Nothing -> pure V2.Branch.empty + Just libdeps -> Cli.runTransaction libdeps.value + when (not (Map.null libdeps.terms) || not (Map.null libdeps.types)) do + done (Output.MergeDefnsInLib who) + + -- Load Alice/Bob/LCA definitions and decl name lookups + (defns3, declNameLookups3) <- do + let load = \case + Nothing -> + pure + ( Nametree {value = Defns Map.empty Map.empty, children = Map.empty}, + DeclNameLookup Map.empty Map.empty + ) + Just (who, branch) -> do + defns <- + Cli.runTransaction (loadNamespaceDefinitions (referent2to1 db) branch) & onLeftM \conflictedName -> + done case conflictedName of + ConflictedName'Term name refs -> Output.MergeConflictedTermName name refs + ConflictedName'Type name refs -> Output.MergeConflictedTypeName name refs + declNameLookup <- + Cli.runTransaction (checkDeclCoherency db.loadDeclNumConstructors defns) & onLeftM \err -> + done case err of + IncoherentDeclReason'ConstructorAlias name1 name2 -> + Output.MergeConstructorAlias who name1 name2 + IncoherentDeclReason'MissingConstructorName name -> Output.MergeMissingConstructorName who name + IncoherentDeclReason'NestedDeclAlias shorterName longerName -> + Output.MergeNestedDeclAlias who shorterName longerName + IncoherentDeclReason'StrayConstructor name -> Output.MergeStrayConstructor who name + pure (defns, declNameLookup) + + (aliceDefns0, aliceDeclNameLookup) <- load (Just (Just mergeTarget, branches.alice)) + (bobDefns0, bobDeclNameLookup) <- load (Just (Just mergeSource, branches.bob)) + (lcaDefns0, lcaDeclNameLookup) <- load ((Nothing,) <$> branches.lca) + + let flatten defns = Defns (flattenNametree (view #terms) defns) (flattenNametree (view #types) defns) + let defns3 = flatten <$> ThreeWay {alice = aliceDefns0, bob = bobDefns0, lca = lcaDefns0} + let declNameLookups3 = ThreeWay {alice = aliceDeclNameLookup, bob = bobDeclNameLookup, lca = lcaDeclNameLookup} + + pure (defns3, declNameLookups3) + + let defns = ThreeWay.forgetLca defns3 + let declNameLookups = ThreeWay.forgetLca declNameLookups3 + + liftIO (debugFunctions.debugDefns defns3 declNameLookups3) + + -- Diff LCA->Alice and LCA->Bob + diffs <- Cli.runTransaction (Merge.nameBasedNamespaceDiff db declNameLookups3 defns3) + + liftIO (debugFunctions.debugDiffs diffs) + + -- Bail early if it looks like we can't proceed with the merge, because Alice or Bob has one or more conflicted alias + for_ ((,) <$> TwoWay mergeTarget mergeSource <*> diffs) \(who, diff) -> + whenJust (findConflictedAlias defns3.lca diff) \(name1, name2) -> + done (Output.MergeConflictedAliases who name1 name2) + + -- Combine the LCA->Alice and LCA->Bob diffs together + let diff = combineDiffs diffs + + liftIO (debugFunctions.debugCombinedDiff diff) + + -- Partition the combined diff into the conflicted things and the unconflicted things + (conflicts, unconflicts) <- + partitionCombinedDiffs defns declNameLookups diff & onLeft \name -> + done (Output.MergeConflictInvolvingBuiltin name) + + liftIO (debugFunctions.debugPartitionedDiff conflicts unconflicts) + + -- Identify the unconflicted dependents we need to pull into the Unison file (either first for typechecking, if there + -- aren't conflicts, or else for manual conflict resolution without a typechecking step, if there are) + dependents <- Cli.runTransaction (identifyDependents defns conflicts unconflicts) + + liftIO (debugFunctions.debugDependents dependents) + + let stageOne :: DefnsF (Map Name) Referent TypeReference + stageOne = + makeStageOne + declNameLookups + conflicts + unconflicts + dependents + (bimap BiMultimap.range BiMultimap.range defns3.lca) + + liftIO (debugFunctions.debugStageOne stageOne) + + -- Load and merge Alice's and Bob's libdeps + mergedLibdeps <- + Cli.runTransaction do + libdeps <- loadLibdeps branches + libdepsToBranch0 db (Merge.mergeLibdeps getTwoFreshNames libdeps) + + -- Make PPE for Alice that contains all of Alice's names, but suffixified against her names + Bob's names + let mkPpes :: TwoWay Names -> Names -> TwoWay PrettyPrintEnvDecl + mkPpes defnsNames libdepsNames = + defnsNames <&> \names -> PPED.makePPED (PPE.namer (names <> libdepsNames)) suffixifier + where + suffixifier = PPE.suffixifyByName (fold defnsNames <> libdepsNames) + let ppes = mkPpes (defnsToNames <$> defns) (Branch.toNames mergedLibdeps) + + hydratedThings <- do + Cli.runTransaction do + for ((,) <$> conflicts <*> dependents) \(conflicts1, dependents1) -> + let hydrate = hydrateDefns (Codebase.unsafeGetTermComponent codebase) Operations.expectDeclComponent + in (,) <$> hydrate conflicts1 <*> hydrate dependents1 + + let (renderedConflicts, renderedDependents) = + let honk declNameLookup ppe defns = + let (types, accessorNames) = + Writer.runWriter $ + defns.types & Map.traverseWithKey \name (ref, typ) -> + renderTypeBinding + -- Sort of a hack; since the decl printer looks in the PPE for names of constructors, + -- we just delete all term names out and add back the constructors... + -- probably no need to wipe out the suffixified side but we do it anyway + (setPpedToConstructorNames declNameLookup name ref ppe) + name + ref + typ + terms = + defns.terms & Map.mapMaybeWithKey \name (term, typ) -> + if Set.member name accessorNames + then Nothing + else Just (renderTermBinding ppe.suffixifiedPPE name term typ) + in Defns {terms, types} + in unzip $ + ( \declNameLookup (conflicts, dependents) ppe -> + let honk1 = honk declNameLookup ppe + in (honk1 conflicts, honk1 dependents) + ) + <$> declNameLookups + <*> hydratedThings + <*> ppes + + let prettyUnisonFile = + makePrettyUnisonFile + TwoWay + { alice = into @Text aliceBranchNames, + bob = + case info.bob.source of + MergeSource'LocalProjectBranch bobBranchNames -> into @Text bobBranchNames + MergeSource'RemoteProjectBranch bobBranchNames + | aliceBranchNames == bobBranchNames -> "remote " <> into @Text bobBranchNames + | otherwise -> into @Text bobBranchNames + MergeSource'RemoteLooseCode info -> + case Path.toName info.path of + Nothing -> "" + Just name -> Name.toText name + MergeSource'RemoteGitRepo info -> + case Path.toName info.path of + Nothing -> "" + Just name -> Name.toText name + } + renderedConflicts + renderedDependents + + let stageOneBranch = defnsAndLibdepsToBranch0 codebase stageOne mergedLibdeps + + maybeTypecheckedUnisonFile <- + let thisMergeHasConflicts = + -- Eh, they'd either both be null, or neither, but just check both maps anyway + not (defnsAreEmpty conflicts.alice) || not (defnsAreEmpty conflicts.bob) + in if thisMergeHasConflicts + then pure Nothing + else do + currentPath <- Cli.getCurrentPath + parsingEnv <- makeParsingEnv currentPath (Branch.toNames stageOneBranch) + prettyParseTypecheck2 prettyUnisonFile parsingEnv <&> eitherToMaybe + + let parents = + (\causal -> (causal.causalHash, Codebase.expectBranchForHash codebase causal.causalHash)) <$> causals + + case maybeTypecheckedUnisonFile of + Nothing -> do + Cli.Env {writeSource} <- ask + _temporaryBranchId <- + HandleInput.Branch.doCreateBranch' + (Branch.mergeNode stageOneBranch parents.alice parents.bob) + (Just info.alice.projectAndBranch.branch.branchId) + info.alice.projectAndBranch.project + (findTemporaryBranchName info.alice.projectAndBranch.project.projectId mergeSourceAndTarget) + info.description + scratchFilePath <- + Cli.getLatestFile <&> \case + Nothing -> "scratch.u" + Just (file, _) -> file + liftIO $ writeSource (Text.pack scratchFilePath) (Text.pack $ Pretty.toPlain 80 prettyUnisonFile) + pure (Output.MergeFailure scratchFilePath mergeSourceAndTarget) + Just tuf -> do + Cli.runTransaction (Codebase.addDefsToCodebase codebase tuf) + let stageTwoBranch = Branch.batchUpdates (typecheckedUnisonFileToBranchAdds tuf) stageOneBranch + _ <- + Cli.updateAt + info.description + alicePath + (\_aliceBranch -> Branch.mergeNode stageTwoBranch parents.alice parents.bob) + pure (Output.MergeSuccess mergeSourceAndTarget) + + Cli.respond finalOutput doMergeLocalBranch :: TwoWay (ProjectAndBranch Project ProjectBranch) -> Cli () doMergeLocalBranch branches = do diff --git a/unison-cli/src/Unison/Codebase/Editor/Input.hs b/unison-cli/src/Unison/Codebase/Editor/Input.hs index e1ee2c73ee..7bd0b82f17 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Input.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Input.hs @@ -243,6 +243,7 @@ data Input MergeI (ProjectAndBranch (Maybe ProjectName) ProjectBranchName) | LibInstallI !(ProjectAndBranch ProjectName (Maybe ProjectBranchNameOrLatestRelease)) | UpgradeCommitI + | MergeCommitI deriving (Eq, Show) -- | The source of a `branch` command: what to make the new branch from. diff --git a/unison-cli/src/Unison/Codebase/Editor/Output.hs b/unison-cli/src/Unison/Codebase/Editor/Output.hs index d30c8ef94d..cc1afca815 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Output.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Output.hs @@ -412,6 +412,7 @@ data Output | MergeStrayConstructor !(Maybe MergeSourceOrTarget) !Name | InstalledLibdep !(ProjectAndBranch ProjectName ProjectBranchName) !NameSegment | NoUpgradeInProgress + | NoMergeInProgress data UpdateOrUpgrade = UOUUpdate | UOUUpgrade @@ -656,6 +657,7 @@ isFailure o = case o of MergeStrayConstructor {} -> True InstalledLibdep {} -> False NoUpgradeInProgress {} -> True + NoMergeInProgress {} -> True isNumberedFailure :: NumberedOutput -> Bool isNumberedFailure = \case diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index a281f02ebe..bb978987cd 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -1934,6 +1934,19 @@ mergeInputPattern = pure (Input.MergeI branch) } +mergeCommitInputPattern :: InputPattern +mergeCommitInputPattern = + InputPattern + { patternName = "merge.commit", + aliases = ["commit.merge"], + visibility = I.Visible, + args = [], + help = P.wrap $ makeExample' mergeCommitInputPattern <> "commits the current merge.", + parse = \case + [] -> Right Input.MergeCommitI + _ -> Left (I.help mergeCommitInputPattern) + } + parseLooseCodeOrProject :: String -> Maybe Input.LooseCodeOrProject parseLooseCodeOrProject inputString = case (asLooseCode, asBranch) of @@ -3256,6 +3269,7 @@ validInputs = mergeOldPreviewInputPattern, mergeOldSquashInputPattern, mergeInputPattern, + mergeCommitInputPattern, names False, -- names names True, -- names.global namespaceDependencies, diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 18e6e8768f..b5ead1f2d1 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -2313,6 +2313,8 @@ notifyUser dir = \case <> P.group (P.text (NameSegment.toEscapedText segment) <> ".") NoUpgradeInProgress -> pure . P.wrap $ "It doesn't look like there's an upgrade in progress." + NoMergeInProgress -> + pure . P.wrap $ "It doesn't look like there's a merge in progress." expectedEmptyPushDest :: WriteRemoteNamespace Void -> Pretty expectedEmptyPushDest namespace = diff --git a/unison-cli/unison-cli.cabal b/unison-cli/unison-cli.cabal index 42f38a34eb..6fabb878d8 100644 --- a/unison-cli/unison-cli.cabal +++ b/unison-cli/unison-cli.cabal @@ -55,6 +55,7 @@ library Unison.Codebase.Editor.HandleInput.Branch Unison.Codebase.Editor.HandleInput.Branches Unison.Codebase.Editor.HandleInput.BranchRename + Unison.Codebase.Editor.HandleInput.CommitMerge Unison.Codebase.Editor.HandleInput.CommitUpgrade Unison.Codebase.Editor.HandleInput.DebugDefinition Unison.Codebase.Editor.HandleInput.DebugFoldRanges diff --git a/unison-src/transcripts/merge.md b/unison-src/transcripts/merge.md index d74605f1cb..de95842c59 100644 --- a/unison-src/transcripts/merge.md +++ b/unison-src/transcripts/merge.md @@ -969,6 +969,72 @@ project/alice> merge bob .> project.delete project ``` +## `merge.commit` example + +After merge conflicts are resolved, you can use `merge.commit` rather than `update` + `switch` + `merge` + +`branch.delete` to "commit" your changes. + +```ucm:hide +.> project.create-empty project +project/main> builtins.mergeio +``` + +Original branch: +```unison:hide +foo : Text +foo = "old foo" +``` + +```ucm:hide +project/main> add +project/main> branch alice +``` + +Alice's changes: +```unison:hide +foo : Text +foo = "alices foo" +``` + +```ucm:hide +project/alice> update +project/main> branch bob +``` + +Bob's changes: + +```unison:hide +foo : Text +foo = "bobs foo" +``` + +Attempt to merge: + +```ucm:hide +project/bob> update +``` +```ucm:error +project/alice> merge /bob +``` + +Resolve conflicts and commit: + +```unison +foo : Text +foo = "alice and bobs foo" +``` + +```ucm +project/merge-bob-into-alice> merge.commit +project/alice> view foo +project/alice> branches +``` + +```ucm:hide +.> project.delete project +``` + + ## Precondition violations There are a number of conditions under which we can't perform a merge, and the user will have to fix up the namespace(s) manually before attempting to merge again. diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index 6b50339eec..4e575fcde0 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -929,6 +929,96 @@ bob _ = 19 ``` +## `merge.commit` example + +After merge conflicts are resolved, you can use `merge.commit` rather than `update` + `switch` + `merge` + +`branch.delete` to "commit" your changes. + +Original branch: +```unison +foo : Text +foo = "old foo" +``` + +Alice's changes: +```unison +foo : Text +foo = "alices foo" +``` + +Bob's changes: + +```unison +foo : Text +foo = "bobs foo" +``` + +Attempt to merge: + +```ucm +project/alice> merge /bob + + I couldn't automatically merge project/bob into project/alice. + However, I've added the definitions that need attention to the + top of scratch.u. + +``` +```unison:added-by-ucm scratch.u +-- project/alice +foo : Text +foo = "alices foo" + +-- project/bob +foo : Text +foo = "bobs foo" + + +``` + +Resolve conflicts and commit: + +```unison +foo : Text +foo = "alice and bobs foo" +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + foo : Text + +``` +```ucm +project/merge-bob-into-alice> merge.commit + + Okay, I'm searching the branch for code that needs to be + updated... + + Done. + + I fast-forward merged project/merge-bob-into-alice into + project/alice. + +project/alice> view foo + + foo : Text + foo = "alice and bobs foo" + +project/alice> branches + + Branch Remote branch + 1. alice + 2. bob + 3. main + +``` ## Precondition violations There are a number of conditions under which we can't perform a merge, and the user will have to fix up the namespace(s) manually before attempting to merge again. From fd1bea7713a387db35be1afd8a708b1f8ee85a79 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 3 Jun 2024 08:52:40 -0400 Subject: [PATCH 02/81] don't update in commit.merge --- .../src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs index 4f8abd347f..1c9061e5d1 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs @@ -12,7 +12,6 @@ import Unison.Cli.ProjectUtils qualified as ProjectUtils import Unison.Codebase.Editor.HandleInput.DeleteBranch qualified as DeleteBranch import Unison.Codebase.Editor.HandleInput.Merge2 qualified as Merge import Unison.Codebase.Editor.HandleInput.ProjectSwitch qualified as ProjectSwitch -import Unison.Codebase.Editor.HandleInput.Update2 qualified as Update import Unison.Codebase.Editor.Output qualified as Output import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Prelude @@ -36,10 +35,6 @@ handleCommitMerge = do let parentProjectAndBranch = ProjectAndBranch mergeProjectAndBranch.project parentBranch - -- Run `update` - - Update.handleUpdate2 - -- Switch to the parent ProjectSwitch.switchToProjectBranch (ProjectUtils.justTheIds parentProjectAndBranch) From 335512e331339cd496db97b6d3bead1efc090f42 Mon Sep 17 00:00:00 2001 From: Arya Irani <538571+aryairani@users.noreply.github.com> Date: Tue, 11 Jun 2024 09:40:43 -0400 Subject: [PATCH 03/81] fix conflicted alias message 2 (#5071) Co-authored-by: aryairani --- unison-cli/src/Unison/CommandLine/OutputMessages.hs | 4 +++- unison-src/transcripts/merge.output.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 98c9bdb271..76355438a9 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -1372,7 +1372,9 @@ notifyUser dir = \case <> "or" <> IP.makeExample' IP.delete <> "all but one of the definitions; I'll use the remaining name when propagating updates." - <> "(You can `rename` it back after the merge.)" + <> "(You can" + <> IP.makeExample' IP.moveAll + <> "it back after the merge.)" ) ] ) diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index b77752a7ba..f1ba873f05 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -977,7 +977,7 @@ project/alice> merge /bob there's nothing for me to decide. * `move` or `delete` all but one of the definitions; I'll use the remaining name when propagating updates. (You can - `rename` it back after the merge.) + `move` it back after the merge.) and then try merging again. From 815c1b1f1ca7db6beb50f003361c0e98740f0d9e Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 12 Jun 2024 06:28:12 -0700 Subject: [PATCH 04/81] Auto-create project-branches referenced in transcript prompts (#5077) --- .../Codebase/SqliteCodebase/Operations.hs | 15 ++++++++++ .../Editor/HandleInput/ProjectCreate.hs | 21 +++----------- .../src/Unison/Codebase/TranscriptParser.hs | 28 +++++++++++++++--- .../transcripts-manual/gen-racket-libs.md | 1 - unison-src/transcripts/definition-diff-api.md | 5 ++-- .../transcripts/definition-diff-api.output.md | 20 ++----------- .../delete-namespace-dependents-check.md | 1 - .../transcripts/delete-project-branch.md | 1 - .../delete-project-branch.output.md | 16 ---------- .../dont-upgrade-refs-that-exist-in-old.md | 1 - unison-src/transcripts/edit-namespace.md | 1 - unison-src/transcripts/fix-ls.md | 1 - unison-src/transcripts/fix-ls.output.md | 16 ---------- unison-src/transcripts/fix4482.md | 1 - unison-src/transcripts/fix4515.md | 1 - unison-src/transcripts/fix4528.md | 1 - unison-src/transcripts/fix5055.md | 1 - unison-src/transcripts/fix5055.output.md | 16 ---------- unison-src/transcripts/fuzzy-options.md | 1 - .../transcripts/fuzzy-options.output.md | 16 ---------- unison-src/transcripts/merge.md | 29 ------------------- unison-src/transcripts/pull-errors.md | 3 -- unison-src/transcripts/pull-errors.output.md | 18 ------------ .../transcripts/release-draft-command.md | 1 - unison-src/transcripts/reset.md | 1 - unison-src/transcripts/reset.output.md | 16 ---------- unison-src/transcripts/switch-command.md | 2 -- unison-src/transcripts/tab-completion.md | 1 - .../transcripts/tab-completion.output.md | 16 ---------- .../update-suffixifies-properly.md | 1 - unison-src/transcripts/upgrade-happy-path.md | 1 - unison-src/transcripts/upgrade-sad-path.md | 1 - .../upgrade-suffixifies-properly.md | 1 - .../transcripts/upgrade-with-old-alias.md | 1 - 34 files changed, 47 insertions(+), 209 deletions(-) diff --git a/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Operations.hs b/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Operations.hs index eee0dcec4f..98a6db75ef 100644 --- a/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Operations.hs +++ b/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Operations.hs @@ -24,11 +24,13 @@ import U.Codebase.Projects qualified as Projects import U.Codebase.Reference qualified as C.Reference import U.Codebase.Referent qualified as C.Referent import U.Codebase.Sqlite.DbId (ObjectId) +import U.Codebase.Sqlite.DbId qualified as Db import U.Codebase.Sqlite.NameLookups (PathSegments (..), ReversedName (..)) import U.Codebase.Sqlite.NamedRef qualified as S import U.Codebase.Sqlite.ObjectType qualified as OT import U.Codebase.Sqlite.Operations (NamesInPerspective (..)) import U.Codebase.Sqlite.Operations qualified as Ops +import U.Codebase.Sqlite.ProjectBranch (ProjectBranch (..)) import U.Codebase.Sqlite.Queries qualified as Q import U.Codebase.Sqlite.V2.HashHandle (v2HashHandle) import Unison.Builtin qualified as Builtins @@ -41,6 +43,7 @@ import Unison.Codebase.SqliteCodebase.Branch.Cache (BranchCache) import Unison.Codebase.SqliteCodebase.Conversions qualified as Cv import Unison.ConstructorReference (GConstructorReference (..)) import Unison.ConstructorType qualified as CT +import Unison.Core.Project (ProjectBranchName, ProjectName) import Unison.DataDeclaration (Decl) import Unison.DataDeclaration qualified as Decl import Unison.Hash (Hash) @@ -731,3 +734,15 @@ makeMaybeCachedTransaction size action = do pure \x -> do conn <- Sqlite.unsafeGetConnection Sqlite.unsafeIO (Cache.applyDefined cache (\x -> Sqlite.unsafeUnTransaction (action x) conn) x) + +insertProjectAndBranch :: Db.ProjectId -> ProjectName -> Db.ProjectBranchId -> ProjectBranchName -> Sqlite.Transaction () +insertProjectAndBranch projectId projectName branchId branchName = do + Q.insertProject projectId projectName + Q.insertProjectBranch + ProjectBranch + { projectId, + branchId, + name = branchName, + parentBranchId = Nothing + } + Q.setMostRecentBranch projectId branchId diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectCreate.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectCreate.hs index 9d95ffca8d..8ffe4e9777 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectCreate.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectCreate.hs @@ -10,7 +10,6 @@ import Data.Text qualified as Text import Data.UUID.V4 qualified as UUID import System.Random.Shuffle qualified as RandomShuffle import U.Codebase.Sqlite.DbId -import U.Codebase.Sqlite.ProjectBranch qualified as Sqlite import U.Codebase.Sqlite.Queries qualified as Queries import Unison.Cli.DownloadUtils (downloadProjectBranchFromShare) import Unison.Cli.Monad (Cli) @@ -22,11 +21,11 @@ import Unison.Codebase qualified as Codebase import Unison.Codebase.Branch qualified as Branch import Unison.Codebase.Editor.Output qualified as Output import Unison.Codebase.Path qualified as Path +import Unison.Codebase.SqliteCodebase.Operations qualified as Ops import Unison.NameSegment qualified as NameSegment import Unison.Prelude -import Unison.Project (ProjectAndBranch (..), ProjectBranchName, ProjectName) +import Unison.Project (ProjectAndBranch (..), ProjectName) import Unison.Share.API.Hash qualified as Share.API -import Unison.Sqlite qualified as Sqlite import Unison.Sync.Common qualified as Sync.Common import Witch (unsafeFrom) @@ -73,7 +72,7 @@ projectCreate tryDownloadingBase maybeProjectName = do projectName : projectNames -> Queries.projectExistsByName projectName >>= \case False -> do - insertProjectAndBranch projectId projectName branchId branchName + Ops.insertProjectAndBranch projectId projectName branchId branchName pure projectName True -> loop projectNames loop randomProjectNames @@ -81,7 +80,7 @@ projectCreate tryDownloadingBase maybeProjectName = do Cli.runTransactionWithRollback \rollback -> do Queries.projectExistsByName projectName >>= \case False -> do - insertProjectAndBranch projectId projectName branchId branchName + Ops.insertProjectAndBranch projectId projectName branchId branchName pure projectName True -> rollback (Output.ProjectNameAlreadyExists projectName) @@ -152,18 +151,6 @@ projectCreate tryDownloadingBase maybeProjectName = do Nothing -> "project.create" Just projectName -> "project.create " <> into @Text projectName -insertProjectAndBranch :: ProjectId -> ProjectName -> ProjectBranchId -> ProjectBranchName -> Sqlite.Transaction () -insertProjectAndBranch projectId projectName branchId branchName = do - Queries.insertProject projectId projectName - Queries.insertProjectBranch - Sqlite.ProjectBranch - { projectId, - branchId, - name = branchName, - parentBranchId = Nothing - } - Queries.setMostRecentBranch projectId branchId - -- An infinite list of random project names that looks like -- -- [ diff --git a/unison-cli/src/Unison/Codebase/TranscriptParser.hs b/unison-cli/src/Unison/Codebase/TranscriptParser.hs index 9746c39f91..b9e82f7ed5 100644 --- a/unison-cli/src/Unison/Codebase/TranscriptParser.hs +++ b/unison-cli/src/Unison/Codebase/TranscriptParser.hs @@ -31,6 +31,7 @@ import Data.List (isSubsequenceOf) import Data.Map qualified as Map import Data.Text qualified as Text import Data.These (These (..)) +import Data.UUID.V4 qualified as UUID import Ki qualified import Network.HTTP.Client qualified as HTTP import System.Directory (doesFileExist) @@ -39,7 +40,11 @@ import System.Exit (die) import System.IO qualified as IO import System.IO.Error (catchIOError) import Text.Megaparsec qualified as P +import U.Codebase.Sqlite.DbId qualified as Db import U.Codebase.Sqlite.Operations qualified as Operations +import U.Codebase.Sqlite.Project (Project (..)) +import U.Codebase.Sqlite.ProjectBranch (ProjectBranch (..)) +import U.Codebase.Sqlite.Queries qualified as Q import Unison.Auth.CredentialManager qualified as AuthN import Unison.Auth.HTTPClient qualified as AuthN import Unison.Auth.Tokens qualified as AuthN @@ -70,6 +75,7 @@ import Unison.Project (ProjectAndBranch (..), ProjectAndBranchNames (ProjectAndB import Unison.Runtime.Interface qualified as RTI import Unison.Server.Backend qualified as Backend import Unison.Server.CodebaseServer qualified as Server +import Unison.Sqlite qualified as Sqlite import Unison.Symbol (Symbol) import Unison.Syntax.Parser qualified as Parser import Unison.Util.Pretty qualified as Pretty @@ -349,10 +355,24 @@ run verbosity dir stanzas codebase runtime sbRuntime nRuntime config ucmVersion if curPath == path then pure Nothing else pure $ Just (SwitchBranchI (Path.absoluteToPath' path)) - UcmContextProject (ProjectAndBranch projectName branchName) -> do - ProjectAndBranch project branch <- - ProjectUtils.expectProjectAndBranchByTheseNames (These projectName branchName) - let projectAndBranchIds = ProjectAndBranch (project ^. #projectId) (branch ^. #branchId) + UcmContextProject (ProjectAndBranch projectName branchName) -> Cli.runTransaction do + Project {projectId, name = projectName} <- + Q.loadProjectByName projectName + >>= \case + Nothing -> do + projectId <- Sqlite.unsafeIO (Db.ProjectId <$> UUID.nextRandom) + Q.insertProject projectId projectName + pure $ Project {projectId, name = projectName} + Just project -> pure project + projectBranch <- + Q.loadProjectBranchByName projectId branchName >>= \case + Nothing -> do + branchId <- Sqlite.unsafeIO (Db.ProjectBranchId <$> UUID.nextRandom) + let projectBranch = ProjectBranch {projectId, parentBranchId = Nothing, branchId, name = branchName} + Q.insertProjectBranch projectBranch + pure projectBranch + Just projBranch -> pure projBranch + let projectAndBranchIds = ProjectAndBranch projectBranch.projectId projectBranch.branchId pure if curPath == ProjectUtils.projectBranchPath projectAndBranchIds then Nothing diff --git a/unison-src/transcripts-manual/gen-racket-libs.md b/unison-src/transcripts-manual/gen-racket-libs.md index 44c078db56..811ec14f50 100644 --- a/unison-src/transcripts-manual/gen-racket-libs.md +++ b/unison-src/transcripts-manual/gen-racket-libs.md @@ -4,7 +4,6 @@ When we start out, `./scheme-libs/racket` contains a bunch of library files that Next, we'll download the jit project and generate a few Racket files from it. ```ucm -.> project.create-empty jit-setup jit-setup/main> lib.install @unison/internal/releases/0.0.17 ``` diff --git a/unison-src/transcripts/definition-diff-api.md b/unison-src/transcripts/definition-diff-api.md index 922a3c2776..f8d21d0687 100644 --- a/unison-src/transcripts/definition-diff-api.md +++ b/unison-src/transcripts/definition-diff-api.md @@ -1,10 +1,9 @@ ```ucm -.> project.create-empty diffs diffs/main> builtins.merge ``` ```unison -term = +term = _ = "Here's some text" 1 + 1 @@ -17,7 +16,7 @@ diffs/main> branch.create new ``` ```unison -term = +term = _ = "Here's some different text" 1 + 2 diff --git a/unison-src/transcripts/definition-diff-api.output.md b/unison-src/transcripts/definition-diff-api.output.md index d0c73dc486..192367ff9f 100644 --- a/unison-src/transcripts/definition-diff-api.output.md +++ b/unison-src/transcripts/definition-diff-api.output.md @@ -1,27 +1,11 @@ ```ucm -.> project.create-empty diffs - - 🎉 I've created the project diffs. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - diffs/main> builtins.merge Done. ``` ```unison -term = +term = _ = "Here's some text" 1 + 1 @@ -59,7 +43,7 @@ diffs/main> branch.create new ``` ```unison -term = +term = _ = "Here's some different text" 1 + 2 diff --git a/unison-src/transcripts/delete-namespace-dependents-check.md b/unison-src/transcripts/delete-namespace-dependents-check.md index 9bbf7b94d3..72aacc311d 100644 --- a/unison-src/transcripts/delete-namespace-dependents-check.md +++ b/unison-src/transcripts/delete-namespace-dependents-check.md @@ -5,7 +5,6 @@ This is a regression test, previously `delete.namespace` allowed a delete as long as the deletions had a name _anywhere_ in your codebase, it should only check the current project branch. ```ucm:hide -.> project.create-empty myproject myproject/main> builtins.merge ``` diff --git a/unison-src/transcripts/delete-project-branch.md b/unison-src/transcripts/delete-project-branch.md index e28558c5fc..c84dc95cc2 100644 --- a/unison-src/transcripts/delete-project-branch.md +++ b/unison-src/transcripts/delete-project-branch.md @@ -2,7 +2,6 @@ Deleting the branch you are on takes you to its parent (though this is impossibl your working directory with each command). ```ucm -.> project.create-empty foo foo/main> branch topic foo/topic> delete.branch /topic ``` diff --git a/unison-src/transcripts/delete-project-branch.output.md b/unison-src/transcripts/delete-project-branch.output.md index 1b8baecc37..d4458e8be0 100644 --- a/unison-src/transcripts/delete-project-branch.output.md +++ b/unison-src/transcripts/delete-project-branch.output.md @@ -2,22 +2,6 @@ Deleting the branch you are on takes you to its parent (though this is impossibl your working directory with each command). ```ucm -.> project.create-empty foo - - 🎉 I've created the project foo. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - foo/main> branch topic Done. I've created the topic branch based off of main. diff --git a/unison-src/transcripts/dont-upgrade-refs-that-exist-in-old.md b/unison-src/transcripts/dont-upgrade-refs-that-exist-in-old.md index 9bd44d5a50..d74ca38e19 100644 --- a/unison-src/transcripts/dont-upgrade-refs-that-exist-in-old.md +++ b/unison-src/transcripts/dont-upgrade-refs-that-exist-in-old.md @@ -2,7 +2,6 @@ If `foo#old` exists in old, and `foo#new` exists in new, you might think `upgrad `#old` with references to `#new`. And it will... !!unless!! `#old` still exists in new. ```ucm:hide -.> project.create-empty foo foo/main> builtins.merge lib.builtin ``` diff --git a/unison-src/transcripts/edit-namespace.md b/unison-src/transcripts/edit-namespace.md index 816922fe82..ad50bc1b0d 100644 --- a/unison-src/transcripts/edit-namespace.md +++ b/unison-src/transcripts/edit-namespace.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio lib.builtin ``` diff --git a/unison-src/transcripts/fix-ls.md b/unison-src/transcripts/fix-ls.md index 3bd9fe5349..5bb9b950e3 100644 --- a/unison-src/transcripts/fix-ls.md +++ b/unison-src/transcripts/fix-ls.md @@ -1,5 +1,4 @@ ```ucm -.> project.create-empty test-ls test-ls/main> builtins.merge ``` diff --git a/unison-src/transcripts/fix-ls.output.md b/unison-src/transcripts/fix-ls.output.md index 0f6b6ff1f5..56277c6925 100644 --- a/unison-src/transcripts/fix-ls.output.md +++ b/unison-src/transcripts/fix-ls.output.md @@ -1,20 +1,4 @@ ```ucm -.> project.create-empty test-ls - - 🎉 I've created the project test-ls. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - test-ls/main> builtins.merge Done. diff --git a/unison-src/transcripts/fix4482.md b/unison-src/transcripts/fix4482.md index 1e4a9b1a51..380d693c87 100644 --- a/unison-src/transcripts/fix4482.md +++ b/unison-src/transcripts/fix4482.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty myproj myproj/main> builtins.merge ``` diff --git a/unison-src/transcripts/fix4515.md b/unison-src/transcripts/fix4515.md index c2dca1d63f..8cae1afc2b 100644 --- a/unison-src/transcripts/fix4515.md +++ b/unison-src/transcripts/fix4515.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty myproject myproject/main> builtins.merge ``` diff --git a/unison-src/transcripts/fix4528.md b/unison-src/transcripts/fix4528.md index e1b1e4f0a9..c6c540c959 100644 --- a/unison-src/transcripts/fix4528.md +++ b/unison-src/transcripts/fix4528.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty foo foo/main> builtins.merge ``` diff --git a/unison-src/transcripts/fix5055.md b/unison-src/transcripts/fix5055.md index b0218766a1..b5c377d381 100644 --- a/unison-src/transcripts/fix5055.md +++ b/unison-src/transcripts/fix5055.md @@ -1,5 +1,4 @@ ```ucm -.> project.create-empty test-5055 test-5055/main> builtins.merge ``` diff --git a/unison-src/transcripts/fix5055.output.md b/unison-src/transcripts/fix5055.output.md index 8dc31da207..a9fe9ee5d0 100644 --- a/unison-src/transcripts/fix5055.output.md +++ b/unison-src/transcripts/fix5055.output.md @@ -1,20 +1,4 @@ ```ucm -.> project.create-empty test-5055 - - 🎉 I've created the project test-5055. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - test-5055/main> builtins.merge Done. diff --git a/unison-src/transcripts/fuzzy-options.md b/unison-src/transcripts/fuzzy-options.md index 96294ce0a6..13d953c938 100644 --- a/unison-src/transcripts/fuzzy-options.md +++ b/unison-src/transcripts/fuzzy-options.md @@ -40,7 +40,6 @@ Namespace args Project Branch args ```ucm -.> project.create-empty myproject myproject/main> branch mybranch .> debug.fuzzy-options switch _ ``` diff --git a/unison-src/transcripts/fuzzy-options.output.md b/unison-src/transcripts/fuzzy-options.output.md index d5ff9b74cb..f48f5cd6fb 100644 --- a/unison-src/transcripts/fuzzy-options.output.md +++ b/unison-src/transcripts/fuzzy-options.output.md @@ -65,22 +65,6 @@ Namespace args Project Branch args ```ucm -.> project.create-empty myproject - - 🎉 I've created the project myproject. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - myproject/main> branch mybranch Done. I've created the mybranch branch based off of main. diff --git a/unison-src/transcripts/merge.md b/unison-src/transcripts/merge.md index d653b84c99..4c6549ac00 100644 --- a/unison-src/transcripts/merge.md +++ b/unison-src/transcripts/merge.md @@ -9,7 +9,6 @@ contains both additions. ## Basic merge: two unconflicted adds ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -52,7 +51,6 @@ project/alice> view foo bar If Alice and Bob also happen to add the same definition, that's not a conflict. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio project/main> branch alice ``` @@ -94,7 +92,6 @@ project/alice> view foo bar Updates that occur in one branch are propagated to the other. In this example, Alice updates `foo`, while Bob adds a new dependent `bar` of the original `foo`. When Bob's branch is merged into Alice's, her update to `foo` is propagated to his `bar`. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -150,7 +147,6 @@ We classify something as an update if its "syntactic hash"—not its normal Unis Let's see an example. We have `foo`, which depends on `bar` and `baz`. Alice updates `bar` (propagating to `foo`), and Bob updates `baz` (propagating to `foo`). When we merge their updates, both updates will be reflected in the final `foo`. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -215,7 +211,6 @@ project/alice> display foo Of course, it's also possible for Alice's update to propagate to one of Bob's updates. In this example, `foo` depends on `bar` which depends on `baz`. Alice updates `baz`, propagating to `bar` and `foo`, while Bob updates `bar` (to something that still depends on `foo`), propagating to `baz`. The merged result will have Alice's update to `foo` incorporated into Bob's updated `bar`, and both updates will propagate to `baz`. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -286,7 +281,6 @@ project/alice> display foo We don't currently consider "update + delete" a conflict like Git does. In this situation, the delete is just ignored, allowing the update to proceed. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -334,7 +328,6 @@ In a future version, we'd like to give the user a warning at least. Library dependencies don't cause merge conflicts, the library dependencies are just unioned together. If two library dependencies have the same name but different namespace hashes, then the merge algorithm makes up two fresh names. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -389,7 +382,6 @@ project/alice> view foo bar baz If Bob is equals Alice, then merging Bob into Alice looks like this. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -408,7 +400,6 @@ project/alice> merge /bob If Bob is behind Alice, then merging Bob into Alice looks like this. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -437,7 +428,6 @@ project/alice> merge /bob If Bob is ahead of Alice, then merging Bob into Alice looks like this. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -470,7 +460,6 @@ This can cause merge failures due to out-of-scope identifiers, and the user may In this example, Alice deletes `foo`, while Bob adds a new dependent of `foo`. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -514,7 +503,6 @@ It may be Alice's and Bob's changes merge together cleanly in the sense that the In this example, Alice updates a `Text` to a `Nat`, while Bob adds a new dependent of the `Text`. Upon merging, propagating Alice's update to Bob's dependent causes a typechecking failure. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -564,7 +552,6 @@ Alice and Bob may disagree about the definition of a term. In this case, the con are presented to the user to resolve. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -629,7 +616,6 @@ project/merge-bob-into-alice> view bar baz Ditto for types; if the hashes don't match, it's a conflict. In this example, Alice and Bob do different things to the same constructor. However, any explicit changes to the same type will result in a conflict, including changes that could concievably be merged (e.g. Alice and Bob both add a new constructor, or edit different constructors). ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -673,7 +659,6 @@ project/alice> merge /bob We model the renaming of a type's constructor as an update, so if Alice updates a type and Bob renames one of its constructors (even without changing its structure), we consider it a conflict. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -717,7 +702,6 @@ project/alice> merge /bob Here is another example demonstrating that constructor renames are modeled as updates. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -756,7 +740,6 @@ project/alice> merge bob A constructor on one side can conflict with a regular term definition on the other. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -798,7 +781,6 @@ project/alice> merge bob Here's a subtle situation where a new type is added on each side of the merge, and an existing term is replaced with a constructor of one of the types. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -848,7 +830,6 @@ project/alice> merge bob Here's a more involved example that demonstrates the same idea. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -928,7 +909,6 @@ which is a parse error. We will resolve this situation automatically in a future version. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -978,7 +958,6 @@ There are a number of conditions under which we can't perform a merge, and the u If `foo` and `bar` are aliases in the nearest common ancestor, but not in Alice's branch, then we don't know whether to update Bob's dependents to Alice's `foo` or Alice's `bar` (and vice-versa). ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1036,7 +1015,6 @@ conflict involving a builtin, we can't perform a merge. One way to fix this in the future would be to introduce a syntax for defining aliases in the scratch file. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1075,7 +1053,6 @@ project/alice> merge /bob Each naming of a decl may not have more than one name for each constructor, within the decl's namespace. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1122,7 +1099,6 @@ project/alice> merge /bob Each naming of a decl must have a name for each constructor, within the decl's namespace. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1170,7 +1146,6 @@ project/alice> merge /bob A decl cannot be aliased within the namespace of another of its aliased. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1219,7 +1194,6 @@ project/alice> merge /bob Constructors may only exist within the corresponding decl's namespace. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1264,7 +1238,6 @@ project/alice> merge bob By convention, `lib` can only namespaces; each of these represents a library dependencies. Individual terms and types are not allowed at the top level of `lib`. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1309,7 +1282,6 @@ Here's an example. We'll delete a constructor name from the LCA and still be abl together. ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` @@ -1374,7 +1346,6 @@ project/alice> merge /bob ```ucm:hide -.> project.create-empty project project/main> builtins.mergeio ``` diff --git a/unison-src/transcripts/pull-errors.md b/unison-src/transcripts/pull-errors.md index f314ad5abb..784221bb8e 100644 --- a/unison-src/transcripts/pull-errors.md +++ b/unison-src/transcripts/pull-errors.md @@ -1,6 +1,3 @@ -```ucm -.> project.create-empty test -``` ```ucm:error test/main> pull @aryairani/test-almost-empty/main lib.base_latest test/main> pull @aryairani/test-almost-empty/main a.b diff --git a/unison-src/transcripts/pull-errors.output.md b/unison-src/transcripts/pull-errors.output.md index 963eaabb52..a2894f6468 100644 --- a/unison-src/transcripts/pull-errors.output.md +++ b/unison-src/transcripts/pull-errors.output.md @@ -1,22 +1,4 @@ ```ucm -.> project.create-empty test - - 🎉 I've created the project test. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - -``` -```ucm test/main> pull @aryairani/test-almost-empty/main lib.base_latest The use of `pull` to install libraries is now deprecated. diff --git a/unison-src/transcripts/release-draft-command.md b/unison-src/transcripts/release-draft-command.md index 7a5652a079..bac0e991b0 100644 --- a/unison-src/transcripts/release-draft-command.md +++ b/unison-src/transcripts/release-draft-command.md @@ -1,7 +1,6 @@ The `release.draft` command drafts a release from the current branch. ```ucm:hide -.> project.create-empty foo foo/main> builtins.merge ``` diff --git a/unison-src/transcripts/reset.md b/unison-src/transcripts/reset.md index 412b173337..a01351233d 100644 --- a/unison-src/transcripts/reset.md +++ b/unison-src/transcripts/reset.md @@ -29,7 +29,6 @@ foo.a = 5 # reset branch ```ucm -.> project.create-empty foo foo/main> history ``` diff --git a/unison-src/transcripts/reset.output.md b/unison-src/transcripts/reset.output.md index d4035c9257..344b2c16f9 100644 --- a/unison-src/transcripts/reset.output.md +++ b/unison-src/transcripts/reset.output.md @@ -103,22 +103,6 @@ foo.a = 5 # reset branch ```ucm -.> project.create-empty foo - - 🎉 I've created the project foo. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - foo/main> history ☝️ The namespace is empty. diff --git a/unison-src/transcripts/switch-command.md b/unison-src/transcripts/switch-command.md index d75b4a9592..c1a2bca962 100644 --- a/unison-src/transcripts/switch-command.md +++ b/unison-src/transcripts/switch-command.md @@ -1,8 +1,6 @@ The `switch` command switches to an existing project or branch. ```ucm:hide -.> project.create-empty foo -.> project.create-empty bar foo/main> builtins.merge bar/main> builtins.merge ``` diff --git a/unison-src/transcripts/tab-completion.md b/unison-src/transcripts/tab-completion.md index a5c7b090ec..c35c4ba347 100644 --- a/unison-src/transcripts/tab-completion.md +++ b/unison-src/transcripts/tab-completion.md @@ -69,7 +69,6 @@ add b = b ## Tab complete projects and branches ```ucm -.> project.create-empty myproject myproject/main> branch mybranch myproject/main> debug.tab-complete branch.delete /mybr myproject/main> debug.tab-complete project.rename my diff --git a/unison-src/transcripts/tab-completion.output.md b/unison-src/transcripts/tab-completion.output.md index 34ce96db90..82961cfd5c 100644 --- a/unison-src/transcripts/tab-completion.output.md +++ b/unison-src/transcripts/tab-completion.output.md @@ -173,22 +173,6 @@ add b = b ## Tab complete projects and branches ```ucm -.> project.create-empty myproject - - 🎉 I've created the project myproject. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - myproject/main> branch mybranch Done. I've created the mybranch branch based off of main. diff --git a/unison-src/transcripts/update-suffixifies-properly.md b/unison-src/transcripts/update-suffixifies-properly.md index 4cd042b494..d983959770 100644 --- a/unison-src/transcripts/update-suffixifies-properly.md +++ b/unison-src/transcripts/update-suffixifies-properly.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty myproject myproject/main> builtins.merge lib.builtin ``` diff --git a/unison-src/transcripts/upgrade-happy-path.md b/unison-src/transcripts/upgrade-happy-path.md index c234e9ac7d..068c8ccf1c 100644 --- a/unison-src/transcripts/upgrade-happy-path.md +++ b/unison-src/transcripts/upgrade-happy-path.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty proj proj/main> builtins.merge lib.builtin ``` diff --git a/unison-src/transcripts/upgrade-sad-path.md b/unison-src/transcripts/upgrade-sad-path.md index ccf51fd605..c2c1fe459a 100644 --- a/unison-src/transcripts/upgrade-sad-path.md +++ b/unison-src/transcripts/upgrade-sad-path.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty proj proj/main> builtins.merge lib.builtin ``` diff --git a/unison-src/transcripts/upgrade-suffixifies-properly.md b/unison-src/transcripts/upgrade-suffixifies-properly.md index 5aba271c10..08c4b002d9 100644 --- a/unison-src/transcripts/upgrade-suffixifies-properly.md +++ b/unison-src/transcripts/upgrade-suffixifies-properly.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty myproject myproject/main> builtins.merge lib.builtin ``` diff --git a/unison-src/transcripts/upgrade-with-old-alias.md b/unison-src/transcripts/upgrade-with-old-alias.md index ed1ae1c184..aeb818947e 100644 --- a/unison-src/transcripts/upgrade-with-old-alias.md +++ b/unison-src/transcripts/upgrade-with-old-alias.md @@ -1,5 +1,4 @@ ```ucm:hide -.> project.create-empty myproject myproject/main> builtins.merge lib.builtin ``` From 53923da774dc1db7cf9f52e30278f7cb6a875983 Mon Sep 17 00:00:00 2001 From: Eduard Date: Wed, 12 Jun 2024 21:54:43 +0100 Subject: [PATCH 05/81] No longer need special instructions for Mac Silicon. --- README.md | 2 - docs/m1-mac-setup-tips.markdown | 164 -------------------------------- 2 files changed, 166 deletions(-) delete mode 100644 docs/m1-mac-setup-tips.markdown diff --git a/README.md b/README.md index 3a857d3801..ee703bcc22 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,6 @@ If these instructions don't work for you or are incomplete, please file an issue The build uses [Stack](http://docs.haskellstack.org/). If you don't already have it installed, [follow the install instructions](http://docs.haskellstack.org/en/stable/README.html#how-to-install) for your platform. (Hint: `brew update && brew install stack`) -If you have not set up the Haskell toolchain before and are trying to contribute to Unison on an M1 Mac, we have [some tips specifically for you](docs/m1-mac-setup-tips.markdown). - ```sh $ git clone https://github.com/unisonweb/unison.git $ cd unison diff --git a/docs/m1-mac-setup-tips.markdown b/docs/m1-mac-setup-tips.markdown deleted file mode 100644 index 207e2382cf..0000000000 --- a/docs/m1-mac-setup-tips.markdown +++ /dev/null @@ -1,164 +0,0 @@ - -# M1 Mac Haskell toolchain setup - -If you are a newcomer to the Haskell ecosystem trying to set up your dev environment on a Mac M1 computer, welcome, you can do this! The tips in this document provide one way to get a working development setup, but are not the only path forward. If you haven't downloaded the Haskell toolchain before, our recommendation is to use GHCup. We've found that issues can arise if you mix ARM native binaries with x86 binaries to be run with Rosetta. If you're a veteran Haskell developer, much of this won't apply to you as it's likely you already have a working development environment. - -Here is a working set of versions you can use to build the Unison executable: - -- GHC version: 8.10.7 -- Stack version: 2.9.1 -- Cabal version 3.6.2.0 -- Haskell language server version: 1.7.0.0 - -The GHC version for the project can be confirmed by looking at the `resolver` key in this project's `stack.yaml`. - -## Newcomer setup tips - -[Install GHCup using the instructions on their website.](https://www.haskell.org/ghcup/) Once it's installed make sure `ghcup` is on your path. - -``` -export PATH="$HOME/.ghcup/bin:$PATH" -``` - -GHCup has a nice ui for setting Haskell toolchain versions for the project. Enter `ghcup tui` to open it up and follow the instructions for installing and setting the versions there. GHCup will try to download M1 native binaries for the versions given. - -Check your clang version. For [hand-wavey reasons](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/301) we recommend you use llvm version 12. See troubleshooting note below about changing your LLVM if your version is different. - -```shell -$ clang --version -Homebrew clang version 12.0.1 -Target: arm64-apple-darwin20.2.0 -Thread model: posix -InstalledDir: /opt/homebrew/opt/llvm@12/bin -``` - -At the end of the process you should see something like the following for executable locations and versions. - -```shell -$ which ghcup -~/.ghcup/bin/ghcup -$ ghcup --version -The GHCup Haskell installer, version 0.1.19.0 -``` - -```bash -$ which stack -~/.ghcup/bin/stack -$ stack --version -Version 2.9.1, Git revision 13c9c8772a6dce093dbeacc08bb5877bdb6cfc2e (dirty) (155 commits) aarch64 -``` - -```shell -$ which ghc -~/.ghcup/bin/ghc -$ ghc --version -The Glorious Glasgow Haskell Compilation System, version 8.10.7 -``` - -Check which GHC version Stack thinks it's using too, for good measure: - -```shell -$ stack ghc -- --version -The Glorious Glasgow Haskell Compilation System, version 8.10.7 -$ stack exec -- which ghc -~/.ghcup/ghc/8.10.7/bin/ghc -``` - -```shell -$ which haskell-language-server-wrapper -~/.ghcup/bin/haskell-language-server-wrapper -$ haskell-language-server-wrapper - -Found "...unison/hie.yaml" for "...unison/a" -Run entered for haskell-language-server-wrapper(haskell-language-server-wrapper) Version 1.7.0.0 aarch64 ghc-9.2.2 -Current directory: ...unison -Operating system: darwin -Arguments: [] -Cradle directory: ...unison -Cradle type: Stack - -Tool versions found on the $PATH -cabal: 3.6.2.0 -stack: 2.9.1 -ghc: 8.10.7 -``` - -If you're a VS Code user, you can download the Haskell extension for IDE support. You may need to configure it in `settings.json`. - -```json - "haskell.manageHLS": "GHCup", - "haskell.toolchain": { - "stack": "2.9.1", - "ghc": "8.10.7", - "cabal": "recommended", - "hls": "1.7.0.0" - } -``` - -These setting blocks say that the VS Code extension will use GHCup for your Haskell language server distribution, and sets the versions for elements in the toolchain. - -## Troubleshooting: - -The VS Code extension has compiled a helpful list of troubleshooting steps here: https://github.com/haskell/vscode-haskell#troubleshooting - -### "Couldn't figure out LLVM version" or "failed to compile a sanity check" errors - -``` -: error: - Warning: Couldn't figure out LLVM version! - Make sure you have installed LLVM between [9 and 13) -ghc: could not execute: opt -``` - -Or - -``` -ld: symbol(s) not found for architecture x86_64 -clang: error: linker command failed with exit code 1 (use -v to see invocation) -`gcc' failed in phase `Linker'. (Exit code: 1) -``` - -Try installing llvm version 12 -`brew install llvm@12` - -and prepend it to your path -``` -export PATH="$(brew --prefix)/opt/llvm@12/bin:$PATH" -``` - -(The GHC version 8.10.7 mentions it supports LLVM versions up to 12. https://www.haskell.org/ghc/download_ghc_8_10_7.html) - -### "GHC ABIs don't match!" - -Follow the steps here: - -https://github.com/haskell/vscode-haskell#ghc-abis-dont-match - -We found some success telling Stack to use the system's GHC instead of managing its own version of GHC. You can try this by setting the following two configuration flags in ~/.stack/config.yaml - -``` -system-ghc: true -install-ghc: false -``` - -This is telling Stack to use the GHC executable that it finds on your $PATH. Make sure the ghc being provided is the proper version, 8.10.7, from ghcup. - -Note that you may need to clean the cache for the project after this failure with `stack clean --full` if you have previously built things with a different stack distribution. - -### "stack" commands like "stack build" cause a segfault: - -1. Make sure your stack state is clean. `stack clean --full` removes the project's stack work directories (things in .stack-work). -2. [Wait for this bug to be fixed (or help fix this bug!)](https://github.com/commercialhaskell/stack/issues/5607) -3. Or subshell out your stack commands `$(stack commandHere)` -4. Or use bash instead of zsh - -### Help! Everything is broken and I want to start over - -Warning, the following will remove ghcup, configuration files, cached packages, and versions of the toolchain. - -``` -ghcup nuke -rm -rf ~/.ghcup -rm -rf ~/.stack -rm -rf ~/.cabal -``` From e903a1c1556c6385ee5be12b0d0d22f858b6e501 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Thu, 13 Jun 2024 08:29:02 -0400 Subject: [PATCH 06/81] add merge.commit failure test --- unison-src/transcripts/merge.md | 23 ++++++++++++++++++++++- unison-src/transcripts/merge.output.md | 21 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/unison-src/transcripts/merge.md b/unison-src/transcripts/merge.md index 1b341fd785..6dd0bdaf91 100644 --- a/unison-src/transcripts/merge.md +++ b/unison-src/transcripts/merge.md @@ -974,7 +974,7 @@ project/alice> merge bob .> project.delete project ``` -## `merge.commit` example +## `merge.commit` example (success) After merge conflicts are resolved, you can use `merge.commit` rather than `switch` + `merge` + `branch.delete` to "commit" your changes. @@ -1040,6 +1040,27 @@ project/alice> branches .> project.delete project ``` +## `merge.commit` example (failure) + +`merge.commit` can only be run on a "merge branch". + +```ucm:hide +.> project.create-empty project +project/main> builtins.mergeio +``` + +```ucm +project/main> branch topic +``` + +```ucm:error +project/topic> merge.commit +``` + +```ucm:hide +.> project.delete project +``` + ## Precondition violations diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index 79d4e373d3..8875bb5cc9 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -1070,7 +1070,7 @@ bob _ = 19 ``` -## `merge.commit` example +## `merge.commit` example (success) After merge conflicts are resolved, you can use `merge.commit` rather than `switch` + `merge` + `branch.delete` to "commit" your changes. @@ -1173,6 +1173,25 @@ project/alice> branches 2. bob 3. main +``` +## `merge.commit` example (failure) + +`merge.commit` can only be run on a "merge branch". + +```ucm +project/main> branch topic + + Done. I've created the topic branch based off of main. + + Tip: To merge your work back into the main branch, first + `switch /main` then `merge /topic`. + +``` +```ucm +project/topic> merge.commit + + It doesn't look like there's a merge in progress. + ``` ## Precondition violations From 84273a556fd170182f9156ad6abfcc14d88b79ad Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Thu, 13 Jun 2024 08:36:03 -0400 Subject: [PATCH 07/81] revert tweak to merge failure output --- .../src/Unison/CommandLine/OutputMessages.hs | 2 +- unison-src/transcripts/merge.output.md | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 370f22f667..bd33365eed 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -2089,7 +2089,7 @@ notifyUser dir = \case "I couldn't automatically merge" <> prettyMergeSource aliceAndBob.bob <> "into" - <> P.group (prettyProjectBranchName aliceAndBob.alice.branch <> ".") + <> P.group (prettyProjectAndBranchName aliceAndBob.alice <> ".") <> "However, I've added the definitions that need attention to the top of" <> P.group (prettyFilePath path <> "."), "", diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index 8875bb5cc9..1ead9f4581 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -506,7 +506,7 @@ project/bob> add project/alice> merge /bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -559,7 +559,7 @@ bar = foo ++ " - " ++ foo ```ucm project/alice> merge /bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -624,7 +624,7 @@ baz = "bobs baz" ```ucm project/alice> merge /bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -693,7 +693,7 @@ unique type Foo = MkFoo Nat Text ```ucm project/alice> merge /bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -742,7 +742,7 @@ unique type Foo = Baz Nat | BobQux Text ```ucm project/alice> merge /bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -795,7 +795,7 @@ project/bob> move.term Foo.Qux Foo.Bob ```ucm project/alice> merge bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -841,7 +841,7 @@ unique ability my.cool where ```ucm project/alice> merge bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -899,7 +899,7 @@ These won't cleanly merge. ```ucm project/alice> merge bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -973,7 +973,7 @@ Notably, Alice's "unconflicted" update on the name "Foo.Bar.Baz" (because she ch ```ucm project/alice> merge bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -1032,7 +1032,7 @@ bob _ = 19 ```ucm project/alice> merge bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. @@ -1099,7 +1099,7 @@ Attempt to merge: ```ucm project/alice> merge /bob - I couldn't automatically merge project/bob into alice. + I couldn't automatically merge project/bob into project/alice. However, I've added the definitions that need attention to the top of scratch.u. From 87f1544ad2c2d188d9d171a71bd78f63ae8bea07 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Thu, 13 Jun 2024 08:47:37 -0400 Subject: [PATCH 08/81] move `todo` input handler into its own module --- .../src/Unison/Codebase/Editor/HandleInput.hs | 85 +------------ .../Codebase/Editor/HandleInput/Todo.hs | 117 ++++++++++++++++++ unison-cli/unison-cli.cabal | 3 +- 3 files changed, 121 insertions(+), 84 deletions(-) create mode 100644 unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index 30d241b40f..c8b66f3a95 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -41,7 +41,6 @@ import Unison.Cli.MonadUtils qualified as Cli import Unison.Cli.NamesUtils qualified as Cli import Unison.Cli.PrettyPrintUtils qualified as Cli import Unison.Cli.ProjectUtils qualified as ProjectUtils -import Unison.Codebase (Codebase) import Unison.Codebase qualified as Codebase import Unison.Codebase.Branch (Branch (..), Branch0) import Unison.Codebase.Branch qualified as Branch @@ -51,7 +50,6 @@ import Unison.Codebase.BranchUtil qualified as BranchUtil import Unison.Codebase.Causal qualified as Causal import Unison.Codebase.Editor.AuthorInfo (AuthorInfo (..)) import Unison.Codebase.Editor.AuthorInfo qualified as AuthorInfo -import Unison.Codebase.Editor.DisplayObject import Unison.Codebase.Editor.HandleInput.AddRun (handleAddRun) import Unison.Codebase.Editor.HandleInput.AuthLogin (authLogin) import Unison.Codebase.Editor.HandleInput.Branch (handleBranch) @@ -77,6 +75,7 @@ import Unison.Codebase.Editor.HandleInput.NamespaceDiffUtils (diffHelper) import Unison.Codebase.Editor.HandleInput.ProjectClone (handleClone) import Unison.Codebase.Editor.HandleInput.ProjectCreate (projectCreate) import Unison.Codebase.Editor.HandleInput.ProjectRename (handleProjectRename) +import Unison.Codebase.Editor.HandleInput.Todo (handleTodo) import Unison.Codebase.Editor.HandleInput.ProjectSwitch (projectSwitch) import Unison.Codebase.Editor.HandleInput.Projects (handleProjects) import Unison.Codebase.Editor.HandleInput.Pull (handlePull, mergeBranchAndPropagateDefaultPatch) @@ -100,11 +99,8 @@ import Unison.Codebase.Editor.RemoteRepo qualified as RemoteRepo import Unison.Codebase.Editor.Slurp qualified as Slurp import Unison.Codebase.Editor.SlurpResult qualified as SlurpResult import Unison.Codebase.Editor.StructuredArgument qualified as SA -import Unison.Codebase.Editor.TodoOutput qualified as TO import Unison.Codebase.IntegrityCheck qualified as IntegrityCheck (integrityCheckFullCodebase) import Unison.Codebase.Metadata qualified as Metadata -import Unison.Codebase.Patch (Patch (..)) -import Unison.Codebase.Patch qualified as Patch import Unison.Codebase.Path (Path, Path' (..)) import Unison.Codebase.Path qualified as HQSplit' import Unison.Codebase.Path qualified as Path @@ -181,7 +177,6 @@ import Unison.Util.Relation qualified as R import Unison.Util.Relation qualified as Relation import Unison.Util.Set qualified as Set import Unison.Util.Star2 qualified as Star2 -import Unison.Util.TransitiveClosure (transitiveClosure) import Unison.Var (Var) import Unison.Var qualified as Var import Unison.WatchKind qualified as WK @@ -742,10 +737,7 @@ loop e = do 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' - doShowTodoOutput patch branchPath + TodoI patchPath branchPath -> handleTodo patchPath branchPath TestI testInput -> Tests.handleTest testInput ExecuteI main args -> handleRun False main args MakeStandaloneI output main -> doCompile False output main @@ -1430,58 +1422,6 @@ doDisplay outputLoc names tm = do else do writeUtf8 filePath txt --- | Show todo output if there are any conflicts or edits. -doShowTodoOutput :: Patch -> Path.Absolute -> Cli () -doShowTodoOutput patch scopePath = do - Cli.Env {codebase} <- ask - names0 <- Branch.toNames <$> Cli.getBranch0At scopePath - todo <- Cli.runTransaction (checkTodo codebase patch names0) - if TO.noConflicts todo && TO.noEdits todo - then Cli.respond NoConflictsOrEdits - else do - Cli.setNumberedArgs $ - SA.HashQualified . HQ.HashOnly . Reference.toShortHash . view _2 - <$> fst (TO.todoFrontierDependents todo) - pped <- Cli.currentPrettyPrintEnvDecl - Cli.respondNumbered $ TodoOutput pped todo - -checkTodo :: Codebase m Symbol Ann -> Patch -> Names -> Sqlite.Transaction (TO.TodoOutput Symbol Ann) -checkTodo codebase patch names0 = do - let -- Get the dependents of a reference which: - -- 1. Don't appear on the LHS of this patch - -- 2. Have a name in this namespace - getDependents :: Reference -> Sqlite.Transaction (Set Reference) - getDependents ref = do - dependents <- Codebase.dependents Queries.ExcludeSelf ref - pure (dependents & removeEditedThings & removeNamelessThings) - -- (r,r2) ∈ dependsOn if r depends on r2, excluding self-references (i.e. (r,r)) - dependsOn <- Monoid.foldMapM (\ref -> R.fromManyDom <$> getDependents ref <*> pure ref) edited - let dirty = R.dom dependsOn - transitiveDirty <- transitiveClosure getDependents dirty - (frontierTerms, frontierTypes) <- loadDisplayInfo codebase (R.ran dependsOn) - (dirtyTerms, dirtyTypes) <- loadDisplayInfo codebase dirty - pure $ - TO.TodoOutput - (Set.size transitiveDirty) - (frontierTerms, frontierTypes) - (score dirtyTerms, score dirtyTypes) - (Names.conflicts names0) - (Patch.conflicts patch) - where - -- Remove from a all references that were edited, i.e. appear on the LHS of this patch. - removeEditedThings :: Set Reference -> Set Reference - removeEditedThings = - (`Set.difference` edited) - -- Remove all references that don't have a name in the given namespace - removeNamelessThings :: Set Reference -> Set Reference - removeNamelessThings = - Set.filter (Names.contains names0) - -- todo: something more intelligent here? - score :: [(a, b)] -> [(TO.Score, a, b)] - score = map (\(x, y) -> (1, x, y)) - edited :: Set Reference - edited = R.dom (Patch._termEdits patch) <> R.dom (Patch._typeEdits patch) - confirmedCommand :: Input -> Cli Bool confirmedCommand i = do loopState <- State.get @@ -1780,27 +1720,6 @@ docsI src = do displayI ConsoleLocation (Names.longestTermName 10 (Set.findMin s) namesInFile) _ -> displayI ConsoleLocation dotDoc -loadDisplayInfo :: - Codebase m Symbol Ann -> - Set Reference -> - Sqlite.Transaction - ( [(Reference, Maybe (Type Symbol Ann))], - [(Reference, DisplayObject () (DD.Decl Symbol Ann))] - ) -loadDisplayInfo codebase refs = do - termRefs <- filterM (Codebase.isTerm codebase) (toList refs) - typeRefs <- filterM (Codebase.isType codebase) (toList refs) - terms <- forM termRefs $ \r -> (r,) <$> Codebase.getTypeOfTerm codebase r - types <- forM typeRefs $ \r -> (r,) <$> loadTypeDisplayObject codebase r - pure (terms, types) - -loadTypeDisplayObject :: Codebase m Symbol Ann -> Reference -> Sqlite.Transaction (DisplayObject () (DD.Decl Symbol Ann)) -loadTypeDisplayObject codebase = \case - Reference.Builtin _ -> pure (BuiltinObject ()) - Reference.DerivedId id -> - maybe (MissingObject $ Reference.idToShortHash id) UserObject - <$> Codebase.getTypeDeclaration codebase id - lexedSource :: Text -> Text -> Cli (Text, [L.Token L.Lexeme]) lexedSource name src = do let tokens = L.lexer (Text.unpack name) (Text.unpack src) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs new file mode 100644 index 0000000000..a34d48bc4f --- /dev/null +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -0,0 +1,117 @@ +-- | @todo@ input handler +module Unison.Codebase.Editor.HandleInput.Todo + ( handleTodo, + ) +where + +import Control.Lens hiding (from) +import Control.Monad.Reader (ask) +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.MonadUtils qualified as Cli +import Unison.Cli.PrettyPrintUtils qualified as Cli +import Unison.Codebase (Codebase) +import Unison.Codebase qualified as Codebase +import Unison.Codebase.Branch.Names qualified as Branch +import Unison.Codebase.Editor.DisplayObject +import Unison.Codebase.Editor.Output +import Unison.Codebase.Editor.StructuredArgument qualified as SA +import Unison.Codebase.Editor.TodoOutput qualified as TO +import Unison.Codebase.Patch (Patch (..)) +import Unison.Codebase.Patch qualified as Patch +import Unison.Codebase.Path qualified as Path +import Unison.DataDeclaration qualified as DD +import Unison.HashQualified qualified as HQ +import Unison.Names (Names) +import Unison.Names qualified as Names +import Unison.Parser.Ann (Ann (..)) +import Unison.Prelude +import Unison.Reference (Reference) +import Unison.Reference qualified as Reference +import Unison.Sqlite qualified as Sqlite +import Unison.Symbol (Symbol) +import Unison.Type (Type) +import Unison.Util.Monoid qualified as Monoid +import Unison.Util.Relation qualified as R +import Unison.Util.TransitiveClosure (transitiveClosure) + +handleTodo :: Maybe Path.Split' -> Path.Path' -> Cli () +handleTodo patchPath branchPath' = do + patch <- Cli.getPatchAt (fromMaybe Cli.defaultPatchPath patchPath) + branchPath <- Cli.resolvePath' branchPath' + doShowTodoOutput patch branchPath + +-- | Show todo output if there are any conflicts or edits. +doShowTodoOutput :: Patch -> Path.Absolute -> Cli () +doShowTodoOutput patch scopePath = do + Cli.Env {codebase} <- ask + names0 <- Branch.toNames <$> Cli.getBranch0At scopePath + todo <- Cli.runTransaction (checkTodo codebase patch names0) + if TO.noConflicts todo && TO.noEdits todo + then Cli.respond NoConflictsOrEdits + else do + Cli.setNumberedArgs $ + SA.HashQualified . HQ.HashOnly . Reference.toShortHash . view _2 + <$> fst (TO.todoFrontierDependents todo) + pped <- Cli.currentPrettyPrintEnvDecl + Cli.respondNumbered $ TodoOutput pped todo + +checkTodo :: Codebase m Symbol Ann -> Patch -> Names -> Sqlite.Transaction (TO.TodoOutput Symbol Ann) +checkTodo codebase patch names0 = do + let -- Get the dependents of a reference which: + -- 1. Don't appear on the LHS of this patch + -- 2. Have a name in this namespace + getDependents :: Reference -> Sqlite.Transaction (Set Reference) + getDependents ref = do + dependents <- Codebase.dependents Queries.ExcludeSelf ref + pure (dependents & removeEditedThings & removeNamelessThings) + -- (r,r2) ∈ dependsOn if r depends on r2, excluding self-references (i.e. (r,r)) + dependsOn <- Monoid.foldMapM (\ref -> R.fromManyDom <$> getDependents ref <*> pure ref) edited + let dirty = R.dom dependsOn + transitiveDirty <- transitiveClosure getDependents dirty + (frontierTerms, frontierTypes) <- loadDisplayInfo codebase (R.ran dependsOn) + (dirtyTerms, dirtyTypes) <- loadDisplayInfo codebase dirty + pure $ + TO.TodoOutput + (Set.size transitiveDirty) + (frontierTerms, frontierTypes) + (score dirtyTerms, score dirtyTypes) + (Names.conflicts names0) + (Patch.conflicts patch) + where + -- Remove from a all references that were edited, i.e. appear on the LHS of this patch. + removeEditedThings :: Set Reference -> Set Reference + removeEditedThings = + (`Set.difference` edited) + -- Remove all references that don't have a name in the given namespace + removeNamelessThings :: Set Reference -> Set Reference + removeNamelessThings = + Set.filter (Names.contains names0) + -- todo: something more intelligent here? + score :: [(a, b)] -> [(TO.Score, a, b)] + score = map (\(x, y) -> (1, x, y)) + edited :: Set Reference + edited = R.dom (Patch._termEdits patch) <> R.dom (Patch._typeEdits patch) + +loadDisplayInfo :: + Codebase m Symbol Ann -> + Set Reference -> + Sqlite.Transaction + ( [(Reference, Maybe (Type Symbol Ann))], + [(Reference, DisplayObject () (DD.Decl Symbol Ann))] + ) +loadDisplayInfo codebase refs = do + termRefs <- filterM (Codebase.isTerm codebase) (toList refs) + typeRefs <- filterM (Codebase.isType codebase) (toList refs) + terms <- forM termRefs $ \r -> (r,) <$> Codebase.getTypeOfTerm codebase r + types <- forM typeRefs $ \r -> (r,) <$> loadTypeDisplayObject codebase r + pure (terms, types) + +loadTypeDisplayObject :: Codebase m Symbol Ann -> Reference -> Sqlite.Transaction (DisplayObject () (DD.Decl Symbol Ann)) +loadTypeDisplayObject codebase = \case + Reference.Builtin _ -> pure (BuiltinObject ()) + Reference.DerivedId id -> + maybe (MissingObject $ Reference.idToShortHash id) UserObject + <$> Codebase.getTypeDeclaration codebase id diff --git a/unison-cli/unison-cli.cabal b/unison-cli/unison-cli.cabal index 403d2f7e73..de8288ccce 100644 --- a/unison-cli/unison-cli.cabal +++ b/unison-cli/unison-cli.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2. +-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack @@ -85,6 +85,7 @@ library Unison.Codebase.Editor.HandleInput.ShowDefinition Unison.Codebase.Editor.HandleInput.TermResolution Unison.Codebase.Editor.HandleInput.Tests + Unison.Codebase.Editor.HandleInput.Todo Unison.Codebase.Editor.HandleInput.UI Unison.Codebase.Editor.HandleInput.Update Unison.Codebase.Editor.HandleInput.Update2 From 6e04dbd08302322d7629c2ad08eb122fb3404dca Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Thu, 13 Jun 2024 10:29:42 -0400 Subject: [PATCH 09/81] gut existing todo implementation --- .../src/Unison/Codebase/Editor/HandleInput.hs | 2 +- .../Codebase/Editor/HandleInput/Todo.hs | 112 +------ .../src/Unison/Codebase/Editor/Input.hs | 2 +- .../src/Unison/Codebase/Editor/Output.hs | 5 +- .../src/Unison/Codebase/Editor/TodoOutput.hs | 69 +---- .../src/Unison/CommandLine/InputPatterns.hs | 29 +- .../src/Unison/CommandLine/OutputMessages.hs | 151 +-------- unison-src/transcripts/fix2000.output.md | 4 - unison-src/transcripts/fix2254.output.md | 4 - unison-src/transcripts/todo.md | 139 --------- unison-src/transcripts/todo.output.md | 292 ------------------ 11 files changed, 32 insertions(+), 777 deletions(-) delete mode 100644 unison-src/transcripts/todo.md delete mode 100644 unison-src/transcripts/todo.output.md diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index c8b66f3a95..5064159af6 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -737,7 +737,7 @@ loop e = do currentNames <- Branch.toNames <$> Cli.getCurrentBranch0 let sr = Slurp.slurpFile uf vars Slurp.UpdateOp currentNames previewResponse sourceName sr uf - TodoI patchPath branchPath -> handleTodo patchPath branchPath + TodoI -> handleTodo TestI testInput -> Tests.handleTest testInput ExecuteI main args -> handleRun False main args MakeStandaloneI output main -> doCompile False output main diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs index a34d48bc4f..4cfe62d3b8 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -4,114 +4,22 @@ module Unison.Codebase.Editor.HandleInput.Todo ) where -import Control.Lens hiding (from) -import Control.Monad.Reader (ask) -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.MonadUtils qualified as Cli import Unison.Cli.PrettyPrintUtils qualified as Cli -import Unison.Codebase (Codebase) -import Unison.Codebase qualified as Codebase import Unison.Codebase.Branch.Names qualified as Branch -import Unison.Codebase.Editor.DisplayObject import Unison.Codebase.Editor.Output -import Unison.Codebase.Editor.StructuredArgument qualified as SA import Unison.Codebase.Editor.TodoOutput qualified as TO -import Unison.Codebase.Patch (Patch (..)) -import Unison.Codebase.Patch qualified as Patch -import Unison.Codebase.Path qualified as Path -import Unison.DataDeclaration qualified as DD -import Unison.HashQualified qualified as HQ -import Unison.Names (Names) import Unison.Names qualified as Names -import Unison.Parser.Ann (Ann (..)) -import Unison.Prelude -import Unison.Reference (Reference) -import Unison.Reference qualified as Reference -import Unison.Sqlite qualified as Sqlite -import Unison.Symbol (Symbol) -import Unison.Type (Type) -import Unison.Util.Monoid qualified as Monoid -import Unison.Util.Relation qualified as R -import Unison.Util.TransitiveClosure (transitiveClosure) -handleTodo :: Maybe Path.Split' -> Path.Path' -> Cli () -handleTodo patchPath branchPath' = do - patch <- Cli.getPatchAt (fromMaybe Cli.defaultPatchPath patchPath) - branchPath <- Cli.resolvePath' branchPath' - doShowTodoOutput patch branchPath - --- | Show todo output if there are any conflicts or edits. -doShowTodoOutput :: Patch -> Path.Absolute -> Cli () -doShowTodoOutput patch scopePath = do - Cli.Env {codebase} <- ask - names0 <- Branch.toNames <$> Cli.getBranch0At scopePath - todo <- Cli.runTransaction (checkTodo codebase patch names0) - if TO.noConflicts todo && TO.noEdits todo - then Cli.respond NoConflictsOrEdits - else do - Cli.setNumberedArgs $ - SA.HashQualified . HQ.HashOnly . Reference.toShortHash . view _2 - <$> fst (TO.todoFrontierDependents todo) - pped <- Cli.currentPrettyPrintEnvDecl - Cli.respondNumbered $ TodoOutput pped todo - -checkTodo :: Codebase m Symbol Ann -> Patch -> Names -> Sqlite.Transaction (TO.TodoOutput Symbol Ann) -checkTodo codebase patch names0 = do - let -- Get the dependents of a reference which: - -- 1. Don't appear on the LHS of this patch - -- 2. Have a name in this namespace - getDependents :: Reference -> Sqlite.Transaction (Set Reference) - getDependents ref = do - dependents <- Codebase.dependents Queries.ExcludeSelf ref - pure (dependents & removeEditedThings & removeNamelessThings) - -- (r,r2) ∈ dependsOn if r depends on r2, excluding self-references (i.e. (r,r)) - dependsOn <- Monoid.foldMapM (\ref -> R.fromManyDom <$> getDependents ref <*> pure ref) edited - let dirty = R.dom dependsOn - transitiveDirty <- transitiveClosure getDependents dirty - (frontierTerms, frontierTypes) <- loadDisplayInfo codebase (R.ran dependsOn) - (dirtyTerms, dirtyTypes) <- loadDisplayInfo codebase dirty - pure $ - TO.TodoOutput - (Set.size transitiveDirty) - (frontierTerms, frontierTypes) - (score dirtyTerms, score dirtyTypes) - (Names.conflicts names0) - (Patch.conflicts patch) - where - -- Remove from a all references that were edited, i.e. appear on the LHS of this patch. - removeEditedThings :: Set Reference -> Set Reference - removeEditedThings = - (`Set.difference` edited) - -- Remove all references that don't have a name in the given namespace - removeNamelessThings :: Set Reference -> Set Reference - removeNamelessThings = - Set.filter (Names.contains names0) - -- todo: something more intelligent here? - score :: [(a, b)] -> [(TO.Score, a, b)] - score = map (\(x, y) -> (1, x, y)) - edited :: Set Reference - edited = R.dom (Patch._termEdits patch) <> R.dom (Patch._typeEdits patch) - -loadDisplayInfo :: - Codebase m Symbol Ann -> - Set Reference -> - Sqlite.Transaction - ( [(Reference, Maybe (Type Symbol Ann))], - [(Reference, DisplayObject () (DD.Decl Symbol Ann))] - ) -loadDisplayInfo codebase refs = do - termRefs <- filterM (Codebase.isTerm codebase) (toList refs) - typeRefs <- filterM (Codebase.isType codebase) (toList refs) - terms <- forM termRefs $ \r -> (r,) <$> Codebase.getTypeOfTerm codebase r - types <- forM typeRefs $ \r -> (r,) <$> loadTypeDisplayObject codebase r - pure (terms, types) - -loadTypeDisplayObject :: Codebase m Symbol Ann -> Reference -> Sqlite.Transaction (DisplayObject () (DD.Decl Symbol Ann)) -loadTypeDisplayObject codebase = \case - Reference.Builtin _ -> pure (BuiltinObject ()) - Reference.DerivedId id -> - maybe (MissingObject $ Reference.idToShortHash id) UserObject - <$> Codebase.getTypeDeclaration codebase id +handleTodo :: Cli () +handleTodo = do + branch0 <- Cli.getCurrentBranch0 + let names0 = Branch.toNames branch0 + let todo = + TO.TodoOutput + { nameConflicts = Names.conflicts names0 + } + pped <- Cli.currentPrettyPrintEnvDecl + Cli.respondNumbered $ TodoOutput pped todo diff --git a/unison-cli/src/Unison/Codebase/Editor/Input.hs b/unison-cli/src/Unison/Codebase/Editor/Input.hs index a29d2ac660..4df4338122 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Input.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Input.hs @@ -150,7 +150,7 @@ data Input | UpdateI OptionalPatch (Set Name) | Update2I | PreviewUpdateI (Set Name) - | TodoI (Maybe PatchPath) Path' + | TodoI | UndoI | -- First `Maybe Int` is cap on number of results, if any -- Second `Maybe Int` is cap on diff elements shown, if any diff --git a/unison-cli/src/Unison/Codebase/Editor/Output.hs b/unison-cli/src/Unison/Codebase/Editor/Output.hs index e6b5608e26..027d95ceb0 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Output.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Output.hs @@ -293,8 +293,6 @@ data Output | PreviewMergeAlreadyUpToDate (Either Path' (ProjectAndBranch Sqlite.Project Sqlite.ProjectBranch)) (Either Path' (ProjectAndBranch Sqlite.Project Sqlite.ProjectBranch)) - | -- | No conflicts or edits remain for the current patch. - NoConflictsOrEdits | NotImplemented | NoBranchWithHash ShortCausalHash | ListDependencies PPE.PrettyPrintEnv (Set LabeledDependency) [HQ.HashQualified Name] [HQ.HashQualified Name] -- types, terms @@ -554,7 +552,6 @@ isFailure o = case o of MergeAlreadyUpToDate {} -> False MergeAlreadyUpToDate2 {} -> False PreviewMergeAlreadyUpToDate {} -> False - NoConflictsOrEdits {} -> False ListShallow _ es -> null es HashAmbiguous {} -> True ShowReflog {} -> False @@ -669,4 +666,4 @@ isNumberedFailure = \case ShowDiffAfterUndo {} -> False ShowDiffNamespace _ _ _ bd -> BD.isEmpty bd ListNamespaceDependencies {} -> False - TodoOutput _ todo -> TO.todoScore todo > 0 || not (TO.noConflicts todo) + TodoOutput {} -> False diff --git a/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs b/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs index f6458ca57b..e4c859a9f1 100644 --- a/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs @@ -1,70 +1,15 @@ -{-# LANGUAGE RecordWildCards #-} +module Unison.Codebase.Editor.TodoOutput + ( TodoOutput (..), + noConflicts, + ) +where -module Unison.Codebase.Editor.TodoOutput where - -import Data.Set qualified as Set -import Unison.Codebase.Editor.DisplayObject (DisplayObject (UserObject)) -import Unison.Codebase.Patch (Patch) -import Unison.Codebase.Patch qualified as Patch -import Unison.DataDeclaration (Decl) -import Unison.DataDeclaration qualified as DD -import Unison.LabeledDependency (LabeledDependency) -import Unison.LabeledDependency qualified as LD import Unison.Names (Names) -import Unison.Names qualified as Names -import Unison.Prelude -import Unison.Reference (Reference) -import Unison.Type (Type) -import Unison.Type qualified as Type -import Unison.Util.Relation qualified as R - -type Score = Int data TodoOutput v a = TodoOutput - { todoScore :: Score, - todoFrontier :: - ( [(Reference, Maybe (Type v a))], - [(Reference, DisplayObject () (Decl v a))] - ), - todoFrontierDependents :: - ( [(Score, Reference, Maybe (Type v a))], - [(Score, Reference, DisplayObject () (Decl v a))] - ), - nameConflicts :: Names, - editConflicts :: Patch + { nameConflicts :: Names } -labeledDependencies :: (Ord v) => TodoOutput v a -> Set LabeledDependency -labeledDependencies TodoOutput {..} = - Set.fromList - ( -- term refs - [LD.termRef r | (r, _) <- fst todoFrontier] - <> [LD.termRef r | (_, r, _) <- fst todoFrontierDependents] - <> [LD.typeRef r | (r, _) <- snd todoFrontier] - <> [LD.typeRef r | (_, r, _) <- snd todoFrontierDependents] - <> - -- types of term refs - [ LD.typeRef r | (_, Just t) <- fst todoFrontier, r <- toList (Type.dependencies t) - ] - <> [ LD.typeRef r | (_, _, Just t) <- fst todoFrontierDependents, r <- toList (Type.dependencies t) - ] - <> - -- and decls of type refs - [ labeledDep | (declRef, UserObject d) <- snd todoFrontier, labeledDep <- toList (DD.labeledDeclDependenciesIncludingSelf declRef d) - ] - <> [ labeledDep | (_, declRef, UserObject d) <- snd todoFrontierDependents, labeledDep <- toList (DD.labeledDeclDependenciesIncludingSelf declRef d) - ] - ) - <> - -- name conflicts - Set.map LD.referent (R.ran (Names.terms nameConflicts)) - <> Set.map LD.typeRef (R.ran (Names.types nameConflicts)) - <> Patch.labeledDependencies editConflicts - noConflicts :: TodoOutput v a -> Bool noConflicts todo = - nameConflicts todo == mempty && editConflicts todo == Patch.empty - -noEdits :: TodoOutput v a -> Bool -noEdits todo = - todoScore todo == 0 + nameConflicts todo == mempty diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index e56b5c8ace..aa49f968ea 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -756,30 +756,15 @@ todo = "todo" [] I.Visible - [("patch", Optional, patchArg), ("namespace", Optional, namespaceArg)] - ( P.wrapColumn2 - [ ( makeExample' todo, - "lists the refactor work remaining in the default patch for the current" - <> " namespace." - ), - ( makeExample todo [""], - "lists the refactor work remaining in the given patch in the current " - <> "namespace." - ), - ( makeExample todo ["", "[path]"], - "lists the refactor work remaining in the given patch in given namespace." - ) - ] + [] + ( P.wrap $ + makeExample' todo + <> "lists the current namespace's outstanding issues, including conflicted names, dependencies with missing" + <> "names, and merge precondition violations." ) \case - patchStr : ws -> first warn $ do - patch <- handleSplit'Arg patchStr - branch <- case ws of - [] -> pure Path.relativeEmpty' - [pathStr] -> handlePath'Arg pathStr - _ -> Left "`todo` just takes a patch and one optional namespace" - Right $ Input.TodoI (Just patch) branch - [] -> Right $ Input.TodoI Nothing Path.relativeEmpty' + [] -> Right Input.TodoI + _ -> Left (I.help todo) load :: InputPattern load = diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 76355438a9..20ff1a4f53 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -65,7 +65,6 @@ import Unison.Codebase.Editor.StructuredArgument (StructuredArgument) import Unison.Codebase.Editor.StructuredArgument qualified as SA import Unison.Codebase.Editor.TodoOutput qualified as TO import Unison.Codebase.IntegrityCheck (IntegrityResult (..), prettyPrintIntegrityErrors) -import Unison.Codebase.Patch (Patch (..)) import Unison.Codebase.Patch qualified as Patch import Unison.Codebase.Path qualified as Path import Unison.Codebase.PushBehavior qualified as PushBehavior @@ -73,8 +72,6 @@ import Unison.Codebase.Runtime qualified as Runtime import Unison.Codebase.ShortCausalHash (ShortCausalHash) import Unison.Codebase.ShortCausalHash qualified as SCH import Unison.Codebase.SqliteCodebase.Conversions qualified as Cv -import Unison.Codebase.TermEdit qualified as TermEdit -import Unison.Codebase.TypeEdit qualified as TypeEdit import Unison.CommandLine (bigproblem, note, tip) import Unison.CommandLine.FZFResolvers qualified as FZFResolvers import Unison.CommandLine.InputPattern (InputPattern) @@ -137,7 +134,6 @@ import Unison.Syntax.NamePrinter prettyReference, prettyReferent, prettyShortHash, - styleHashQualified, ) import Unison.Syntax.NameSegment qualified as NameSegment import Unison.Syntax.TermPrinter qualified as TermPrinter @@ -1483,8 +1479,6 @@ notifyUser dir = \case <> P.group (prettyNamespaceKey src <> ".") DumpNumberedArgs schLength args -> pure . P.numberedList $ fmap (P.text . IP.formatStructuredArgument (pure schLength)) args - NoConflictsOrEdits -> - pure (P.okCallout "No conflicts or edits in progress.") HelpMessage pat -> pure $ IP.showPatternHelp pat NoOp -> pure $ P.string "I didn't make any changes." DumpBitBooster head map -> @@ -2628,66 +2622,6 @@ renderNameConflicts ppe conflictedNames = do ) `P.hang` P.lines prettyConflicts -renderEditConflicts :: - PPE.PrettyPrintEnv -> Patch -> Numbered Pretty -renderEditConflicts ppe Patch {..} = do - formattedConflicts <- for editConflicts formatConflict - pure . Monoid.unlessM (null editConflicts) . P.callout "❓" . P.sep "\n\n" $ - [ P.wrap $ - "These" - <> P.bold "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:", - P.indentN 2 (P.lines formattedConflicts) - -- , tip $ "Use " <> makeExample IP.resolve [name (head editConflicts), " "] <> " to pick a replacement." -- todo: eventually something with `edit` - ] - where - -- todo: could possibly simplify all of this, but today is a copy/paste day. - editConflicts :: [Either (Reference, Set TypeEdit.TypeEdit) (Reference, Set TermEdit.TermEdit)] - editConflicts = - (fmap Left . Map.toList . R.toMultimap . R.filterManyDom $ _typeEdits) - <> (fmap Right . Map.toList . R.toMultimap . R.filterManyDom $ _termEdits) - numberedHQName :: HQ.HashQualified Name -> Numbered Pretty - numberedHQName hqName = do - n <- addNumberedArg $ SA.HashQualified hqName - pure $ formatNum n <> styleHashQualified P.bold hqName - formatTypeEdits :: - (Reference, Set TypeEdit.TypeEdit) -> - Numbered Pretty - formatTypeEdits (r, toList -> es) = do - replacedType <- numberedHQName (PPE.typeName ppe r) - replacements <- for [PPE.typeName ppe r | TypeEdit.Replace r <- es] numberedHQName - pure . P.wrap $ - "The type" - <> replacedType - <> "was" - <> ( if TypeEdit.Deprecate `elem` es - then "deprecated and also replaced with" - else "replaced with" - ) - `P.hang` P.lines replacements - formatTermEdits :: - (Reference.TermReference, Set TermEdit.TermEdit) -> - Numbered Pretty - formatTermEdits (r, toList -> es) = do - replacedTerm <- numberedHQName (PPE.termName ppe (Referent.Ref r)) - replacements <- for [PPE.termName ppe (Referent.Ref r) | TermEdit.Replace r _ <- es] numberedHQName - pure . P.wrap $ - "The term" - <> replacedTerm - <> "was" - <> ( if TermEdit.Deprecate `elem` es - then "deprecated and also replaced with" - else "replaced with" - ) - `P.hang` P.lines replacements - formatConflict :: - Either - (Reference, Set TypeEdit.TypeEdit) - (Reference.TermReference, Set TermEdit.TermEdit) -> - Numbered Pretty - formatConflict = either formatTypeEdits formatTermEdits - type Numbered = State.State (Int, Seq.Seq StructuredArgument) addNumberedArg :: StructuredArgument -> Numbered Int @@ -2706,88 +2640,13 @@ runNumbered m = todoOutput :: (Var v) => PPED.PrettyPrintEnvDecl -> TO.TodoOutput v a -> (Pretty, NumberedArgs) todoOutput ppe todo = runNumbered do - conflicts <- todoConflicts - edits <- todoEdits - pure (conflicts <> edits) + if TO.noConflicts todo + then pure mempty + else do + nameConflicts <- renderNameConflicts ppeu (TO.nameConflicts todo) + pure $ P.lines $ P.nonEmpty [nameConflicts] where ppeu = PPED.unsuffixifiedPPE ppe - ppes = PPED.suffixifiedPPE ppe - (frontierTerms, frontierTypes) = TO.todoFrontier todo - (dirtyTerms, dirtyTypes) = TO.todoFrontierDependents todo - corruptTerms = - [(PPE.termName ppeu (Referent.Ref r), r) | (r, Nothing) <- frontierTerms] - corruptTypes = - [(PPE.typeName ppeu r, r) | (r, MissingObject _) <- frontierTypes] - goodTerms ts = - [(Referent.Ref r, PPE.termName ppeu (Referent.Ref r), typ) | (r, Just typ) <- ts] - todoConflicts :: Numbered Pretty - todoConflicts = do - if TO.noConflicts todo - then pure mempty - else do - editConflicts <- renderEditConflicts ppeu (TO.editConflicts todo) - nameConflicts <- renderNameConflicts ppeu conflictedNames - pure $ P.lines . P.nonEmpty $ [editConflicts, nameConflicts] - where - -- If a conflict is both an edit and a name conflict, we show it in the edit - -- conflicts section - conflictedNames :: Names - conflictedNames = removeEditConflicts (TO.editConflicts todo) (TO.nameConflicts todo) - -- e.g. `foo#a` has been independently updated to `foo#b` and `foo#c`. - -- This means there will be a name conflict: - -- foo -> #b - -- foo -> #c - -- as well as an edit conflict: - -- #a -> #b - -- #a -> #c - -- We want to hide/ignore the name conflicts that are also targets of an - -- edit conflict, so that the edit conflict will be dealt with first. - -- For example, if hash `h` has multiple edit targets { #x, #y, #z, ...}, - -- we'll temporarily remove name conflicts pointing to { #x, #y, #z, ...}. - removeEditConflicts :: Patch -> Names -> Names - removeEditConflicts Patch {..} Names {..} = Names terms' types' - where - terms' = R.filterRan (`Set.notMember` conflictedTermEditTargets) terms - types' = R.filterRan (`Set.notMember` conflictedTypeEditTargets) types - conflictedTypeEditTargets :: Set Reference - conflictedTypeEditTargets = - Set.fromList $ toList (R.ran typeEditConflicts) >>= TypeEdit.references - conflictedTermEditTargets :: Set Referent.Referent - conflictedTermEditTargets = - Set.fromList . fmap Referent.Ref $ - toList (R.ran termEditConflicts) >>= TermEdit.references - typeEditConflicts = R.filterDom (`R.manyDom` _typeEdits) _typeEdits - termEditConflicts = R.filterDom (`R.manyDom` _termEdits) _termEdits - - todoEdits :: Numbered Pretty - todoEdits = do - numberedTypes <- for (unscore <$> dirtyTypes) \(ref, displayObj) -> do - n <- addNumberedArg . SA.HashQualified $ PPE.typeName ppeu ref - pure $ formatNum n <> prettyDeclPair ppeu (ref, displayObj) - let filteredTerms = goodTerms (unscore <$> dirtyTerms) - termNumbers <- for filteredTerms \(ref, _, _) -> do - n <- addNumberedArg . SA.HashQualified $ PPE.termName ppeu ref - pure $ formatNum n - let formattedTerms = TypePrinter.prettySignaturesCT ppes filteredTerms - numberedTerms = zipWith (<>) termNumbers formattedTerms - pure $ - Monoid.unlessM (TO.noEdits todo) . P.callout "🚧" . P.sep "\n\n" . P.nonEmpty $ - [ P.wrap - ( "The namespace has" - <> fromString (show (TO.todoScore todo)) - <> "transitive dependent(s) left to upgrade." - <> "Your edit frontier is the dependents of these definitions:" - ), - P.indentN 2 . P.lines $ - ( (prettyDeclPair ppeu <$> toList frontierTypes) - ++ TypePrinter.prettySignaturesCT ppes (goodTerms frontierTerms) - ), - P.wrap "I recommend working on them in the following order:", - P.lines $ numberedTypes ++ numberedTerms, - formatMissingStuff corruptTerms corruptTypes - ] - unscore :: (a, b, c) -> (b, c) - unscore (_score, b, c) = (b, c) listOfDefinitions :: (Var v) => Input.FindScope -> PPE.PrettyPrintEnv -> E.ListDetailed -> [SR'.SearchResult' v a] -> IO Pretty diff --git a/unison-src/transcripts/fix2000.output.md b/unison-src/transcripts/fix2000.output.md index 84a674b1d7..cd388f7e55 100644 --- a/unison-src/transcripts/fix2000.output.md +++ b/unison-src/transcripts/fix2000.output.md @@ -179,14 +179,10 @@ Merge back into the ancestor. .s> todo - ✅ - No conflicts or edits in progress. .m> todo - ✅ - No conflicts or edits in progress. ``` diff --git a/unison-src/transcripts/fix2254.output.md b/unison-src/transcripts/fix2254.output.md index 61af269b2c..dbbdf46852 100644 --- a/unison-src/transcripts/fix2254.output.md +++ b/unison-src/transcripts/fix2254.output.md @@ -106,9 +106,7 @@ Let's do the update now, and verify that the definitions all look good and there .a2> todo - ✅ - No conflicts or edits in progress. ``` ## Record updates @@ -213,8 +211,6 @@ And checking that after updating this record, there's nothing `todo`: .a4> todo - ✅ - No conflicts or edits in progress. ``` diff --git a/unison-src/transcripts/todo.md b/unison-src/transcripts/todo.md deleted file mode 100644 index 39fece2f61..0000000000 --- a/unison-src/transcripts/todo.md +++ /dev/null @@ -1,139 +0,0 @@ -# Test the `todo` command - -## Simple type-changing update. - -```ucm:hide -.simple> builtins.merge -``` - -```unison:hide -x = 1 -useX = x + 10 - -type MyType = MyType Nat -useMyType = match MyType 1 with - MyType a -> a + 10 -``` - -```ucm:hide -.simple> add -``` - -Perform a type-changing update so dependents are added to our update frontier. - -```unison:hide -x = -1 - -type MyType = MyType Text -``` - -```ucm:error -.simple> update.old -.simple> todo -``` - -## A merge with conflicting updates. - -```ucm:hide -.mergeA> builtins.merge -``` - -```unison:hide -x = 1 -type MyType = MyType -``` - -Set up two branches with the same starting point. - -```ucm:hide -.mergeA> add -.> fork .mergeA .mergeB -``` - -Update `x` to a different term in each branch. - -```unison:hide -x = 2 -type MyType = MyType Nat -``` - -```ucm:hide -.mergeA> update.old -``` - -```unison:hide -x = 3 -type MyType = MyType Int -``` - -```ucm:hide -.mergeB> update.old -``` - -```ucm:error -.mergeA> merge.old .mergeB -.mergeA> todo -``` - -## A named value that appears on the LHS of a patch isn't shown - -```ucm:hide -.lhs> builtins.merge -``` - -```unison -foo = 801 -``` - -```ucm -.lhs> add -``` - -```unison -foo = 802 -``` - -```ucm -.lhs> update.old -``` - -```unison -oldfoo = 801 -``` - -```ucm -.lhs> add -.lhs> todo -``` - -## A type-changing update to one element of a cycle, which doesn't propagate to the other - -```ucm:hide -.cycle2> builtins.merge -``` - -```unison -even = cases - 0 -> true - n -> odd (drop 1 n) - -odd = cases - 0 -> false - n -> even (drop 1 n) -``` - -```ucm -.cycle2> add -``` - -```unison -even = 17 -``` - -```ucm -.cycle2> update.old -``` - -```ucm:error -.cycle2> todo -``` diff --git a/unison-src/transcripts/todo.output.md b/unison-src/transcripts/todo.output.md deleted file mode 100644 index b0a9d69c6d..0000000000 --- a/unison-src/transcripts/todo.output.md +++ /dev/null @@ -1,292 +0,0 @@ -# Test the `todo` command - -## Simple type-changing update. - -```unison -x = 1 -useX = x + 10 - -type MyType = MyType Nat -useMyType = match MyType 1 with - MyType a -> a + 10 -``` - -Perform a type-changing update so dependents are added to our update frontier. - -```unison -x = -1 - -type MyType = MyType Text -``` - -```ucm -.simple> update.old - - ⍟ I've updated these names to your new definition: - - type MyType - x : Int - -.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 - - - -``` -## A merge with conflicting updates. - -```unison -x = 1 -type MyType = MyType -``` - -Set up two branches with the same starting point. - -Update `x` to a different term in each branch. - -```unison -x = 2 -type MyType = MyType Nat -``` - -```unison -x = 3 -type MyType = MyType Int -``` - -```ucm -.mergeA> merge.old .mergeB - - Here's what's changed in the current namespace after the - merge: - - New name conflicts: - - 1. type MyType#ig1g2ka7lv - ↓ - 2. ┌ type MyType#8c6f40i3tj - 3. └ type MyType#ig1g2ka7lv - - 4. MyType.MyType#ig1g2ka7lv#0 : Nat -> MyType#ig1g2ka7lv - ↓ - 5. ┌ MyType.MyType#8c6f40i3tj#0 : Int -> MyType#8c6f40i3tj - 6. └ MyType.MyType#ig1g2ka7lv#0 : Nat -> MyType#ig1g2ka7lv - - 7. x#dcgdua2lj6 : Nat - ↓ - 8. ┌ x#dcgdua2lj6 : Nat - 9. └ x#f3lgjvjqoo : Nat - - Updates: - - 10. patch patch (added 2 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. - -.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 term MyType.MyType has conflicting definitions: - 7. MyType.MyType#8c6f40i3tj#0 - 8. MyType.MyType#ig1g2ka7lv#0 - - Tip: This occurs when merging branches that both independently - introduce the same name. Use `move.term` or `delete.term` - to resolve the conflicts. - -``` -## A named value that appears on the LHS of a patch isn't shown - -```unison -foo = 801 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - foo : Nat - -``` -```ucm -.lhs> add - - ⍟ I've added these definitions: - - foo : Nat - -``` -```unison -foo = 802 -``` - -```ucm - - 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: - - ⍟ These names already exist. You can `update` them to your - new definition: - - foo : Nat - -``` -```ucm -.lhs> update.old - - ⍟ I've updated these names to your new definition: - - foo : Nat - -``` -```unison -oldfoo = 801 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - oldfoo : Nat - -``` -```ucm -.lhs> add - - ⍟ I've added these definitions: - - oldfoo : Nat - -.lhs> todo - - ✅ - - No conflicts or edits in progress. - -``` -## A type-changing update to one element of a cycle, which doesn't propagate to the other - -```unison -even = cases - 0 -> true - n -> odd (drop 1 n) - -odd = cases - 0 -> false - n -> even (drop 1 n) -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - even : Nat -> Boolean - odd : Nat -> Boolean - -``` -```ucm -.cycle2> add - - ⍟ I've added these definitions: - - even : Nat -> Boolean - odd : Nat -> Boolean - -``` -```unison -even = 17 -``` - -```ucm - - 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: - - ⍟ These names already exist. You can `update` them to your - new definition: - - even : Nat - -``` -```ucm -.cycle2> update.old - - ⍟ I've updated these names to your new definition: - - even : Nat - -``` -```ucm -.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 - - - -``` From b820365690505afa6c8ae0f6bb098fe93df2bb46 Mon Sep 17 00:00:00 2001 From: Eduard Date: Thu, 13 Jun 2024 20:39:34 +0100 Subject: [PATCH 10/81] add neduard to CONTRIBUTORS --- CONTRIBUTORS.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.markdown b/CONTRIBUTORS.markdown index 408c1bc08c..5649b15dd0 100644 --- a/CONTRIBUTORS.markdown +++ b/CONTRIBUTORS.markdown @@ -86,3 +86,4 @@ The format for this list: name, GitHub handle * Upendra Upadhyay (@upendra1997) * Dan Doel (@dolio) * Eric Torreborre (@etorreborre) +* Eduard Nicodei (@neduard) From 574dd6e838ca0529e324242fef2ff9ff8a7cf109 Mon Sep 17 00:00:00 2001 From: Arya Irani <538571+aryairani@users.noreply.github.com> Date: Thu, 13 Jun 2024 16:53:30 -0400 Subject: [PATCH 11/81] Update .mergify.yml try renaming contributor check --- .mergify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index eff5a6fcc3..e20da83972 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -1,7 +1,7 @@ pull_request_rules: - name: automatic merge on CI success and review conditions: - - check-success=Contributor signed CONTRIBUTORS.markdown + - check-success=check-contributor - check-success=build ucm (ubuntu-20.04) - check-success=build ucm (macOS-12) - check-success=build ucm (windows-2019) From 119e9c8e78437a8b78adb175b2e6195451380116 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Fri, 14 Jun 2024 09:58:53 -0400 Subject: [PATCH 12/81] add query to get direct dependencies of a set of dependents --- .../U/Codebase/Sqlite/Operations.hs | 17 ++ .../U/Codebase/Sqlite/Queries.hs | 195 +++++++++++------- .../U/Codebase/Sqlite/Reference.hs | 8 + codebase2/codebase-sqlite/package.yaml | 2 + .../unison-codebase-sqlite.cabal | 2 + 5 files changed, 150 insertions(+), 74 deletions(-) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs index b402620333..d0b9935014 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs @@ -63,6 +63,7 @@ module U.Codebase.Sqlite.Operations causalHashesByPrefix, -- ** dependents index + directDependenciesOfScope, dependents, dependentsOfComponent, dependentsWithinScope, @@ -205,6 +206,7 @@ import Unison.NameSegment.Internal qualified as NameSegment import Unison.Prelude import Unison.ShortHash (ShortCausalHash (..), ShortNamespaceHash (..)) import Unison.Sqlite +import Unison.Util.Defns (DefnsF) import Unison.Util.List qualified as List import Unison.Util.Map qualified as Map import Unison.Util.Monoid (foldMapM) @@ -1121,6 +1123,21 @@ causalHashesByPrefix (ShortCausalHash b32prefix) = do hashes <- traverse (Q.expectHash . Db.unCausalHashId) hashIds pure $ Set.fromList . map CausalHash $ hashes +directDependenciesOfScope :: + DefnsF Set C.TermReferenceId C.TypeReferenceId -> + Transaction (DefnsF Set C.TermReference C.TypeReference) +directDependenciesOfScope scope0 = do + -- Convert C -> S + scope1 <- bitraverse (Set.traverse c2sReferenceId) (Set.traverse c2sReferenceId) scope0 + + -- Do the query + dependencies0 <- Q.getDirectDependenciesOfScope scope1 + + -- Convert S -> C + dependencies1 <- bitraverse (Set.traverse s2cReference) (Set.traverse s2cReference) dependencies0 + + pure dependencies1 + -- | returns a list of known definitions referencing `r` dependents :: Q.DependentsSelector -> C.Reference -> Transaction (Set C.Reference.Id) dependents selector r = do diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs index 880d3cdf04..66af3c846a 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs @@ -165,6 +165,7 @@ module U.Codebase.Sqlite.Queries getDependenciesForDependent, getDependencyIdsForDependent, getDependenciesBetweenTerms, + getDirectDependenciesOfScope, getDependentsWithinScope, -- ** type index @@ -321,7 +322,7 @@ import U.Codebase.Decl qualified as C import U.Codebase.Decl qualified as C.Decl import U.Codebase.HashTags (BranchHash (..), CausalHash (..), PatchHash (..)) import U.Codebase.Reference (Reference' (..)) -import U.Codebase.Reference qualified as C +import U.Codebase.Reference qualified as C (Reference) import U.Codebase.Reference qualified as C.Reference import U.Codebase.Referent qualified as C.Referent import U.Codebase.Reflog qualified as Reflog @@ -365,10 +366,9 @@ import U.Codebase.Sqlite.Orphans () import U.Codebase.Sqlite.Patch.Format qualified as PatchFormat import U.Codebase.Sqlite.Project (Project (..)) import U.Codebase.Sqlite.ProjectBranch (ProjectBranch (..)) -import U.Codebase.Sqlite.Reference qualified as Reference -import U.Codebase.Sqlite.Reference qualified as S +import U.Codebase.Sqlite.Reference qualified as S (Reference, ReferenceH, TermReference, TermReferenceId, TextReference, TypeReference, TypeReferenceId) import U.Codebase.Sqlite.Reference qualified as S.Reference -import U.Codebase.Sqlite.Referent qualified as Referent +import U.Codebase.Sqlite.Referent qualified as S (TextReferent) import U.Codebase.Sqlite.Referent qualified as S.Referent import U.Codebase.Sqlite.RemoteProject (RemoteProject (..)) import U.Codebase.Sqlite.RemoteProjectBranch (RemoteProjectBranch) @@ -399,6 +399,7 @@ import Unison.NameSegment.Internal qualified as NameSegment import Unison.Prelude import Unison.Sqlite import Unison.Util.Alternative qualified as Alternative +import Unison.Util.Defns (Defns (..), DefnsF) import Unison.Util.FileEmbed (embedProjectStringFile) import Unison.Util.Lens qualified as Lens import Unison.Util.Map qualified as Map @@ -1361,7 +1362,7 @@ setNamespaceRoot id = False -> execute [sql| INSERT INTO namespace_root VALUES (:id) |] True -> execute [sql| UPDATE namespace_root SET causal_id = :id |] -saveWatch :: WatchKind -> Reference.IdH -> ByteString -> Transaction () +saveWatch :: WatchKind -> S.Reference.IdH -> ByteString -> Transaction () saveWatch k r blob = do execute [sql| @@ -1379,7 +1380,7 @@ saveWatch k r blob = do loadWatch :: SqliteExceptionReason e => WatchKind -> - Reference.IdH -> + S.Reference.IdH -> (ByteString -> Either e a) -> Transaction (Maybe a) loadWatch k r check = @@ -1395,7 +1396,7 @@ loadWatch k r check = |] check -loadWatchKindsByReference :: Reference.IdH -> Transaction [WatchKind] +loadWatchKindsByReference :: S.Reference.IdH -> Transaction [WatchKind] loadWatchKindsByReference r = queryListCol [sql| @@ -1407,7 +1408,7 @@ loadWatchKindsByReference r = AND watch.component_index = @ |] -loadWatchesByWatchKind :: WatchKind -> Transaction [Reference.IdH] +loadWatchesByWatchKind :: WatchKind -> Transaction [S.Reference.IdH] loadWatchesByWatchKind k = queryListRow [sql| @@ -1423,7 +1424,7 @@ clearWatches = do execute [sql| DELETE FROM watch |] -- * Index-building -addToTypeIndex :: Reference' TextId HashId -> Referent.Id -> Transaction () +addToTypeIndex :: S.ReferenceH -> S.Referent.Id -> Transaction () addToTypeIndex tp tm = execute [sql| @@ -1438,7 +1439,7 @@ addToTypeIndex tp tm = ON CONFLICT DO NOTHING |] -getReferentsByType :: Reference' TextId HashId -> Transaction [Referent.Id] +getReferentsByType :: S.ReferenceH -> Transaction [S.Referent.Id] getReferentsByType r = queryListRow [sql| @@ -1452,7 +1453,7 @@ getReferentsByType r = AND type_reference_component_index IS @ |] -getTypeReferenceForReferent :: Referent.Id -> Transaction (Reference' TextId HashId) +getTypeReferenceForReferent :: S.Referent.Id -> Transaction S.ReferenceH getTypeReferenceForReferent r = queryOneRow [sql| @@ -1467,7 +1468,7 @@ getTypeReferenceForReferent r = |] -- todo: error if no results -getTypeReferencesForComponent :: ObjectId -> Transaction [(Reference' TextId HashId, Referent.Id)] +getTypeReferencesForComponent :: ObjectId -> Transaction [(S.ReferenceH, S.Referent.Id)] getTypeReferencesForComponent oId = fmap (map fixupTypeIndexRow) $ queryListRow @@ -1553,7 +1554,7 @@ filterTermsByReferenceHavingType typ terms = create *> for_ terms insert *> sele drop = execute [sql|DROP TABLE filter_query|] -addToTypeMentionsIndex :: Reference' TextId HashId -> Referent.Id -> Transaction () +addToTypeMentionsIndex :: S.ReferenceH -> S.Referent.Id -> Transaction () addToTypeMentionsIndex tp tm = execute [sql| @@ -1568,7 +1569,7 @@ addToTypeMentionsIndex tp tm = ON CONFLICT DO NOTHING |] -getReferentsByTypeMention :: Reference' TextId HashId -> Transaction [Referent.Id] +getReferentsByTypeMention :: S.ReferenceH -> Transaction [S.Referent.Id] getReferentsByTypeMention r = queryListRow [sql| @@ -1583,7 +1584,7 @@ getReferentsByTypeMention r = |] -- todo: error if no results -getTypeMentionsReferencesForComponent :: ObjectId -> Transaction [(Reference' TextId HashId, Referent.Id)] +getTypeMentionsReferencesForComponent :: ObjectId -> Transaction [(S.ReferenceH, S.Referent.Id)] getTypeMentionsReferencesForComponent r = fmap (map fixupTypeIndexRow) $ queryListRow @@ -1599,7 +1600,7 @@ getTypeMentionsReferencesForComponent r = WHERE term_referent_object_id IS :r |] -fixupTypeIndexRow :: Reference' TextId HashId :. Referent.Id -> (Reference' TextId HashId, Referent.Id) +fixupTypeIndexRow :: S.ReferenceH :. S.Referent.Id -> (S.ReferenceH, S.Referent.Id) fixupTypeIndexRow (rh :. ri) = (rh, ri) -- | Delete objects without hashes. An object typically *would* have a hash, but (for example) during a migration in which an object's hash @@ -1653,7 +1654,7 @@ garbageCollectWatchesWithoutObjects = do (SELECT hash_object.hash_id FROM hash_object) |] -addToDependentsIndex :: [Reference.Reference] -> Reference.Id -> Transaction () +addToDependentsIndex :: [S.Reference] -> S.Reference.Id -> Transaction () addToDependentsIndex dependencies dependent = for_ dependencies \dependency -> execute @@ -1682,7 +1683,7 @@ data DependentsSelector | ExcludeOwnComponent -- | Get dependents of a dependency. -getDependentsForDependency :: DependentsSelector -> Reference.Reference -> Transaction (Set Reference.Id) +getDependentsForDependency :: DependentsSelector -> S.Reference -> Transaction (Set S.Reference.Id) getDependentsForDependency selector dependency = do dependents <- queryListRow @@ -1699,19 +1700,19 @@ getDependentsForDependency selector dependency = do ExcludeSelf -> filter isNotSelfReference dependents ExcludeOwnComponent -> filter isNotReferenceFromOwnComponent dependents where - isNotReferenceFromOwnComponent :: Reference.Id -> Bool + isNotReferenceFromOwnComponent :: S.Reference.Id -> Bool isNotReferenceFromOwnComponent = case dependency of ReferenceBuiltin _ -> const True ReferenceDerived (C.Reference.Id oid0 _pos0) -> \(C.Reference.Id oid1 _pos1) -> oid0 /= oid1 - isNotSelfReference :: Reference.Id -> Bool + isNotSelfReference :: S.Reference.Id -> Bool isNotSelfReference = case dependency of ReferenceBuiltin _ -> const True ReferenceDerived ref -> (ref /=) -getDependentsForDependencyComponent :: ObjectId -> Transaction [Reference.Id] +getDependentsForDependencyComponent :: ObjectId -> Transaction [S.Reference.Id] getDependentsForDependencyComponent dependency = filter isNotSelfReference <$> queryListRow @@ -1722,12 +1723,12 @@ getDependentsForDependencyComponent dependency = AND dependency_object_id IS :dependency |] where - isNotSelfReference :: Reference.Id -> Bool + isNotSelfReference :: S.Reference.Id -> Bool isNotSelfReference = \case (C.Reference.Id oid1 _pos1) -> dependency /= oid1 -- | Get non-self dependencies of a user-defined dependent. -getDependenciesForDependent :: Reference.Id -> Transaction [Reference.Reference] +getDependenciesForDependent :: S.Reference.Id -> Transaction [S.Reference] getDependenciesForDependent dependent@(C.Reference.Id oid0 _) = fmap (filter isNotSelfReference) $ queryListRow @@ -1738,13 +1739,13 @@ getDependenciesForDependent dependent@(C.Reference.Id oid0 _) = AND dependent_component_index IS @ |] where - isNotSelfReference :: Reference.Reference -> Bool + isNotSelfReference :: S.Reference -> Bool isNotSelfReference = \case ReferenceBuiltin _ -> True ReferenceDerived (C.Reference.Id oid1 _) -> oid0 /= oid1 -- | Get non-self, user-defined dependencies of a user-defined dependent. -getDependencyIdsForDependent :: Reference.Id -> Transaction [Reference.Id] +getDependencyIdsForDependent :: S.Reference.Id -> Transaction [S.Reference.Id] getDependencyIdsForDependent dependent@(C.Reference.Id oid0 _) = fmap (filter isNotSelfReference) $ queryListRow @@ -1756,7 +1757,7 @@ getDependencyIdsForDependent dependent@(C.Reference.Id oid0 _) = AND dependent_component_index = @ |] where - isNotSelfReference :: Reference.Id -> Bool + isNotSelfReference :: S.Reference.Id -> Bool isNotSelfReference (C.Reference.Id oid1 _) = oid0 /= oid1 @@ -1869,21 +1870,54 @@ getDependenciesBetweenTerms oid1 oid2 = WHERE path_elem IS NOT null |] +-- Mitchell says: why are we enabling and disabling ormolu all over this file? Let's just enable. But right now I'm only +-- adding this one query and don't want a big diff in my PR. + +{- ORMOLU_ENABLE -} + +getDirectDependenciesOfScope :: + DefnsF Set S.TermReferenceId S.TypeReferenceId -> + Transaction (DefnsF Set S.TermReference S.TypeReference) +getDirectDependenciesOfScope scope = do + let tempTableName = [sql| temp_dependents |] + + -- Populate a temporary table with all of the references in `scope` + createTemporaryTableOfReferenceIds tempTableName (Set.union scope.terms scope.types) + + -- Get their direct dependencies (tagged with object type) + dependencies0 <- + queryListRow @(S.Reference :. Only ObjectType) + [sql| + SELECT d.dependency_builtin, d.dependency_object_id, d.dependency_component_index, o.type_id + FROM dependents_index d + JOIN object o ON d.dependency_object_id = o.id + WHERE (d.dependent_object_id, d.dependent_component_index) IN ( + SELECT object_id, component_index + FROM $tempTableName + ) + |] + + -- Post-process the query result + let dependencies1 = + List.foldl' + ( \deps -> \case + dep :. Only TermComponent -> Defns (Set.insert dep deps.terms) deps.types + dep :. Only DeclComponent -> Defns deps.terms (Set.insert dep deps.types) + _ -> deps -- impossible; could error here + ) + (Defns Set.empty Set.empty) + dependencies0 + + pure dependencies1 + +{- ORMOLU_DISABLE -} + -- | `getDependentsWithinScope scope query` returns all of transitive dependents of `query` that are in `scope` (not -- including `query` itself). Each dependent is also tagged with whether it is a term or decl. -getDependentsWithinScope :: Set Reference.Id -> Set S.Reference -> Transaction (Map Reference.Id ObjectType) +getDependentsWithinScope :: Set S.Reference.Id -> Set S.Reference -> Transaction (Map S.Reference.Id ObjectType) getDependentsWithinScope scope query = do -- Populate a temporary table with all of the references in `scope` - execute - [sql| - CREATE TEMPORARY TABLE dependents_search_scope ( - dependent_object_id INTEGER NOT NULL, - dependent_component_index INTEGER NOT NULL, - PRIMARY KEY (dependent_object_id, dependent_component_index) - ) - |] - for_ scope \r -> - execute [sql|INSERT INTO dependents_search_scope VALUES (@r, @)|] + createTemporaryTableOfReferenceIds [sql| dependents_search_scope |] scope -- Populate a temporary table with all of the references in `query` execute @@ -1917,7 +1951,7 @@ getDependentsWithinScope scope query = do -- We use `UNION` rather than `UNION ALL` so as to not track down the transitive dependents of any particular -- reference more than once. - result :: [Reference.Id :. Only ObjectType] <- queryListRow [sql| + result :: [S.Reference.Id :. Only ObjectType] <- queryListRow [sql| WITH RECURSIVE transitive_dependents (dependent_object_id, dependent_component_index, type_id) AS ( SELECT d.dependent_object_id, d.dependent_component_index, object.type_id FROM dependents_index d @@ -1927,8 +1961,8 @@ getDependentsWithinScope scope query = do AND q.dependency_object_id IS d.dependency_object_id AND q.dependency_component_index IS d.dependency_component_index JOIN dependents_search_scope s - ON s.dependent_object_id = d.dependent_object_id - AND s.dependent_component_index = d.dependent_component_index + ON s.object_id = d.dependent_object_id + AND s.component_index = d.dependent_component_index UNION SELECT d.dependent_object_id, d.dependent_component_index, object.type_id FROM dependents_index d @@ -1937,15 +1971,28 @@ getDependentsWithinScope scope query = do ON t.dependent_object_id = d.dependency_object_id AND t.dependent_component_index = d.dependency_component_index JOIN dependents_search_scope s - ON s.dependent_object_id = d.dependent_object_id - AND s.dependent_component_index = d.dependent_component_index + ON s.object_id = d.dependent_object_id + AND s.component_index = d.dependent_component_index ) SELECT * FROM transitive_dependents |] - execute [sql|DROP TABLE dependents_search_scope|] - execute [sql|DROP TABLE dependencies_query|] + execute [sql| DROP TABLE dependents_search_scope |] + execute [sql| DROP TABLE dependencies_query |] pure . Map.fromList $ [(r, t) | r :. Only t <- result] +createTemporaryTableOfReferenceIds :: Sql -> Set S.Reference.Id -> Transaction () +createTemporaryTableOfReferenceIds tableName refs = do + execute + [sql| + CREATE TEMPORARY TABLE $tableName ( + object_id INTEGER NOT NULL, + component_index INTEGER NOT NULL, + PRIMARY KEY (object_id, component_index) + ) + |] + for_ refs \ref -> + execute [sql| INSERT INTO $tableName VALUES (@ref, @) |] + objectIdByBase32Prefix :: ObjectType -> Text -> Transaction [ObjectId] objectIdByBase32Prefix objType prefix = queryListCol @@ -2086,7 +2133,7 @@ deleteNameLookupsExceptFor hashIds = do |] -- | Insert the given set of term names into the name lookup table -insertScopedTermNames :: BranchHashId -> [NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType)] -> Transaction () +insertScopedTermNames :: BranchHashId -> [NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType)] -> Transaction () insertScopedTermNames bhId = do traverse_ \name0 -> do let name = NamedRef.ScopedRow (refToRow <$> name0) @@ -2106,11 +2153,11 @@ insertScopedTermNames bhId = do VALUES (:bhId, @name, @, @, @, @, @, @, @) |] where - refToRow :: (Referent.TextReferent, Maybe NamedRef.ConstructorType) -> (Referent.TextReferent :. Only (Maybe NamedRef.ConstructorType)) + refToRow :: (S.TextReferent, Maybe NamedRef.ConstructorType) -> (S.TextReferent :. Only (Maybe NamedRef.ConstructorType)) refToRow (ref, ct) = ref :. Only ct -- | Insert the given set of type names into the name lookup table -insertScopedTypeNames :: BranchHashId -> [NamedRef Reference.TextReference] -> Transaction () +insertScopedTypeNames :: BranchHashId -> [NamedRef S.TextReference] -> Transaction () insertScopedTypeNames bhId = traverse_ \name0 -> do let name = NamedRef.ScopedRow name0 @@ -2129,7 +2176,7 @@ insertScopedTypeNames bhId = |] -- | Remove the given set of term names into the name lookup table -removeScopedTermNames :: BranchHashId -> [NamedRef Referent.TextReferent] -> Transaction () +removeScopedTermNames :: BranchHashId -> [NamedRef S.TextReferent] -> Transaction () removeScopedTermNames bhId names = do for_ names \name -> execute @@ -2144,7 +2191,7 @@ removeScopedTermNames bhId names = do |] -- | Remove the given set of term names into the name lookup table -removeScopedTypeNames :: BranchHashId -> [NamedRef Reference.TextReference] -> Transaction () +removeScopedTypeNames :: BranchHashId -> [NamedRef S.TextReference] -> Transaction () removeScopedTypeNames bhId names = do for_ names \name -> execute @@ -2203,9 +2250,9 @@ likeEscape escapeChar pat = -- -- Get the list of a term names in the provided name lookup and relative namespace. -- Includes dependencies, but not transitive dependencies. -termNamesWithinNamespace :: BranchHashId -> PathSegments -> Transaction [NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType)] +termNamesWithinNamespace :: BranchHashId -> PathSegments -> Transaction [NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType)] termNamesWithinNamespace bhId namespace = do - results :: [NamedRef (Referent.TextReferent :. Only (Maybe NamedRef.ConstructorType))] <- + results :: [NamedRef (S.TextReferent :. Only (Maybe NamedRef.ConstructorType))] <- queryListRow [sql| SELECT reversed_name, referent_builtin, referent_component_hash, referent_component_index, referent_constructor_index, referent_constructor_type @@ -2236,7 +2283,7 @@ termNamesWithinNamespace bhId namespace = do -- -- Get the list of a type names in the provided name lookup and relative namespace. -- Includes dependencies, but not transitive dependencies. -typeNamesWithinNamespace :: BranchHashId -> PathSegments -> Transaction [NamedRef Reference.TextReference] +typeNamesWithinNamespace :: BranchHashId -> PathSegments -> Transaction [NamedRef S.TextReference] typeNamesWithinNamespace bhId namespace = queryListRow [sql| @@ -2265,13 +2312,13 @@ typeNamesWithinNamespace bhId namespace = -- is only true on Share. -- -- Get the list of term names within a given namespace which have the given suffix. -termNamesBySuffix :: BranchHashId -> PathSegments -> ReversedName -> Transaction [NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType)] +termNamesBySuffix :: BranchHashId -> PathSegments -> ReversedName -> Transaction [NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType)] termNamesBySuffix bhId namespaceRoot suffix = do Debug.debugM Debug.Server "termNamesBySuffix" (namespaceRoot, suffix) let namespaceGlob = toNamespaceGlob namespaceRoot let lastSegment = NonEmpty.head . into @(NonEmpty Text) $ suffix let reversedNameGlob = toSuffixGlob suffix - results :: [NamedRef (Referent.TextReferent :. Only (Maybe NamedRef.ConstructorType))] <- + results :: [NamedRef (S.TextReferent :. Only (Maybe NamedRef.ConstructorType))] <- -- Note: It may seem strange that we do a last_name_segment constraint AND a reversed_name -- GLOB, but this helps improve query performance. -- The SQLite query optimizer is smart enough to do a prefix-search on globs, but will @@ -2304,7 +2351,7 @@ termNamesBySuffix bhId namespaceRoot suffix = do -- is only true on Share. -- -- Get the list of type names within a given namespace which have the given suffix. -typeNamesBySuffix :: BranchHashId -> PathSegments -> ReversedName -> Transaction [NamedRef Reference.TextReference] +typeNamesBySuffix :: BranchHashId -> PathSegments -> ReversedName -> Transaction [NamedRef S.TextReference] typeNamesBySuffix bhId namespaceRoot suffix = do Debug.debugM Debug.Server "typeNamesBySuffix" (namespaceRoot, suffix) let namespaceGlob = toNamespaceGlob namespaceRoot @@ -2343,10 +2390,10 @@ typeNamesBySuffix bhId namespaceRoot suffix = do -- id. It's the caller's job to select the correct name lookup for your exact name. -- -- See termRefsForExactName in U.Codebase.Sqlite.Operations -termRefsForExactName :: BranchHashId -> ReversedName -> Transaction [NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType)] +termRefsForExactName :: BranchHashId -> ReversedName -> Transaction [NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType)] termRefsForExactName bhId reversedSegments = do let reversedName = toReversedName reversedSegments - results :: [NamedRef (Referent.TextReferent :. Only (Maybe NamedRef.ConstructorType))] <- + results :: [NamedRef (S.TextReferent :. Only (Maybe NamedRef.ConstructorType))] <- queryListRow [sql| SELECT reversed_name, referent_builtin, referent_component_hash, referent_component_index, referent_constructor_index, referent_constructor_type @@ -2366,7 +2413,7 @@ termRefsForExactName bhId reversedSegments = do -- id. It's the caller's job to select the correct name lookup for your exact name. -- -- See termRefsForExactName in U.Codebase.Sqlite.Operations -typeRefsForExactName :: BranchHashId -> ReversedName -> Transaction [NamedRef Reference.TextReference] +typeRefsForExactName :: BranchHashId -> ReversedName -> Transaction [NamedRef S.TextReference] typeRefsForExactName bhId reversedSegments = do let reversedName = toReversedName reversedSegments queryListRow @@ -2382,7 +2429,7 @@ typeRefsForExactName bhId reversedSegments = do -- -- Get the list of term names for a given Referent within a given namespace. -- Considers one level of dependencies, but not transitive dependencies. -termNamesForRefWithinNamespace :: BranchHashId -> PathSegments -> Referent.TextReferent -> Maybe ReversedName -> Transaction [ReversedName] +termNamesForRefWithinNamespace :: BranchHashId -> PathSegments -> S.TextReferent -> Maybe ReversedName -> Transaction [ReversedName] termNamesForRefWithinNamespace bhId namespaceRoot ref maySuffix = do let namespaceGlob = toNamespaceGlob namespaceRoot let suffixGlob = case maySuffix of @@ -2431,7 +2478,7 @@ termNamesForRefWithinNamespace bhId namespaceRoot ref maySuffix = do -- -- Get the list of type names for a given Reference within a given namespace. -- Considers one level of dependencies, but not transitive dependencies. -typeNamesForRefWithinNamespace :: BranchHashId -> PathSegments -> Reference.TextReference -> Maybe ReversedName -> Transaction [ReversedName] +typeNamesForRefWithinNamespace :: BranchHashId -> PathSegments -> S.TextReference -> Maybe ReversedName -> Transaction [ReversedName] typeNamesForRefWithinNamespace bhId namespaceRoot ref maySuffix = do let namespaceGlob = toNamespaceGlob namespaceRoot let suffixGlob = case maySuffix of @@ -2511,7 +2558,7 @@ transitiveDependenciesSql rootBranchHashId = -- Note: this returns the first name it finds by searching in order of: -- Names in the current namespace, then names in the current namespace's dependencies, then -- through the current namespace's dependencies' dependencies, etc. -recursiveTermNameSearch :: BranchHashId -> Referent.TextReferent -> Transaction (Maybe ReversedName) +recursiveTermNameSearch :: BranchHashId -> S.TextReferent -> Transaction (Maybe ReversedName) recursiveTermNameSearch bhId ref = do queryMaybeColCheck [sql| @@ -2548,7 +2595,7 @@ recursiveTermNameSearch bhId ref = do -- Note: this returns the first name it finds by searching in order of: -- Names in the current namespace, then names in the current namespace's dependencies, then -- through the current namespace's dependencies' dependencies, etc. -recursiveTypeNameSearch :: BranchHashId -> Reference.TextReference -> Transaction (Maybe ReversedName) +recursiveTypeNameSearch :: BranchHashId -> S.TextReference -> Transaction (Maybe ReversedName) recursiveTypeNameSearch bhId ref = do queryMaybeColCheck [sql| @@ -2589,13 +2636,13 @@ recursiveTypeNameSearch bhId ref = do -- the longest matching suffix. -- -- Considers one level of dependencies, but not transitive dependencies. -longestMatchingTermNameForSuffixification :: BranchHashId -> PathSegments -> NamedRef Referent.TextReferent -> Transaction (Maybe (NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType))) +longestMatchingTermNameForSuffixification :: BranchHashId -> PathSegments -> NamedRef S.TextReferent -> Transaction (Maybe (NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType))) longestMatchingTermNameForSuffixification bhId namespaceRoot (NamedRef.NamedRef {reversedSegments = revSuffix@(ReversedName (lastSegment NonEmpty.:| _)), ref}) = do let namespaceGlob = toNamespaceGlob namespaceRoot <> ".*" - let loop :: [Text] -> MaybeT Transaction (NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType)) + let loop :: [Text] -> MaybeT Transaction (NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType)) loop [] = empty loop (suffGlob : rest) = do - result :: Maybe (NamedRef (Referent.TextReferent :. Only (Maybe NamedRef.ConstructorType))) <- + result :: Maybe (NamedRef (S.TextReferent :. Only (Maybe NamedRef.ConstructorType))) <- lift $ queryMaybeRow -- Note: It may seem strange that we do a last_name_segment constraint AND a reversed_name @@ -2664,13 +2711,13 @@ longestMatchingTermNameForSuffixification bhId namespaceRoot (NamedRef.NamedRef -- the longest matching suffix. -- -- Considers one level of dependencies, but not transitive dependencies. -longestMatchingTypeNameForSuffixification :: BranchHashId -> PathSegments -> NamedRef Reference.TextReference -> Transaction (Maybe (NamedRef Reference.TextReference)) +longestMatchingTypeNameForSuffixification :: BranchHashId -> PathSegments -> NamedRef S.TextReference -> Transaction (Maybe (NamedRef S.TextReference)) longestMatchingTypeNameForSuffixification bhId namespaceRoot (NamedRef.NamedRef {reversedSegments = revSuffix@(ReversedName (lastSegment NonEmpty.:| _)), ref}) = do let namespaceGlob = toNamespaceGlob namespaceRoot <> ".*" - let loop :: [Text] -> MaybeT Transaction (NamedRef Reference.TextReference) + let loop :: [Text] -> MaybeT Transaction (NamedRef S.TextReference) loop [] = empty loop (suffGlob : rest) = do - result :: Maybe (NamedRef (Reference.TextReference)) <- + result :: Maybe (NamedRef (S.TextReference)) <- lift $ queryMaybeRow -- Note: It may seem strange that we do a last_name_segment constraint AND a reversed_name @@ -3036,12 +3083,12 @@ saveTermComponent hh@HashHandle {toReference, toReferenceMentions} maybeEncodedT tpRefs' = Foldable.toList $ C.Type.dependencies tp getTermSRef :: S.Term.TermRef -> S.Reference getTermSRef = \case - C.ReferenceBuiltin t -> C.ReferenceBuiltin (tIds Vector.! fromIntegral t) + ReferenceBuiltin t -> ReferenceBuiltin (tIds Vector.! fromIntegral t) C.Reference.Derived Nothing i -> C.Reference.Derived oId i -- index self-references C.Reference.Derived (Just h) i -> C.Reference.Derived (oIds Vector.! fromIntegral h) i getTypeSRef :: S.Term.TypeRef -> S.Reference getTypeSRef = \case - C.ReferenceBuiltin t -> C.ReferenceBuiltin (tIds Vector.! fromIntegral t) + ReferenceBuiltin t -> ReferenceBuiltin (tIds Vector.! fromIntegral t) C.Reference.Derived h i -> C.Reference.Derived (oIds Vector.! fromIntegral h) i getSTypeLink = getTypeSRef getSTermLink :: S.Term.TermLink -> S.Reference @@ -3096,7 +3143,7 @@ saveDeclComponent hh@HashHandle {toReferenceDecl, toReferenceDeclMentions} maybe dependencies :: Set S.Decl.TypeRef = C.Decl.dependencies decl getSRef :: C.Reference.Reference' LocalTextId (Maybe LocalDefnId) -> S.Reference.Reference getSRef = \case - C.ReferenceBuiltin t -> C.ReferenceBuiltin (tIds Vector.! fromIntegral t) + ReferenceBuiltin t -> ReferenceBuiltin (tIds Vector.! fromIntegral t) C.Reference.Derived Nothing i -> C.Reference.Derived oId i -- index self-references C.Reference.Derived (Just h) i -> C.Reference.Derived (oIds Vector.! fromIntegral h) i in (Set.map getSRef dependencies, self) @@ -4144,7 +4191,7 @@ loadMostRecentBranch projectId = -- | Searches for all names within the given name lookup which contain the provided list of segments -- in order. -- Search is case insensitive. -fuzzySearchTerms :: Bool -> BranchHashId -> Int -> PathSegments -> [Text] -> Transaction [(NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType))] +fuzzySearchTerms :: Bool -> BranchHashId -> Int -> PathSegments -> [Text] -> Transaction [(NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType))] fuzzySearchTerms includeDependencies bhId limit namespace querySegments = do -- Union in the dependencies if required. let dependenciesSql = @@ -4179,14 +4226,14 @@ fuzzySearchTerms includeDependencies bhId limit namespace querySegments = do where namespaceGlob = toNamespaceGlob namespace preparedQuery = prepareFuzzyQuery '\\' querySegments - unRow :: NamedRef (Referent.TextReferent :. Only (Maybe NamedRef.ConstructorType)) -> NamedRef (Referent.TextReferent, Maybe NamedRef.ConstructorType) + unRow :: NamedRef (S.TextReferent :. Only (Maybe NamedRef.ConstructorType)) -> NamedRef (S.TextReferent, Maybe NamedRef.ConstructorType) unRow = fmap \(a :. Only b) -> (a, b) -- | Searches for all names within the given name lookup which contain the provided list of segments -- in order. -- -- Search is case insensitive. -fuzzySearchTypes :: Bool -> BranchHashId -> Int -> PathSegments -> [Text] -> Transaction [(NamedRef Reference.TextReference)] +fuzzySearchTypes :: Bool -> BranchHashId -> Int -> PathSegments -> [Text] -> Transaction [(NamedRef S.TextReference)] fuzzySearchTypes includeDependencies bhId limit namespace querySegments = do -- Union in the dependencies if required. let dependenciesSql = diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Reference.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Reference.hs index ca228f83d1..7c45dbc97d 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Reference.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Reference.hs @@ -14,12 +14,20 @@ import Unison.Sqlite (FromField, FromRow (fromRow), Only (..), RowParser, SQLDat type Reference = Reference' TextId ObjectId +type TermReference = Reference + +type TypeReference = Reference + -- | The name lookup table uses this because normalizing/denormalizing hashes to ids is slower -- than we'd like when writing/reading the entire name lookup table. type TextReference = Reference' Text Base32Hex type Id = Id' ObjectId +type TermReferenceId = Id + +type TypeReferenceId = Id + type LocalReferenceH = Reference' LocalTextId LocalHashId type LocalReference = Reference' LocalTextId LocalDefnId diff --git a/codebase2/codebase-sqlite/package.yaml b/codebase2/codebase-sqlite/package.yaml index a04fce3a56..bf0bed4ee4 100644 --- a/codebase2/codebase-sqlite/package.yaml +++ b/codebase2/codebase-sqlite/package.yaml @@ -39,6 +39,7 @@ dependencies: - unison-util-base32hex - unison-util-cache - unison-util-file-embed + - unison-util-nametree - unison-util-serialization - unison-util-term - unliftio @@ -71,6 +72,7 @@ default-extensions: - MultiParamTypeClasses - NamedFieldPuns - OverloadedLabels + - OverloadedRecordDot - OverloadedStrings - PatternSynonyms - QuasiQuotes diff --git a/codebase2/codebase-sqlite/unison-codebase-sqlite.cabal b/codebase2/codebase-sqlite/unison-codebase-sqlite.cabal index ac1f606921..b91e2a51a1 100644 --- a/codebase2/codebase-sqlite/unison-codebase-sqlite.cabal +++ b/codebase2/codebase-sqlite/unison-codebase-sqlite.cabal @@ -91,6 +91,7 @@ library MultiParamTypeClasses NamedFieldPuns OverloadedLabels + OverloadedRecordDot OverloadedStrings PatternSynonyms QuasiQuotes @@ -132,6 +133,7 @@ library , unison-util-base32hex , unison-util-cache , unison-util-file-embed + , unison-util-nametree , unison-util-serialization , unison-util-term , unliftio From b3baee3ed80674676ee0a327b950f8095e192ccb Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 17 Jun 2024 16:17:00 -0400 Subject: [PATCH 13/81] make `todo` show dependencies without names --- .../src/Unison/Codebase/Branch.hs | 14 ++++- .../Codebase/Editor/HandleInput/Todo.hs | 34 +++++++++-- .../src/Unison/Codebase/Editor/Output.hs | 3 +- .../src/Unison/Codebase/Editor/TodoOutput.hs | 6 +- .../src/Unison/CommandLine/OutputMessages.hs | 57 ++++++++++++++----- 5 files changed, 93 insertions(+), 21 deletions(-) diff --git a/parser-typechecker/src/Unison/Codebase/Branch.hs b/parser-typechecker/src/Unison/Codebase/Branch.hs index 2e981501c9..68c55c88f8 100644 --- a/parser-typechecker/src/Unison/Codebase/Branch.hs +++ b/parser-typechecker/src/Unison/Codebase/Branch.hs @@ -90,7 +90,9 @@ module Unison.Codebase.Branch deepPaths, deepReferents, deepTermReferences, + deepTermReferenceIds, deepTypeReferences, + deepTypeReferenceIds, consBranchSnapshot, ) where @@ -136,7 +138,7 @@ import Unison.Name qualified as Name import Unison.NameSegment (NameSegment) import Unison.NameSegment qualified as NameSegment import Unison.Prelude hiding (empty) -import Unison.Reference (TermReference, TypeReference) +import Unison.Reference (TermReference, TermReferenceId, TypeReference, TypeReferenceId) import Unison.Referent (Referent) import Unison.Referent qualified as Referent import Unison.Util.List qualified as List @@ -146,6 +148,7 @@ import Unison.Util.Set qualified as Set import Unison.Util.Star2 qualified as Star2 import Witherable (FilterableWithIndex (imapMaybe)) import Prelude hiding (head, read, subtract) +import qualified Unison.Reference as Reference instance AsEmpty (Branch m) where _Empty = prism' (const empty) matchEmpty @@ -201,9 +204,18 @@ deepTermReferences :: Branch0 m -> Set TermReference deepTermReferences = Set.mapMaybe Referent.toTermReference . deepReferents +deepTermReferenceIds :: Branch0 m -> Set TermReferenceId +deepTermReferenceIds = + Set.mapMaybe Referent.toTermReferenceId . deepReferents + deepTypeReferences :: Branch0 m -> Set TypeReference deepTypeReferences = R.dom . deepTypes +deepTypeReferenceIds :: Branch0 m -> Set TypeReferenceId +deepTypeReferenceIds = + Set.mapMaybe Reference.toId . deepTypeReferences + + namespaceStats :: Branch0 m -> NamespaceStats namespaceStats b = NamespaceStats diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs index 4cfe62d3b8..2b24d33128 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -4,22 +4,48 @@ module Unison.Codebase.Editor.HandleInput.Todo ) where +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.PrettyPrintUtils 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.Output import Unison.Codebase.Editor.TodoOutput qualified as TO import Unison.Names qualified as Names +import Unison.Util.Defns (Defns (..)) handleTodo :: Cli () handleTodo = do - branch0 <- Cli.getCurrentBranch0 - let names0 = Branch.toNames branch0 + -- For now, we don't go through any great trouble to seek out the root of the project branch. Just assume the current + -- namespace is the root, which will be the case unless the user uses `deprecated.cd`. + currentNamespace <- Cli.getCurrentBranch0 + let currentNamespaceWithoutLibdeps = Branch.deleteLibdeps currentNamespace + + (hashLen, directDependencies) <- + Cli.runTransaction do + hashLen <- Codebase.hashLength + directDependencies <- + Operations.directDependenciesOfScope + Defns + { terms = Branch.deepTermReferenceIds currentNamespaceWithoutLibdeps, + types = Branch.deepTypeReferenceIds currentNamespaceWithoutLibdeps + } + pure (hashLen, directDependencies) + let todo = TO.TodoOutput - { nameConflicts = Names.conflicts names0 + { directDependenciesWithoutNames = + Defns + { terms = Set.difference directDependencies.terms (Branch.deepTermReferences currentNamespace), + types = Set.difference directDependencies.types (Branch.deepTypeReferences currentNamespace) + }, + nameConflicts = Names.conflicts (Branch.toNames currentNamespaceWithoutLibdeps) } + pped <- Cli.currentPrettyPrintEnvDecl - Cli.respondNumbered $ TodoOutput pped todo + + Cli.respondNumbered (TodoOutput hashLen pped todo) diff --git a/unison-cli/src/Unison/Codebase/Editor/Output.hs b/unison-cli/src/Unison/Codebase/Editor/Output.hs index 027d95ceb0..d36c7dfee5 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Output.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Output.hs @@ -117,8 +117,7 @@ data NumberedOutput | ShowDiffAfterPull Path.Path' Path.Absolute PPE.PrettyPrintEnv (BranchDiffOutput Symbol Ann) | -- ShowDiffAfterCreateAuthor NameSegment Path.Path' Path.Absolute PPE.PrettyPrintEnv (BranchDiffOutput Symbol Ann) - | -- | Invariant: there's at least one conflict or edit in the TodoOutput. - TodoOutput PPE.PrettyPrintEnvDecl (TO.TodoOutput Symbol Ann) + | TodoOutput !Int !PPE.PrettyPrintEnvDecl !(TO.TodoOutput Symbol Ann) | -- | CantDeleteDefinitions ppe couldntDelete becauseTheseStillReferenceThem CantDeleteDefinitions PPE.PrettyPrintEnvDecl (Map LabeledDependency (NESet LabeledDependency)) | -- | CantDeleteNamespace ppe couldntDelete becauseTheseStillReferenceThem diff --git a/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs b/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs index e4c859a9f1..48b1e40a85 100644 --- a/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs @@ -5,9 +5,13 @@ module Unison.Codebase.Editor.TodoOutput where import Unison.Names (Names) +import Unison.Prelude +import Unison.Reference (TermReference, TypeReference) +import Unison.Util.Defns (DefnsF) data TodoOutput v a = TodoOutput - { nameConflicts :: Names + { directDependenciesWithoutNames :: DefnsF Set TermReference TypeReference, + nameConflicts :: Names } noConflicts :: TodoOutput v a -> Bool diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 20ff1a4f53..251d11c758 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -142,6 +142,7 @@ import Unison.Term (Term) import Unison.Term qualified as Term import Unison.Type (Type) import Unison.UnisonFile qualified as UF +import Unison.Util.Defns (Defns (..), defnsAreEmpty) import Unison.Util.List qualified as List import Unison.Util.Monoid (intercalateMap) import Unison.Util.Monoid qualified as Monoid @@ -306,7 +307,7 @@ notifyNumbered = \case ] ) (showDiffNamespace ShowNumbers ppe (absPathToBranchId bAbs) (absPathToBranchId bAbs) diff) - TodoOutput names todo -> todoOutput names todo + TodoOutput hashLen names todo -> todoOutput hashLen names todo CantDeleteDefinitions ppeDecl endangerments -> ( P.warnCallout $ P.lines @@ -2590,8 +2591,7 @@ renderNameConflicts ppe conflictedNames = do [ prettyConflictedTypes, prettyConflictedTerms, tip $ - "This occurs when merging branches that both independently introduce the same name." - <> "Use " + "Use " <> makeExample' ( if (not . null) conflictedTypeNames then IP.renameType @@ -2609,7 +2609,7 @@ renderNameConflicts ppe conflictedNames = do showConflictedNames :: Pretty -> Map Name [HQ.HashQualified Name] -> Numbered Pretty showConflictedNames thingKind conflictedNames = P.lines <$> do - for (Map.toList conflictedNames) $ \(name, hashes) -> do + for (Map.toList conflictedNames) \(name, hashes) -> do prettyConflicts <- for hashes \hash -> do n <- addNumberedArg $ SA.HashQualified hash pure $ formatNum n <> (P.blue . P.syntaxToColor . prettyHashQualified $ hash) @@ -2627,8 +2627,9 @@ type Numbered = State.State (Int, Seq.Seq StructuredArgument) addNumberedArg :: StructuredArgument -> Numbered Int addNumberedArg s = do (n, args) <- State.get - State.put (n + 1, args Seq.|> s) - pure $ (n + 1) + let !n' = n + 1 + State.put (n', args Seq.|> s) + pure n' formatNum :: Int -> Pretty formatNum n = P.string (show n <> ". ") @@ -2638,13 +2639,43 @@ runNumbered m = let (a, (_, args)) = State.runState m (0, mempty) in (a, Foldable.toList args) -todoOutput :: (Var v) => PPED.PrettyPrintEnvDecl -> TO.TodoOutput v a -> (Pretty, NumberedArgs) -todoOutput ppe todo = runNumbered do - if TO.noConflicts todo - then pure mempty - else do - nameConflicts <- renderNameConflicts ppeu (TO.nameConflicts todo) - pure $ P.lines $ P.nonEmpty [nameConflicts] +todoOutput :: (Var v) => Int -> PPED.PrettyPrintEnvDecl -> TO.TodoOutput v a -> (Pretty, NumberedArgs) +todoOutput hashLen ppe todo = + runNumbered do + prettyConflicts <- + if TO.noConflicts todo + then pure mempty + else renderNameConflicts ppeu todo.nameConflicts + + prettyDirectTermDependenciesWithoutNames <- do + if Set.null todo.directDependenciesWithoutNames.terms + then pure mempty + else do + terms <- + for (Set.toList todo.directDependenciesWithoutNames.terms) \term -> do + n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash term))) + pure (formatNum n <> P.syntaxToColor (prettyReference hashLen term)) + pure $ + P.wrap "These terms do not have any names in the current namespace:" + `P.hang` P.lines terms + + prettyDirectTypeDependenciesWithoutNames <- do + if Set.null todo.directDependenciesWithoutNames.types + then pure mempty + else do + types <- + for (Set.toList todo.directDependenciesWithoutNames.types) \typ -> do + n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash typ))) + pure (formatNum n <> P.syntaxToColor (prettyReference hashLen typ)) + pure $ + P.wrap "These types do not have any names in the current namespace:" + `P.hang` P.lines types + + (pure . P.sep "\n\n" . P.nonEmpty) + [ prettyConflicts, + prettyDirectTermDependenciesWithoutNames, + prettyDirectTypeDependenciesWithoutNames + ] where ppeu = PPED.unsuffixifiedPPE ppe From 87c2f868be11303ec584bac7c874670452bb7cee Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 17 Jun 2024 22:14:29 -0400 Subject: [PATCH 14/81] bugfix: switch to most recently visited branch --- unison-cli/src/Unison/Cli/ProjectUtils.hs | 4 +-- .../Editor/HandleInput/CommitUpgrade.hs | 20 +++++------ .../Editor/HandleInput/ProjectSwitch.hs | 35 +++++++++---------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/unison-cli/src/Unison/Cli/ProjectUtils.hs b/unison-cli/src/Unison/Cli/ProjectUtils.hs index f184349fd4..0735d4d1ae 100644 --- a/unison-cli/src/Unison/Cli/ProjectUtils.hs +++ b/unison-cli/src/Unison/Cli/ProjectUtils.hs @@ -123,8 +123,8 @@ justTheIds x = ProjectAndBranch x.project.projectId x.branch.branchId justTheIds' :: Sqlite.ProjectBranch -> ProjectAndBranch ProjectId ProjectBranchId -justTheIds' x = - ProjectAndBranch x.projectId x.branchId +justTheIds' branch = + ProjectAndBranch branch.projectId branch.branchId justTheNames :: ProjectAndBranch Sqlite.Project Sqlite.ProjectBranch -> ProjectAndBranch ProjectName ProjectBranchName justTheNames x = diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs index 76229b8bfd..daa3c38e13 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitUpgrade.hs @@ -5,13 +5,13 @@ module Unison.Codebase.Editor.HandleInput.CommitUpgrade where import U.Codebase.Sqlite.Project qualified +import U.Codebase.Sqlite.ProjectBranch (ProjectBranch (..)) import U.Codebase.Sqlite.Queries qualified as Queries import Unison.Cli.Monad (Cli) import Unison.Cli.Monad qualified as Cli import Unison.Cli.ProjectUtils qualified as ProjectUtils import Unison.Codebase.Editor.HandleInput.DeleteBranch qualified as DeleteBranch import Unison.Codebase.Editor.HandleInput.Merge2 qualified as Merge -import Unison.Codebase.Editor.HandleInput.ProjectSwitch qualified as ProjectSwitch import Unison.Codebase.Editor.Output qualified as Output import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Prelude @@ -21,27 +21,25 @@ handleCommitUpgrade :: Cli () handleCommitUpgrade = do (upgradeProjectAndBranch, _path) <- ProjectUtils.expectCurrentProjectBranch - -- Assert that this is an "upgrade" branch and get its parent, which is the branch we were on when we ran `upgrade`. + -- Assert that this is an "upgrade" branch, get its parent (which is the branch we were on when we ran `upgrade`), + -- and switch to the parent. parentBranchId <- ProjectUtils.getUpgradeBranchParent upgradeProjectAndBranch.branch & onNothing (Cli.returnEarly Output.NoUpgradeInProgress) + parentBranch <- Cli.runTransaction do - Queries.expectProjectBranch upgradeProjectAndBranch.project.projectId parentBranchId - - let parentProjectAndBranch = - ProjectAndBranch upgradeProjectAndBranch.project parentBranch - - -- Switch to the parent - - ProjectSwitch.switchToProjectBranch (ProjectUtils.justTheIds parentProjectAndBranch) + parentBranch <- Queries.expectProjectBranch upgradeProjectAndBranch.project.projectId parentBranchId + Queries.setMostRecentBranch parentBranch.projectId parentBranch.branchId + pure parentBranch + Cli.cd (ProjectUtils.projectBranchPath (ProjectAndBranch parentBranch.projectId parentBranch.branchId)) -- Merge the upgrade branch into the parent Merge.doMergeLocalBranch TwoWay - { alice = parentProjectAndBranch, + { alice = ProjectAndBranch upgradeProjectAndBranch.project parentBranch, bob = upgradeProjectAndBranch } diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectSwitch.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectSwitch.hs index 688ba58363..ef668fa477 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectSwitch.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/ProjectSwitch.hs @@ -1,13 +1,12 @@ -- | @switch@ input handler module Unison.Codebase.Editor.HandleInput.ProjectSwitch ( projectSwitch, - switchToProjectBranch, ) where import Data.These (These (..)) -import U.Codebase.Sqlite.DbId (ProjectBranchId, ProjectId) import U.Codebase.Sqlite.Project qualified +import U.Codebase.Sqlite.ProjectBranch (ProjectBranch (..)) import U.Codebase.Sqlite.Queries qualified as Queries import Unison.Cli.Monad (Cli) import Unison.Cli.Monad qualified as Cli @@ -59,21 +58,21 @@ switchToProjectAndBranchByTheseNames projectAndBranchNames0 = do project <- Queries.loadProjectByName projectName & onNothingM do rollback (Output.LocalProjectDoesntExist projectName) - let branchName = unsafeFrom @Text "main" - Queries.loadProjectBranchByName project.projectId branchName & onNothingM do - rollback (Output.LocalProjectBranchDoesntExist (ProjectAndBranch projectName branchName)) + Queries.loadMostRecentBranch project.projectId >>= \case + Nothing -> do + let branchName = unsafeFrom @Text "main" + branch <- + Queries.loadProjectBranchByName project.projectId branchName & onNothingM do + rollback (Output.LocalProjectBranchDoesntExist (ProjectAndBranch projectName branchName)) + Queries.setMostRecentBranch branch.projectId branch.branchId + pure branch + Just branchId -> Queries.expectProjectBranch project.projectId branchId _ -> do - projectAndBranchNames@(ProjectAndBranch projectName branchName) <- ProjectUtils.hydrateNames projectAndBranchNames0 + projectAndBranchNames <- ProjectUtils.hydrateNames projectAndBranchNames0 Cli.runTransactionWithRollback \rollback -> do - Queries.loadProjectBranchByNames projectName branchName & onNothingM do - rollback (Output.LocalProjectBranchDoesntExist projectAndBranchNames) - switchToProjectBranch (ProjectUtils.justTheIds' branch) - --- | Switch to a branch: --- --- * Record it as the most-recent branch (so it's restored when ucm starts). --- * Change the current path in the in-memory loop state. -switchToProjectBranch :: ProjectAndBranch ProjectId ProjectBranchId -> Cli () -switchToProjectBranch x = do - Cli.runTransaction (Queries.setMostRecentBranch x.project x.branch) - Cli.cd (ProjectUtils.projectBranchPath x) + branch <- + Queries.loadProjectBranchByNames projectAndBranchNames.project projectAndBranchNames.branch & onNothingM do + rollback (Output.LocalProjectBranchDoesntExist projectAndBranchNames) + Queries.setMostRecentBranch branch.projectId branch.branchId + pure branch + Cli.cd (ProjectUtils.projectBranchPath (ProjectAndBranch branch.projectId branch.branchId)) From 3ec5a5f27a0fd19c8303c700d1a19142f5a43740 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 17 Jun 2024 22:19:05 -0400 Subject: [PATCH 15/81] fix up merge.commit --- .../Editor/HandleInput/CommitMerge.hs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs index 1c9061e5d1..9631295219 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/CommitMerge.hs @@ -5,13 +5,13 @@ module Unison.Codebase.Editor.HandleInput.CommitMerge where import U.Codebase.Sqlite.Project qualified +import U.Codebase.Sqlite.ProjectBranch (ProjectBranch (..)) import U.Codebase.Sqlite.Queries qualified as Queries import Unison.Cli.Monad (Cli) import Unison.Cli.Monad qualified as Cli import Unison.Cli.ProjectUtils qualified as ProjectUtils import Unison.Codebase.Editor.HandleInput.DeleteBranch qualified as DeleteBranch import Unison.Codebase.Editor.HandleInput.Merge2 qualified as Merge -import Unison.Codebase.Editor.HandleInput.ProjectSwitch qualified as ProjectSwitch import Unison.Codebase.Editor.Output qualified as Output import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Prelude @@ -23,27 +23,25 @@ handleCommitMerge :: Cli () handleCommitMerge = do (mergeProjectAndBranch, _path) <- ProjectUtils.expectCurrentProjectBranch - -- Assert that this is a "merge" branch and get its parent, which is the branch we were on when we ran `merge`. + -- Assert that this is a "merge" branch, get its parent (which is the branch we were on when we ran `merge`), + -- and switch to the parent. parentBranchId <- ProjectUtils.getMergeBranchParent mergeProjectAndBranch.branch & onNothing (Cli.returnEarly Output.NoMergeInProgress) + parentBranch <- Cli.runTransaction do - Queries.expectProjectBranch mergeProjectAndBranch.project.projectId parentBranchId - - let parentProjectAndBranch = - ProjectAndBranch mergeProjectAndBranch.project parentBranch - - -- Switch to the parent - - ProjectSwitch.switchToProjectBranch (ProjectUtils.justTheIds parentProjectAndBranch) + parentBranch <- Queries.expectProjectBranch mergeProjectAndBranch.project.projectId parentBranchId + Queries.setMostRecentBranch parentBranch.projectId parentBranch.branchId + pure parentBranch + Cli.cd (ProjectUtils.projectBranchPath (ProjectAndBranch parentBranch.projectId parentBranch.branchId)) -- Merge the merge branch into the parent Merge.doMergeLocalBranch TwoWay - { alice = parentProjectAndBranch, + { alice = ProjectAndBranch mergeProjectAndBranch.project parentBranch, bob = mergeProjectAndBranch } From a0527457a611ebb4ac68606cbdc0fbeeec36c332 Mon Sep 17 00:00:00 2001 From: Dan Doel Date: Tue, 18 Jun 2024 12:26:25 -0400 Subject: [PATCH 16/81] Implement custom Word64 delay The builtin is specified to take a Nat, but Haskell's threadDelay takes an Int. So the previous implementation was erroneously treating large numbers as negatives and not sleeping at all. Instead we split the wait into multiple delays for large numbers (although practically the second delay won't be reached). --- parser-typechecker/src/Unison/Runtime/Builtin.hs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/parser-typechecker/src/Unison/Runtime/Builtin.hs b/parser-typechecker/src/Unison/Runtime/Builtin.hs index 3feb0d55e0..f9c827fda9 100644 --- a/parser-typechecker/src/Unison/Runtime/Builtin.hs +++ b/parser-typechecker/src/Unison/Runtime/Builtin.hs @@ -2599,8 +2599,16 @@ declareForeigns = do declareForeign Tracked "IO.kill.impl.v3" boxToEF0 $ mkForeignIOF killThread + let mx :: Word64 + mx = fromIntegral (maxBound :: Int) + + customDelay :: Word64 -> IO () + customDelay n + | n < mx = threadDelay (fromIntegral n) + | otherwise = threadDelay maxBound >> customDelay (n - mx) + declareForeign Tracked "IO.delay.impl.v3" natToEFUnit $ - mkForeignIOF threadDelay + mkForeignIOF customDelay declareForeign Tracked "IO.stdHandle" standard'handle . mkForeign From c004c59189d7b2405fcfea59b30f695537cc35ce Mon Sep 17 00:00:00 2001 From: Arya Irani <538571+aryairani@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:56:49 -0400 Subject: [PATCH 17/81] Update ormolu.yaml --- .github/workflows/ormolu.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ormolu.yaml b/.github/workflows/ormolu.yaml index bc0f67f460..008745ee66 100644 --- a/.github/workflows/ormolu.yaml +++ b/.github/workflows/ormolu.yaml @@ -21,7 +21,7 @@ jobs: - name: create pull request with formatting changes uses: peter-evans/create-pull-request@v6 with: - commit_message: automatically run ormolu + commit-message: automatically run ormolu branch: autoformat/${{github.ref_name}} # branch_suffix: random title: format `${{github.ref_name}}` with ormolu ${{env.ormolu_version}} From 0f2e4ba18f6c1e77526b68d388bb23fe34a5193e Mon Sep 17 00:00:00 2001 From: Arya Irani <538571+aryairani@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:02:47 -0400 Subject: [PATCH 18/81] Update ormolu.yaml --- .github/workflows/ormolu.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ormolu.yaml b/.github/workflows/ormolu.yaml index 008745ee66..6a7fe9f22b 100644 --- a/.github/workflows/ormolu.yaml +++ b/.github/workflows/ormolu.yaml @@ -23,5 +23,5 @@ jobs: with: commit-message: automatically run ormolu branch: autoformat/${{github.ref_name}} - # branch_suffix: random + branch-suffix: short-commit-hash title: format `${{github.ref_name}}` with ormolu ${{env.ormolu_version}} From 56a7ff4bedc22ef3d18e5e838240a861a892a588 Mon Sep 17 00:00:00 2001 From: Arya Irani <538571+aryairani@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:13:20 -0400 Subject: [PATCH 19/81] updating tj-actions/changed-files to v44 to hopefully avoid this error https://github.com/unisonweb/unison/actions/runs/9551944735/job/26327430910 https://github.com/tj-actions/changed-files/issues/778 --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4ee48187bd..35489112f1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -38,7 +38,7 @@ jobs: - uses: actions/checkout@v4 - name: Get changed files id: changed-files - uses: tj-actions/changed-files@v41 + uses: tj-actions/changed-files@v44 with: # globs copied from default settings for run-ormolu files: | From a9f05bbf8acc076fba6fd4179e5af99864a6258e Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Thu, 20 Jun 2024 09:29:53 -0400 Subject: [PATCH 20/81] tweak rendering --- unison-cli/src/Unison/CommandLine/OutputMessages.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index f03bcd93a2..c2531ab46f 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -2640,7 +2640,9 @@ renderNameConflicts ppe conflictedNames = do <> P.green (prettyName name) <> " has conflicting definitions:" ) - `P.hang` P.lines prettyConflicts + <> P.newline + <> P.newline + <> P.indentN 2 (P.lines prettyConflicts) type Numbered = State.State (Int, Seq.Seq StructuredArgument) @@ -2677,7 +2679,9 @@ todoOutput hashLen ppe todo = pure (formatNum n <> P.syntaxToColor (prettyReference hashLen term)) pure $ P.wrap "These terms do not have any names in the current namespace:" - `P.hang` P.lines terms + <> P.newline + <> P.newline + <> P.indentN 2 (P.lines terms) prettyDirectTypeDependenciesWithoutNames <- do if Set.null todo.directDependenciesWithoutNames.types @@ -2689,7 +2693,9 @@ todoOutput hashLen ppe todo = pure (formatNum n <> P.syntaxToColor (prettyReference hashLen typ)) pure $ P.wrap "These types do not have any names in the current namespace:" - `P.hang` P.lines types + <> P.newline + <> P.newline + <> P.indentN 2 (P.lines types) (pure . P.sep "\n\n" . P.nonEmpty) [ prettyConflicts, From 3504ae7e563ff4273e677d9f600deb8d882e1468 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Thu, 20 Jun 2024 11:48:51 -0400 Subject: [PATCH 21/81] delete unused import --- unison-cli/src/Unison/CommandLine/OutputMessages.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index c2531ab46f..87e06282d0 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -142,7 +142,7 @@ import Unison.Term (Term) import Unison.Term qualified as Term import Unison.Type (Type) import Unison.UnisonFile qualified as UF -import Unison.Util.Defns (Defns (..), defnsAreEmpty) +import Unison.Util.Defns (Defns (..)) import Unison.Util.List qualified as List import Unison.Util.Monoid (intercalateMap) import Unison.Util.Monoid qualified as Monoid From caac3c47d7bf7986509cfb89bf67212a92f714a9 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Sat, 15 Jun 2024 11:14:28 -0500 Subject: [PATCH 22/81] Improve some lexer error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The message mentioned in #5060 was incorrect. This also uses the passed- in `id` for that message and related messages (removing the reliance on ANSI colors for conveying where the error is). And it adds a suggestion on how to avoid the error. It also does some minor adjustment of highlighting – styling individual identifiers rather that a list of them. Fixes #5060 --- parser-typechecker/src/Unison/PrintError.hs | 38 ++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/parser-typechecker/src/Unison/PrintError.hs b/parser-typechecker/src/Unison/PrintError.hs index d95ffed4a1..77dcdd0b0a 100644 --- a/parser-typechecker/src/Unison/PrintError.hs +++ b/parser-typechecker/src/Unison/PrintError.hs @@ -1366,31 +1366,45 @@ renderParseErrors s = \case <> style ErrorSite (fromString open) <> ".\n\n" <> excerpt - L.InvalidWordyId _id -> + L.InvalidWordyId id -> Pr.lines - [ "This identifier isn't valid syntax: ", + [ "The identifier " <> style Code id <> " isn't valid syntax: ", "", excerpt, "Here's a few examples of valid syntax: " - <> style Code "abba1', snake_case, Foo.zoink!, 🌻" + <> style Code "abba1'" + <> ", " + <> style Code "snake_case" + <> ", " + <> style Code "Foo.zoink!" + <> ", and " + <> style Code "🌻" ] - L.ReservedWordyId _id -> + L.ReservedWordyId id -> Pr.lines - [ "The identifier used here isn't allowed to be a reserved keyword: ", + [ "The identifier " <> style Code id <> " used here is a reserved keyword: ", "", - excerpt + excerpt, + Pr.wrap $ + "You can avoid this problem either by renaming the identifier or wrapping it in backticks (like " + <> style Code ("`" <> id <> "`") + <> ")." ] - L.InvalidSymbolyId _id -> + L.InvalidSymbolyId id -> Pr.lines - [ "This infix identifier isn't valid syntax: ", + [ "The infix identifier " <> style Code id <> " isn’t valid syntax: ", "", excerpt, - "Here's a few valid examples: " - <> style Code "++, Float./, `List.map`" + "Here are a few valid examples: " + <> style Code "++" + <> ", " + <> style Code "Float./" + <> ", and " + <> style Code "`List.map`" ] - L.ReservedSymbolyId _id -> + L.ReservedSymbolyId id -> Pr.lines - [ "This identifier is reserved by Unison and can't be used as an operator: ", + [ "The identifier " <> style Code id <> " is reserved by Unison and can't be used as an operator: ", "", excerpt ] From bb4f39fb2feb0525622647b3f4041a1293223155 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Thu, 20 Jun 2024 13:44:55 -0400 Subject: [PATCH 23/81] Update error message in transcripts --- unison-src/transcripts/error-messages.output.md | 4 +++- unison-src/transcripts/generic-parse-errors.output.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/unison-src/transcripts/error-messages.output.md b/unison-src/transcripts/error-messages.output.md index 525df31ee9..eb01548045 100644 --- a/unison-src/transcripts/error-messages.output.md +++ b/unison-src/transcripts/error-messages.output.md @@ -343,10 +343,12 @@ use.keyword.in.namespace = 1 Loading changes detected in scratch.u. - The identifier used here isn't allowed to be a reserved keyword: + The identifier namespace used here is a reserved keyword: 1 | use.keyword.in.namespace = 1 + You can avoid this problem either by renaming the identifier + or wrapping it in backticks (like `namespace` ). ``` ```unison diff --git a/unison-src/transcripts/generic-parse-errors.output.md b/unison-src/transcripts/generic-parse-errors.output.md index b055ba9689..039a1fb002 100644 --- a/unison-src/transcripts/generic-parse-errors.output.md +++ b/unison-src/transcripts/generic-parse-errors.output.md @@ -30,10 +30,12 @@ namespace.blah = 1 Loading changes detected in scratch.u. - The identifier used here isn't allowed to be a reserved keyword: + The identifier namespace used here is a reserved keyword: 1 | namespace.blah = 1 + You can avoid this problem either by renaming the identifier + or wrapping it in backticks (like `namespace` ). ``` ```unison From 5353a4e64cb77e43bde3ff45baa633573b007aa5 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Fri, 14 Jun 2024 10:16:19 -0400 Subject: [PATCH 24/81] properly handle deleted constructors in merge --- .../Codebase/Editor/HandleInput/Merge2.hs | 57 ++++---- .../src/Unison/Merge/DeclCoherencyCheck.hs | 38 ++++-- unison-merge/src/Unison/Merge/Diff.hs | 122 +++++++++++------- .../src/Unison/Merge/PartialDeclNameLookup.hs | 15 +++ unison-merge/src/Unison/Merge/Synhash.hs | 48 +++---- unison-merge/unison-merge.cabal | 1 + unison-src/transcripts/merge.md | 105 ++++++++++----- unison-src/transcripts/merge.output.md | 99 +++++++++++++- 8 files changed, 333 insertions(+), 152 deletions(-) create mode 100644 unison-merge/src/Unison/Merge/PartialDeclNameLookup.hs diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs index ba382842ba..ceee0aa836 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs @@ -138,6 +138,7 @@ import Unison.Util.SyntaxText (SyntaxText') import Unison.Var (Var) import Witch (unsafeFrom) import Prelude hiding (unzip, zip, zipWith) +import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup) handleMerge :: ProjectAndBranch (Maybe ProjectName) ProjectBranchName -> Cli () handleMerge (ProjectAndBranch maybeBobProjectName bobBranchName) = do @@ -245,7 +246,7 @@ doMerge info = do done (Output.MergeDefnsInLib who) -- Load Alice/Bob/LCA definitions and decl name lookups - (defns3, declNameLookups, lcaDeclToConstructors) <- do + (defns3, declNameLookups, lcaDeclNameLookup) <- do let emptyNametree = Nametree {value = Defns Map.empty Map.empty, children = Map.empty} let loadDefns branch = Cli.runTransaction (loadNamespaceDefinitions (referent2to1 db) branch) & onLeftM \conflictedName -> @@ -270,20 +271,20 @@ doMerge info = do (aliceDefns0, aliceDeclNameLookup) <- load (Just (mergeTarget, branches.alice)) (bobDefns0, bobDeclNameLookup) <- load (Just (mergeSource, branches.bob)) lcaDefns0 <- maybe (pure emptyNametree) loadDefns branches.lca - lcaDeclToConstructors <- Cli.runTransaction (lenientCheckDeclCoherency db.loadDeclNumConstructors lcaDefns0) + lcaDeclNameLookup <- Cli.runTransaction (lenientCheckDeclCoherency db.loadDeclNumConstructors lcaDefns0) let flatten defns = Defns (flattenNametree (view #terms) defns) (flattenNametree (view #types) defns) let defns3 = flatten <$> ThreeWay {alice = aliceDefns0, bob = bobDefns0, lca = lcaDefns0} let declNameLookups = TwoWay {alice = aliceDeclNameLookup, bob = bobDeclNameLookup} - pure (defns3, declNameLookups, lcaDeclToConstructors) + pure (defns3, declNameLookups, lcaDeclNameLookup) let defns = ThreeWay.forgetLca defns3 - liftIO (debugFunctions.debugDefns defns3 declNameLookups lcaDeclToConstructors) + liftIO (debugFunctions.debugDefns defns3 declNameLookups lcaDeclNameLookup) -- Diff LCA->Alice and LCA->Bob - diffs <- Cli.runTransaction (Merge.nameBasedNamespaceDiff db declNameLookups lcaDeclToConstructors defns3) + diffs <- Cli.runTransaction (Merge.nameBasedNamespaceDiff db declNameLookups lcaDeclNameLookup defns3) liftIO (debugFunctions.debugDiffs diffs) @@ -1038,7 +1039,7 @@ data DebugFunctions = DebugFunctions debugDefns :: ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) -> TwoWay DeclNameLookup -> - Map Name [Maybe Name] -> + PartialDeclNameLookup -> IO (), debugDiffs :: TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference) -> IO (), debugCombinedDiff :: DefnsF2 (Map Name) CombinedDiffOp Referent TypeReference -> IO (), @@ -1080,7 +1081,7 @@ realDebugCausals causals = do realDebugDefns :: ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) -> TwoWay DeclNameLookup -> - Map Name [Maybe Name] -> + PartialDeclNameLookup -> IO () realDebugDefns defns declNameLookups _lcaDeclNameLookup = do Text.putStrLn (Text.bold "\n=== Alice definitions ===") @@ -1200,28 +1201,28 @@ realDebugPartitionedDiff conflicts unconflicts = do renderConflicts "typeid" conflicts.bob.types (Bob ()) Text.putStrLn (Text.bold "\n=== Alice unconflicts ===") - renderUnconflicts Text.green "+" referentLabel Referent.toText unconflicts.terms.adds.alice (OnlyAlice ()) - renderUnconflicts Text.green "+" (const "type") Reference.toText unconflicts.types.adds.alice (OnlyAlice ()) - renderUnconflicts Text.red "-" referentLabel Referent.toText unconflicts.terms.deletes.alice (OnlyAlice ()) - renderUnconflicts Text.red "-" (const "type") Reference.toText unconflicts.types.deletes.alice (OnlyAlice ()) - renderUnconflicts Text.yellow "%" referentLabel Referent.toText unconflicts.terms.updates.alice (OnlyAlice ()) - renderUnconflicts Text.yellow "%" (const "type") Reference.toText unconflicts.types.updates.alice (OnlyAlice ()) + renderUnconflicts Text.green "+" referentLabel Referent.toText unconflicts.terms.adds.alice + renderUnconflicts Text.green "+" (const "type") Reference.toText unconflicts.types.adds.alice + renderUnconflicts Text.red "-" referentLabel Referent.toText unconflicts.terms.deletes.alice + renderUnconflicts Text.red "-" (const "type") Reference.toText unconflicts.types.deletes.alice + renderUnconflicts Text.yellow "%" referentLabel Referent.toText unconflicts.terms.updates.alice + renderUnconflicts Text.yellow "%" (const "type") Reference.toText unconflicts.types.updates.alice Text.putStrLn (Text.bold "\n=== Bob unconflicts ===") - renderUnconflicts Text.green "+" referentLabel Referent.toText unconflicts.terms.adds.bob (OnlyBob ()) - renderUnconflicts Text.green "+" (const "type") Reference.toText unconflicts.types.adds.bob (OnlyBob ()) - renderUnconflicts Text.red "-" referentLabel Referent.toText unconflicts.terms.deletes.bob (OnlyBob ()) - renderUnconflicts Text.red "-" (const "type") Reference.toText unconflicts.types.deletes.bob (OnlyBob ()) - renderUnconflicts Text.yellow "%" referentLabel Referent.toText unconflicts.terms.updates.bob (OnlyBob ()) - renderUnconflicts Text.yellow "%" (const "type") Reference.toText unconflicts.types.updates.bob (OnlyBob ()) + renderUnconflicts Text.green "+" referentLabel Referent.toText unconflicts.terms.adds.bob + renderUnconflicts Text.green "+" (const "type") Reference.toText unconflicts.types.adds.bob + renderUnconflicts Text.red "-" referentLabel Referent.toText unconflicts.terms.deletes.bob + renderUnconflicts Text.red "-" (const "type") Reference.toText unconflicts.types.deletes.bob + renderUnconflicts Text.yellow "%" referentLabel Referent.toText unconflicts.terms.updates.bob + renderUnconflicts Text.yellow "%" (const "type") Reference.toText unconflicts.types.updates.bob Text.putStrLn (Text.bold "\n=== Alice-and-Bob unconflicts ===") - renderUnconflicts Text.green "+" referentLabel Referent.toText unconflicts.terms.adds.both (AliceAndBob ()) - renderUnconflicts Text.green "+" (const "type") Reference.toText unconflicts.types.adds.both (AliceAndBob ()) - renderUnconflicts Text.red "-" referentLabel Referent.toText unconflicts.terms.deletes.both (AliceAndBob ()) - renderUnconflicts Text.red "-" (const "type") Reference.toText unconflicts.types.deletes.both (AliceAndBob ()) - renderUnconflicts Text.yellow "%" referentLabel Referent.toText unconflicts.terms.updates.both (AliceAndBob ()) - renderUnconflicts Text.yellow "%" (const "type") Reference.toText unconflicts.types.updates.both (AliceAndBob ()) + renderUnconflicts Text.green "+" referentLabel Referent.toText unconflicts.terms.adds.both + renderUnconflicts Text.green "+" (const "type") Reference.toText unconflicts.types.adds.both + renderUnconflicts Text.red "-" referentLabel Referent.toText unconflicts.terms.deletes.both + renderUnconflicts Text.red "-" (const "type") Reference.toText unconflicts.types.deletes.both + renderUnconflicts Text.yellow "%" referentLabel Referent.toText unconflicts.terms.updates.both + renderUnconflicts Text.yellow "%" (const "type") Reference.toText unconflicts.types.updates.both where renderConflicts :: Text -> Map Name Reference.Id -> EitherWay () -> IO () renderConflicts label conflicts who = @@ -1244,9 +1245,8 @@ realDebugPartitionedDiff conflicts unconflicts = do (ref -> Text) -> (ref -> Text) -> Map Name ref -> - EitherWayI () -> IO () - renderUnconflicts color action label renderRef unconflicts who = + renderUnconflicts color action label renderRef unconflicts = for_ (Map.toList unconflicts) \(name, ref) -> Text.putStrLn $ color $ @@ -1257,9 +1257,6 @@ realDebugPartitionedDiff conflicts unconflicts = do <> Name.toText name <> " " <> renderRef ref - <> " (" - <> (case who of OnlyAlice () -> "Alice"; OnlyBob () -> "Bob"; AliceAndBob () -> "Alice and Bob") - <> ")" realDebugDependents :: TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) -> IO () realDebugDependents dependents = do diff --git a/unison-merge/src/Unison/Merge/DeclCoherencyCheck.hs b/unison-merge/src/Unison/Merge/DeclCoherencyCheck.hs index a215354b3b..2a75252fcd 100644 --- a/unison-merge/src/Unison/Merge/DeclCoherencyCheck.hs +++ b/unison-merge/src/Unison/Merge/DeclCoherencyCheck.hs @@ -104,6 +104,7 @@ import U.Codebase.Reference (Reference' (..), TypeReference, TypeReferenceId) import Unison.ConstructorReference (GConstructorReference (..)) import Unison.DataDeclaration.ConstructorId (ConstructorId) import Unison.Merge.DeclNameLookup (DeclNameLookup (..)) +import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup (..)) import Unison.Name (Name) import Unison.Name qualified as Name import Unison.NameSegment (NameSegment) @@ -217,21 +218,21 @@ checkDeclCoherency loadDeclNumConstructors = fullName name = Name.fromReverseSegments (name :| prefix) --- | A lenient variant of 'checkDeclCoherency' - so lenient it can't even fail! It returns a mapping from decl name to --- constructor names, where constructor names can be missing. +-- | A lenient variant of 'checkDeclCoherency' - so lenient it can't even fail! It returns partial decl name lookup, +-- which doesn't require a name for every constructor, and allows a constructor with a nameless decl. -- --- This function exists merely to extract a best-effort decl-name-to-constructor-name mapping for the LCA of a merge. --- We require Alice and Bob to have coherent decls, but their LCA is out of the user's control and may have incoherent --- decls, and whether or not it does, we still need to compute *some* syntactic hash for its decls. +-- This function exists merely to extract a best-effort name mapping for the LCA of a merge. We require Alice and Bob to +-- have coherent decls, but their LCA is out of the user's control and may have incoherent decls, and whether or not it +-- does, we still need to compute *some* syntactic hash for its decls. lenientCheckDeclCoherency :: forall m. Monad m => (TypeReferenceId -> m Int) -> Nametree (DefnsF (Map NameSegment) Referent TypeReference) -> - m (Map Name [Maybe Name]) + m PartialDeclNameLookup lenientCheckDeclCoherency loadDeclNumConstructors = - fmap (view #declToConstructors) - . (`State.execStateT` LenientDeclCoherencyCheckState Map.empty Map.empty) + fmap (view #declNameLookup) + . (`State.execStateT` LenientDeclCoherencyCheckState Map.empty (PartialDeclNameLookup Map.empty Map.empty)) . go [] where go :: @@ -259,14 +260,14 @@ lenientCheckDeclCoherency loadDeclNumConstructors = lift (getCompose (Map.upsertF (\_ -> Compose recordNewDecl) typeRef state.expectedConstructors)) case whatHappened of UninhabitedDecl -> do - #declToConstructors %= Map.insert typeName [] + #declNameLookup . #declToConstructors %= Map.insert typeName [] pure Nothing InhabitedDecl expectedConstructors1 -> do let child = Map.findWithDefault (Nametree (Defns Map.empty Map.empty) Map.empty) name children #expectedConstructors .= expectedConstructors1 go (name : prefix) child state <- State.get - let (maybeConstructorNames, expectedConstructors) = + let (constructorNames0, expectedConstructors) = Map.alterF f typeRef state.expectedConstructors where f :: @@ -278,8 +279,21 @@ lenientCheckDeclCoherency loadDeclNumConstructors = fromJust >>> Map.deleteLookupJust typeName >>> over _2 \m -> if Map.null m then Nothing else Just m + + constructorNames :: [Maybe Name] + constructorNames = + IntMap.elems constructorNames0 + #expectedConstructors .= expectedConstructors - #declToConstructors %= Map.insert typeName (IntMap.elems maybeConstructorNames) + #declNameLookup . #constructorToDecl %= \constructorToDecl -> + List.foldl' + ( \acc -> \case + Nothing -> acc + Just constructorName -> Map.insert constructorName typeName acc + ) + constructorToDecl + constructorNames + #declNameLookup . #declToConstructors %= Map.insert typeName constructorNames pure (Just name) where typeName = fullName name @@ -298,7 +312,7 @@ data DeclCoherencyCheckState = DeclCoherencyCheckState data LenientDeclCoherencyCheckState = LenientDeclCoherencyCheckState { expectedConstructors :: !(Map TypeReferenceId (Map Name ConstructorNames)), - declToConstructors :: !(Map Name [Maybe Name]) + declNameLookup :: !PartialDeclNameLookup } deriving stock (Generic) diff --git a/unison-merge/src/Unison/Merge/Diff.hs b/unison-merge/src/Unison/Merge/Diff.hs index 754b36be78..1ad67238a4 100644 --- a/unison-merge/src/Unison/Merge/Diff.hs +++ b/unison-merge/src/Unison/Merge/Diff.hs @@ -9,6 +9,7 @@ import Data.Semialign (alignWith) import Data.Set qualified as Set import Data.These (These (..)) import U.Codebase.Reference (TypeReference) +import Unison.ConstructorReference (GConstructorReference (..)) import Unison.DataDeclaration (Decl) import Unison.DataDeclaration qualified as DataDeclaration import Unison.Hash (Hash (Hash)) @@ -17,6 +18,7 @@ import Unison.Merge.Database (MergeDatabase (..)) import Unison.Merge.DeclNameLookup (DeclNameLookup) import Unison.Merge.DeclNameLookup qualified as DeclNameLookup import Unison.Merge.DiffOp (DiffOp (..)) +import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup (..)) import Unison.Merge.Synhash import Unison.Merge.Synhashed (Synhashed (..)) import Unison.Merge.ThreeWay (ThreeWay (..)) @@ -30,6 +32,7 @@ import Unison.PrettyPrintEnv (PrettyPrintEnv (..)) import Unison.PrettyPrintEnv qualified as Ppe import Unison.Reference (Reference' (..), TypeReferenceId) import Unison.Referent (Referent) +import Unison.Referent qualified as Referent import Unison.Sqlite (Transaction) import Unison.Symbol (Symbol) import Unison.Syntax.Name qualified as Name @@ -48,52 +51,14 @@ import Unison.Util.Defns (Defns (..), DefnsF2, DefnsF3, zipDefnsWith) nameBasedNamespaceDiff :: MergeDatabase -> TwoWay DeclNameLookup -> - Map Name [Maybe Name] -> + PartialDeclNameLookup -> ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) -> Transaction (TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference)) -nameBasedNamespaceDiff db declNameLookups lcaDeclToConstructors defns = do - lcaHashes <- - synhashDefnsWith - hashTerm - ( \name -> \case - ReferenceBuiltin builtin -> pure (synhashBuiltinDecl builtin) - ReferenceDerived ref -> - case sequence (lcaDeclToConstructors Map.! name) of - -- If we don't have a name for every constructor, that's okay, just use a dummy syntactic hash here. - -- This is safe; Alice/Bob can't have such a decl (it violates a merge precondition), so there's no risk - -- that we accidentally get an equal hash and classify a real update as unchanged. - Nothing -> pure (Hash mempty) - Just names -> do - decl <- loadDeclWithGoodConstructorNames names ref - pure (synhashDerivedDecl ppe name decl) - ) - defns.lca - hashes <- sequence (synhashDefns <$> declNameLookups <*> ThreeWay.forgetLca defns) +nameBasedNamespaceDiff db declNameLookups lcaDeclNameLookup defns = do + lcaHashes <- synhashLcaDefns db ppe lcaDeclNameLookup defns.lca + hashes <- sequence (synhashDefns db ppe <$> declNameLookups <*> ThreeWay.forgetLca defns) pure (diffNamespaceDefns lcaHashes <$> hashes) where - synhashDefns :: - DeclNameLookup -> - Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name) -> - Transaction (DefnsF2 (Map Name) Synhashed Referent TypeReference) - synhashDefns declNameLookup = - -- FIXME: use cache so we only synhash each thing once - synhashDefnsWith hashTerm hashType - where - hashType :: Name -> TypeReference -> Transaction Hash - hashType name = \case - ReferenceBuiltin builtin -> pure (synhashBuiltinDecl builtin) - ReferenceDerived ref -> do - decl <- loadDeclWithGoodConstructorNames (DeclNameLookup.expectConstructorNames declNameLookup name) ref - pure (synhashDerivedDecl ppe name decl) - - loadDeclWithGoodConstructorNames :: [Name] -> TypeReferenceId -> Transaction (Decl Symbol Ann) - loadDeclWithGoodConstructorNames names = - fmap (DataDeclaration.setConstructorNames (map Name.toVar names)) . db.loadV1Decl - - hashTerm :: Referent -> Transaction Hash - hashTerm = - synhashTerm db.loadV1Term ppe - ppe :: PrettyPrintEnv ppe = -- The order between Alice and Bob isn't important here for syntactic hashing; not sure right now if it matters @@ -102,6 +67,71 @@ nameBasedNamespaceDiff db declNameLookups lcaDeclToConstructors defns = do `Ppe.addFallback` deepNamespaceDefinitionsToPpe defns.bob `Ppe.addFallback` deepNamespaceDefinitionsToPpe defns.lca +synhashLcaDefns :: + MergeDatabase -> + PrettyPrintEnv -> + PartialDeclNameLookup -> + Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name) -> + Transaction (DefnsF2 (Map Name) Synhashed Referent TypeReference) +synhashLcaDefns db ppe declNameLookup = + synhashDefnsWith hashReferent hashType + where + -- For the LCA only, if we don't have a name for every constructor, or we don't have a name for a decl, that's okay, + -- just use a dummy syntactic hash (e.g. where we return `Hash mempty` below in two places). + -- + -- This is safe and correct; Alice/Bob can't have such a decl (it violates a merge precondition), so there's no risk + -- that we accidentally get an equal hash and classify a real update as unchanged. + + hashReferent :: Name -> Referent -> Transaction Hash + hashReferent name = \case + Referent.Con (ConstructorReference ref _) _ -> + case Map.lookup name declNameLookup.constructorToDecl of + Nothing -> pure (Hash mempty) -- see note above + Just declName -> hashType declName ref + Referent.Ref ref -> synhashTerm db.loadV1Term ppe ref + + hashType :: Name -> TypeReference -> Transaction Hash + hashType name = \case + ReferenceBuiltin builtin -> pure (synhashBuiltinDecl builtin) + ReferenceDerived ref -> + case sequence (declNameLookup.declToConstructors Map.! name) of + Nothing -> pure (Hash mempty) -- see note above + Just names -> do + decl <- loadDeclWithGoodConstructorNames db names ref + pure (synhashDerivedDecl ppe name decl) + +synhashDefns :: + MergeDatabase -> + PrettyPrintEnv -> + DeclNameLookup -> + Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name) -> + Transaction (DefnsF2 (Map Name) Synhashed Referent TypeReference) +synhashDefns db ppe declNameLookup = + -- FIXME: use cache so we only synhash each thing once + synhashDefnsWith hashReferent hashType + where + hashReferent :: Name -> Referent -> Transaction Hash + hashReferent name = \case + -- We say that a referent constructor *in the namespace* (distinct from a referent that is in a term body) has a + -- synhash that is simply equal to the synhash of its type declaration. This is because the type declaration and + -- constructors are changed in lock-step: it is not possible to change one, but not the other. + -- + -- For example, if Alice updates `type Foo = Bar Nat` to `type Foo = Bar Nat Nat`, we want different synhashes on + -- both the type (Foo) and the constructor (Foo.Bar). + Referent.Con (ConstructorReference ref _) _ -> hashType (DeclNameLookup.expectDeclName declNameLookup name) ref + Referent.Ref ref -> synhashTerm db.loadV1Term ppe ref + + hashType :: Name -> TypeReference -> Transaction Hash + hashType name = \case + ReferenceBuiltin builtin -> pure (synhashBuiltinDecl builtin) + ReferenceDerived ref -> do + decl <- loadDeclWithGoodConstructorNames db (DeclNameLookup.expectConstructorNames declNameLookup name) ref + pure (synhashDerivedDecl ppe name decl) + +loadDeclWithGoodConstructorNames :: MergeDatabase -> [Name] -> TypeReferenceId -> Transaction (Decl Symbol Ann) +loadDeclWithGoodConstructorNames db names = + fmap (DataDeclaration.setConstructorNames (map Name.toVar names)) . db.loadV1Decl + diffNamespaceDefns :: DefnsF2 (Map Name) Synhashed term typ -> DefnsF2 (Map Name) Synhashed term typ -> @@ -139,17 +169,17 @@ deepNamespaceDefinitionsToPpe Defns {terms, types} = synhashDefnsWith :: Monad m => - (term -> m Hash) -> + (Name -> term -> m Hash) -> (Name -> typ -> m Hash) -> Defns (BiMultimap term Name) (BiMultimap typ Name) -> m (DefnsF2 (Map Name) Synhashed term typ) synhashDefnsWith hashTerm hashType = do bitraverse - (traverse hashTerm1 . BiMultimap.range) + (Map.traverseWithKey hashTerm1 . BiMultimap.range) (Map.traverseWithKey hashType1 . BiMultimap.range) where - hashTerm1 term = do - hash <- hashTerm term + hashTerm1 name term = do + hash <- hashTerm name term pure (Synhashed hash term) hashType1 name typ = do diff --git a/unison-merge/src/Unison/Merge/PartialDeclNameLookup.hs b/unison-merge/src/Unison/Merge/PartialDeclNameLookup.hs new file mode 100644 index 0000000000..556ea9f5dc --- /dev/null +++ b/unison-merge/src/Unison/Merge/PartialDeclNameLookup.hs @@ -0,0 +1,15 @@ +module Unison.Merge.PartialDeclNameLookup + ( PartialDeclNameLookup (..), + ) +where + +import Unison.Name (Name) +import Unison.Prelude + +-- | Like a @DeclNameLookup@, but "partial" / more lenient - because we don't require the LCA of a merge to have a full +-- @DeclNameLookup@. +data PartialDeclNameLookup = PartialDeclNameLookup + { constructorToDecl :: !(Map Name Name), + declToConstructors :: !(Map Name [Maybe Name]) + } + deriving stock (Generic) diff --git a/unison-merge/src/Unison/Merge/Synhash.hs b/unison-merge/src/Unison/Merge/Synhash.hs index 29559690bf..6acf835a75 100644 --- a/unison-merge/src/Unison/Merge/Synhash.hs +++ b/unison-merge/src/Unison/Merge/Synhash.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE DataKinds #-} + -- | Utilities for computing the "syntactic hash" of a decl or term, which is a hash that is computed after substituting -- references to other terms and decls with names from a pretty-print environment. -- @@ -35,7 +37,6 @@ import Data.Char (ord) import Data.Text qualified as Text import U.Codebase.Reference (TypeReference) import Unison.ABT qualified as ABT -import Unison.ConstructorReference (GConstructorReference (..)) import Unison.ConstructorType (ConstructorType) import Unison.ConstructorType qualified as CT import Unison.DataDeclaration (DataDeclaration, Decl) @@ -51,8 +52,9 @@ import Unison.Prelude import Unison.PrettyPrintEnv (PrettyPrintEnv) import Unison.PrettyPrintEnv qualified as PPE import Unison.Reference (Reference' (..), TypeReferenceId) -import Unison.Referent qualified as V1 (Referent) -import Unison.Referent qualified as V1.Referent +import Unison.Reference qualified as V1 +import Unison.Referent (Referent) +import Unison.Referent qualified as Referent import Unison.Syntax.Name qualified as Name (toText, unsafeParseVar) import Unison.Term (Term) import Unison.Term qualified as Term @@ -107,7 +109,7 @@ hashConstructorNameToken declName conName = hashDerivedTerm :: Var v => PrettyPrintEnv -> Term v a -> Hash hashDerivedTerm ppe t = - H.accumulate $ isNotBuiltinTag : hashTermTokens ppe t + H.accumulate $ isNotBuiltinTag : isTermTag : hashTermTokens ppe t hashConstructorType :: ConstructorType -> Token hashConstructorType = \case @@ -138,7 +140,7 @@ hashDeclTokens ppe name decl = -- syntactic hashes. synhashDerivedDecl :: Var v => PrettyPrintEnv -> Name -> Decl v a -> Hash synhashDerivedDecl ppe name decl = - H.accumulate $ isNotBuiltinTag : hashDeclTokens ppe name decl + H.accumulate $ isNotBuiltinTag : isDeclTag : hashDeclTokens ppe name decl hashHQNameToken :: HashQualified Name -> Token hashHQNameToken = @@ -170,14 +172,14 @@ hashPatternTokens ppe = \case Pattern.Char _ c -> [H.Tag 7, H.Nat (fromIntegral (ord c))] Pattern.Constructor _ cr ps -> H.Tag 8 - : hashReferentToken ppe (V1.Referent.Con cr CT.Data) + : hashReferentToken ppe (Referent.Con cr CT.Data) : hashLengthToken ps : (ps >>= hashPatternTokens ppe) Pattern.As _ p -> H.Tag 9 : hashPatternTokens ppe p Pattern.EffectPure _ p -> H.Tag 10 : hashPatternTokens ppe p Pattern.EffectBind _ cr ps k -> H.Tag 11 - : hashReferentToken ppe (V1.Referent.Con cr CT.Effect) + : hashReferentToken ppe (Referent.Con cr CT.Effect) : hashLengthToken ps : hashPatternTokens ppe k <> (ps >>= hashPatternTokens ppe) Pattern.SequenceLiteral _ ps -> H.Tag 12 : hashLengthToken ps : (ps >>= hashPatternTokens ppe) @@ -188,36 +190,20 @@ hashPatternTokens ppe = \case Pattern.Snoc -> H.Tag 1 Pattern.Cons -> H.Tag 2 -hashReferentToken :: PrettyPrintEnv -> V1.Referent -> Token +hashReferentToken :: PrettyPrintEnv -> Referent -> Token hashReferentToken ppe = - H.Hashed . H.accumulate . hashReferentTokens ppe - -hashReferentTokens :: PrettyPrintEnv -> V1.Referent -> [Token] -hashReferentTokens ppe referent = - case referent of - -- distinguish constructor name from terms by tumbling in a name (of any alias of) its decl - V1.Referent.Con (ConstructorReference ref _i) _ct -> [hashTypeReferenceToken ppe ref, nameTok] - V1.Referent.Ref _ -> [nameTok] - where - nameTok :: Token - nameTok = - hashHQNameToken (PPE.termNameOrHashOnlyFq ppe referent) + hashHQNameToken . PPE.termNameOrHashOnlyFq ppe --- | Syntactically hash a term, using reference names rather than hashes. --- Two terms will have the same syntactic hash if they would --- print the the same way under the given pretty-print env. synhashTerm :: forall m v a. (Monad m, Var v) => (TypeReferenceId -> m (Term v a)) -> PrettyPrintEnv -> - V1.Referent -> + V1.TermReference -> m Hash synhashTerm loadTerm ppe = \case - V1.Referent.Con ref CT.Data -> pure (hashDerivedTerm ppe (Term.constructor @v () ref)) - V1.Referent.Con ref CT.Effect -> pure (hashDerivedTerm ppe (Term.request @v () ref)) - V1.Referent.Ref (ReferenceBuiltin builtin) -> pure (hashBuiltinTerm builtin) - V1.Referent.Ref (ReferenceDerived ref) -> hashDerivedTerm ppe <$> loadTerm ref + ReferenceBuiltin builtin -> pure (hashBuiltinTerm builtin) + ReferenceDerived ref -> hashDerivedTerm ppe <$> loadTerm ref hashTermTokens :: forall v a. Var v => PrettyPrintEnv -> Term v a -> [Token] hashTermTokens ppe = @@ -242,9 +228,9 @@ hashTermFTokens ppe = \case Term.Char c -> [H.Tag 5, H.Nat (fromIntegral (ord c))] Term.Blank {} -> error "tried to hash a term with blanks, something's very wrong" -- note: these are all hashed the same, just based on the name - Term.Ref r -> [H.Tag 7, hashReferentToken ppe (V1.Referent.Ref r)] - Term.Constructor cr -> [H.Tag 7, hashReferentToken ppe (V1.Referent.Con cr CT.Data)] - Term.Request cr -> [H.Tag 7, hashReferentToken ppe (V1.Referent.Con cr CT.Effect)] + Term.Ref r -> [H.Tag 7, hashReferentToken ppe (Referent.Ref r)] + Term.Constructor cr -> [H.Tag 7, hashReferentToken ppe (Referent.Con cr CT.Data)] + Term.Request cr -> [H.Tag 7, hashReferentToken ppe (Referent.Con cr CT.Effect)] Term.Handle {} -> [H.Tag 8] Term.App {} -> [H.Tag 9] Term.Ann _ ty -> H.Tag 10 : hashTypeTokens ppe ty diff --git a/unison-merge/unison-merge.cabal b/unison-merge/unison-merge.cabal index 84baab088f..ab6bebe3db 100644 --- a/unison-merge/unison-merge.cabal +++ b/unison-merge/unison-merge.cabal @@ -26,6 +26,7 @@ library Unison.Merge.EitherWay Unison.Merge.EitherWayI Unison.Merge.Libdeps + Unison.Merge.PartialDeclNameLookup Unison.Merge.PartitionCombinedDiffs Unison.Merge.Synhash Unison.Merge.Synhashed diff --git a/unison-src/transcripts/merge.md b/unison-src/transcripts/merge.md index 9808153561..9436ae5232 100644 --- a/unison-src/transcripts/merge.md +++ b/unison-src/transcripts/merge.md @@ -14,7 +14,7 @@ contains both additions. ## Basic merge: two unconflicted adds ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm:hide @@ -56,7 +56,7 @@ project/alice> view foo bar If Alice and Bob also happen to add the same definition, that's not a conflict. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins project/main> branch alice ``` @@ -97,7 +97,7 @@ project/alice> view foo bar Updates that occur in one branch are propagated to the other. In this example, Alice updates `foo`, while Bob adds a new dependent `bar` of the original `foo`. When Bob's branch is merged into Alice's, her update to `foo` is propagated to his `bar`. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -152,7 +152,7 @@ We classify something as an update if its "syntactic hash"—not its normal Unis Let's see an example. We have `foo`, which depends on `bar` and `baz`. Alice updates `bar` (propagating to `foo`), and Bob updates `baz` (propagating to `foo`). When we merge their updates, both updates will be reflected in the final `foo`. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -216,7 +216,7 @@ project/alice> display foo Of course, it's also possible for Alice's update to propagate to one of Bob's updates. In this example, `foo` depends on `bar` which depends on `baz`. Alice updates `baz`, propagating to `bar` and `foo`, while Bob updates `bar` (to something that still depends on `foo`), propagating to `baz`. The merged result will have Alice's update to `foo` incorporated into Bob's updated `bar`, and both updates will propagate to `baz`. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -286,7 +286,7 @@ project/alice> display foo We don't currently consider "update + delete" a conflict like Git does. In this situation, the delete is just ignored, allowing the update to proceed. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -333,7 +333,7 @@ In a future version, we'd like to give the user a warning at least. Library dependencies don't cause merge conflicts, the library dependencies are just unioned together. If two library dependencies have the same name but different namespace hashes, then the merge algorithm makes up two fresh names. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Alice's adds: @@ -387,7 +387,7 @@ project/alice> view foo bar baz If Bob is equals Alice, then merging Bob into Alice looks like this. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm @@ -405,7 +405,7 @@ project/alice> merge /bob If Bob is behind Alice, then merging Bob into Alice looks like this. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm @@ -433,7 +433,7 @@ project/alice> merge /bob If Bob is ahead of Alice, then merging Bob into Alice looks like this. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm @@ -465,7 +465,7 @@ This can cause merge failures due to out-of-scope identifiers, and the user may In this example, Alice deletes `foo`, while Bob adds a new dependent of `foo`. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -508,7 +508,7 @@ It may be Alice's and Bob's changes merge together cleanly in the sense that the In this example, Alice updates a `Text` to a `Nat`, while Bob adds a new dependent of the `Text`. Upon merging, propagating Alice's update to Bob's dependent causes a typechecking failure. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -557,7 +557,7 @@ Alice and Bob may disagree about the definition of a term. In this case, the con are presented to the user to resolve. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -621,7 +621,7 @@ project/merge-bob-into-alice> view bar baz Ditto for types; if the hashes don't match, it's a conflict. In this example, Alice and Bob do different things to the same constructor. However, any explicit changes to the same type will result in a conflict, including changes that could concievably be merged (e.g. Alice and Bob both add a new constructor, or edit different constructors). ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -664,7 +664,7 @@ project/alice> merge /bob We model the renaming of a type's constructor as an update, so if Alice updates a type and Bob renames one of its constructors (even without changing its structure), we consider it a conflict. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -707,7 +707,7 @@ project/alice> merge /bob Here is another example demonstrating that constructor renames are modeled as updates. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -745,7 +745,7 @@ project/alice> merge bob A constructor on one side can conflict with a regular term definition on the other. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm:hide @@ -786,7 +786,7 @@ project/alice> merge bob Here's a subtle situation where a new type is added on each side of the merge, and an existing term is replaced with a constructor of one of the types. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -835,7 +835,7 @@ project/alice> merge bob Here's a more involved example that demonstrates the same idea. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` In the LCA, we have a type with two constructors, and some term. @@ -914,7 +914,7 @@ which is a parse error. We will resolve this situation automatically in a future version. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm:hide @@ -961,7 +961,7 @@ After merge conflicts are resolved, you can use `merge.commit` rather than `swit ```ucm:hide .> project.create-empty project -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -1026,7 +1026,7 @@ project/alice> branches ```ucm:hide .> project.create-empty project -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm @@ -1051,7 +1051,7 @@ There are a number of conditions under which we can't perform a merge, and the u If `foo` and `bar` are aliases in the nearest common ancestor, but not in Alice's branch, then we don't know whether to update Bob's dependents to Alice's `foo` or Alice's `bar` (and vice-versa). ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Original branch: @@ -1108,7 +1108,7 @@ conflict involving a builtin, we can't perform a merge. One way to fix this in the future would be to introduce a syntax for defining aliases in the scratch file. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm:hide @@ -1117,7 +1117,7 @@ project/main> branch alice Alice's branch: ```ucm -project/alice> alias.type builtin.Nat MyNat +project/alice> alias.type lib.builtins.Nat MyNat ``` Bob's branch: @@ -1146,7 +1146,7 @@ project/alice> merge /bob Each naming of a decl may not have more than one name for each constructor, within the decl's namespace. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```ucm:hide @@ -1192,7 +1192,7 @@ project/alice> merge /bob Each naming of a decl must have a name for each constructor, within the decl's namespace. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Alice's branch: @@ -1239,7 +1239,7 @@ project/alice> merge /bob A decl cannot be aliased within the namespace of another of its aliased. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Alice's branch: @@ -1287,7 +1287,7 @@ project/alice> merge /bob Constructors may only exist within the corresponding decl's namespace. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Alice's branch: @@ -1331,7 +1331,7 @@ project/alice> merge bob By convention, `lib` can only namespaces; each of these represents a library dependencies. Individual terms and types are not allowed at the top level of `lib`. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` Alice's branch: @@ -1375,7 +1375,7 @@ Here's an example. We'll delete a constructor name from the LCA and still be abl together. ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` LCA: @@ -1439,7 +1439,7 @@ project/alice> merge /bob ```ucm:hide -project/main> builtins.mergeio +project/main> builtins.mergeio lib.builtins ``` ```unison @@ -1477,3 +1477,44 @@ project/alice> merge /bob ```ucm:hide .> project.delete project ``` + +### Delete a constructor + + +```ucm:hide +project/main> builtins.mergeio lib.builtins +``` + +```unison +type Foo = Bar | Baz +``` + +```ucm +project/main> add +project/main> branch topic +``` + +```unison +boop = "boop" +``` + +```ucm +project/topic> add +``` + +```unison +type Foo = Bar +``` + +```ucm +project/main> update +``` + +```ucm +project/main> merge topic +project/main> view Foo +``` + +```ucm:hide +.> project.delete project +``` diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index 1ead9f4581..6334b362da 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -1255,7 +1255,7 @@ One way to fix this in the future would be to introduce a syntax for defining al Alice's branch: ```ucm -project/alice> alias.type builtin.Nat MyNat +project/alice> alias.type lib.builtins.Nat MyNat Done. @@ -1696,3 +1696,100 @@ project/alice> merge /bob I merged project/bob into project/alice. ``` +### Delete a constructor + + +```unison +type Foo = Bar | Baz +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + type Foo + +``` +```ucm +project/main> add + + ⍟ I've added these definitions: + + type Foo + +project/main> branch topic + + Done. I've created the topic branch based off of main. + + Tip: To merge your work back into the main branch, first + `switch /main` then `merge /topic`. + +``` +```unison +boop = "boop" +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + boop : Text + +``` +```ucm +project/topic> add + + ⍟ I've added these definitions: + + boop : Text + +``` +```unison +type Foo = Bar +``` + +```ucm + + 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: + + ⍟ These names already exist. You can `update` them to your + new definition: + + type Foo + +``` +```ucm +project/main> update + + Okay, I'm searching the branch for code that needs to be + updated... + + Done. + +``` +```ucm +project/main> merge topic + + I merged project/topic into project/main. + +project/main> view Foo + + type Foo = Bar + +``` From 6ab0ebe8d7da4653eec2a23ce9ab1704e7db19df Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Thu, 20 Jun 2024 14:17:46 -0400 Subject: [PATCH 25/81] Improve Haddock for `Path` and `Name` Added some simple docs and converted some comments to Haddock. --- .../src/Unison/Codebase/Path.hs | 10 +++++++- unison-core/src/Unison/Name/Internal.hs | 25 ++++++++++--------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/parser-typechecker/src/Unison/Codebase/Path.hs b/parser-typechecker/src/Unison/Codebase/Path.hs index 516a6c86f6..390c1b1a6a 100644 --- a/parser-typechecker/src/Unison/Codebase/Path.hs +++ b/parser-typechecker/src/Unison/Codebase/Path.hs @@ -100,7 +100,12 @@ import Unison.Prelude hiding (empty, toList) import Unison.Syntax.Name qualified as Name (toText, unsafeParseText) import Unison.Util.List qualified as List --- `Foo.Bar.baz` becomes ["Foo", "Bar", "baz"] +-- | A `Path` is an internal structure representing some namespace in the codebase. +-- +-- @Foo.Bar.baz@ becomes @["Foo", "Bar", "baz"]@. +-- +-- __NB__: This shouldn’t be exposed outside of this module (prefer`Path'`, `Absolute`, or `Relative`), but it’s +-- currently used pretty widely. Such usage should be replaced when encountered. newtype Path = Path {toSeq :: Seq NameSegment} deriving stock (Eq, Ord) deriving newtype (Semigroup, Monoid) @@ -112,10 +117,13 @@ instance GHC.IsList Path where toList (Path segs) = Foldable.toList segs fromList = Path . Seq.fromList +-- | A namespace path that starts from the root. newtype Absolute = Absolute {unabsolute :: Path} deriving (Eq, Ord) +-- | A namespace path that doesn’t necessarily start from the root. newtype Relative = Relative {unrelative :: Path} deriving (Eq, Ord) +-- | A namespace that may be either absolute or relative, This is the most general type that should be used. newtype Path' = Path' {unPath' :: Either Absolute Relative} deriving (Eq, Ord) diff --git a/unison-core/src/Unison/Name/Internal.hs b/unison-core/src/Unison/Name/Internal.hs index fcd855001e..3272d43df1 100644 --- a/unison-core/src/Unison/Name/Internal.hs +++ b/unison-core/src/Unison/Name/Internal.hs @@ -21,21 +21,22 @@ import Unison.Position (Position (..)) import Unison.Prelude import Unison.Util.Alphabetical --- | A name is an absolute-or-relative non-empty list of name segments. +-- | A name is an absolute-or-relative non-empty list of name segments. It is used to represent the path to a +-- definition. +-- +-- A few example names: +-- +-- - "foo.bar" --> Name Relative ("bar" :| ["foo"]) +-- - ".foo.bar" --> Name Absolute ("bar" :| ["foo"]) +-- - "|>.<|" --> Name Relative ("<|" :| ["|>"]) +-- - "." --> Name Relative ("." :| []) +-- - ".." --> Name Absolute (".." :| []) data Name - = -- A few example names: - -- - -- "foo.bar" --> Name Relative ["bar", "foo"] - -- ".foo.bar" --> Name Absolute ["bar", "foo"] - -- "|>.<|" --> Name Relative ["<|", "|>"] - -- "." --> Name Relative ["."] - -- ".." --> Name Absolute ["."] - -- - Name - -- whether the name is positioned absolutely (to some arbitrary root namespace), or relatively + = Name Position - -- the name segments in reverse order + -- ^ whether the name is positioned absolutely (to some arbitrary root namespace), or relatively (List.NonEmpty NameSegment) + -- ^ the name segments in reverse order deriving stock (Eq, Generic, Show) -- | Compare names (kinda) alphabetically: absolute comes before relative, but otherwise compare the name segments From 9e7a9376696eb4f2eaa898a255361e547e3ead32 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Thu, 20 Jun 2024 16:19:55 -0400 Subject: [PATCH 26/81] add todo.md transcript --- unison-src/transcripts/todo.md | 27 +++++++++++++ unison-src/transcripts/todo.output.md | 55 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 unison-src/transcripts/todo.md create mode 100644 unison-src/transcripts/todo.output.md diff --git a/unison-src/transcripts/todo.md b/unison-src/transcripts/todo.md new file mode 100644 index 0000000000..8478e4f298 --- /dev/null +++ b/unison-src/transcripts/todo.md @@ -0,0 +1,27 @@ +# Conflicted names + +The todo command shows conflicted names (not demonstrated here yet because it is not easy to create them for tests, yet). + +# Direct dependencies without names + +The `todo` command shows hashes of direct dependencies of local (outside `lib`) definitions that don't have names in +the current namespace. + +```ucm:hide +project/main> builtins.mergeio lib.builtins +``` + +```unison +foo.bar = 15 +baz = foo.bar + foo.bar +``` + +```ucm +project/main> add +project/main> delete.namespace.force foo +project/main> todo +``` + +```ucm:hide +project/main> delete.project project +``` diff --git a/unison-src/transcripts/todo.output.md b/unison-src/transcripts/todo.output.md new file mode 100644 index 0000000000..7f3affeb12 --- /dev/null +++ b/unison-src/transcripts/todo.output.md @@ -0,0 +1,55 @@ +# Conflicted names + +The todo command shows conflicted names (not demonstrated here yet because it is not easy to create them for tests, yet). + +# Direct dependencies without names + +The `todo` command shows hashes of direct dependencies of local (outside `lib`) definitions that don't have names in +the current namespace. + +```unison +foo.bar = 15 +baz = foo.bar + foo.bar +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + baz : Nat + foo.bar : Nat + +``` +```ucm +project/main> add + + ⍟ I've added these definitions: + + baz : Nat + foo.bar : Nat + +project/main> delete.namespace.force foo + + Done. + + ⚠️ + + Of the things I deleted, the following are still used in the + following definitions. They now contain un-named references. + + Dependency Referenced In + bar 1. baz + +project/main> todo + + These terms do not have any names in the current namespace: + + 1. #1jujb8oelv + +``` From f4f55b9b4516ef01335907119eb84b68b0bbc5b8 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Thu, 20 Jun 2024 17:00:42 -0400 Subject: [PATCH 27/81] Support branch-relative paths for `docs.to-html` Fixes #4402. --- .../src/Unison/Codebase/Editor/HandleInput.hs | 2 +- .../src/Unison/Codebase/Editor/Input.hs | 2 +- .../src/Unison/CommandLine/InputPatterns.hs | 14 ++-- unison-src/transcripts/fix4402.md | 20 +++++ unison-src/transcripts/fix4402.output.md | 75 +++++++++++++++++++ 5 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 unison-src/transcripts/fix4402.md create mode 100644 unison-src/transcripts/fix4402.output.md diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index 491cdccea5..a86887313d 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -470,7 +470,7 @@ loop e = do Cli.respond $ Output.MarkdownOut (Text.intercalate "\n---\n" mdText) DocsToHtmlI namespacePath' sourceDirectory -> do Cli.Env {codebase, sandboxedRuntime} <- ask - absPath <- Cli.resolvePath' namespacePath' + absPath <- ProjectUtils.branchRelativePathToAbsolute namespacePath' branch <- liftIO $ Codebase.getBranchAtPath codebase absPath _evalErrs <- liftIO $ (Backend.docsInBranchToHtmlFiles sandboxedRuntime codebase branch sourceDirectory) pure () diff --git a/unison-cli/src/Unison/Codebase/Editor/Input.hs b/unison-cli/src/Unison/Codebase/Editor/Input.hs index efcc2be7e6..c372f07d69 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Input.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Input.hs @@ -208,7 +208,7 @@ data Input | ApiI | UiI Path' | DocToMarkdownI Name - | DocsToHtmlI Path' FilePath + | DocsToHtmlI BranchRelativePath FilePath | AuthLoginI | VersionI | ProjectCreateI Bool {- try downloading base? -} (Maybe ProjectName) diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index 65c2c80e13..fbf57765c7 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -2768,19 +2768,21 @@ docsToHtml = "docs.to-html" [] I.Visible - [("namespace", Required, namespaceArg), ("", Required, filePathArg)] + [("namespace", Required, branchRelativePathArg), ("", Required, filePathArg)] ( P.wrapColumn2 - [ ( "`docs.to-html .path.to.namespace ~/path/to/file/output`", - "Render all docs contained within a namespace, no matter how deep," - <> "to html files on a file path" + [ ( makeExample docsToHtml [".path.to.ns", "doc-dir"], + "Render all docs contained within the namespace `.path.to.ns`, no matter how deep, to html files in `doc-dir` in the directory UCM was run from." + ), + ( makeExample docsToHtml ["project0/branch0:a.path", "/tmp/doc-dir"], + "Renders all docs anywhere in the namespace `a.path` from `branch0` of `project0` to html in `/tmp/doc-dir`." ) ] ) \case [namespacePath, destinationFilePath] -> Input.DocsToHtmlI - <$> handlePath'Arg namespacePath - <*> unsupportedStructuredArgument "a file name" destinationFilePath + <$> handleBranchRelativePathArg namespacePath + <*> unsupportedStructuredArgument "a directory name" destinationFilePath _ -> Left $ showPatternHelp docsToHtml docToMarkdown :: InputPattern diff --git a/unison-src/transcripts/fix4402.md b/unison-src/transcripts/fix4402.md new file mode 100644 index 0000000000..e79d243e5c --- /dev/null +++ b/unison-src/transcripts/fix4402.md @@ -0,0 +1,20 @@ +```ucm +.> project.create test-4402 +test-4402/main> builtins.merge +``` + +```unison +{{A doc directly in the namespace.}} +some.ns.direct = 1 + +{{A doc pretty deeply nested in the namespace.}} +some.ns.pretty.deeply.nested = 2 + +{{A doc outside the namespace.}} +some.outside = 3 +``` + +```ucm +test-4402/main> add +test-4402/main> docs.to-html some.ns /tmp/test-4402 +``` diff --git a/unison-src/transcripts/fix4402.output.md b/unison-src/transcripts/fix4402.output.md new file mode 100644 index 0000000000..858f6474c5 --- /dev/null +++ b/unison-src/transcripts/fix4402.output.md @@ -0,0 +1,75 @@ +```ucm +.> project.create test-4402 + + 🎉 I've created the project test-4402. + + I'll now fetch the latest version of the base Unison + library... + + Downloaded 14053 entities. + + 🎨 Type `ui` to explore this project's code in your browser. + 🔭 Discover libraries at https://share.unison-lang.org + 📖 Use `help-topic projects` to learn more about projects. + + Write your first Unison code with UCM: + + 1. Open scratch.u. + 2. Write some Unison code and save the file. + 3. In UCM, type `add` to save it to your new project. + + 🎉 🥳 Happy coding! + +test-4402/main> builtins.merge + + Done. + +``` +```unison +{{A doc directly in the namespace.}} +some.ns.direct = 1 + +{{A doc pretty deeply nested in the namespace.}} +some.ns.pretty.deeply.nested = 2 + +{{A doc outside the namespace.}} +some.outside = 3 +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + some.ns.direct : Nat + some.ns.direct.doc : Doc + some.ns.pretty.deeply.nested : Nat + (also named lib.base.data.Map.internal.ratio) + some.ns.pretty.deeply.nested.doc : Doc + some.outside : Nat + (also named lib.base.data.Map.internal.delta) + some.outside.doc : Doc + +``` +```ucm +test-4402/main> add + + ⍟ I've added these definitions: + + some.ns.direct : Nat + some.ns.direct.doc : Doc + some.ns.pretty.deeply.nested : Nat + (also named lib.base.data.Map.internal.ratio) + some.ns.pretty.deeply.nested.doc : Doc + some.outside : Nat + (also named lib.base.data.Map.internal.delta) + some.outside.doc : Doc + +test-4402/main> docs.to-html some.ns /tmp/test-4402 + +``` From d4a2ed906600325fcf36232a4f3ce534c6d8027b Mon Sep 17 00:00:00 2001 From: Paul Chiusano Date: Thu, 20 Jun 2024 16:52:57 -0500 Subject: [PATCH 28/81] Switch to `foo()` with no space as preferred syntax instead of `!foo` --- .../src/Unison/Syntax/TermParser.hs | 27 +++++++++++++++++-- .../src/Unison/Syntax/TermPrinter.hs | 2 +- .../transcripts/cycle-update-1.output.md | 4 +-- .../transcripts/cycle-update-2.output.md | 2 +- .../transcripts/cycle-update-3.output.md | 2 +- .../transcripts/cycle-update-4.output.md | 6 ++--- .../transcripts/cycle-update-5.output.md | 2 +- unison-src/transcripts/delete.output.md | 2 +- unison-src/transcripts/formatter.output.md | 2 +- unison-syntax/src/Unison/Syntax/Parser.hs | 1 + 10 files changed, 37 insertions(+), 13 deletions(-) diff --git a/parser-typechecker/src/Unison/Syntax/TermParser.hs b/parser-typechecker/src/Unison/Syntax/TermParser.hs index 8c91633700..6682d6b872 100644 --- a/parser-typechecker/src/Unison/Syntax/TermParser.hs +++ b/parser-typechecker/src/Unison/Syntax/TermParser.hs @@ -38,6 +38,7 @@ import Unison.NameSegment qualified as NameSegment import Unison.Names (Names) import Unison.Names qualified as Names import Unison.NamesWithHistory qualified as Names +import Unison.Parser.Ann qualified as Ann import Unison.Parser.Ann (Ann) import Unison.Pattern (Pattern) import Unison.Pattern qualified as Pattern @@ -48,7 +49,7 @@ import Unison.Syntax.Lexer qualified as L import Unison.Syntax.Name qualified as Name (toText, toVar, unsafeParseVar) import Unison.Syntax.NameSegment qualified as NameSegment import Unison.Syntax.Parser hiding (seq) -import Unison.Syntax.Parser qualified as Parser (seq, uniqueName) +import Unison.Syntax.Parser qualified as Parser (seq, uniqueName, seq') import Unison.Syntax.TypeParser qualified as TypeParser import Unison.Term (IsTop, Term) import Unison.Term qualified as Term @@ -439,7 +440,8 @@ resolveHashQualified tok = do termLeaf :: forall m v. (Monad m, Var v) => TermP v m termLeaf = asum - [ hashQualifiedPrefixTerm, + [ forceOrFnApplication, + hashQualifiedPrefixTerm, text, char, number, @@ -991,6 +993,27 @@ bang = P.label "bang" do e <- termLeaf pure $ DD.forceTerm (ann start <> ann e) (ann start) e +forceOrFnApplication :: forall m v . (Monad m, Var v) => TermP v m +forceOrFnApplication = P.label "force" do + -- `foo sqrt(2.0)` parses as `foo (sqrt 2.0)` + -- `forkAt pool() blah` parses as `forkAt (pool ()) blah` + -- `foo max(x, y) z` parsed as `foo (max x y) z` + -- That is, parens immediately (no space) following a symbol is + -- treated as function application, but higher precedence than + -- the usual application syntax where args are separated by spaces + fn <- P.try do + r <- hashQualifiedPrefixTerm + P.lookAhead do + tok <- ann <$> openBlockWith "(" + guard (L.column (Ann.start tok) == L.column (Ann.end (ann r))) + pure r + Parser.seq' "(" (done fn) term + where + done :: Term v Ann -> Ann -> [Term v Ann] -> Term v Ann + done fn a [] = DD.forceTerm a a fn + done fn _ [arg] = Term.apps' fn [arg] + done fn _ args = Term.apps' fn args + seqOp :: (Ord v) => P v m Pattern.SeqOp seqOp = Pattern.Snoc diff --git a/parser-typechecker/src/Unison/Syntax/TermPrinter.hs b/parser-typechecker/src/Unison/Syntax/TermPrinter.hs index bc33c43ca2..6a70a04067 100644 --- a/parser-typechecker/src/Unison/Syntax/TermPrinter.hs +++ b/parser-typechecker/src/Unison/Syntax/TermPrinter.hs @@ -490,7 +490,7 @@ pretty0 (App' x (Constructor' (ConstructorReference DD.UnitRef 0)), _) | isLeaf x -> do px <- pretty0 (ac (if isBlock x then 0 else 9) Normal im doc) x pure . paren (p >= 11 || isBlock x && p >= 3) $ - fmt S.DelayForceChar (l "!") <> PP.indentNAfterNewline 1 px + px <> fmt S.DelayForceChar (l "()") (Apps' f (unsnoc -> Just (args, lastArg)), _) | isSoftHangable lastArg -> do fun <- goNormal 9 f diff --git a/unison-src/transcripts/cycle-update-1.output.md b/unison-src/transcripts/cycle-update-1.output.md index 3906248333..b5f381b73c 100644 --- a/unison-src/transcripts/cycle-update-1.output.md +++ b/unison-src/transcripts/cycle-update-1.output.md @@ -67,11 +67,11 @@ ping _ = !pong + 3 ping : 'Nat ping _ = use Nat + - !pong + 3 + pong() + 3 pong : 'Nat pong _ = use Nat + - !ping + 2 + ping() + 2 ``` diff --git a/unison-src/transcripts/cycle-update-2.output.md b/unison-src/transcripts/cycle-update-2.output.md index 6884788130..4d9639a9c4 100644 --- a/unison-src/transcripts/cycle-update-2.output.md +++ b/unison-src/transcripts/cycle-update-2.output.md @@ -70,6 +70,6 @@ ping _ = 3 pong : 'Nat pong _ = use Nat + - !ping + 2 + ping() + 2 ``` diff --git a/unison-src/transcripts/cycle-update-3.output.md b/unison-src/transcripts/cycle-update-3.output.md index 7a0a499dbc..281e389e61 100644 --- a/unison-src/transcripts/cycle-update-3.output.md +++ b/unison-src/transcripts/cycle-update-3.output.md @@ -65,6 +65,6 @@ ping = 3 pong : 'Nat pong _ = use Nat + - !#4t465jk908.1 + 2 + #4t465jk908.1() + 2 ``` diff --git a/unison-src/transcripts/cycle-update-4.output.md b/unison-src/transcripts/cycle-update-4.output.md index fd525176bf..d4b8b8030c 100644 --- a/unison-src/transcripts/cycle-update-4.output.md +++ b/unison-src/transcripts/cycle-update-4.output.md @@ -74,16 +74,16 @@ clang _ = !pong + 3 clang : 'Nat clang _ = use Nat + - !pong + 3 + pong() + 3 ping : 'Nat ping _ = use Nat + - !clang + 1 + clang() + 1 pong : 'Nat pong _ = use Nat + - !ping + 2 + ping() + 2 ``` diff --git a/unison-src/transcripts/cycle-update-5.output.md b/unison-src/transcripts/cycle-update-5.output.md index 3e3361f70c..017d92b347 100644 --- a/unison-src/transcripts/cycle-update-5.output.md +++ b/unison-src/transcripts/cycle-update-5.output.md @@ -65,7 +65,7 @@ inner.ping _ = !pong + 3 inner.ping : 'Nat inner.ping _ = use Nat + - !pong + 1 + pong() + 1 ``` The bug here is that `inner.ping` still refers to `pong` by name. But if we properly identified the nameless (in the diff --git a/unison-src/transcripts/delete.output.md b/unison-src/transcripts/delete.output.md index 05a998cc1e..02b757d4c4 100644 --- a/unison-src/transcripts/delete.output.md +++ b/unison-src/transcripts/delete.output.md @@ -486,6 +486,6 @@ pong _ = 4 Nat.+ !ping pong : 'Nat pong _ = use Nat + - 4 + !#l9uq1dpl5v.1 + 4 + #l9uq1dpl5v.1() ``` diff --git a/unison-src/transcripts/formatter.output.md b/unison-src/transcripts/formatter.output.md index 95af2a545d..5ddf656afe 100644 --- a/unison-src/transcripts/formatter.output.md +++ b/unison-src/transcripts/formatter.output.md @@ -142,7 +142,7 @@ provide a action = h = cases { ask -> resume } -> handle resume a with h { r } -> r - handle !action with h + handle action() with h Optional.doc = {{ A Doc before a type }} structural type Optional a = More Text | Some | Other a | None Nat diff --git a/unison-syntax/src/Unison/Syntax/Parser.hs b/unison-syntax/src/Unison/Syntax/Parser.hs index 28bbdf042e..9dee6337e9 100644 --- a/unison-syntax/src/Unison/Syntax/Parser.hs +++ b/unison-syntax/src/Unison/Syntax/Parser.hs @@ -39,6 +39,7 @@ module Unison.Syntax.Parser run, semi, Unison.Syntax.Parser.seq, + Unison.Syntax.Parser.seq', sepBy, sepBy1, string, From 6302089b8e28579749ff13fd387fad31b62ba574 Mon Sep 17 00:00:00 2001 From: Arya Irani <538571+aryairani@users.noreply.github.com> Date: Thu, 20 Jun 2024 18:00:16 -0400 Subject: [PATCH 29/81] drop branch filter for CI runs We had previously tried to avoid what we saw as duplicate runs of CI on each PR commit. We didn't realize (or didn't document) that the two runs represent the actual branch (`on: push`) vs a hypothetical merge (`on: pull_request`). --- .github/workflows/ci.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 35489112f1..e0649b5ef5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,16 +5,10 @@ defaults: shell: bash on: - # Build on every pull request (and new PR commit) + # Run on the post-merge result of every PR commit pull_request: - # Build on new pushes to trunk (E.g. Merge commits) - # Without the branch filter, each commit on a branch with a PR is triggered twice. - # See: https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + # Build on the pre-merge result of every branch commit push: - branches: - - trunk - tags: - - release/* workflow_dispatch: env: From 72a05780a02e08905d6172e7be7dd38596efa91e Mon Sep 17 00:00:00 2001 From: Paul Chiusano Date: Thu, 20 Jun 2024 17:18:04 -0500 Subject: [PATCH 30/81] add round trip test case --- .../transcripts-round-trip/main.output.md | 22 ++++++++++++++----- .../reparses-with-same-hash.u | 10 ++++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/unison-src/transcripts-round-trip/main.output.md b/unison-src/transcripts-round-trip/main.output.md index fdeb756531..54f4cf9d53 100644 --- a/unison-src/transcripts-round-trip/main.output.md +++ b/unison-src/transcripts-round-trip/main.output.md @@ -24,7 +24,7 @@ So we can see the pretty-printed output: ☝️ - I added 105 definitions to the top of scratch.u + I added 106 definitions to the top of scratch.u You can edit them there, then run `update` to replace the definitions currently in this namespace. @@ -73,13 +73,13 @@ structural ability Zoink where Abort.toDefault! : a -> '{g, Abort} a ->{g} a Abort.toDefault! default thunk = h x = Abort.toDefault! (handler_1778 default x) thunk - handle !thunk with h + handle thunk() with h Abort.toOptional : '{g, Abort} a -> '{g} Optional a Abort.toOptional thunk = do toOptional! thunk Abort.toOptional! : '{g, Abort} a ->{g} Optional a -Abort.toOptional! thunk = toDefault! None do Some !thunk +Abort.toOptional! thunk = toDefault! None do Some thunk() catchAll : x -> Nat catchAll x = 99 @@ -87,7 +87,7 @@ catchAll x = 99 Decode.remainder : '{Ask (Optional Bytes)} Bytes Decode.remainder = do match ask with None -> Bytes.empty - Some b -> b Bytes.++ !Decode.remainder + Some b -> b Bytes.++ Decode.remainder() ex1 : Nat ex1 = @@ -194,7 +194,7 @@ fix_2650 = use Nat + y = 12 13 + y - !addNumbers + addNumbers() fix_2650a : tvar -> fun -> () fix_2650a tvar fun = () @@ -342,6 +342,16 @@ fix_525_exampleTerm quaffle = fix_525_exampleType : Id qualifiedName -> Id Fully.qualifiedName fix_525_exampleType z = Id (Dontcare () 19) +fnApplicationSyntax : Nat +fnApplicationSyntax = + use Nat + + Environment.default = do 1 + 1 + oog = do 2 + 2 + blah : Nat -> Float -> Nat + blah x y = x + 1 + _ = blah Environment.default() 1.0 + blah oog() (max 1.0 2.0) + Foo.bar.qux1 : Nat Foo.bar.qux1 = 42 @@ -672,7 +682,7 @@ UUID.random = do UUID 0 (0, 0) UUID.randomUUIDBytes : 'Bytes UUID.randomUUIDBytes = do - (UUID a (b, _)) = !random + (UUID a (b, _)) = random() encodeNat64be a Bytes.++ encodeNat64be b (|>) : a -> (a ->{e} b) ->{e} b diff --git a/unison-src/transcripts-round-trip/reparses-with-same-hash.u b/unison-src/transcripts-round-trip/reparses-with-same-hash.u index 09b941ff64..d719a178c3 100644 --- a/unison-src/transcripts-round-trip/reparses-with-same-hash.u +++ b/unison-src/transcripts-round-trip/reparses-with-same-hash.u @@ -542,4 +542,12 @@ fix_4384d = {{ {{ docExampleBlock 0 '[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, fix_4384e = id : x -> x id x = x - {{ {{ docExampleBlock 0 (id id id id id id id id id id id id id id id id id id id id id (x -> 0) }} }} \ No newline at end of file + {{ {{ docExampleBlock 0 (id id id id id id id id id id id id id id id id id id id id id (x -> 0) }} }} + +fnApplicationSyntax = + Environment.default = do 1 + 1 + oog = do 2 + 2 + blah : Nat -> Float -> Nat + blah x y = x + 1 + _ = blah Environment.default() 1.0 + blah oog() Float.max(1.0, 2.0) \ No newline at end of file From 6f26a1640150541f040caa6a615c64e5d38ad870 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Thu, 20 Jun 2024 21:40:07 -0400 Subject: [PATCH 31/81] Add `quoteCode` for printing errors This both colorizes and wraps code in backticks, in order to separate it from surrounding context. --- parser-typechecker/src/Unison/PrintError.hs | 41 +++++++++++-------- .../transcripts/error-messages.output.md | 12 +++--- .../generic-parse-errors.output.md | 2 +- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/parser-typechecker/src/Unison/PrintError.hs b/parser-typechecker/src/Unison/PrintError.hs index 77dcdd0b0a..56bf11fcfd 100644 --- a/parser-typechecker/src/Unison/PrintError.hs +++ b/parser-typechecker/src/Unison/PrintError.hs @@ -126,6 +126,10 @@ styleAnnotated sty a = (,sty) <$> rangeForAnnotated a style :: s -> String -> Pretty (AnnotatedText s) style sty str = Pr.lit . AT.annotate sty $ fromString str +-- | Applies the color highlighting for `Code`, but also quotes the code, to separate it from the containing context. +quoteCode :: String -> Pretty ColorText +quoteCode = Pr.backticked . style Code + stylePretty :: Color -> Pretty ColorText -> Pretty ColorText stylePretty = Pr.map . AT.annotate @@ -1368,21 +1372,21 @@ renderParseErrors s = \case <> excerpt L.InvalidWordyId id -> Pr.lines - [ "The identifier " <> style Code id <> " isn't valid syntax: ", + [ "The identifier, " <> quoteCode id <> ", isn't valid syntax: ", "", excerpt, "Here's a few examples of valid syntax: " - <> style Code "abba1'" + <> quoteCode "abba1'" <> ", " - <> style Code "snake_case" + <> quoteCode "snake_case" <> ", " - <> style Code "Foo.zoink!" + <> quoteCode "Foo.zoink!" <> ", and " - <> style Code "🌻" + <> quoteCode "🌻" ] L.ReservedWordyId id -> Pr.lines - [ "The identifier " <> style Code id <> " used here is a reserved keyword: ", + [ "The identifier, " <> quoteCode id <> ", used here is a reserved keyword: ", "", excerpt, Pr.wrap $ @@ -1392,19 +1396,19 @@ renderParseErrors s = \case ] L.InvalidSymbolyId id -> Pr.lines - [ "The infix identifier " <> style Code id <> " isn’t valid syntax: ", + [ "The infix identifier, " <> quoteCode id <> ", isn’t valid syntax: ", "", excerpt, "Here are a few valid examples: " - <> style Code "++" + <> quoteCode "++" <> ", " - <> style Code "Float./" + <> quoteCode "Float./" <> ", and " - <> style Code "`List.map`" + <> quoteCode "List.map" ] L.ReservedSymbolyId id -> Pr.lines - [ "The identifier " <> style Code id <> " is reserved by Unison and can't be used as an operator: ", + [ "The identifier, " <> quoteCode id <> ", is reserved by Unison and can't be used as an operator: ", "", excerpt ] @@ -1458,11 +1462,12 @@ renderParseErrors s = \case "", excerpt, Pr.wrap $ - "I was expecting some digits after the '.'," - <> "for example: " - <> style Code (n <> "0") + "I was expecting some digits after the " + <> quoteCode "." + <> ", for example: " + <> quoteCode (n <> "0") <> "or" - <> Pr.group (style Code (n <> "1e37") <> ".") + <> Pr.group (quoteCode (n <> "1e37") <> ".") ] L.MissingExponent n -> Pr.lines @@ -1472,7 +1477,7 @@ renderParseErrors s = \case Pr.wrap $ "I was expecting some digits for the exponent," <> "for example: " - <> Pr.group (style Code (n <> "37") <> ".") + <> Pr.group (quoteCode (n <> "37") <> ".") ] L.TextLiteralMissingClosingQuote _txt -> Pr.lines @@ -1488,7 +1493,7 @@ renderParseErrors s = \case "", "I only know about the following escape characters:", "", - let s ch = style Code (fromString $ "\\" <> [ch]) + let s ch = quoteCode (fromString $ "\\" <> [ch]) in Pr.indentN 2 $ intercalateMap "," s (fst <$> L.escapeChars) ] L.LayoutError -> @@ -1719,7 +1724,7 @@ renderParseErrors s = \case let msg = mconcat [ "This looks like the start of an expression here but I was expecting a binding.", - "\nDid you mean to use a single " <> style Code ":", + "\nDid you mean to use a single " <> quoteCode ":", " here for a type signature?", "\n\n", tokenAsErrorSite s t diff --git a/unison-src/transcripts/error-messages.output.md b/unison-src/transcripts/error-messages.output.md index eb01548045..dacc86ac7a 100644 --- a/unison-src/transcripts/error-messages.output.md +++ b/unison-src/transcripts/error-messages.output.md @@ -19,8 +19,8 @@ x = 1. -- missing some digits after the decimal 1 | x = 1. -- missing some digits after the decimal - I was expecting some digits after the '.', for example: 1.0 or - 1.1e37. + I was expecting some digits after the `.` , for example: `1.0` + or `1.1e37`. ``` ```unison @@ -36,7 +36,7 @@ x = 1e -- missing an exponent 1 | x = 1e -- missing an exponent I was expecting some digits for the exponent, for example: - 1e37. + `1e37`. ``` ```unison @@ -52,7 +52,7 @@ x = 1e- -- missing an exponent 1 | x = 1e- -- missing an exponent I was expecting some digits for the exponent, for example: - 1e-37. + `1e-37`. ``` ```unison @@ -68,7 +68,7 @@ x = 1E+ -- missing an exponent 1 | x = 1E+ -- missing an exponent I was expecting some digits for the exponent, for example: - 1e+37. + `1e+37`. ``` ### Hex, octal, and bytes literals @@ -343,7 +343,7 @@ use.keyword.in.namespace = 1 Loading changes detected in scratch.u. - The identifier namespace used here is a reserved keyword: + The identifier, `namespace`, used here is a reserved keyword: 1 | use.keyword.in.namespace = 1 diff --git a/unison-src/transcripts/generic-parse-errors.output.md b/unison-src/transcripts/generic-parse-errors.output.md index 039a1fb002..3a1c7b19ec 100644 --- a/unison-src/transcripts/generic-parse-errors.output.md +++ b/unison-src/transcripts/generic-parse-errors.output.md @@ -30,7 +30,7 @@ namespace.blah = 1 Loading changes detected in scratch.u. - The identifier namespace used here is a reserved keyword: + The identifier, `namespace`, used here is a reserved keyword: 1 | namespace.blah = 1 From 91dc53d246459a7fada00393ab79e7809c081b25 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 21 Jun 2024 01:42:26 -0400 Subject: [PATCH 32/81] Remove a very lawless type class `Convert` mostly just hides some rather unsavory (but at least not partial) mappings between types. --- .../src/Unison/Codebase/Path.hs | 37 +------------------ unison-cli/src/Unison/Cli/MonadUtils.hs | 4 +- .../src/Unison/Codebase/Editor/HandleInput.hs | 20 +++++----- .../Codebase/Editor/HandleInput/MoveTerm.hs | 6 +-- .../Codebase/Editor/HandleInput/MoveType.hs | 6 +-- .../Unison/CommandLine/BranchRelativePath.hs | 4 +- .../src/Unison/CommandLine/InputPatterns.hs | 8 +++- unison-core/src/Unison/HashQualified'.hs | 10 +---- unison-core/src/Unison/HashQualified.hs | 8 +--- unison-core/src/Unison/Name.hs | 4 -- unison-core/src/Unison/NamesWithHistory.hs | 2 +- unison-core/src/Unison/Term.hs | 5 ++- unison-core/src/Unison/Type/Names.hs | 3 +- 13 files changed, 36 insertions(+), 81 deletions(-) diff --git a/parser-typechecker/src/Unison/Codebase/Path.hs b/parser-typechecker/src/Unison/Codebase/Path.hs index 516a6c86f6..b849d6bc8d 100644 --- a/parser-typechecker/src/Unison/Codebase/Path.hs +++ b/parser-typechecker/src/Unison/Codebase/Path.hs @@ -43,7 +43,7 @@ module Unison.Codebase.Path isRoot, isRoot', - -- * things that could be replaced with `Convert` instances + -- * conversions absoluteToPath', fromList, fromName, @@ -76,8 +76,6 @@ module Unison.Codebase.Path -- * things that could be replaced with `Snoc` instances snoc, unsnoc, - -- This should be moved to a common util module, or we could use the 'witch' package. - Convert (..), ) where @@ -93,7 +91,7 @@ import Data.Sequence qualified as Seq import Data.Text qualified as Text import GHC.Exts qualified as GHC import Unison.HashQualified' qualified as HQ' -import Unison.Name (Convert (..), Name) +import Unison.Name (Name) import Unison.Name qualified as Name import Unison.NameSegment (NameSegment) import Unison.Prelude hiding (empty, toList) @@ -534,34 +532,3 @@ instance Resolve Absolute HQSplit HQSplitAbsolute where instance Resolve Absolute Path' Absolute where resolve _ (AbsolutePath' a) = a resolve a (RelativePath' r) = resolve a r - -instance Convert Absolute Path where convert = unabsolute - -instance Convert Absolute Path' where convert = absoluteToPath' - -instance Convert Absolute Text where convert = toText' . absoluteToPath' - -instance Convert Relative Text where convert = toText . unrelative - -instance Convert Absolute String where convert = Text.unpack . convert - -instance Convert Relative String where convert = Text.unpack . convert - -instance Convert [NameSegment] Path where convert = fromList - -instance Convert Path [NameSegment] where convert = toList - -instance Convert HQSplit (HQ'.HashQualified Path) where convert = unsplitHQ - -instance Convert HQSplit' (HQ'.HashQualified Path') where convert = unsplitHQ' - -instance Convert Name Split where - convert = splitFromName - -instance Convert (path, NameSegment) (path, HQ'.HQSegment) where - convert (path, name) = - (path, HQ'.fromName name) - -instance (Convert path0 path1) => Convert (path0, name) (path1, name) where - convert = - over _1 convert diff --git a/unison-cli/src/Unison/Cli/MonadUtils.hs b/unison-cli/src/Unison/Cli/MonadUtils.hs index 5aa583ee4c..a397a3b093 100644 --- a/unison-cli/src/Unison/Cli/MonadUtils.hs +++ b/unison-cli/src/Unison/Cli/MonadUtils.hs @@ -481,7 +481,7 @@ updateRoot new reason = getTermsAt :: (Path.Absolute, HQ'.HQSegment) -> Cli (Set Referent) getTermsAt path = do rootBranch0 <- getRootBranch0 - pure (BranchUtil.getTerm (Path.convert path) rootBranch0) + pure (BranchUtil.getTerm (first Path.unabsolute path) rootBranch0) ------------------------------------------------------------------------------------------------------------------------ -- Getting types @@ -489,7 +489,7 @@ getTermsAt path = do getTypesAt :: (Path.Absolute, HQ'.HQSegment) -> Cli (Set TypeReference) getTypesAt path = do rootBranch0 <- getRootBranch0 - pure (BranchUtil.getType (Path.convert path) rootBranch0) + pure (BranchUtil.getType (first Path.unabsolute path) rootBranch0) ------------------------------------------------------------------------------------------------------------------------ -- Getting patches diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index 491cdccea5..dc73a118cc 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -352,7 +352,7 @@ loop e = do Left hash -> (,WhichBranchEmptyHash hash) <$> Cli.resolveShortCausalHash hash Right path' -> do absPath <- ProjectUtils.branchRelativePathToAbsolute path' - let srcp = Path.convert absPath + let srcp = Path.AbsolutePath' absPath srcb <- Cli.expectBranchAtPath' srcp pure (srcb, WhichBranchEmptyPath srcp) description <- inputDescription input @@ -492,11 +492,11 @@ loop e = do hqLength <- Cli.runTransaction Codebase.hashLength pure (DeleteNameAmbiguous hqLength name srcTerms Set.empty) dest <- Cli.resolveSplit' dest' - destTerms <- Cli.getTermsAt (Path.convert dest) + destTerms <- Cli.getTermsAt (HQ'.NameOnly <$> dest) when (not (Set.null destTerms)) do Cli.returnEarly (TermAlreadyExists dest' destTerms) description <- inputDescription input - Cli.stepAt description (BranchUtil.makeAddTermName (Path.convert dest) srcTerm) + Cli.stepAt description (BranchUtil.makeAddTermName (first Path.unabsolute dest) srcTerm) Cli.respond Success AliasTypeI src' dest' -> do src <- traverseOf _Right Cli.resolveSplit' src' @@ -515,11 +515,11 @@ loop e = do hqLength <- Cli.runTransaction Codebase.hashLength pure (DeleteNameAmbiguous hqLength name Set.empty srcTypes) dest <- Cli.resolveSplit' dest' - destTypes <- Cli.getTypesAt (Path.convert dest) + destTypes <- Cli.getTypesAt (HQ'.NameOnly <$> dest) when (not (Set.null destTypes)) do Cli.returnEarly (TypeAlreadyExists dest' destTypes) description <- inputDescription input - Cli.stepAt description (BranchUtil.makeAddTypeName (Path.convert dest) srcType) + Cli.stepAt description (BranchUtil.makeAddTypeName (first Path.unabsolute dest) srcType) Cli.respond Success -- this implementation will happily produce name conflicts, @@ -621,9 +621,9 @@ loop e = do guidPath <- Cli.resolveSplit' (authorPath' |> NameSegment.guidSegment) Cli.stepManyAt description - [ BranchUtil.makeAddTermName (Path.convert authorPath) (d authorRef), - BranchUtil.makeAddTermName (Path.convert copyrightHolderPath) (d copyrightHolderRef), - BranchUtil.makeAddTermName (Path.convert guidPath) (d guidRef) + [ BranchUtil.makeAddTermName (first Path.unabsolute authorPath) (d authorRef), + BranchUtil.makeAddTermName (first Path.unabsolute copyrightHolderPath) (d copyrightHolderRef), + BranchUtil.makeAddTermName (first Path.unabsolute guidPath) (d guidRef) ] currentPath <- Cli.getCurrentPath finalBranch <- Cli.getCurrentBranch0 @@ -1624,7 +1624,7 @@ checkDeletes typesTermsTuples doutput inputs = do (Path.HQSplit', Set Reference, Set Referent) -> Cli (Path.Split, Name, Set Reference, Set Referent) toSplitName hq = do - resolvedPath <- Path.convert <$> Cli.resolveSplit' (HQ'.toName <$> hq ^. _1) + resolvedPath <- first Path.unabsolute <$> Cli.resolveSplit' (HQ'.toName <$> hq ^. _1) return (resolvedPath, Path.unsafeToName (Path.unsplit resolvedPath), hq ^. _2, hq ^. _3) -- get the splits and names with terms and types splitsNames <- traverse toSplitName typesTermsTuples @@ -1771,7 +1771,7 @@ docsI src = do (codebaseByName) Lastly check for `foo.doc` in the codebase and if found do `display foo.doc` -} dotDoc :: HQ.HashQualified Name - dotDoc = Name.convert . Name.joinDot src $ Name.fromSegment NameSegment.docSegment + dotDoc = HQ.NameOnly . Name.joinDot src $ Name.fromSegment NameSegment.docSegment findInScratchfileByName :: Cli () findInScratchfileByName = do diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveTerm.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveTerm.hs index 374e58ac56..c329060303 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveTerm.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveTerm.hs @@ -26,14 +26,14 @@ moveTermSteps src' dest' = do Cli.returnEarly (Output.DeleteNameAmbiguous hqLength src' srcTerms Set.empty) [srcTerm] -> do dest <- Cli.resolveSplit' dest' - destTerms <- Cli.getTermsAt (Path.convert dest) + destTerms <- Cli.getTermsAt (HQ'.NameOnly <$> dest) when (not (Set.null destTerms)) do Cli.returnEarly (Output.TermAlreadyExists dest' destTerms) - let p = Path.convert src + let p = first Path.unabsolute src pure [ -- Mitchell: throwing away any hash-qualification here seems wrong! BranchUtil.makeDeleteTermName (over _2 HQ'.toName p) srcTerm, - BranchUtil.makeAddTermName (Path.convert dest) srcTerm + BranchUtil.makeAddTermName (first Path.unabsolute dest) srcTerm ] doMoveTerm :: (Path', HQ'.HQSegment) -> (Path', NameSegment) -> Text -> Cli () diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveType.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveType.hs index 95f3ba09b5..bdf9fe88cd 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveType.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/MoveType.hs @@ -26,14 +26,14 @@ moveTypeSteps src' dest' = do Cli.returnEarly (Output.DeleteNameAmbiguous hqLength src' Set.empty srcTypes) [srcType] -> do dest <- Cli.resolveSplit' dest' - destTypes <- Cli.getTypesAt (Path.convert dest) + destTypes <- Cli.getTypesAt (HQ'.NameOnly <$> dest) when (not (Set.null destTypes)) do Cli.returnEarly (Output.TypeAlreadyExists dest' destTypes) - let p = Path.convert src + let p = first Path.unabsolute src pure [ -- Mitchell: throwing away any hash-qualification here seems wrong! BranchUtil.makeDeleteTypeName (over _2 HQ'.toName p) srcType, - BranchUtil.makeAddTypeName (Path.convert dest) srcType + BranchUtil.makeAddTypeName (first Path.unabsolute dest) srcType ] doMoveType :: (Path', HQ'.HQSegment) -> (Path', NameSegment) -> Text -> Cli () diff --git a/unison-cli/src/Unison/CommandLine/BranchRelativePath.hs b/unison-cli/src/Unison/CommandLine/BranchRelativePath.hs index a999edbbe0..7e0a0682ac 100644 --- a/unison-cli/src/Unison/CommandLine/BranchRelativePath.hs +++ b/unison-cli/src/Unison/CommandLine/BranchRelativePath.hs @@ -63,13 +63,13 @@ instance From BranchRelativePath Text where That path -> Text.Builder.run ( Text.Builder.char ':' - <> Text.Builder.text (Path.convert path) + <> Text.Builder.text (Path.toText' $ Path.RelativePath' path) ) These eitherProj path -> Text.Builder.run ( Text.Builder.text (eitherProjToText eitherProj) <> Text.Builder.char ':' - <> Text.Builder.text (Path.convert path) + <> Text.Builder.text (Path.toText' $ Path.RelativePath' path) ) LoosePath path -> Path.toText' path where diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index 65c2c80e13..54b65279d2 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -3837,7 +3837,8 @@ branchRelativePathSuggestions config inputStr codebase _httpClient currentPath = Just projectBranch -> do let branchPath = review ProjectUtils.projectBranchPathPrism (projectAndBranch, mempty) projectAndBranch = ProjectAndBranch (projectBranch ^. #projectId) (projectBranch ^. #branchId) - map prefixPathSep <$> prefixCompleteNamespace (Path.convert relPath) branchPath + map prefixPathSep + <$> prefixCompleteNamespace (Text.unpack . Path.toText' $ Path.RelativePath' relPath) branchPath BranchRelativePath.IncompletePath projStuff mpath -> do Codebase.runTransaction codebase do mprojectBranch <- runMaybeT do @@ -3853,7 +3854,10 @@ branchRelativePathSuggestions config inputStr codebase _httpClient currentPath = Just (projectBranch, prefix) -> do let branchPath = review ProjectUtils.projectBranchPathPrism (projectAndBranch, mempty) projectAndBranch = ProjectAndBranch (projectBranch ^. #projectId) (projectBranch ^. #branchId) - map (addBranchPrefix prefix) <$> prefixCompleteNamespace (maybe "" Path.convert mpath) branchPath + map (addBranchPrefix prefix) + <$> prefixCompleteNamespace + (maybe "" (Text.unpack . Path.toText' . Path.RelativePath') mpath) + branchPath where (mayCurrentProjectId, mayCurrentBranchId) = case projectContextFromPath currentPath of LooseCodePath {} -> (Nothing, Nothing) diff --git a/unison-core/src/Unison/HashQualified'.hs b/unison-core/src/Unison/HashQualified'.hs index 48bacfc6d1..90659df641 100644 --- a/unison-core/src/Unison/HashQualified'.hs +++ b/unison-core/src/Unison/HashQualified'.hs @@ -2,7 +2,7 @@ module Unison.HashQualified' where import Data.Text qualified as Text import Unison.HashQualified qualified as HQ -import Unison.Name (Convert, Name, Parse) +import Unison.Name (Name, Parse) import Unison.Name qualified as Name import Unison.NameSegment (NameSegment) import Unison.Prelude @@ -114,13 +114,5 @@ instance (Name.Alphabetical n) => Name.Alphabetical (HashQualified n) where compareAlphabetical HashQualified {} NameOnly {} = GT compareAlphabetical (HashQualified n sh) (HashQualified n2 sh2) = Name.compareAlphabetical n n2 <> compare sh sh2 -instance (Convert n n2) => Parse (HashQualified n) n2 where - parse = \case - NameOnly n -> Just (Name.convert n) - _ -> Nothing - -instance Convert (HashQualified n) (HQ.HashQualified n) where - convert = toHQ - instance Parse (HQ.HashQualified n) (HashQualified n) where parse = fromHQ diff --git a/unison-core/src/Unison/HashQualified.hs b/unison-core/src/Unison/HashQualified.hs index cc1a0aa548..d143dc4740 100644 --- a/unison-core/src/Unison/HashQualified.hs +++ b/unison-core/src/Unison/HashQualified.hs @@ -3,7 +3,7 @@ module Unison.HashQualified where import Data.Text qualified as Text import Unison.ConstructorReference (ConstructorReference) import Unison.ConstructorReference qualified as ConstructorReference -import Unison.Name (Convert, Name) +import Unison.Name (Name) import Unison.Name qualified as Name import Unison.Prelude hiding (fromString) import Unison.Reference (Reference) @@ -139,9 +139,3 @@ instance (Name.Alphabetical n) => Name.Alphabetical (HashQualified n) where (Nothing, Just _) -> LT -- prefer NameOnly to HashQualified (Just _, Nothing) -> GT (Just sh, Just sh2) -> compare sh sh2 - -instance (Convert n n2) => Convert (HashQualified n) (HashQualified n2) where - convert = fmap Name.convert - -instance Convert n (HashQualified n) where - convert = NameOnly diff --git a/unison-core/src/Unison/Name.hs b/unison-core/src/Unison/Name.hs index 30f4f6d59a..570519e452 100644 --- a/unison-core/src/Unison/Name.hs +++ b/unison-core/src/Unison/Name.hs @@ -1,6 +1,5 @@ module Unison.Name ( Name, - Convert (..), Parse (..), -- * Basic construction @@ -571,9 +570,6 @@ commonPrefix x@(Name p1 _) y@(Name p2 _) | a == b = a : commonPrefix' as bs commonPrefix' _ _ = [] -class Convert a b where - convert :: a -> b - class Parse a b where parse :: a -> Maybe b diff --git a/unison-core/src/Unison/NamesWithHistory.hs b/unison-core/src/Unison/NamesWithHistory.hs index 5ba7ea72fa..7e2d126ec4 100644 --- a/unison-core/src/Unison/NamesWithHistory.hs +++ b/unison-core/src/Unison/NamesWithHistory.hs @@ -225,7 +225,7 @@ longestTermName :: Int -> Referent -> Names -> HQ.HashQualified Name longestTermName length r ns = case reverse (termNamesByLength length r ns) of [] -> HQ.take length (HQ.fromReferent r) - (h : _) -> Name.convert h + (h : _) -> HQ'.toHQ h termName :: Int -> Referent -> Names -> Set (HQ'.HashQualified Name) termName length r names = diff --git a/unison-core/src/Unison/Term.hs b/unison-core/src/Unison/Term.hs index acde4533fb..73df6fc3ae 100644 --- a/unison-core/src/Unison/Term.hs +++ b/unison-core/src/Unison/Term.hs @@ -19,6 +19,7 @@ import Unison.Blank qualified as B import Unison.ConstructorReference (ConstructorReference, GConstructorReference (..)) import Unison.ConstructorType qualified as CT import Unison.DataDeclaration.ConstructorId (ConstructorId) +import Unison.HashQualified qualified as HQ import Unison.LabeledDependency (LabeledDependency) import Unison.LabeledDependency qualified as LD import Unison.Name qualified as Name @@ -160,14 +161,14 @@ bindNames unsafeVarToName keepFreeTerms ns e = do -- !_ = trace "bindNames.free type vars: " () -- !_ = traceShow $ fst <$> freeTyVars okTm :: (v, a) -> Names.ResolutionResult v a (v, Term v a) - okTm (v, a) = case Names.lookupHQTerm Names.IncludeSuffixes (Name.convert $ unsafeVarToName v) ns of + okTm (v, a) = case Names.lookupHQTerm Names.IncludeSuffixes (HQ.NameOnly $ unsafeVarToName v) ns of rs | Set.size rs == 1 -> pure (v, fromReferent a $ Set.findMin rs) | otherwise -> case NES.nonEmptySet rs of Nothing -> Left (pure (Names.TermResolutionFailure v a Names.NotFound)) Just refs -> Left (pure (Names.TermResolutionFailure v a (Names.Ambiguous ns refs))) - okTy (v, a) = case Names.lookupHQType Names.IncludeSuffixes (Name.convert $ unsafeVarToName v) ns of + okTy (v, a) = case Names.lookupHQType Names.IncludeSuffixes (HQ.NameOnly $ unsafeVarToName v) ns of rs | Set.size rs == 1 -> pure (v, Type.ref a $ Set.findMin rs) | otherwise -> case NES.nonEmptySet rs of diff --git a/unison-core/src/Unison/Type/Names.hs b/unison-core/src/Unison/Type/Names.hs index a88a913c66..5451406cdd 100644 --- a/unison-core/src/Unison/Type/Names.hs +++ b/unison-core/src/Unison/Type/Names.hs @@ -6,6 +6,7 @@ where import Data.Set qualified as Set import Data.Set.NonEmpty qualified as NES import Unison.ABT qualified as ABT +import Unison.HashQualified qualified as HQ import Unison.Name qualified as Name import Unison.Names qualified as Names import Unison.Names.ResolutionResult qualified as Names @@ -24,7 +25,7 @@ bindNames :: Names.ResolutionResult v a (Type v a) bindNames unsafeVarToName keepFree ns t = let fvs = ABT.freeVarOccurrences keepFree t - rs = [(v, a, Names.lookupHQType Names.IncludeSuffixes (Name.convert $ unsafeVarToName v) ns) | (v, a) <- fvs] + rs = [(v, a, Names.lookupHQType Names.IncludeSuffixes (HQ.NameOnly $ unsafeVarToName v) ns) | (v, a) <- fvs] ok (v, a, rs) = if Set.size rs == 1 then pure (v, Set.findMin rs) From 8b12645745f066304d8d0c87d81769d29aa45a3f Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 21 Jun 2024 01:55:43 -0400 Subject: [PATCH 33/81] Remove an even more lawless type class At least `Parse` was unused. --- unison-core/src/Unison/HashQualified'.hs | 5 +---- unison-core/src/Unison/Name.hs | 7 ------- unison-syntax/src/Unison/Syntax/HashQualified'.hs | 6 +----- unison-syntax/src/Unison/Syntax/HashQualified.hs | 6 +----- 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/unison-core/src/Unison/HashQualified'.hs b/unison-core/src/Unison/HashQualified'.hs index 90659df641..b1ea8c1deb 100644 --- a/unison-core/src/Unison/HashQualified'.hs +++ b/unison-core/src/Unison/HashQualified'.hs @@ -2,7 +2,7 @@ module Unison.HashQualified' where import Data.Text qualified as Text import Unison.HashQualified qualified as HQ -import Unison.Name (Name, Parse) +import Unison.Name (Name) import Unison.Name qualified as Name import Unison.NameSegment (NameSegment) import Unison.Prelude @@ -113,6 +113,3 @@ instance (Name.Alphabetical n) => Name.Alphabetical (HashQualified n) where compareAlphabetical NameOnly {} HashQualified {} = LT compareAlphabetical HashQualified {} NameOnly {} = GT compareAlphabetical (HashQualified n sh) (HashQualified n2 sh2) = Name.compareAlphabetical n n2 <> compare sh sh2 - -instance Parse (HQ.HashQualified n) (HashQualified n) where - parse = fromHQ diff --git a/unison-core/src/Unison/Name.hs b/unison-core/src/Unison/Name.hs index 570519e452..371a567e66 100644 --- a/unison-core/src/Unison/Name.hs +++ b/unison-core/src/Unison/Name.hs @@ -1,6 +1,5 @@ module Unison.Name ( Name, - Parse (..), -- * Basic construction cons, @@ -569,9 +568,3 @@ commonPrefix x@(Name p1 _) y@(Name p2 _) commonPrefix' (a : as) (b : bs) | a == b = a : commonPrefix' as bs commonPrefix' _ _ = [] - -class Parse a b where - parse :: a -> Maybe b - -instance (Parse a a2, Parse b b2) => Parse (a, b) (a2, b2) where - parse (a, b) = (,) <$> parse a <*> parse b diff --git a/unison-syntax/src/Unison/Syntax/HashQualified'.hs b/unison-syntax/src/Unison/Syntax/HashQualified'.hs index 56fb96304b..de5c4bfeab 100644 --- a/unison-syntax/src/Unison/Syntax/HashQualified'.hs +++ b/unison-syntax/src/Unison/Syntax/HashQualified'.hs @@ -17,17 +17,13 @@ import Text.Megaparsec (ParsecT) import Text.Megaparsec qualified as P import Text.Megaparsec.Internal qualified as P (withParsecT) import Unison.HashQualified' qualified as HQ' -import Unison.Name (Name, Parse) -import Unison.Name qualified as Name +import Unison.Name (Name) import Unison.Prelude hiding (fromString) import Unison.Syntax.Lexer.Token (Token) import Unison.Syntax.Name qualified as Name (nameP, toText) import Unison.Syntax.NameSegment qualified as NameSegment import Unison.Syntax.ShortHash qualified as ShortHash (shortHashP) -instance Parse Text (HQ'.HashQualified Name) where - parse = parseText - ------------------------------------------------------------------------------------------------------------------------ -- String conversions diff --git a/unison-syntax/src/Unison/Syntax/HashQualified.hs b/unison-syntax/src/Unison/Syntax/HashQualified.hs index cb7175555a..e90d8c6cb7 100644 --- a/unison-syntax/src/Unison/Syntax/HashQualified.hs +++ b/unison-syntax/src/Unison/Syntax/HashQualified.hs @@ -22,8 +22,7 @@ import Text.Megaparsec.Internal qualified as P (withParsecT) import Unison.HashQualified (HashQualified (..)) import Unison.HashQualified qualified as HashQualified import Unison.HashQualified' qualified as HQ' -import Unison.Name (Name, Parse) -import Unison.Name qualified as Name +import Unison.Name (Name) import Unison.Prelude hiding (fromString) import Unison.Syntax.HashQualified' qualified as HQ' import Unison.Syntax.Lexer.Token (Token) @@ -34,9 +33,6 @@ import Unison.Var (Var) import Unison.Var qualified as Var import Prelude hiding (take) -instance Parse Text (HashQualified Name) where - parse = parseText - parseText :: Text -> Maybe (HashQualified Name) parseText text = eitherToMaybe (P.runParser parser "" (Text.unpack text)) From ff05361e402595c2c744ab1bba8cdfab4feac02e Mon Sep 17 00:00:00 2001 From: Paul Chiusano Date: Fri, 21 Jun 2024 08:48:08 -0500 Subject: [PATCH 34/81] update transcripts --- unison-src/transcripts-manual/rewrites.output.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unison-src/transcripts-manual/rewrites.output.md b/unison-src/transcripts-manual/rewrites.output.md index a4764c7735..415330f135 100644 --- a/unison-src/transcripts-manual/rewrites.output.md +++ b/unison-src/transcripts-manual/rewrites.output.md @@ -183,7 +183,7 @@ woot1to2 x = wootEx : Nat ->{Woot2} Nat wootEx a = - _ = !Woot2.woot2 + _ = Woot2.woot2() blah2 blah = 123 @@ -198,7 +198,7 @@ After adding the rewritten form to the codebase, here's the rewritten `Woot1` to wootEx : Nat ->{Woot2} Nat wootEx a = - _ = !woot2 + _ = woot2() blah2 ``` From 58818242a3b03d1a2ee8661bfebfa2af015a3f8e Mon Sep 17 00:00:00 2001 From: Paul Chiusano Date: Fri, 21 Jun 2024 09:41:32 -0500 Subject: [PATCH 35/81] reduce scope of PR to just `!foo` vs `foo()`, refresh transcripts --- .../src/Unison/Syntax/TermParser.hs | 32 +++++++------------ .../reparses-with-same-hash.u | 2 +- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/parser-typechecker/src/Unison/Syntax/TermParser.hs b/parser-typechecker/src/Unison/Syntax/TermParser.hs index 6682d6b872..044a29ead5 100644 --- a/parser-typechecker/src/Unison/Syntax/TermParser.hs +++ b/parser-typechecker/src/Unison/Syntax/TermParser.hs @@ -49,7 +49,7 @@ import Unison.Syntax.Lexer qualified as L import Unison.Syntax.Name qualified as Name (toText, toVar, unsafeParseVar) import Unison.Syntax.NameSegment qualified as NameSegment import Unison.Syntax.Parser hiding (seq) -import Unison.Syntax.Parser qualified as Parser (seq, uniqueName, seq') +import Unison.Syntax.Parser qualified as Parser (seq, uniqueName) import Unison.Syntax.TypeParser qualified as TypeParser import Unison.Term (IsTop, Term) import Unison.Term qualified as Term @@ -440,7 +440,7 @@ resolveHashQualified tok = do termLeaf :: forall m v. (Monad m, Var v) => TermP v m termLeaf = asum - [ forceOrFnApplication, + [ force, hashQualifiedPrefixTerm, text, char, @@ -993,26 +993,16 @@ bang = P.label "bang" do e <- termLeaf pure $ DD.forceTerm (ann start <> ann e) (ann start) e -forceOrFnApplication :: forall m v . (Monad m, Var v) => TermP v m -forceOrFnApplication = P.label "force" do - -- `foo sqrt(2.0)` parses as `foo (sqrt 2.0)` +force :: forall m v . (Monad m, Var v) => TermP v m +force = P.label "force" $ P.try do -- `forkAt pool() blah` parses as `forkAt (pool ()) blah` - -- `foo max(x, y) z` parsed as `foo (max x y) z` - -- That is, parens immediately (no space) following a symbol is - -- treated as function application, but higher precedence than - -- the usual application syntax where args are separated by spaces - fn <- P.try do - r <- hashQualifiedPrefixTerm - P.lookAhead do - tok <- ann <$> openBlockWith "(" - guard (L.column (Ann.start tok) == L.column (Ann.end (ann r))) - pure r - Parser.seq' "(" (done fn) term - where - done :: Term v Ann -> Ann -> [Term v Ann] -> Term v Ann - done fn a [] = DD.forceTerm a a fn - done fn _ [arg] = Term.apps' fn [arg] - done fn _ args = Term.apps' fn args + -- That is, empty parens immediately (no space) following a symbol + -- is treated as high precedence function application of `Unit` + fn <- hashQualifiedPrefixTerm + tok <- ann <$> openBlockWith "(" + guard (L.column (Ann.start tok) == L.column (Ann.end (ann fn))) + close <- closeBlock + pure $ DD.forceTerm (ann fn <> ann close) (tok <> ann close) fn seqOp :: (Ord v) => P v m Pattern.SeqOp seqOp = diff --git a/unison-src/transcripts-round-trip/reparses-with-same-hash.u b/unison-src/transcripts-round-trip/reparses-with-same-hash.u index d719a178c3..6add4b6e25 100644 --- a/unison-src/transcripts-round-trip/reparses-with-same-hash.u +++ b/unison-src/transcripts-round-trip/reparses-with-same-hash.u @@ -550,4 +550,4 @@ fnApplicationSyntax = blah : Nat -> Float -> Nat blah x y = x + 1 _ = blah Environment.default() 1.0 - blah oog() Float.max(1.0, 2.0) \ No newline at end of file + blah oog() (Float.max 1.0 2.0) \ No newline at end of file From 0c2e8c5193fa210483bd6ee2a080dcc760cc88f3 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Fri, 21 Jun 2024 13:52:58 -0400 Subject: [PATCH 36/81] bugfix: don't emit virtual semis inside { or [ --- unison-syntax/src/Unison/Syntax/Lexer.hs | 8 +++++++- unison-syntax/test/Main.hs | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/unison-syntax/src/Unison/Syntax/Lexer.hs b/unison-syntax/src/Unison/Syntax/Lexer.hs index 9938e2e41c..e17074b519 100644 --- a/unison-syntax/src/Unison/Syntax/Lexer.hs +++ b/unison-syntax/src/Unison/Syntax/Lexer.hs @@ -224,7 +224,7 @@ token'' tok p = do pops p = do env <- S.get let l = layout env - if top l == column p && topBlockName l /= Just "(" -- don't emit virtual semis inside parens + if top l == column p && topContainsVirtualSemis l then pure [Token (Semi True) p p] else if column p > top l || topHasClosePair l @@ -234,6 +234,12 @@ token'' tok p = do then S.put (env {layout = pop l}) >> ((Token Close p p :) <$> pops p) else error "impossible" + -- don't emit virtual semis in (, {, or [ blocks + topContainsVirtualSemis :: Layout -> Bool + topContainsVirtualSemis = \case + [] -> False + ((name, _) : _) -> name /= "(" && name /= "{" && name /= "[" + topHasClosePair :: Layout -> Bool topHasClosePair [] = False topHasClosePair ((name, _) : _) = diff --git a/unison-syntax/test/Main.hs b/unison-syntax/test/Main.hs index e566b52609..3b3319567d 100644 --- a/unison-syntax/test/Main.hs +++ b/unison-syntax/test/Main.hs @@ -210,7 +210,13 @@ test = [Textual "test escaped quotes \"in quotes\""], t "\"\\n \\t \\b \\a\"" [Textual "\n \t \b \a"], -- Delayed string - t "'\"\"" [Reserved "'", Textual ""] + t "'\"\"" [Reserved "'", Textual ""], + -- https://github.com/unisonweb/unison/issues/4683 + -- don't emit virtual semis in ability lists or normal lists + t "{foo\n,bar}" [Open "{", simpleWordyId "foo", Reserved ",", simpleWordyId "bar", Close], + t "{foo\n ,bar}" [Open "{", simpleWordyId "foo", Reserved ",", simpleWordyId "bar", Close], + t "[foo\n,bar]" [Open "[", simpleWordyId "foo", Reserved ",", simpleWordyId "bar", Close], + t "[foo\n ,bar]" [Open "[", simpleWordyId "foo", Reserved ",", simpleWordyId "bar", Close] ] t :: String -> [Lexeme] -> Test () From b96044c2692cbb38c8c2202ffde98c31c37693a4 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Fri, 21 Jun 2024 15:37:56 -0400 Subject: [PATCH 37/81] add `alias.term.force` --- .../src/Unison/Codebase/Editor/HandleInput.hs | 8 +- .../src/Unison/Codebase/Editor/Input.hs | 2 +- .../src/Unison/CommandLine/InputPatterns.hs | 31 +++-- unison-src/transcripts/alias-term.md | 30 +++++ unison-src/transcripts/alias-term.output.md | 108 ++++++++++++++++++ 5 files changed, 166 insertions(+), 13 deletions(-) create mode 100644 unison-src/transcripts/alias-term.md create mode 100644 unison-src/transcripts/alias-term.output.md diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index dc73a118cc..9e01500cd8 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -474,7 +474,7 @@ loop e = do branch <- liftIO $ Codebase.getBranchAtPath codebase absPath _evalErrs <- liftIO $ (Backend.docsInBranchToHtmlFiles sandboxedRuntime codebase branch sourceDirectory) pure () - AliasTermI src' dest' -> do + AliasTermI force src' dest' -> do Cli.Env {codebase} <- ask src <- traverseOf _Right Cli.resolveSplit' src' srcTerms <- @@ -493,7 +493,7 @@ loop e = do pure (DeleteNameAmbiguous hqLength name srcTerms Set.empty) dest <- Cli.resolveSplit' dest' destTerms <- Cli.getTermsAt (HQ'.NameOnly <$> dest) - when (not (Set.null destTerms)) do + when (not force && not (Set.null destTerms)) do Cli.returnEarly (TermAlreadyExists dest' destTerms) description <- inputDescription input Cli.stepAt description (BranchUtil.makeAddTermName (first Path.unabsolute dest) srcTerm) @@ -998,10 +998,10 @@ inputDescription input = ResetRootI src0 -> do src <- hp' src0 pure ("reset-root " <> src) - AliasTermI src0 dest0 -> do + AliasTermI force src0 dest0 -> do src <- hhqs' src0 dest <- ps' dest0 - pure ("alias.term " <> src <> " " <> dest) + pure ((if force then "alias.term.force " else "alias.term ") <> src <> " " <> dest) AliasTypeI src0 dest0 -> do src <- hhqs' src0 dest <- ps' dest0 diff --git a/unison-cli/src/Unison/Codebase/Editor/Input.hs b/unison-cli/src/Unison/Codebase/Editor/Input.hs index efcc2be7e6..fb968dc5af 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Input.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Input.hs @@ -132,7 +132,7 @@ data Input -- > names .foo.bar#asdflkjsdf -- > names #sdflkjsdfhsdf NamesI IsGlobal (HQ.HashQualified Name) - | AliasTermI HashOrHQSplit' Path.Split' + | AliasTermI !Bool HashOrHQSplit' Path.Split' -- bool = force? | AliasTypeI HashOrHQSplit' Path.Split' | AliasManyI [Path.HQSplit] Path' | MoveAllI Path.Path' Path.Path' diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index 54b65279d2..7f5bc46dc6 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -1397,14 +1397,28 @@ deleteBranch = aliasTerm :: InputPattern aliasTerm = InputPattern - "alias.term" - [] - I.Visible - [("term to alias", Required, exactDefinitionTermQueryArg), ("alias name", Required, newNameArg)] - "`alias.term foo bar` introduces `bar` with the same definition as `foo`." - $ \case - [oldName, newName] -> Input.AliasTermI <$> handleShortHashOrHQSplit'Arg oldName <*> handleSplit'Arg newName - _ -> Left . warn $ P.wrap "`alias.term` takes two arguments, like `alias.term oldname newname`." + { patternName = "alias.term", + aliases = [], + visibility = I.Visible, + args = [("term to alias", Required, exactDefinitionTermQueryArg), ("alias name", Required, newNameArg)], + help = "`alias.term foo bar` introduces `bar` with the same definition as `foo`.", + parse = \case + [oldName, newName] -> Input.AliasTermI False <$> handleShortHashOrHQSplit'Arg oldName <*> handleSplit'Arg newName + _ -> Left . warn $ P.wrap "`alias.term` takes two arguments, like `alias.term oldname newname`." + } + +aliasTermForce :: InputPattern +aliasTermForce = + InputPattern + { patternName = "alias.term.force", + aliases = [], + visibility = I.Hidden, + args = [("term to alias", Required, exactDefinitionTermQueryArg), ("alias name", Required, newNameArg)], + help = "`alias.term.force foo bar` introduces `bar` with the same definition as `foo`.", + parse = \case + [oldName, newName] -> Input.AliasTermI True <$> handleShortHashOrHQSplit'Arg oldName <*> handleSplit'Arg newName + _ -> Left . warn $ P.wrap "`alias.term.force` takes two arguments, like `alias.term.force oldname newname`." + } aliasType :: InputPattern aliasType = @@ -3296,6 +3310,7 @@ validInputs = [ add, aliasMany, aliasTerm, + aliasTermForce, aliasType, api, authLogin, diff --git a/unison-src/transcripts/alias-term.md b/unison-src/transcripts/alias-term.md new file mode 100644 index 0000000000..b9a3a5e4a9 --- /dev/null +++ b/unison-src/transcripts/alias-term.md @@ -0,0 +1,30 @@ +`alias.term` makes a new name for a term. + +```ucm:hide +project/main> builtins.mergeio lib.builtins +``` + +```ucm +project/main> alias.term lib.builtins.bug foo +project/main> ls +project/main> reflog +``` + +It won't create a conflicted name, though. + +```ucm:error +project/main> alias.term lib.builtins.todo foo +``` + +```ucm +project/main> ls +project/main> reflog +``` + +You can use `alias.term.force` for that. + +```ucm +project/main> alias.term.force lib.builtins.todo foo +project/main> ls +project/main> reflog +``` diff --git a/unison-src/transcripts/alias-term.output.md b/unison-src/transcripts/alias-term.output.md new file mode 100644 index 0000000000..16b0648706 --- /dev/null +++ b/unison-src/transcripts/alias-term.output.md @@ -0,0 +1,108 @@ +`alias.term` makes a new name for a term. + +```ucm +project/main> alias.term lib.builtins.bug foo + + Done. + +project/main> ls + + 1. foo (a -> b) + 2. lib/ (643 terms, 92 types) + +project/main> reflog + + Here is a log of the root namespace hashes, starting with the + most recent, along with the command that got us there. Try: + + `fork 2 .old` + `fork #akvmucsmam .old` to make an old namespace + accessible again, + + `reset-root #akvmucsmam` to reset the root namespace and + its history to that of the + specified namespace. + + When Root Hash Action + 1. now #94cs49dp5a alias.term .__projects._f3c06c2f_7513_4da4_87a2_5b7860d8895f... + 2. now #akvmucsmam builtins.mergeio .__projects._f3c06c2f_7513_4da4_87a2_5b7860... + 3. #sg60bvjo91 history starts here + + Tip: Use `diff.namespace 1 7` to compare namespaces between + two points in history. + +``` +It won't create a conflicted name, though. + +```ucm +project/main> alias.term lib.builtins.todo foo + + ⚠️ + + A term by that name already exists. + +``` +```ucm +project/main> ls + + 1. foo (a -> b) + 2. lib/ (643 terms, 92 types) + +project/main> reflog + + Here is a log of the root namespace hashes, starting with the + most recent, along with the command that got us there. Try: + + `fork 2 .old` + `fork #akvmucsmam .old` to make an old namespace + accessible again, + + `reset-root #akvmucsmam` to reset the root namespace and + its history to that of the + specified namespace. + + When Root Hash Action + 1. now #94cs49dp5a alias.term .__projects._f3c06c2f_7513_4da4_87a2_5b7860d8895f... + 2. now #akvmucsmam builtins.mergeio .__projects._f3c06c2f_7513_4da4_87a2_5b7860... + 3. #sg60bvjo91 history starts here + + Tip: Use `diff.namespace 1 7` to compare namespaces between + two points in history. + +``` +You can use `alias.term.force` for that. + +```ucm +project/main> alias.term.force lib.builtins.todo foo + + Done. + +project/main> ls + + 1. foo (a -> b) + 2. foo (a -> b) + 3. lib/ (643 terms, 92 types) + +project/main> reflog + + Here is a log of the root namespace hashes, starting with the + most recent, along with the command that got us there. Try: + + `fork 2 .old` + `fork #94cs49dp5a .old` to make an old namespace + accessible again, + + `reset-root #94cs49dp5a` to reset the root namespace and + its history to that of the + specified namespace. + + When Root Hash Action + 1. now #agpq4mvdbu alias.term.force .__projects._f3c06c2f_7513_4da4_87a2_5b7860... + 2. now #94cs49dp5a alias.term .__projects._f3c06c2f_7513_4da4_87a2_5b7860d8895f... + 3. now #akvmucsmam builtins.mergeio .__projects._f3c06c2f_7513_4da4_87a2_5b7860... + 4. #sg60bvjo91 history starts here + + Tip: Use `diff.namespace 1 7` to compare namespaces between + two points in history. + +``` From db3d0e73b35aab4e694e81ce35b029cffb0625df Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Fri, 21 Jun 2024 16:09:50 -0400 Subject: [PATCH 38/81] delete `reflog` calls to make transcript more idempotent --- unison-src/transcripts/alias-term.md | 3 - unison-src/transcripts/alias-term.output.md | 64 --------------------- 2 files changed, 67 deletions(-) diff --git a/unison-src/transcripts/alias-term.md b/unison-src/transcripts/alias-term.md index b9a3a5e4a9..cd4f7454ae 100644 --- a/unison-src/transcripts/alias-term.md +++ b/unison-src/transcripts/alias-term.md @@ -7,7 +7,6 @@ project/main> builtins.mergeio lib.builtins ```ucm project/main> alias.term lib.builtins.bug foo project/main> ls -project/main> reflog ``` It won't create a conflicted name, though. @@ -18,7 +17,6 @@ project/main> alias.term lib.builtins.todo foo ```ucm project/main> ls -project/main> reflog ``` You can use `alias.term.force` for that. @@ -26,5 +24,4 @@ You can use `alias.term.force` for that. ```ucm project/main> alias.term.force lib.builtins.todo foo project/main> ls -project/main> reflog ``` diff --git a/unison-src/transcripts/alias-term.output.md b/unison-src/transcripts/alias-term.output.md index 16b0648706..733ff13849 100644 --- a/unison-src/transcripts/alias-term.output.md +++ b/unison-src/transcripts/alias-term.output.md @@ -10,27 +10,6 @@ project/main> ls 1. foo (a -> b) 2. lib/ (643 terms, 92 types) -project/main> reflog - - Here is a log of the root namespace hashes, starting with the - most recent, along with the command that got us there. Try: - - `fork 2 .old` - `fork #akvmucsmam .old` to make an old namespace - accessible again, - - `reset-root #akvmucsmam` to reset the root namespace and - its history to that of the - specified namespace. - - When Root Hash Action - 1. now #94cs49dp5a alias.term .__projects._f3c06c2f_7513_4da4_87a2_5b7860d8895f... - 2. now #akvmucsmam builtins.mergeio .__projects._f3c06c2f_7513_4da4_87a2_5b7860... - 3. #sg60bvjo91 history starts here - - Tip: Use `diff.namespace 1 7` to compare namespaces between - two points in history. - ``` It won't create a conflicted name, though. @@ -48,27 +27,6 @@ project/main> ls 1. foo (a -> b) 2. lib/ (643 terms, 92 types) -project/main> reflog - - Here is a log of the root namespace hashes, starting with the - most recent, along with the command that got us there. Try: - - `fork 2 .old` - `fork #akvmucsmam .old` to make an old namespace - accessible again, - - `reset-root #akvmucsmam` to reset the root namespace and - its history to that of the - specified namespace. - - When Root Hash Action - 1. now #94cs49dp5a alias.term .__projects._f3c06c2f_7513_4da4_87a2_5b7860d8895f... - 2. now #akvmucsmam builtins.mergeio .__projects._f3c06c2f_7513_4da4_87a2_5b7860... - 3. #sg60bvjo91 history starts here - - Tip: Use `diff.namespace 1 7` to compare namespaces between - two points in history. - ``` You can use `alias.term.force` for that. @@ -83,26 +41,4 @@ project/main> ls 2. foo (a -> b) 3. lib/ (643 terms, 92 types) -project/main> reflog - - Here is a log of the root namespace hashes, starting with the - most recent, along with the command that got us there. Try: - - `fork 2 .old` - `fork #94cs49dp5a .old` to make an old namespace - accessible again, - - `reset-root #94cs49dp5a` to reset the root namespace and - its history to that of the - specified namespace. - - When Root Hash Action - 1. now #agpq4mvdbu alias.term.force .__projects._f3c06c2f_7513_4da4_87a2_5b7860... - 2. now #94cs49dp5a alias.term .__projects._f3c06c2f_7513_4da4_87a2_5b7860d8895f... - 3. now #akvmucsmam builtins.mergeio .__projects._f3c06c2f_7513_4da4_87a2_5b7860... - 4. #sg60bvjo91 history starts here - - Tip: Use `diff.namespace 1 7` to compare namespaces between - two points in history. - ``` From e367a67d897e8d14e1a02caa683793b6d8e048aa Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Sat, 22 Jun 2024 13:37:14 +0100 Subject: [PATCH 39/81] Use a stack to keep track of section levels --- unison-syntax/src/Unison/Syntax/Lexer.hs | 48 +++++++++++++++--------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/unison-syntax/src/Unison/Syntax/Lexer.hs b/unison-syntax/src/Unison/Syntax/Lexer.hs index e17074b519..eb78edb020 100644 --- a/unison-syntax/src/Unison/Syntax/Lexer.hs +++ b/unison-syntax/src/Unison/Syntax/Lexer.hs @@ -71,11 +71,18 @@ type BlockName = String type Layout = [(BlockName, Column)] data ParsingEnv = ParsingEnv - { layout :: !Layout, -- layout stack - opening :: Maybe BlockName, -- `Just b` if a block of type `b` is being opened - inLayout :: Bool, -- are we inside a construct that uses layout? - parentSection :: Int, -- 1 means we are inside a # Heading 1 - parentListColumn :: Int -- 4 means we are inside a list starting at the fourth column + { -- layout stack + layout :: !Layout, + -- `Just b` if a block of type `b` is being opened + opening :: Maybe BlockName, + -- are we inside a construct that uses layout? + inLayout :: Bool, + -- Use a stack to remember the parent section and + -- allow docSections within docSessions. + -- 1 means we are inside a # Heading 1 + parentSections :: [Int], + -- 4 means we are inside a list starting at the fourth column + parentListColumn :: Int } deriving (Show) @@ -309,7 +316,7 @@ lexer0' scope rem = (P.EndOfInput) -> "end of input" customErrs es = [Err <$> e | P.ErrorCustom e <- toList es] toPos (P.SourcePos _ line col) = Pos (P.unPos line) (P.unPos col) - env0 = ParsingEnv [] (Just scope) True 0 0 + env0 = ParsingEnv [] (Just scope) True [0] 0 -- hacky postprocessing pass to do some cleanup of stuff that's annoying to -- fix without adding more state to the lexer: -- - 1+1 lexes as [1, +1], convert this to [1, +, 1] @@ -429,13 +436,20 @@ lexemes' eof = -- Construct the token for opening the doc block. let openTok = Token (Open "syntax.docUntitledSection") openStart openEnd env0 <- S.get - -- Disable layout while parsing the doc block - (bodyToks0, closeTok) <- local (\env -> env {inLayout = False}) do - bodyToks <- body - closeStart <- posP - lit "}}" - closeEnd <- posP - pure (bodyToks, Token Close closeStart closeEnd) + -- Disable layout while parsing the doc block and reset the section number + (bodyToks0, closeTok) <- local + ( \env -> + env + { inLayout = False, + parentSections = 0 : (parentSections env0) + } + ) + do + bodyToks <- body + closeStart <- posP + lit "}}" + closeEnd <- posP + pure (bodyToks, Token Close closeStart closeEnd) let docToks = beforeStartToks <> [openTok] <> bodyToks0 <> [closeTok] -- Parse any layout tokens after the doc block, e.g. virtual semicolon endToks <- token' ignore (pure ()) @@ -814,12 +828,12 @@ lexemes' eof = -- # A section title (not a subsection) section :: P [Token Lexeme] section = wrap "syntax.docSection" $ do - n <- S.gets parentSection - hashes <- P.try $ lit (replicate n '#') *> P.takeWhile1P Nothing (== '#') <* sp + ns <- S.gets parentSections + hashes <- P.try $ lit (replicate (head ns) '#') *> P.takeWhile1P Nothing (== '#') <* sp title <- paragraph <* CP.space - let m = length hashes + n + let m = length hashes + head ns body <- - local (\env -> env {parentSection = m}) $ + local (\env -> env {parentSections = (m : (tail ns))}) $ P.many (sectionElem <* CP.space) pure $ title <> join body From 25ce29301bb939c7b43b34c6ed7ea905768c5fdd Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Sat, 22 Jun 2024 13:37:32 +0100 Subject: [PATCH 40/81] add tests --- .../transcripts-round-trip/main.output.md | 55 ++++++++++++++++++- .../reparses-with-same-hash.u | 31 ++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/unison-src/transcripts-round-trip/main.output.md b/unison-src/transcripts-round-trip/main.output.md index fdeb756531..a085eba7cb 100644 --- a/unison-src/transcripts-round-trip/main.output.md +++ b/unison-src/transcripts-round-trip/main.output.md @@ -24,7 +24,7 @@ So we can see the pretty-printed output: ☝️ - I added 105 definitions to the top of scratch.u + I added 107 definitions to the top of scratch.u You can edit them there, then run `update` to replace the definitions currently in this namespace. @@ -331,6 +331,59 @@ fix_4384e = }} }} +fix_4729a : Doc2 +fix_4729a = + {{ + # H1A + + ## H2A + + ``` + {{ + # H1B + + ## B2B + + + }} + ``` + + ## H2A + + + }} + +fix_4729b : Doc2 +fix_4729b = + {{ + # H1A + + ## H2A + + {{ docTable + [[{{ + # HA + + + }}, {{ + # HB + + + }}], [{{ + # a + + + }}, {{ + # b + + + }}]] }} + + ## H2A + + + }} + Fix_525.bar.quaffle : Nat Fix_525.bar.quaffle = 32 diff --git a/unison-src/transcripts-round-trip/reparses-with-same-hash.u b/unison-src/transcripts-round-trip/reparses-with-same-hash.u index 09b941ff64..7c41b2d83c 100644 --- a/unison-src/transcripts-round-trip/reparses-with-same-hash.u +++ b/unison-src/transcripts-round-trip/reparses-with-same-hash.u @@ -542,4 +542,33 @@ fix_4384d = {{ {{ docExampleBlock 0 '[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, fix_4384e = id : x -> x id x = x - {{ {{ docExampleBlock 0 (id id id id id id id id id id id id id id id id id id id id id (x -> 0) }} }} \ No newline at end of file + {{ {{ docExampleBlock 0 (id id id id id id id id id id id id id id id id id id id id id (x -> 0) }} }} + +fix_4729a = {{ + # H1A + + ## H2A + + ``` + {{ + # H1B + + ## B2B + }} + ``` + + ## H2A +}} + +fix_4729b = {{ + # H1A + + ## H2A + + {{ docTable [ + [ {{ # HA }}, {{ # HB }} ], + [ {{ ## a }}, {{ ## b }} ] + ] }} + + ## H2A +}} From 7108787a5a7826a751d9836651a06cb286454ee0 Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Sat, 22 Jun 2024 14:02:40 +0100 Subject: [PATCH 41/81] add more tests --- .../transcripts-round-trip/main.output.md | 25 ++++++++++++++++++- .../reparses-with-same-hash.u | 12 +++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/unison-src/transcripts-round-trip/main.output.md b/unison-src/transcripts-round-trip/main.output.md index a085eba7cb..7b03f20f3e 100644 --- a/unison-src/transcripts-round-trip/main.output.md +++ b/unison-src/transcripts-round-trip/main.output.md @@ -24,7 +24,7 @@ So we can see the pretty-printed output: ☝️ - I added 107 definitions to the top of scratch.u + I added 108 definitions to the top of scratch.u You can edit them there, then run `update` to replace the definitions currently in this namespace. @@ -384,6 +384,29 @@ fix_4729b = }} +fix_4729c : Doc2 +fix_4729c = + {{ + # Examples `` + docCallout + (Some + (syntax.docUntitledSection + [syntax.docSection (syntax.docParagraph [syntax.docWord "Title"]) []])) + (syntax.docUntitledSection + [ syntax.docParagraph + [ syntax.docWord "This" + , syntax.docWord "is" + , syntax.docWord "a" + , syntax.docWord "callout" + , syntax.docWord "with" + , syntax.docWord "a" + , syntax.docWord "title" + ] + ]) `` + + + }} + Fix_525.bar.quaffle : Nat Fix_525.bar.quaffle = 32 diff --git a/unison-src/transcripts-round-trip/reparses-with-same-hash.u b/unison-src/transcripts-round-trip/reparses-with-same-hash.u index 7c41b2d83c..4209b09b1e 100644 --- a/unison-src/transcripts-round-trip/reparses-with-same-hash.u +++ b/unison-src/transcripts-round-trip/reparses-with-same-hash.u @@ -572,3 +572,15 @@ fix_4729b = {{ ## H2A }} + +fix_4729c = {{ + # Examples + ``` + docCallout + (Some + {{ + # Title + + }}) {{ This is a callout with a title }} + ``` +}} From e806225540f51854f257e7b8783adce0f3223e90 Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Sat, 22 Jun 2024 17:13:40 +0100 Subject: [PATCH 42/81] Add group such that elements are concatenated without space --- parser-typechecker/src/Unison/Syntax/TermPrinter.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser-typechecker/src/Unison/Syntax/TermPrinter.hs b/parser-typechecker/src/Unison/Syntax/TermPrinter.hs index bc33c43ca2..596ca1d77b 100644 --- a/parser-typechecker/src/Unison/Syntax/TermPrinter.hs +++ b/parser-typechecker/src/Unison/Syntax/TermPrinter.hs @@ -459,7 +459,7 @@ pretty0 go tm = goNormal 10 tm PP.hang kw <$> fmap PP.lines (traverse go rs) (Bytes' bs, _) -> - pure $ fmt S.BytesLiteral "0xs" <> PP.shown (Bytes.fromWord8s (map fromIntegral bs)) + pure $ PP.group $ fmt S.BytesLiteral "0xs" <> PP.shown (Bytes.fromWord8s (map fromIntegral bs)) BinaryAppsPred' apps lastArg -> do prettyLast <- pretty0 (ac 3 Normal im doc) lastArg prettyApps <- binaryApps apps prettyLast From 85ab99f60590119ec1d81816ad76a5cf3dd21976 Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Sat, 22 Jun 2024 17:21:02 +0100 Subject: [PATCH 43/81] add test in reparses-with-same-hash # Conflicts: # unison-src/transcripts-round-trip/reparses-with-same-hash.u --- unison-src/transcripts-round-trip/reparses-with-same-hash.u | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unison-src/transcripts-round-trip/reparses-with-same-hash.u b/unison-src/transcripts-round-trip/reparses-with-same-hash.u index 09b941ff64..0e702cc793 100644 --- a/unison-src/transcripts-round-trip/reparses-with-same-hash.u +++ b/unison-src/transcripts-round-trip/reparses-with-same-hash.u @@ -542,4 +542,6 @@ fix_4384d = {{ {{ docExampleBlock 0 '[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, fix_4384e = id : x -> x id x = x - {{ {{ docExampleBlock 0 (id id id id id id id id id id id id id id id id id id id id id (x -> 0) }} }} \ No newline at end of file + {{ {{ docExampleBlock 0 (id id id id id id id id id id id id id id id id id id id id id (x -> 0) }} }} + +fix_4727 = {{ `` 0xs900dc0ffee `` }} From 9c610510c1e1d21e29ccdd2e2122a16e9cc1405e Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Sat, 22 Jun 2024 18:08:33 +0100 Subject: [PATCH 44/81] update main.output.md --- unison-src/transcripts-round-trip/main.output.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unison-src/transcripts-round-trip/main.output.md b/unison-src/transcripts-round-trip/main.output.md index fdeb756531..15839be0d8 100644 --- a/unison-src/transcripts-round-trip/main.output.md +++ b/unison-src/transcripts-round-trip/main.output.md @@ -24,7 +24,7 @@ So we can see the pretty-printed output: ☝️ - I added 105 definitions to the top of scratch.u + I added 106 definitions to the top of scratch.u You can edit them there, then run `update` to replace the definitions currently in this namespace. @@ -331,6 +331,9 @@ fix_4384e = }} }} +fix_4727 : Doc2 +fix_4727 = {{ `` 0xs900dc0ffee `` }} + Fix_525.bar.quaffle : Nat Fix_525.bar.quaffle = 32 From d4946ed22c990866a6cac9f6643849e845fd9718 Mon Sep 17 00:00:00 2001 From: Ed Date: Sat, 22 Jun 2024 20:03:24 +0100 Subject: [PATCH 45/81] Fix typo Co-authored-by: Arya Irani <538571+aryairani@users.noreply.github.com> --- unison-syntax/src/Unison/Syntax/Lexer.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unison-syntax/src/Unison/Syntax/Lexer.hs b/unison-syntax/src/Unison/Syntax/Lexer.hs index eb78edb020..2a5e2506ae 100644 --- a/unison-syntax/src/Unison/Syntax/Lexer.hs +++ b/unison-syntax/src/Unison/Syntax/Lexer.hs @@ -78,7 +78,7 @@ data ParsingEnv = ParsingEnv -- are we inside a construct that uses layout? inLayout :: Bool, -- Use a stack to remember the parent section and - -- allow docSections within docSessions. + -- allow docSections within docSections. -- 1 means we are inside a # Heading 1 parentSections :: [Int], -- 4 means we are inside a list starting at the fourth column From 3a9e8e51a09e037f40c77a61191f268af480ff7b Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Sun, 23 Jun 2024 19:34:27 -0500 Subject: [PATCH 46/81] Test that `docs.to-html` actually writes files --- .github/workflows/ci.yaml | 10 +++++++++- .github/workflows/update-transcripts.yaml | 3 +++ scripts/check.sh | 1 + .../fix4402.md => transcripts-manual/docs.to-html.md} | 8 ++++---- .../docs.to-html.output.md} | 10 +++++----- .../transcripts-manual/docs.to-html/direct/doc.html | 1 + .../docs.to-html/pretty/deeply/nested/doc.html | 1 + 7 files changed, 24 insertions(+), 10 deletions(-) rename unison-src/{transcripts/fix4402.md => transcripts-manual/docs.to-html.md} (56%) rename unison-src/{transcripts/fix4402.output.md => transcripts-manual/docs.to-html.output.md} (88%) create mode 100644 unison-src/transcripts-manual/docs.to-html/direct/doc.html create mode 100644 unison-src/transcripts-manual/docs.to-html/pretty/deeply/nested/doc.html diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 35489112f1..91fac8abea 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -270,6 +270,14 @@ jobs: ${{env.transcripts}} # Fail if any transcripts cause git diffs. git diff --ignore-cr-at-eol --exit-code unison-src/transcripts + - name: docs.to-html + if: steps.cache-transcript-test-results.outputs.cache-hit != 'true' + run: | + ${{env.ucm}} transcript unison-src/transcripts-manual/docs.to-html.md + # Fail if the output or generated docs differ. + git diff --ignore-cr-at-eol --exit-code \ + unison-src/transcripts-manual/docs.to-html.output.md \ + unison-src/transcripts-manual/docs.to-html - name: mark transcripts as passing if: steps.cache-transcript-test-results.outputs.cache-hit != 'true' run: | @@ -417,7 +425,7 @@ jobs: build-jit-binary: name: build jit binary needs: generate-jit-source - uses: ./.github/workflows/ci-build-jit-binary.yaml + uses: ./.github/workflows/ci-build-jit-binary.yaml test-jit: name: test jit diff --git a/.github/workflows/update-transcripts.yaml b/.github/workflows/update-transcripts.yaml index 3c35e9f04f..68b7ec1e92 100644 --- a/.github/workflows/update-transcripts.yaml +++ b/.github/workflows/update-transcripts.yaml @@ -36,6 +36,9 @@ jobs: stack exec unison transcript unison-src/transcripts-manual/rewrites.md - name: transcripts run: stack exec transcripts + - name: docs.to-html + run: | + stack exec unison transcript unison-src/transcripts-manual/docs.to-html.md - name: save transcript changes uses: stefanzweifel/git-auto-commit-action@v5 with: diff --git a/scripts/check.sh b/scripts/check.sh index 03bb6609f9..1784f69c6d 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -6,4 +6,5 @@ true \ && stack exec transcripts \ && stack exec unison transcript unison-src/transcripts-round-trip/main.md \ && stack exec unison transcript unison-src/transcripts-manual/rewrites.md \ + && stack exec unison transcript unison-src/transcripts-manual/docs.to-html.md \ && stack exec cli-integration-tests diff --git a/unison-src/transcripts/fix4402.md b/unison-src/transcripts-manual/docs.to-html.md similarity index 56% rename from unison-src/transcripts/fix4402.md rename to unison-src/transcripts-manual/docs.to-html.md index e79d243e5c..528d038e49 100644 --- a/unison-src/transcripts/fix4402.md +++ b/unison-src/transcripts-manual/docs.to-html.md @@ -1,6 +1,6 @@ ```ucm -.> project.create test-4402 -test-4402/main> builtins.merge +.> project.create test-html-docs +test-html-docs/main> builtins.merge ``` ```unison @@ -15,6 +15,6 @@ some.outside = 3 ``` ```ucm -test-4402/main> add -test-4402/main> docs.to-html some.ns /tmp/test-4402 +test-html-docs/main> add +test-html-docs/main> docs.to-html some.ns unison-src/transcripts-manual/docs.to-html ``` diff --git a/unison-src/transcripts/fix4402.output.md b/unison-src/transcripts-manual/docs.to-html.output.md similarity index 88% rename from unison-src/transcripts/fix4402.output.md rename to unison-src/transcripts-manual/docs.to-html.output.md index 858f6474c5..bdfc5fa4a6 100644 --- a/unison-src/transcripts/fix4402.output.md +++ b/unison-src/transcripts-manual/docs.to-html.output.md @@ -1,7 +1,7 @@ ```ucm -.> project.create test-4402 +.> project.create test-html-docs - 🎉 I've created the project test-4402. + 🎉 I've created the project test-html-docs. I'll now fetch the latest version of the base Unison library... @@ -20,7 +20,7 @@ 🎉 🥳 Happy coding! -test-4402/main> builtins.merge +test-html-docs/main> builtins.merge Done. @@ -57,7 +57,7 @@ some.outside = 3 ``` ```ucm -test-4402/main> add +test-html-docs/main> add ⍟ I've added these definitions: @@ -70,6 +70,6 @@ test-4402/main> add (also named lib.base.data.Map.internal.delta) some.outside.doc : Doc -test-4402/main> docs.to-html some.ns /tmp/test-4402 +test-html-docs/main> docs.to-html some.ns unison-src/transcripts-manual/docs.to-html ``` diff --git a/unison-src/transcripts-manual/docs.to-html/direct/doc.html b/unison-src/transcripts-manual/docs.to-html/direct/doc.html new file mode 100644 index 0000000000..0e9f37a540 --- /dev/null +++ b/unison-src/transcripts-manual/docs.to-html/direct/doc.html @@ -0,0 +1 @@ +
A doc directly in the namespace.
\ No newline at end of file diff --git a/unison-src/transcripts-manual/docs.to-html/pretty/deeply/nested/doc.html b/unison-src/transcripts-manual/docs.to-html/pretty/deeply/nested/doc.html new file mode 100644 index 0000000000..1e5a75f500 --- /dev/null +++ b/unison-src/transcripts-manual/docs.to-html/pretty/deeply/nested/doc.html @@ -0,0 +1 @@ +
A doc pretty deeply nested in the namespace.
\ No newline at end of file From 10d26229910ac77024ba6072c80f7bf6a2af0174 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Sun, 23 Jun 2024 22:09:51 -0500 Subject: [PATCH 47/81] Remove unused error case --- parser-typechecker/src/Unison/PrintError.hs | 14 -------------- unison-syntax/src/Unison/Syntax/Lexer.hs | 3 +-- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/parser-typechecker/src/Unison/PrintError.hs b/parser-typechecker/src/Unison/PrintError.hs index 56bf11fcfd..835b2c4e2a 100644 --- a/parser-typechecker/src/Unison/PrintError.hs +++ b/parser-typechecker/src/Unison/PrintError.hs @@ -1370,20 +1370,6 @@ renderParseErrors s = \case <> style ErrorSite (fromString open) <> ".\n\n" <> excerpt - L.InvalidWordyId id -> - Pr.lines - [ "The identifier, " <> quoteCode id <> ", isn't valid syntax: ", - "", - excerpt, - "Here's a few examples of valid syntax: " - <> quoteCode "abba1'" - <> ", " - <> quoteCode "snake_case" - <> ", " - <> quoteCode "Foo.zoink!" - <> ", and " - <> quoteCode "🌻" - ] L.ReservedWordyId id -> Pr.lines [ "The identifier, " <> quoteCode id <> ", used here is a reserved keyword: ", diff --git a/unison-syntax/src/Unison/Syntax/Lexer.hs b/unison-syntax/src/Unison/Syntax/Lexer.hs index 9938e2e41c..e708bc772c 100644 --- a/unison-syntax/src/Unison/Syntax/Lexer.hs +++ b/unison-syntax/src/Unison/Syntax/Lexer.hs @@ -95,8 +95,7 @@ parseFailure :: EP.ParseError [Char] (Token Err) -> P a parseFailure e = PI.ParsecT $ \s _ _ _ eerr -> eerr e s data Err - = InvalidWordyId String - | ReservedWordyId String + = ReservedWordyId String | InvalidSymbolyId String | ReservedSymbolyId String | InvalidShortHash String From 782ac4164d54c02457bcbc10134fce22894bbd0b Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Sun, 23 Jun 2024 22:18:51 -0500 Subject: [PATCH 48/81] Remove redundant `,` from lexer errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Appositives only need to be offset by commas if there isn’t already some other punctuation. --- parser-typechecker/src/Unison/PrintError.hs | 6 +++--- unison-src/transcripts/error-messages.output.md | 2 +- unison-src/transcripts/generic-parse-errors.output.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/parser-typechecker/src/Unison/PrintError.hs b/parser-typechecker/src/Unison/PrintError.hs index 835b2c4e2a..d6e50ebbb5 100644 --- a/parser-typechecker/src/Unison/PrintError.hs +++ b/parser-typechecker/src/Unison/PrintError.hs @@ -1372,7 +1372,7 @@ renderParseErrors s = \case <> excerpt L.ReservedWordyId id -> Pr.lines - [ "The identifier, " <> quoteCode id <> ", used here is a reserved keyword: ", + [ "The identifier " <> quoteCode id <> " used here is a reserved keyword: ", "", excerpt, Pr.wrap $ @@ -1382,7 +1382,7 @@ renderParseErrors s = \case ] L.InvalidSymbolyId id -> Pr.lines - [ "The infix identifier, " <> quoteCode id <> ", isn’t valid syntax: ", + [ "The infix identifier " <> quoteCode id <> " isn’t valid syntax: ", "", excerpt, "Here are a few valid examples: " @@ -1394,7 +1394,7 @@ renderParseErrors s = \case ] L.ReservedSymbolyId id -> Pr.lines - [ "The identifier, " <> quoteCode id <> ", is reserved by Unison and can't be used as an operator: ", + [ "The identifier " <> quoteCode id <> " is reserved by Unison and can't be used as an operator: ", "", excerpt ] diff --git a/unison-src/transcripts/error-messages.output.md b/unison-src/transcripts/error-messages.output.md index dacc86ac7a..82ae8a88b9 100644 --- a/unison-src/transcripts/error-messages.output.md +++ b/unison-src/transcripts/error-messages.output.md @@ -343,7 +343,7 @@ use.keyword.in.namespace = 1 Loading changes detected in scratch.u. - The identifier, `namespace`, used here is a reserved keyword: + The identifier `namespace` used here is a reserved keyword: 1 | use.keyword.in.namespace = 1 diff --git a/unison-src/transcripts/generic-parse-errors.output.md b/unison-src/transcripts/generic-parse-errors.output.md index 3a1c7b19ec..7800cbab47 100644 --- a/unison-src/transcripts/generic-parse-errors.output.md +++ b/unison-src/transcripts/generic-parse-errors.output.md @@ -30,7 +30,7 @@ namespace.blah = 1 Loading changes detected in scratch.u. - The identifier, `namespace`, used here is a reserved keyword: + The identifier `namespace` used here is a reserved keyword: 1 | namespace.blah = 1 From 0d441f3b75a91c527a7a781f4889766684dc4aaf Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 08:26:52 -0400 Subject: [PATCH 49/81] move todo data types around --- .../Codebase/Editor/HandleInput/Todo.hs | 26 +++++++++---------- .../src/Unison/Codebase/Editor/Output.hs | 17 +++++++++--- .../src/Unison/Codebase/Editor/TodoOutput.hs | 19 -------------- .../src/Unison/CommandLine/OutputMessages.hs | 18 ++++++------- unison-cli/unison-cli.cabal | 1 - 5 files changed, 34 insertions(+), 47 deletions(-) delete mode 100644 unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs index 2b24d33128..6795581dc2 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -14,7 +14,6 @@ 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.Output -import Unison.Codebase.Editor.TodoOutput qualified as TO import Unison.Names qualified as Names import Unison.Util.Defns (Defns (..)) @@ -36,16 +35,17 @@ handleTodo = do } pure (hashLen, directDependencies) - let todo = - TO.TodoOutput - { directDependenciesWithoutNames = - Defns - { terms = Set.difference directDependencies.terms (Branch.deepTermReferences currentNamespace), - types = Set.difference directDependencies.types (Branch.deepTypeReferences currentNamespace) - }, - nameConflicts = Names.conflicts (Branch.toNames currentNamespaceWithoutLibdeps) - } + ppe <- Cli.currentPrettyPrintEnvDecl - pped <- Cli.currentPrettyPrintEnvDecl - - Cli.respondNumbered (TodoOutput hashLen pped todo) + Cli.respondNumbered $ + Output'Todo + TodoOutput + { hashLen, + ppe, + directDependenciesWithoutNames = + Defns + { terms = Set.difference directDependencies.terms (Branch.deepTermReferences currentNamespace), + types = Set.difference directDependencies.types (Branch.deepTypeReferences currentNamespace) + }, + nameConflicts = Names.conflicts (Branch.toNames currentNamespaceWithoutLibdeps) + } diff --git a/unison-cli/src/Unison/Codebase/Editor/Output.hs b/unison-cli/src/Unison/Codebase/Editor/Output.hs index de47f53a83..8a5efe3949 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Output.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Output.hs @@ -8,6 +8,7 @@ module Unison.Codebase.Editor.Output ListDetailed, HistoryTail (..), TestReportStats (..), + TodoOutput (..), UndoFailureReason (..), ShareError (..), UpdateOrUpgrade (..), @@ -37,7 +38,6 @@ import Unison.Codebase.Editor.RemoteRepo import Unison.Codebase.Editor.SlurpResult (SlurpResult (..)) import Unison.Codebase.Editor.SlurpResult qualified as SR import Unison.Codebase.Editor.StructuredArgument (StructuredArgument) -import Unison.Codebase.Editor.TodoOutput qualified as TO import Unison.Codebase.IntegrityCheck (IntegrityResult (..)) import Unison.Codebase.Path (Path') import Unison.Codebase.Path qualified as Path @@ -59,9 +59,10 @@ import Unison.NamesWithHistory qualified as Names import Unison.Parser.Ann (Ann) import Unison.Prelude import Unison.PrettyPrintEnv qualified as PPE +import Unison.PrettyPrintEnvDecl (PrettyPrintEnvDecl) import Unison.PrettyPrintEnvDecl qualified as PPE import Unison.Project (ProjectAndBranch, ProjectBranchName, ProjectName, Semver) -import Unison.Reference (Reference, TermReferenceId, TypeReference) +import Unison.Reference (Reference, TermReference, TermReferenceId, TypeReference) import Unison.Reference qualified as Reference import Unison.Referent (Referent) import Unison.Server.Backend (ShallowListEntry (..)) @@ -75,6 +76,7 @@ import Unison.Term (Term) import Unison.Type (Type) import Unison.Typechecker.Context qualified as Context import Unison.UnisonFile qualified as UF +import Unison.Util.Defns (DefnsF) import Unison.Util.Pretty qualified as P import Unison.Util.Relation (Relation) import Unison.WatchKind qualified as WK @@ -117,7 +119,7 @@ data NumberedOutput | ShowDiffAfterPull Path.Path' Path.Absolute PPE.PrettyPrintEnv (BranchDiffOutput Symbol Ann) | -- ShowDiffAfterCreateAuthor NameSegment Path.Path' Path.Absolute PPE.PrettyPrintEnv (BranchDiffOutput Symbol Ann) - | TodoOutput !Int !PPE.PrettyPrintEnvDecl !(TO.TodoOutput Symbol Ann) + | Output'Todo !TodoOutput | -- | CantDeleteDefinitions ppe couldntDelete becauseTheseStillReferenceThem CantDeleteDefinitions PPE.PrettyPrintEnvDecl (Map LabeledDependency (NESet LabeledDependency)) | -- | CantDeleteNamespace ppe couldntDelete becauseTheseStillReferenceThem @@ -140,6 +142,13 @@ data NumberedOutput Path.Absolute -- The namespace we're checking dependencies for. (Map LabeledDependency (Set Name)) -- Mapping of external dependencies to their local dependents. +data TodoOutput = TodoOutput + { hashLen :: !Int, + ppe :: !PrettyPrintEnvDecl, + directDependenciesWithoutNames :: DefnsF Set TermReference TypeReference, + nameConflicts :: Names + } + data AmbiguousReset'Argument = AmbiguousReset'Hash | AmbiguousReset'Target @@ -667,4 +676,4 @@ isNumberedFailure = \case ShowDiffAfterUndo {} -> False ShowDiffNamespace _ _ _ bd -> BD.isEmpty bd ListNamespaceDependencies {} -> False - TodoOutput {} -> False + Output'Todo {} -> False diff --git a/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs b/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs deleted file mode 100644 index 48b1e40a85..0000000000 --- a/unison-cli/src/Unison/Codebase/Editor/TodoOutput.hs +++ /dev/null @@ -1,19 +0,0 @@ -module Unison.Codebase.Editor.TodoOutput - ( TodoOutput (..), - noConflicts, - ) -where - -import Unison.Names (Names) -import Unison.Prelude -import Unison.Reference (TermReference, TypeReference) -import Unison.Util.Defns (DefnsF) - -data TodoOutput v a = TodoOutput - { directDependenciesWithoutNames :: DefnsF Set TermReference TypeReference, - nameConflicts :: Names - } - -noConflicts :: TodoOutput v a -> Bool -noConflicts todo = - nameConflicts todo == mempty diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 87e06282d0..5b83541a00 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -53,6 +53,7 @@ import Unison.Codebase.Editor.Output Output (..), ShareError (..), TestReportStats (CachedTests, NewlyComputed), + TodoOutput, UndoFailureReason (CantUndoPastMerge, CantUndoPastStart), ) import Unison.Codebase.Editor.Output qualified as E @@ -63,7 +64,6 @@ import Unison.Codebase.Editor.RemoteRepo qualified as RemoteRepo import Unison.Codebase.Editor.SlurpResult qualified as SlurpResult import Unison.Codebase.Editor.StructuredArgument (StructuredArgument) import Unison.Codebase.Editor.StructuredArgument qualified as SA -import Unison.Codebase.Editor.TodoOutput qualified as TO import Unison.Codebase.IntegrityCheck (IntegrityResult (..), prettyPrintIntegrityErrors) import Unison.Codebase.Patch qualified as Patch import Unison.Codebase.Path qualified as Path @@ -307,7 +307,7 @@ notifyNumbered = \case ] ) (showDiffNamespace ShowNumbers ppe (absPathToBranchId bAbs) (absPathToBranchId bAbs) diff) - TodoOutput hashLen names todo -> todoOutput hashLen names todo + Output'Todo todoOutput -> handleTodoOutput todoOutput CantDeleteDefinitions ppeDecl endangerments -> ( P.warnCallout $ P.lines @@ -2661,13 +2661,13 @@ runNumbered m = let (a, (_, args)) = State.runState m (0, mempty) in (a, Foldable.toList args) -todoOutput :: (Var v) => Int -> PPED.PrettyPrintEnvDecl -> TO.TodoOutput v a -> (Pretty, NumberedArgs) -todoOutput hashLen ppe todo = +handleTodoOutput :: TodoOutput -> (Pretty, NumberedArgs) +handleTodoOutput todo = runNumbered do prettyConflicts <- - if TO.noConflicts todo + if todo.nameConflicts == mempty then pure mempty - else renderNameConflicts ppeu todo.nameConflicts + else renderNameConflicts (PPED.unsuffixifiedPPE todo.ppe) todo.nameConflicts prettyDirectTermDependenciesWithoutNames <- do if Set.null todo.directDependenciesWithoutNames.terms @@ -2676,7 +2676,7 @@ todoOutput hashLen ppe todo = terms <- for (Set.toList todo.directDependenciesWithoutNames.terms) \term -> do n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash term))) - pure (formatNum n <> P.syntaxToColor (prettyReference hashLen term)) + pure (formatNum n <> P.syntaxToColor (prettyReference todo.hashLen term)) pure $ P.wrap "These terms do not have any names in the current namespace:" <> P.newline @@ -2690,7 +2690,7 @@ todoOutput hashLen ppe todo = types <- for (Set.toList todo.directDependenciesWithoutNames.types) \typ -> do n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash typ))) - pure (formatNum n <> P.syntaxToColor (prettyReference hashLen typ)) + pure (formatNum n <> P.syntaxToColor (prettyReference todo.hashLen typ)) pure $ P.wrap "These types do not have any names in the current namespace:" <> P.newline @@ -2702,8 +2702,6 @@ todoOutput hashLen ppe todo = prettyDirectTermDependenciesWithoutNames, prettyDirectTypeDependenciesWithoutNames ] - where - ppeu = PPED.unsuffixifiedPPE ppe listOfDefinitions :: (Var v) => Input.FindScope -> PPE.PrettyPrintEnv -> E.ListDetailed -> [SR'.SearchResult' v a] -> IO Pretty diff --git a/unison-cli/unison-cli.cabal b/unison-cli/unison-cli.cabal index 59b0445d9a..03e6533b13 100644 --- a/unison-cli/unison-cli.cabal +++ b/unison-cli/unison-cli.cabal @@ -101,7 +101,6 @@ library Unison.Codebase.Editor.SlurpComponent Unison.Codebase.Editor.SlurpResult Unison.Codebase.Editor.StructuredArgument - Unison.Codebase.Editor.TodoOutput Unison.Codebase.Editor.UCMVersion Unison.Codebase.Editor.UriParser Unison.Codebase.TranscriptParser From a2e2ae475162ba0e37efbd4dce309cc3cb2cb54a Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 08:44:51 -0400 Subject: [PATCH 50/81] sketch out todo dependent rendering --- .../Codebase/Editor/HandleInput/Todo.hs | 5 +- .../src/Unison/Codebase/Editor/Output.hs | 9 +- .../src/Unison/CommandLine/OutputMessages.hs | 102 +++++++++++------- 3 files changed, 69 insertions(+), 47 deletions(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs index 6795581dc2..7fddd132fc 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -41,11 +41,12 @@ handleTodo = do Output'Todo TodoOutput { hashLen, - ppe, + dependentsOfTodo = Set.empty, directDependenciesWithoutNames = Defns { terms = Set.difference directDependencies.terms (Branch.deepTermReferences currentNamespace), types = Set.difference directDependencies.types (Branch.deepTypeReferences currentNamespace) }, - nameConflicts = Names.conflicts (Branch.toNames currentNamespaceWithoutLibdeps) + nameConflicts = Names.conflicts (Branch.toNames currentNamespaceWithoutLibdeps), + ppe } diff --git a/unison-cli/src/Unison/Codebase/Editor/Output.hs b/unison-cli/src/Unison/Codebase/Editor/Output.hs index 8a5efe3949..105683c2b2 100644 --- a/unison-cli/src/Unison/Codebase/Editor/Output.hs +++ b/unison-cli/src/Unison/Codebase/Editor/Output.hs @@ -143,10 +143,11 @@ data NumberedOutput (Map LabeledDependency (Set Name)) -- Mapping of external dependencies to their local dependents. data TodoOutput = TodoOutput - { hashLen :: !Int, - ppe :: !PrettyPrintEnvDecl, - directDependenciesWithoutNames :: DefnsF Set TermReference TypeReference, - nameConflicts :: Names + { dependentsOfTodo :: !(Set TermReferenceId), + directDependenciesWithoutNames :: !(DefnsF Set TermReference TypeReference), + hashLen :: !Int, + nameConflicts :: !Names, + ppe :: !PrettyPrintEnvDecl } data AmbiguousReset'Argument diff --git a/unison-cli/src/Unison/CommandLine/OutputMessages.hs b/unison-cli/src/Unison/CommandLine/OutputMessages.hs index 5b83541a00..291fcf6e2e 100644 --- a/unison-cli/src/Unison/CommandLine/OutputMessages.hs +++ b/unison-cli/src/Unison/CommandLine/OutputMessages.hs @@ -307,7 +307,7 @@ notifyNumbered = \case ] ) (showDiffNamespace ShowNumbers ppe (absPathToBranchId bAbs) (absPathToBranchId bAbs) diff) - Output'Todo todoOutput -> handleTodoOutput todoOutput + Output'Todo todoOutput -> runNumbered (handleTodoOutput todoOutput) CantDeleteDefinitions ppeDecl endangerments -> ( P.warnCallout $ P.lines @@ -2661,47 +2661,67 @@ runNumbered m = let (a, (_, args)) = State.runState m (0, mempty) in (a, Foldable.toList args) -handleTodoOutput :: TodoOutput -> (Pretty, NumberedArgs) -handleTodoOutput todo = - runNumbered do - prettyConflicts <- - if todo.nameConflicts == mempty - then pure mempty - else renderNameConflicts (PPED.unsuffixifiedPPE todo.ppe) todo.nameConflicts - - prettyDirectTermDependenciesWithoutNames <- do - if Set.null todo.directDependenciesWithoutNames.terms - then pure mempty - else do - terms <- - for (Set.toList todo.directDependenciesWithoutNames.terms) \term -> do - n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash term))) - pure (formatNum n <> P.syntaxToColor (prettyReference todo.hashLen term)) - pure $ - P.wrap "These terms do not have any names in the current namespace:" - <> P.newline - <> P.newline - <> P.indentN 2 (P.lines terms) - - prettyDirectTypeDependenciesWithoutNames <- do - if Set.null todo.directDependenciesWithoutNames.types - then pure mempty - else do - types <- - for (Set.toList todo.directDependenciesWithoutNames.types) \typ -> do - n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash typ))) - pure (formatNum n <> P.syntaxToColor (prettyReference todo.hashLen typ)) - pure $ - P.wrap "These types do not have any names in the current namespace:" - <> P.newline - <> P.newline - <> P.indentN 2 (P.lines types) +handleTodoOutput :: TodoOutput -> Numbered Pretty +handleTodoOutput todo = do + prettyConflicts <- + if todo.nameConflicts == mempty + then pure mempty + else renderNameConflicts todo.ppe.unsuffixifiedPPE todo.nameConflicts + + prettyDependentsOfTodo <- do + if Set.null todo.dependentsOfTodo + then pure mempty + else do + terms <- + for (Set.toList todo.dependentsOfTodo) \term -> do + n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.idToShortHash term))) + let name = + term + & Referent.fromTermReferenceId + & PPE.termName todo.ppe.suffixifiedPPE + & prettyHashQualified + & P.syntaxToColor + pure (formatNum n <> name) + pure $ + P.wrap "These terms call `todo`:" + <> P.newline + <> P.newline + <> P.indentN 2 (P.lines terms) + + prettyDirectTermDependenciesWithoutNames <- do + if Set.null todo.directDependenciesWithoutNames.terms + then pure mempty + else do + terms <- + for (Set.toList todo.directDependenciesWithoutNames.terms) \term -> do + n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash term))) + pure (formatNum n <> P.syntaxToColor (prettyReference todo.hashLen term)) + pure $ + P.wrap "These terms do not have any names in the current namespace:" + <> P.newline + <> P.newline + <> P.indentN 2 (P.lines terms) + + prettyDirectTypeDependenciesWithoutNames <- do + if Set.null todo.directDependenciesWithoutNames.types + then pure mempty + else do + types <- + for (Set.toList todo.directDependenciesWithoutNames.types) \typ -> do + n <- addNumberedArg (SA.HashQualified (HQ.HashOnly (Reference.toShortHash typ))) + pure (formatNum n <> P.syntaxToColor (prettyReference todo.hashLen typ)) + pure $ + P.wrap "These types do not have any names in the current namespace:" + <> P.newline + <> P.newline + <> P.indentN 2 (P.lines types) - (pure . P.sep "\n\n" . P.nonEmpty) - [ prettyConflicts, - prettyDirectTermDependenciesWithoutNames, - prettyDirectTypeDependenciesWithoutNames - ] + (pure . P.sep "\n\n" . P.nonEmpty) + [ prettyDependentsOfTodo, + prettyDirectTermDependenciesWithoutNames, + prettyDirectTypeDependenciesWithoutNames, + prettyConflicts + ] listOfDefinitions :: (Var v) => Input.FindScope -> PPE.PrettyPrintEnv -> E.ListDetailed -> [SR'.SearchResult' v a] -> IO Pretty From 2ddea6372dabf48dbd3f1f12701642e4e1c7f306 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 08:55:54 -0400 Subject: [PATCH 51/81] drop temp table --- codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs index 66af3c846a..9a386fe1d3 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs @@ -1897,6 +1897,9 @@ getDirectDependenciesOfScope scope = do ) |] + -- Drop the temporary table + execute [sql| DROP TABLE $tempTableName |] + -- Post-process the query result let dependencies1 = List.foldl' From 827cb0c0a23db18092e4a7291070bd188aea35fc Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:04:38 -0400 Subject: [PATCH 52/81] make dependentsWithinScope return type less awkward --- .../U/Codebase/Sqlite/Operations.hs | 18 ++++++------ .../U/Codebase/Sqlite/Queries.hs | 19 +++++++++++-- .../Codebase/Editor/HandleInput/Update2.hs | 28 +++++-------------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs index d0b9935014..dc1015d5fc 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs @@ -1156,17 +1156,19 @@ dependents selector r = do -- | `dependentsWithinScope scope query` returns all of transitive dependents of `query` that are in `scope` (not -- including `query` itself). Each dependent is also tagged with whether it is a term or decl. -dependentsWithinScope :: Set C.Reference.Id -> Set C.Reference -> Transaction (Map C.Reference.Id C.ReferenceType) +dependentsWithinScope :: Set C.Reference.Id -> Set C.Reference -> Transaction (DefnsF Set C.TermReferenceId C.TypeReferenceId) dependentsWithinScope scope query = do + -- Convert C -> S scope' <- Set.traverse c2sReferenceId scope query' <- Set.traverse c2sReference query - Q.getDependentsWithinScope scope' query' - >>= Map.bitraverse s2cReferenceId (pure . objectTypeToReferenceType) - where - objectTypeToReferenceType = \case - ObjectType.TermComponent -> C.RtTerm - ObjectType.DeclComponent -> C.RtType - _ -> error "Q.getDependentsWithinScope shouldn't return any other types" + + -- Do the query + dependents0 <- Q.getDependentsWithinScope scope' query' + + -- Convert S -> C + dependents1 <- bitraverse (Set.traverse s2cReferenceId) (Set.traverse s2cReferenceId) dependents0 + + pure dependents1 -- | returns a list of known definitions referencing `h` dependentsOfComponent :: H.Hash -> Transaction (Set C.Reference.Id) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs index 66af3c846a..02afe493a4 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs @@ -1914,7 +1914,7 @@ getDirectDependenciesOfScope scope = do -- | `getDependentsWithinScope scope query` returns all of transitive dependents of `query` that are in `scope` (not -- including `query` itself). Each dependent is also tagged with whether it is a term or decl. -getDependentsWithinScope :: Set S.Reference.Id -> Set S.Reference -> Transaction (Map S.Reference.Id ObjectType) +getDependentsWithinScope :: Set S.Reference.Id -> Set S.Reference -> Transaction (DefnsF Set S.TermReferenceId S.TypeReferenceId) getDependentsWithinScope scope query = do -- Populate a temporary table with all of the references in `scope` createTemporaryTableOfReferenceIds [sql| dependents_search_scope |] scope @@ -1951,7 +1951,7 @@ getDependentsWithinScope scope query = do -- We use `UNION` rather than `UNION ALL` so as to not track down the transitive dependents of any particular -- reference more than once. - result :: [S.Reference.Id :. Only ObjectType] <- queryListRow [sql| + result0 :: [S.Reference.Id :. Only ObjectType] <- queryListRow [sql| WITH RECURSIVE transitive_dependents (dependent_object_id, dependent_component_index, type_id) AS ( SELECT d.dependent_object_id, d.dependent_component_index, object.type_id FROM dependents_index d @@ -1976,9 +1976,22 @@ getDependentsWithinScope scope query = do ) SELECT * FROM transitive_dependents |] + execute [sql| DROP TABLE dependents_search_scope |] execute [sql| DROP TABLE dependencies_query |] - pure . Map.fromList $ [(r, t) | r :. Only t <- result] + + -- Post-process the query result + let result1 = + List.foldl' + ( \deps -> \case + dep :. Only TermComponent -> Defns (Set.insert dep deps.terms) deps.types + dep :. Only DeclComponent -> Defns deps.terms (Set.insert dep deps.types) + _ -> deps -- impossible; could error here + ) + (Defns Set.empty Set.empty) + result0 + + pure result1 createTemporaryTableOfReferenceIds :: Sql -> Set S.Reference.Id -> Transaction () createTemporaryTableOfReferenceIds tableName refs = do diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs index 1fb4e5eda4..f2682cca3b 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs @@ -512,16 +512,7 @@ getNamespaceDependentsOf :: Transaction (DefnsF (Relation Name) TermReferenceId TypeReferenceId) getNamespaceDependentsOf names dependencies = do dependents <- Ops.dependentsWithinScope (Names.referenceIds names) dependencies - let dependents1 :: DefnsF Set TermReferenceId TypeReferenceId - dependents1 = - Map.foldlWithKey' - ( \defns refId -> \case - Reference.RtTerm -> let !terms1 = Set.insert refId defns.terms in defns & #terms .~ terms1 - Reference.RtType -> let !types1 = Set.insert refId defns.types in defns & #types .~ types1 - ) - (Defns Set.empty Set.empty) - dependents - pure (bimap (foldMap nameTerm) (foldMap nameType) dependents1) + pure (bimap (foldMap nameTerm) (foldMap nameType) dependents) where nameTerm :: TermReferenceId -> Relation Name TermReferenceId nameTerm ref = @@ -544,24 +535,19 @@ getNamespaceDependentsOf2 defns dependencies = do dependents <- Ops.dependentsWithinScope scope dependencies - let (termDependentRefs, typeDependentRefs) = - dependents & Map.partition \case - Reference.RtTerm -> True - Reference.RtType -> False - pure Defns - { terms = Map.foldlWithKey' addTerms Map.empty termDependentRefs, - types = Map.foldlWithKey' addTypes Map.empty typeDependentRefs + { terms = Set.foldl' addTerms Map.empty dependents.terms, + types = Set.foldl' addTypes Map.empty dependents.types } where - addTerms :: Map Name TermReferenceId -> TermReferenceId -> ignored -> Map Name TermReferenceId - addTerms acc0 ref _ = + addTerms :: Map Name TermReferenceId -> TermReferenceId -> Map Name TermReferenceId + addTerms acc0 ref = let names = BiMultimap.lookupDom (Referent.fromTermReferenceId ref) defns.terms in Set.foldl' (\acc name -> Map.insert name ref acc) acc0 names - addTypes :: Map Name TypeReferenceId -> TypeReferenceId -> ignored -> Map Name TypeReferenceId - addTypes acc0 ref _ = + addTypes :: Map Name TypeReferenceId -> TypeReferenceId -> Map Name TypeReferenceId + addTypes acc0 ref = let names = BiMultimap.lookupDom (Reference.fromId ref) defns.types in Set.foldl' (\acc name -> Map.insert name ref acc) acc0 names From b57ada5e929c45fddeac98adb73f0f895f7ba9db Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:15:32 -0400 Subject: [PATCH 53/81] include dependents of `todo` builtin in `todo` command output --- .../Codebase/Editor/HandleInput/Todo.hs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs index 7fddd132fc..6e1a7d9944 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -6,6 +6,7 @@ where import Data.Set qualified as Set import U.Codebase.Sqlite.Operations qualified as Operations +import Unison.Builtin qualified as Builtin import Unison.Cli.Monad (Cli) import Unison.Cli.Monad qualified as Cli import Unison.Cli.MonadUtils qualified as Cli @@ -15,7 +16,11 @@ import Unison.Codebase.Branch qualified as Branch import Unison.Codebase.Branch.Names qualified as Branch import Unison.Codebase.Editor.Output import Unison.Names qualified as Names +import Unison.Prelude +import Unison.Reference (TermReference) +import Unison.Syntax.Name qualified as Name import Unison.Util.Defns (Defns (..)) +import Unison.Util.Set qualified as Set handleTodo :: Cli () handleTodo = do @@ -24,16 +29,29 @@ handleTodo = do currentNamespace <- Cli.getCurrentBranch0 let currentNamespaceWithoutLibdeps = Branch.deleteLibdeps currentNamespace - (hashLen, directDependencies) <- + (dependentsOfTodo, directDependencies, hashLen) <- Cli.runTransaction do - hashLen <- Codebase.hashLength + let todoReference :: TermReference + todoReference = + Set.asSingleton (Names.refTermsNamed Builtin.names (Name.unsafeParseText "todo")) + & fromMaybe (error (reportBug "E260496" "No reference for builtin named 'todo'")) + + -- All type-and-term dependents of the `todo` builtin, but we know they're all terms. + dependentsOfTodo <- + Operations.dependentsWithinScope + (Branch.deepTermReferenceIds currentNamespaceWithoutLibdeps) + (Set.singleton todoReference) + directDependencies <- Operations.directDependenciesOfScope Defns { terms = Branch.deepTermReferenceIds currentNamespaceWithoutLibdeps, types = Branch.deepTypeReferenceIds currentNamespaceWithoutLibdeps } - pure (hashLen, directDependencies) + + hashLen <- Codebase.hashLength + + pure (dependentsOfTodo.terms, directDependencies, hashLen) ppe <- Cli.currentPrettyPrintEnvDecl @@ -41,7 +59,7 @@ handleTodo = do Output'Todo TodoOutput { hashLen, - dependentsOfTodo = Set.empty, + dependentsOfTodo, directDependenciesWithoutNames = Defns { terms = Set.difference directDependencies.terms (Branch.deepTermReferences currentNamespace), From 4ef8450130b56d0e9d1d90cafba1ca18c74bd907 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:24:29 -0400 Subject: [PATCH 54/81] rename `dependentsWithinScope` to `transitiveDependentsWithinScope` --- .../U/Codebase/Sqlite/Operations.hs | 15 ++-- .../U/Codebase/Sqlite/Queries.hs | 69 ++++++++++--------- .../Codebase/Editor/HandleInput/Todo.hs | 2 +- .../Codebase/Editor/HandleInput/Update2.hs | 4 +- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs index dc1015d5fc..d2335e6ece 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs @@ -66,7 +66,7 @@ module U.Codebase.Sqlite.Operations directDependenciesOfScope, dependents, dependentsOfComponent, - dependentsWithinScope, + transitiveDependentsWithinScope, -- ** type index Q.addTypeToIndexForTerm, @@ -1154,16 +1154,19 @@ dependents selector r = do sIds <- Q.getDependentsForDependency selector r' Set.traverse s2cReferenceId sIds --- | `dependentsWithinScope scope query` returns all of transitive dependents of `query` that are in `scope` (not --- including `query` itself). Each dependent is also tagged with whether it is a term or decl. -dependentsWithinScope :: Set C.Reference.Id -> Set C.Reference -> Transaction (DefnsF Set C.TermReferenceId C.TypeReferenceId) -dependentsWithinScope scope query = do +-- | `transitiveDependentsWithinScope scope query` returns all of transitive dependents of `query` that are in `scope` +-- (not including `query` itself). Each dependent is also tagged with whether it is a term or decl. +transitiveDependentsWithinScope :: + Set C.Reference.Id -> + Set C.Reference -> + Transaction (DefnsF Set C.TermReferenceId C.TypeReferenceId) +transitiveDependentsWithinScope scope query = do -- Convert C -> S scope' <- Set.traverse c2sReferenceId scope query' <- Set.traverse c2sReference query -- Do the query - dependents0 <- Q.getDependentsWithinScope scope' query' + dependents0 <- Q.getTransitiveDependentsWithinScope scope' query' -- Convert S -> C dependents1 <- bitraverse (Set.traverse s2cReferenceId) (Set.traverse s2cReferenceId) dependents0 diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs index 02afe493a4..10e5aa05cf 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs @@ -166,7 +166,7 @@ module U.Codebase.Sqlite.Queries getDependencyIdsForDependent, getDependenciesBetweenTerms, getDirectDependenciesOfScope, - getDependentsWithinScope, + getTransitiveDependentsWithinScope, -- ** type index addToTypeIndex, @@ -1910,12 +1910,13 @@ getDirectDependenciesOfScope scope = do pure dependencies1 -{- ORMOLU_DISABLE -} - --- | `getDependentsWithinScope scope query` returns all of transitive dependents of `query` that are in `scope` (not --- including `query` itself). Each dependent is also tagged with whether it is a term or decl. -getDependentsWithinScope :: Set S.Reference.Id -> Set S.Reference -> Transaction (DefnsF Set S.TermReferenceId S.TypeReferenceId) -getDependentsWithinScope scope query = do +-- | `getTransitiveDependentsWithinScope scope query` returns all of transitive dependents of `query` that are in +-- `scope` (not including `query` itself). +getTransitiveDependentsWithinScope :: + Set S.Reference.Id -> + Set S.Reference -> + Transaction (DefnsF Set S.TermReferenceId S.TypeReferenceId) +getTransitiveDependentsWithinScope scope query = do -- Populate a temporary table with all of the references in `scope` createTemporaryTableOfReferenceIds [sql| dependents_search_scope |] scope @@ -1951,31 +1952,33 @@ getDependentsWithinScope scope query = do -- We use `UNION` rather than `UNION ALL` so as to not track down the transitive dependents of any particular -- reference more than once. - result0 :: [S.Reference.Id :. Only ObjectType] <- queryListRow [sql| - WITH RECURSIVE transitive_dependents (dependent_object_id, dependent_component_index, type_id) AS ( - SELECT d.dependent_object_id, d.dependent_component_index, object.type_id - FROM dependents_index d - JOIN object ON d.dependent_object_id = object.id - JOIN dependencies_query q - ON q.dependency_builtin IS d.dependency_builtin - AND q.dependency_object_id IS d.dependency_object_id - AND q.dependency_component_index IS d.dependency_component_index - JOIN dependents_search_scope s - ON s.object_id = d.dependent_object_id - AND s.component_index = d.dependent_component_index - - UNION SELECT d.dependent_object_id, d.dependent_component_index, object.type_id - FROM dependents_index d - JOIN object ON d.dependent_object_id = object.id - JOIN transitive_dependents t - ON t.dependent_object_id = d.dependency_object_id - AND t.dependent_component_index = d.dependency_component_index - JOIN dependents_search_scope s - ON s.object_id = d.dependent_object_id - AND s.component_index = d.dependent_component_index - ) - SELECT * FROM transitive_dependents - |] + result0 :: [S.Reference.Id :. Only ObjectType] <- + queryListRow + [sql| + WITH RECURSIVE transitive_dependents (dependent_object_id, dependent_component_index, type_id) AS ( + SELECT d.dependent_object_id, d.dependent_component_index, object.type_id + FROM dependents_index d + JOIN object ON d.dependent_object_id = object.id + JOIN dependencies_query q + ON q.dependency_builtin IS d.dependency_builtin + AND q.dependency_object_id IS d.dependency_object_id + AND q.dependency_component_index IS d.dependency_component_index + JOIN dependents_search_scope s + ON s.object_id = d.dependent_object_id + AND s.component_index = d.dependent_component_index + + UNION SELECT d.dependent_object_id, d.dependent_component_index, object.type_id + FROM dependents_index d + JOIN object ON d.dependent_object_id = object.id + JOIN transitive_dependents t + ON t.dependent_object_id = d.dependency_object_id + AND t.dependent_component_index = d.dependency_component_index + JOIN dependents_search_scope s + ON s.object_id = d.dependent_object_id + AND s.component_index = d.dependent_component_index + ) + SELECT * FROM transitive_dependents + |] execute [sql| DROP TABLE dependents_search_scope |] execute [sql| DROP TABLE dependencies_query |] @@ -1993,6 +1996,8 @@ getDependentsWithinScope scope query = do pure result1 +{- ORMOLU_DISABLE -} + createTemporaryTableOfReferenceIds :: Sql -> Set S.Reference.Id -> Transaction () createTemporaryTableOfReferenceIds tableName refs = do execute diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs index 6e1a7d9944..c3b37c7054 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -38,7 +38,7 @@ handleTodo = do -- All type-and-term dependents of the `todo` builtin, but we know they're all terms. dependentsOfTodo <- - Operations.dependentsWithinScope + Operations.transitiveDependentsWithinScope (Branch.deepTermReferenceIds currentNamespaceWithoutLibdeps) (Set.singleton todoReference) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs index f2682cca3b..b0dd664a25 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Update2.hs @@ -511,7 +511,7 @@ getNamespaceDependentsOf :: Set Reference -> Transaction (DefnsF (Relation Name) TermReferenceId TypeReferenceId) getNamespaceDependentsOf names dependencies = do - dependents <- Ops.dependentsWithinScope (Names.referenceIds names) dependencies + dependents <- Ops.transitiveDependentsWithinScope (Names.referenceIds names) dependencies pure (bimap (foldMap nameTerm) (foldMap nameType) dependents) where nameTerm :: TermReferenceId -> Relation Name TermReferenceId @@ -533,7 +533,7 @@ getNamespaceDependentsOf2 defns dependencies = do let scope = bifoldMap toTermScope toTypeScope defns dependents <- - Ops.dependentsWithinScope scope dependencies + Ops.transitiveDependentsWithinScope scope dependencies pure Defns From 058882b388b366d070727e4e0d1ceb3276cfc37b Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:29:57 -0400 Subject: [PATCH 55/81] pull out createTemporaryTableOfReferences helper --- .../U/Codebase/Sqlite/Queries.hs | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs index 10e5aa05cf..8647a86273 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs @@ -1918,21 +1918,12 @@ getTransitiveDependentsWithinScope :: Transaction (DefnsF Set S.TermReferenceId S.TypeReferenceId) getTransitiveDependentsWithinScope scope query = do -- Populate a temporary table with all of the references in `scope` - createTemporaryTableOfReferenceIds [sql| dependents_search_scope |] scope + let scopeTableName = [sql| dependents_search_scope |] + createTemporaryTableOfReferenceIds scopeTableName scope -- Populate a temporary table with all of the references in `query` - execute - [sql| - CREATE TEMPORARY TABLE dependencies_query ( - dependency_builtin INTEGER NULL, - dependency_object_id INTEGER NULL, - dependency_component_index INTEGER NULL, - CHECK ((dependency_builtin IS NULL) = (dependency_object_id IS NOT NULL)), - CHECK ((dependency_object_id IS NULL) = (dependency_component_index IS NULL)) - ) - |] - for_ query \r -> - execute [sql|INSERT INTO dependencies_query VALUES (@r, @, @)|] + let queryTableName = [sql| dependencies_query |] + createTemporaryTableOfReferences queryTableName query -- Say the query set is { #foo, #bar }, and the scope set is { #foo, #bar, #baz, #qux, #honk }. -- @@ -1959,11 +1950,11 @@ getTransitiveDependentsWithinScope scope query = do SELECT d.dependent_object_id, d.dependent_component_index, object.type_id FROM dependents_index d JOIN object ON d.dependent_object_id = object.id - JOIN dependencies_query q - ON q.dependency_builtin IS d.dependency_builtin - AND q.dependency_object_id IS d.dependency_object_id - AND q.dependency_component_index IS d.dependency_component_index - JOIN dependents_search_scope s + JOIN $queryTableName q + ON q.builtin IS d.dependency_builtin + AND q.object_id IS d.dependency_object_id + AND q.component_index IS d.dependency_component_index + JOIN $scopeTableName s ON s.object_id = d.dependent_object_id AND s.component_index = d.dependent_component_index @@ -1973,15 +1964,15 @@ getTransitiveDependentsWithinScope scope query = do JOIN transitive_dependents t ON t.dependent_object_id = d.dependency_object_id AND t.dependent_component_index = d.dependency_component_index - JOIN dependents_search_scope s + JOIN $scopeTableName s ON s.object_id = d.dependent_object_id AND s.component_index = d.dependent_component_index ) SELECT * FROM transitive_dependents |] - execute [sql| DROP TABLE dependents_search_scope |] - execute [sql| DROP TABLE dependencies_query |] + execute [sql| DROP TABLE $scopeTableName |] + execute [sql| DROP TABLE $queryTableName |] -- Post-process the query result let result1 = @@ -1996,7 +1987,21 @@ getTransitiveDependentsWithinScope scope query = do pure result1 -{- ORMOLU_DISABLE -} +createTemporaryTableOfReferences :: Sql -> Set S.Reference -> Transaction () +createTemporaryTableOfReferences tableName refs = do + execute + [sql| + CREATE TEMPORARY TABLE $tableName ( + builtin INTEGER NULL, + object_id INTEGER NULL, + component_index INTEGER NULL + CHECK ((builtin IS NULL) = (object_id IS NOT NULL)), + CHECK ((object_id IS NULL) = (component_index IS NULL)) + ) + |] + + for_ refs \ref -> + execute [sql| INSERT INTO $tableName VALUES (@ref, @, @) |] createTemporaryTableOfReferenceIds :: Sql -> Set S.Reference.Id -> Transaction () createTemporaryTableOfReferenceIds tableName refs = do @@ -2011,6 +2016,8 @@ createTemporaryTableOfReferenceIds tableName refs = do for_ refs \ref -> execute [sql| INSERT INTO $tableName VALUES (@ref, @) |] +{- ORMOLU_DISABLE -} + objectIdByBase32Prefix :: ObjectType -> Text -> Transaction [ObjectId] objectIdByBase32Prefix objType prefix = queryListCol From 297bfd8d7fcb48a8c2f207fb9858b097ab0dee61 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:38:36 -0400 Subject: [PATCH 56/81] add getDirectDependentsWithinScope --- .../U/Codebase/Sqlite/Operations.hs | 32 ++++++++--- .../U/Codebase/Sqlite/Queries.hs | 53 ++++++++++++++++++- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs index d2335e6ece..324177438f 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Operations.hs @@ -66,6 +66,7 @@ module U.Codebase.Sqlite.Operations directDependenciesOfScope, dependents, dependentsOfComponent, + directDependentsWithinScope, transitiveDependentsWithinScope, -- ** type index @@ -1154,19 +1155,38 @@ dependents selector r = do sIds <- Q.getDependentsForDependency selector r' Set.traverse s2cReferenceId sIds --- | `transitiveDependentsWithinScope scope query` returns all of transitive dependents of `query` that are in `scope` --- (not including `query` itself). Each dependent is also tagged with whether it is a term or decl. +-- | `directDependentsWithinScope scope query` returns all direct dependents of `query` that are in `scope` (not +-- including `query` itself). +directDependentsWithinScope :: + Set C.Reference.Id -> + Set C.Reference -> + Transaction (DefnsF Set C.TermReferenceId C.TypeReferenceId) +directDependentsWithinScope scope0 query0 = do + -- Convert C -> S + scope1 <- Set.traverse c2sReferenceId scope0 + query1 <- Set.traverse c2sReference query0 + + -- Do the query + dependents0 <- Q.getDirectDependentsWithinScope scope1 query1 + + -- Convert S -> C + dependents1 <- bitraverse (Set.traverse s2cReferenceId) (Set.traverse s2cReferenceId) dependents0 + + pure dependents1 + +-- | `transitiveDependentsWithinScope scope query` returns all transitive dependents of `query` that are in `scope` (not +-- including `query` itself). transitiveDependentsWithinScope :: Set C.Reference.Id -> Set C.Reference -> Transaction (DefnsF Set C.TermReferenceId C.TypeReferenceId) -transitiveDependentsWithinScope scope query = do +transitiveDependentsWithinScope scope0 query0 = do -- Convert C -> S - scope' <- Set.traverse c2sReferenceId scope - query' <- Set.traverse c2sReference query + scope1 <- Set.traverse c2sReferenceId scope0 + query1 <- Set.traverse c2sReference query0 -- Do the query - dependents0 <- Q.getTransitiveDependentsWithinScope scope' query' + dependents0 <- Q.getTransitiveDependentsWithinScope scope1 query1 -- Convert S -> C dependents1 <- bitraverse (Set.traverse s2cReferenceId) (Set.traverse s2cReferenceId) dependents0 diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs index 8647a86273..615b39caf0 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs @@ -166,6 +166,7 @@ module U.Codebase.Sqlite.Queries getDependencyIdsForDependent, getDependenciesBetweenTerms, getDirectDependenciesOfScope, + getDirectDependentsWithinScope, getTransitiveDependentsWithinScope, -- ** type index @@ -1910,8 +1911,56 @@ getDirectDependenciesOfScope scope = do pure dependencies1 --- | `getTransitiveDependentsWithinScope scope query` returns all of transitive dependents of `query` that are in --- `scope` (not including `query` itself). +-- | `getDirectDependentsWithinScope scope query` returns all direct dependents of `query` that are in `scope` (not +-- including `query` itself). +getDirectDependentsWithinScope :: + Set S.Reference.Id -> + Set S.Reference -> + Transaction (DefnsF Set S.TermReferenceId S.TypeReferenceId) +getDirectDependentsWithinScope scope query = do + -- Populate a temporary table with all of the references in `scope` + let scopeTableName = [sql| dependents_search_scope |] + createTemporaryTableOfReferenceIds scopeTableName scope + + -- Populate a temporary table with all of the references in `query` + let queryTableName = [sql| dependencies_query |] + createTemporaryTableOfReferences queryTableName query + + -- Get their direct dependents (tagged with object type) + dependents0 <- + queryListRow @(S.Reference.Id :. Only ObjectType) + [sql| + SELECT s.object_id, s.component_index, o.type_id + FROM $queryTableName q + JOIN dependents_index d + ON q.builtin IS d.dependency_builtin + AND q.object_id IS d.dependency_object_id + AND q.component_index IS d.dependency_component_index + JOIN $scopeTableName s + ON d.dependent_object_id = s.object_id + AND d.dependent_component_index = s.component_index + JOIN object o ON s.object_id = o.id + |] + + -- Drop the temporary tables + execute [sql| DROP TABLE $scopeTableName |] + execute [sql| DROP TABLE $queryTableName |] + + -- Post-process the query result + let dependents1 = + List.foldl' + ( \deps -> \case + dep :. Only TermComponent -> Defns (Set.insert dep deps.terms) deps.types + dep :. Only DeclComponent -> Defns deps.terms (Set.insert dep deps.types) + _ -> deps -- impossible; could error here + ) + (Defns Set.empty Set.empty) + dependents0 + + pure dependents1 + +-- | `getTransitiveDependentsWithinScope scope query` returns all transitive dependents of `query` that are in `scope` +-- (not including `query` itself). getTransitiveDependentsWithinScope :: Set S.Reference.Id -> Set S.Reference -> From 40ef6afda261f7bc3691bb12172f7a71442bce11 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:40:54 -0400 Subject: [PATCH 57/81] make `todo` show direct dependents of `todo`, not transitive dependents --- unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs index c3b37c7054..1a8ccf64f3 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Todo.hs @@ -38,7 +38,7 @@ handleTodo = do -- All type-and-term dependents of the `todo` builtin, but we know they're all terms. dependentsOfTodo <- - Operations.transitiveDependentsWithinScope + Operations.directDependentsWithinScope (Branch.deepTermReferenceIds currentNamespaceWithoutLibdeps) (Set.singleton todoReference) From d8240cd106450d5767789bb1ed90e12bc6ae8e64 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:45:11 -0400 Subject: [PATCH 58/81] add transcripts for showing dependents of `todo` --- unison-src/transcripts/todo.md | 25 ++++++++++++++++ unison-src/transcripts/todo.output.md | 41 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/unison-src/transcripts/todo.md b/unison-src/transcripts/todo.md index 8478e4f298..097854dcce 100644 --- a/unison-src/transcripts/todo.md +++ b/unison-src/transcripts/todo.md @@ -2,6 +2,31 @@ The todo command shows conflicted names (not demonstrated here yet because it is not easy to create them for tests, yet). +# Dependents of `todo` + +The `todo` command shows local (outside `lib`) terms that directly call `todo`. + +```ucm:hide +project/main> builtins.mergeio lib.builtins +``` + +```unison +foo : Nat +foo = todo "implement foo" + +bar : Nat +bar = foo + foo +``` + +```ucm +project/main> add +project/main> todo +``` + +```ucm:hide +project/main> delete.project project +``` + # Direct dependencies without names The `todo` command shows hashes of direct dependencies of local (outside `lib`) definitions that don't have names in diff --git a/unison-src/transcripts/todo.output.md b/unison-src/transcripts/todo.output.md index 7f3affeb12..a491922c7a 100644 --- a/unison-src/transcripts/todo.output.md +++ b/unison-src/transcripts/todo.output.md @@ -2,6 +2,47 @@ The todo command shows conflicted names (not demonstrated here yet because it is not easy to create them for tests, yet). +# Dependents of `todo` + +The `todo` command shows local (outside `lib`) terms that directly call `todo`. + +```unison +foo : Nat +foo = todo "implement foo" + +bar : Nat +bar = foo + foo +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + bar : Nat + foo : Nat + +``` +```ucm +project/main> add + + ⍟ I've added these definitions: + + bar : Nat + foo : Nat + +project/main> todo + + These terms call `todo`: + + 1. foo + +``` # Direct dependencies without names The `todo` command shows hashes of direct dependencies of local (outside `lib`) definitions that don't have names in From c3b7091784b9b029383beae8031919b1de1ae16c Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:52:47 -0400 Subject: [PATCH 59/81] move ls handler into its own module --- .../src/Unison/Codebase/Editor/HandleInput.hs | 17 ++-------- .../Unison/Codebase/Editor/HandleInput/Ls.hs | 33 +++++++++++++++++++ unison-cli/unison-cli.cabal | 3 +- 3 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 unison-cli/src/Unison/Codebase/Editor/HandleInput/Ls.hs diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs index 2176418e54..d8b1e92ced 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput.hs @@ -68,6 +68,7 @@ import Unison.Codebase.Editor.HandleInput.FindAndReplace (handleStructuredFindI, import Unison.Codebase.Editor.HandleInput.FormatFile qualified as Format import Unison.Codebase.Editor.HandleInput.InstallLib (handleInstallLib) import Unison.Codebase.Editor.HandleInput.Load (EvalMode (Sandboxed), evalUnisonFile, handleLoad, loadUnisonFile) +import Unison.Codebase.Editor.HandleInput.Ls (handleLs) import Unison.Codebase.Editor.HandleInput.Merge2 (handleMerge) import Unison.Codebase.Editor.HandleInput.MoveAll (handleMoveAll) import Unison.Codebase.Editor.HandleInput.MoveBranch (doMoveBranch) @@ -692,21 +693,7 @@ loop e = do traverse_ (displayI outputLoc) namesToDisplay ShowDefinitionI outputLoc showDefinitionScope query -> handleShowDefinition outputLoc showDefinitionScope query EditNamespaceI paths -> handleEditNamespace LatestFileLocation paths - FindShallowI pathArg -> do - Cli.Env {codebase} <- ask - - pathArgAbs <- Cli.resolvePath' pathArg - entries <- liftIO (Backend.lsAtPath codebase Nothing pathArgAbs) - Cli.setNumberedArgs $ fmap (SA.ShallowListEntry pathArg) entries - pped <- Cli.currentPrettyPrintEnvDecl - let suffixifiedPPE = PPED.suffixifiedPPE pped - -- This used to be a delayed action which only forced the loading of the root - -- branch when it was necessary for printing the results, but that got wiped out - -- when we ported to the new Cli monad. - -- It would be nice to restore it, but it's pretty rare that it actually results - -- in an improvement, so perhaps it's not worth the effort. - let buildPPE = pure suffixifiedPPE - Cli.respond $ ListShallow buildPPE entries + FindShallowI pathArg -> handleLs pathArg FindI isVerbose fscope ws -> handleFindI isVerbose fscope ws input StructuredFindI _fscope ws -> handleStructuredFindI ws StructuredFindReplaceI ws -> handleStructuredFindReplaceI ws diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Ls.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Ls.hs new file mode 100644 index 0000000000..3fd6e43f4f --- /dev/null +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Ls.hs @@ -0,0 +1,33 @@ +module Unison.Codebase.Editor.HandleInput.Ls + ( handleLs, + ) +where + +import Control.Monad.Reader (ask) +import Unison.Cli.Monad (Cli) +import Unison.Cli.Monad qualified as Cli +import Unison.Cli.MonadUtils qualified as Cli +import Unison.Cli.PrettyPrintUtils qualified as Cli +import Unison.Codebase.Editor.Output +import Unison.Codebase.Editor.StructuredArgument qualified as SA +import Unison.Codebase.Path (Path') +import Unison.Prelude +import Unison.PrettyPrintEnvDecl qualified as PPED +import Unison.Server.Backend qualified as Backend + +handleLs :: Path' -> Cli () +handleLs pathArg = do + Cli.Env {codebase} <- ask + + pathArgAbs <- Cli.resolvePath' pathArg + entries <- liftIO (Backend.lsAtPath codebase Nothing pathArgAbs) + Cli.setNumberedArgs $ fmap (SA.ShallowListEntry pathArg) entries + pped <- Cli.currentPrettyPrintEnvDecl + let suffixifiedPPE = PPED.suffixifiedPPE pped + -- This used to be a delayed action which only forced the loading of the root + -- branch when it was necessary for printing the results, but that got wiped out + -- when we ported to the new Cli monad. + -- It would be nice to restore it, but it's pretty rare that it actually results + -- in an improvement, so perhaps it's not worth the effort. + let buildPPE = pure suffixifiedPPE + Cli.respond $ ListShallow buildPPE entries diff --git a/unison-cli/unison-cli.cabal b/unison-cli/unison-cli.cabal index d530ed68b2..a80454156d 100644 --- a/unison-cli/unison-cli.cabal +++ b/unison-cli/unison-cli.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2. +-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack @@ -66,6 +66,7 @@ library Unison.Codebase.Editor.HandleInput.FormatFile Unison.Codebase.Editor.HandleInput.InstallLib Unison.Codebase.Editor.HandleInput.Load + Unison.Codebase.Editor.HandleInput.Ls Unison.Codebase.Editor.HandleInput.Merge2 Unison.Codebase.Editor.HandleInput.MoveAll Unison.Codebase.Editor.HandleInput.MoveBranch From 35e2dfb8e5f81976286f5340d1c9ff025571e225 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 24 Jun 2024 09:57:17 -0400 Subject: [PATCH 60/81] delete patches from ls output --- unison-share-api/src/Unison/Server/Backend.hs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/unison-share-api/src/Unison/Server/Backend.hs b/unison-share-api/src/Unison/Server/Backend.hs index c2e2ceffb0..fe54c93d15 100644 --- a/unison-share-api/src/Unison/Server/Backend.hs +++ b/unison-share-api/src/Unison/Server/Backend.hs @@ -579,14 +579,10 @@ lsBranch codebase b0 = do (ns, (h, stats)) <- Map.toList $ childrenWithStats guard $ V2Branch.hasDefinitions stats pure $ ShallowBranchEntry ns (V2Causal.causalHash h) stats - patchEntries :: [ShallowListEntry Symbol Ann] = do - (ns, _h) <- Map.toList $ V2Branch.patches b0 - pure $ ShallowPatchEntry ns pure . List.sortOn listEntryName $ termEntries ++ typeEntries ++ branchEntries - ++ patchEntries -- Any absolute names in the input which have `root` as a prefix -- are converted to names relative to current path. All other names are From d745d241140a4c61440cd38c95c1b83bd322326e Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Tue, 18 Jun 2024 23:45:30 +0100 Subject: [PATCH 61/81] Trivial fixes * trivial additions of functor * limit imports to just used functions * remove unnecesary imports --- codebase2/codebase-sqlite/U/Codebase/Sqlite/LocalIds.hs | 2 +- .../codebase-sqlite/U/Codebase/Sqlite/Patch/TypeEdit.hs | 2 +- codebase2/codebase/U/Codebase/Referent.hs | 2 +- codebase2/codebase/U/Codebase/Reflog.hs | 1 + codebase2/core/U/Codebase/Reference.hs | 2 +- codebase2/core/Unison/Core/Project.hs | 2 +- lib/unison-util-nametree/src/Unison/Util/Defns.hs | 2 +- parser-typechecker/src/Unison/Codebase/Execute.hs | 3 ++- .../SqliteCodebase/Migrations/MigrateSchema6To7.hs | 1 - .../src/Unison/KindInference/Solve/Monad.hs | 3 ++- unison-cli/src/Unison/LSP/CodeLens.hs | 1 - unison-cli/src/Unison/LSP/UCMWorker.hs | 5 ++++- .../src/Unison/Server/Local/Endpoints/Current.hs | 1 - .../src/Unison/Server/Local/Endpoints/FuzzyFind.hs | 1 - .../src/Unison/Server/Local/Endpoints/NamespaceDetails.hs | 1 - .../src/Unison/Server/Local/Endpoints/NamespaceListing.hs | 1 - unison-share-api/src/Unison/Server/Types.hs | 2 +- unison-share-api/src/Unison/Sync/Types.hs | 8 ++++---- 18 files changed, 20 insertions(+), 20 deletions(-) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/LocalIds.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/LocalIds.hs index 1cfd697365..d8645b81ae 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/LocalIds.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/LocalIds.hs @@ -15,7 +15,7 @@ data LocalIds' t h = LocalIds { textLookup :: Vector t, defnLookup :: Vector h } - deriving (Show) + deriving (Functor, Show) type LocalIds = LocalIds' TextId ObjectId diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TypeEdit.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TypeEdit.hs index 6b8d3ea48c..ae0816b6b9 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TypeEdit.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TypeEdit.hs @@ -17,7 +17,7 @@ type TypeEdit = TypeEdit' Db.TextId Db.ObjectId type HashTypeEdit = TypeEdit' Text ComponentHash data TypeEdit' t h = Replace (Reference' t h) | Deprecate - deriving (Eq, Ord, Show) + deriving (Eq, Functor, Ord, Show) _Replace :: Prism (TypeEdit' t h) (TypeEdit' t' h') (Reference' t h) (Reference' t' h') _Replace = prism Replace project diff --git a/codebase2/codebase/U/Codebase/Referent.hs b/codebase2/codebase/U/Codebase/Referent.hs index 7eeeccba85..93aec093a8 100644 --- a/codebase2/codebase/U/Codebase/Referent.hs +++ b/codebase2/codebase/U/Codebase/Referent.hs @@ -63,7 +63,7 @@ type Id = Id' Hash Hash data Id' hTm hTp = RefId (Reference.Id' hTm) | ConId (Reference.Id' hTp) ConstructorId - deriving (Eq, Ord, Show) + deriving (Eq, Functor, Ord, Show) instance Bifunctor Referent' where bimap f g = \case diff --git a/codebase2/codebase/U/Codebase/Reflog.hs b/codebase2/codebase/U/Codebase/Reflog.hs index 971bc48395..27cc5ea59d 100644 --- a/codebase2/codebase/U/Codebase/Reflog.hs +++ b/codebase2/codebase/U/Codebase/Reflog.hs @@ -13,6 +13,7 @@ data Entry causal text = Entry toRootCausalHash :: causal, reason :: text } + deriving (Functor) instance Bifunctor Entry where bimap = bimapDefault diff --git a/codebase2/core/U/Codebase/Reference.hs b/codebase2/core/U/Codebase/Reference.hs index 1146ca8aa1..e40ce2ac37 100644 --- a/codebase2/core/U/Codebase/Reference.hs +++ b/codebase2/core/U/Codebase/Reference.hs @@ -74,7 +74,7 @@ data ReferenceType = RtTerm | RtType deriving (Eq, Ord, Show) data Reference' t h = ReferenceBuiltin t | ReferenceDerived (Id' h) - deriving stock (Eq, Generic, Ord, Show) + deriving stock (Eq, Generic, Functor, Ord, Show) -- | A type declaration reference. type TermReference' t h = Reference' t h diff --git a/codebase2/core/Unison/Core/Project.hs b/codebase2/core/Unison/Core/Project.hs index 632f9702ec..8f5e05eca6 100644 --- a/codebase2/core/Unison/Core/Project.hs +++ b/codebase2/core/Unison/Core/Project.hs @@ -29,7 +29,7 @@ data ProjectAndBranch a b = ProjectAndBranch { project :: a, branch :: b } - deriving stock (Eq, Generic, Show) + deriving stock (Eq, Generic, Show, Functor) instance Bifunctor ProjectAndBranch where bimap f g (ProjectAndBranch a b) = ProjectAndBranch (f a) (g b) diff --git a/lib/unison-util-nametree/src/Unison/Util/Defns.hs b/lib/unison-util-nametree/src/Unison/Util/Defns.hs index 9dde575531..34e17de7e7 100644 --- a/lib/unison-util-nametree/src/Unison/Util/Defns.hs +++ b/lib/unison-util-nametree/src/Unison/Util/Defns.hs @@ -28,7 +28,7 @@ data Defns terms types = Defns { terms :: terms, types :: types } - deriving stock (Generic, Show) + deriving stock (Generic, Functor, Show) deriving (Monoid, Semigroup) via GenericSemigroupMonoid (Defns terms types) instance Bifoldable Defns where diff --git a/parser-typechecker/src/Unison/Codebase/Execute.hs b/parser-typechecker/src/Unison/Codebase/Execute.hs index e7f1ef0762..7d16261821 100644 --- a/parser-typechecker/src/Unison/Codebase/Execute.hs +++ b/parser-typechecker/src/Unison/Codebase/Execute.hs @@ -6,7 +6,8 @@ module Unison.Codebase.Execute where import Control.Exception (finally) -import Control.Monad.Except +import Control.Monad.Except (throwError, runExceptT) +import Control.Monad.IO.Class (liftIO) import Unison.Codebase qualified as Codebase import Unison.Codebase.Branch qualified as Branch import Unison.Codebase.Branch.Names qualified as Branch diff --git a/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Migrations/MigrateSchema6To7.hs b/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Migrations/MigrateSchema6To7.hs index b62708f70c..f09ff8559c 100644 --- a/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Migrations/MigrateSchema6To7.hs +++ b/parser-typechecker/src/Unison/Codebase/SqliteCodebase/Migrations/MigrateSchema6To7.hs @@ -4,7 +4,6 @@ module Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema6To7 (migrateSchema6To7) where -import Control.Monad.Except import Control.Monad.State import U.Codebase.Branch.Type (NamespaceStats) import U.Codebase.Sqlite.DbId qualified as DB diff --git a/parser-typechecker/src/Unison/KindInference/Solve/Monad.hs b/parser-typechecker/src/Unison/KindInference/Solve/Monad.hs index d0d8fc58fb..82090bf237 100644 --- a/parser-typechecker/src/Unison/KindInference/Solve/Monad.hs +++ b/parser-typechecker/src/Unison/KindInference/Solve/Monad.hs @@ -14,6 +14,7 @@ module Unison.KindInference.Solve.Monad where import Control.Lens (Lens', (%%~)) +import Control.Monad.Fix (MonadFix (..)) import Control.Monad.Reader qualified as M import Control.Monad.State.Strict qualified as M import Data.Functor.Identity @@ -64,7 +65,7 @@ newtype Solve v loc a = Solve {unSolve :: Env -> SolveState v loc -> (a, SolveSt ( Functor, Applicative, Monad, - M.MonadFix, + MonadFix, M.MonadReader Env, M.MonadState (SolveState v loc) ) diff --git a/unison-cli/src/Unison/LSP/CodeLens.hs b/unison-cli/src/Unison/LSP/CodeLens.hs index 38df42a72e..a5017ee99d 100644 --- a/unison-cli/src/Unison/LSP/CodeLens.hs +++ b/unison-cli/src/Unison/LSP/CodeLens.hs @@ -6,7 +6,6 @@ module Unison.LSP.CodeLens where import Control.Lens hiding (List) -import Control.Monad.Except import Data.Aeson qualified as Aeson import Data.Map qualified as Map import Data.Text qualified as Text diff --git a/unison-cli/src/Unison/LSP/UCMWorker.hs b/unison-cli/src/Unison/LSP/UCMWorker.hs index 2f28955021..d404bc6d19 100644 --- a/unison-cli/src/Unison/LSP/UCMWorker.hs +++ b/unison-cli/src/Unison/LSP/UCMWorker.hs @@ -1,6 +1,9 @@ module Unison.LSP.UCMWorker where -import Control.Monad.Reader +import Control.Monad (guard) +import Control.Monad.State (liftIO) +import Control.Monad.Reader.Class (ask) +import Data.Functor (void) import U.Codebase.HashTags import Unison.Codebase qualified as Codebase import Unison.Codebase.Branch qualified as Branch diff --git a/unison-share-api/src/Unison/Server/Local/Endpoints/Current.hs b/unison-share-api/src/Unison/Server/Local/Endpoints/Current.hs index 5cc218b7eb..15972297f1 100644 --- a/unison-share-api/src/Unison/Server/Local/Endpoints/Current.hs +++ b/unison-share-api/src/Unison/Server/Local/Endpoints/Current.hs @@ -3,7 +3,6 @@ module Unison.Server.Local.Endpoints.Current where -import Control.Monad.Except import Data.Aeson import Data.OpenApi (ToSchema (..)) import Servant ((:>)) diff --git a/unison-share-api/src/Unison/Server/Local/Endpoints/FuzzyFind.hs b/unison-share-api/src/Unison/Server/Local/Endpoints/FuzzyFind.hs index 5aaa434463..6b6cc031ca 100644 --- a/unison-share-api/src/Unison/Server/Local/Endpoints/FuzzyFind.hs +++ b/unison-share-api/src/Unison/Server/Local/Endpoints/FuzzyFind.hs @@ -4,7 +4,6 @@ module Unison.Server.Local.Endpoints.FuzzyFind where -import Control.Monad.Except import Data.Aeson import Data.OpenApi (ToSchema) import Servant diff --git a/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceDetails.hs b/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceDetails.hs index bcb6ca5fa1..1ea65e553b 100644 --- a/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceDetails.hs +++ b/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceDetails.hs @@ -4,7 +4,6 @@ module Unison.Server.Local.Endpoints.NamespaceDetails where -import Control.Monad.Except import Data.Set qualified as Set import Servant (Capture, QueryParam, (:>)) import Servant.Docs (DocCapture (..), ToCapture (..)) diff --git a/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceListing.hs b/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceListing.hs index fe5e5ee06a..b683131f40 100644 --- a/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceListing.hs +++ b/unison-share-api/src/Unison/Server/Local/Endpoints/NamespaceListing.hs @@ -4,7 +4,6 @@ module Unison.Server.Local.Endpoints.NamespaceListing (serve, NamespaceListingAPI, NamespaceListing (..), NamespaceObject (..), NamedNamespace (..), NamedPatch (..), KindExpression (..)) where -import Control.Monad.Except import Data.Aeson import Data.OpenApi (ToSchema) import Servant diff --git a/unison-share-api/src/Unison/Server/Types.hs b/unison-share-api/src/Unison/Server/Types.hs index 48f9ace2bc..6a3421709d 100644 --- a/unison-share-api/src/Unison/Server/Types.hs +++ b/unison-share-api/src/Unison/Server/Types.hs @@ -104,7 +104,7 @@ data ExactName name ref = ExactName { name :: name, ref :: ref } - deriving stock (Show, Eq, Ord) + deriving stock (Show, Eq, Functor, Ord) instance ToParamSchema (ExactName Name ShortHash) where toParamSchema _ = diff --git a/unison-share-api/src/Unison/Sync/Types.hs b/unison-share-api/src/Unison/Sync/Types.hs index ccd680135f..45da44748d 100644 --- a/unison-share-api/src/Unison/Sync/Types.hs +++ b/unison-share-api/src/Unison/Sync/Types.hs @@ -203,7 +203,7 @@ entityDependencies = \case C Causal {namespaceHash, parents} -> Set.insert namespaceHash parents data TermComponent text hash = TermComponent [(LocalIds text hash, ByteString)] - deriving stock (Show, Eq, Ord) + deriving stock (Show, Eq, Functor, Ord) instance Bifoldable TermComponent where bifoldMap = bifoldMapDefault @@ -252,7 +252,7 @@ decodeComponentPiece = Aeson.withObject "Component Piece" \obj -> do pure (localIDs, bytes) data DeclComponent text hash = DeclComponent [(LocalIds text hash, ByteString)] - deriving stock (Show, Eq, Ord) + deriving stock (Show, Eq, Functor, Ord) instance Bifoldable DeclComponent where bifoldMap = bifoldMapDefault @@ -280,7 +280,7 @@ data LocalIds text hash = LocalIds { texts :: [text], hashes :: [hash] } - deriving stock (Show, Eq, Ord) + deriving stock (Show, Eq, Functor, Ord) instance Bifoldable LocalIds where bifoldMap = bifoldMapDefault @@ -381,7 +381,7 @@ data Namespace text hash = Namespace childLookup :: [(hash, hash)], -- (namespace hash, causal hash) bytes :: LocalBranchBytes } - deriving stock (Eq, Ord, Show) + deriving stock (Eq, Functor, Ord, Show) instance Bifoldable Namespace where bifoldMap = bifoldMapDefault From 56874a6082f518b05f21d80848aeef0f0e657a5a Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Tue, 18 Jun 2024 23:45:32 +0100 Subject: [PATCH 62/81] s/forall/forAll/ as it will become a restricted keyword -Wforall-identifier --- codebase2/util-term/U/Util/Type.hs | 6 ++--- parser-typechecker/src/Unison/Builtin.hs | 2 +- .../src/Unison/Builtin/Decls.hs | 2 +- .../src/Unison/Codebase/MainTerm.hs | 2 +- .../src/Unison/Syntax/TypeParser.hs | 6 ++--- .../src/Unison/Typechecker/Context.hs | 8 +++--- .../tests/Unison/Test/DataDeclaration.hs | 8 +++--- parser-typechecker/tests/Unison/Test/Term.hs | 4 +-- .../tests/Unison/Test/Typechecker.hs | 4 +-- unison-core/src/Unison/Term.hs | 2 +- unison-core/src/Unison/Type.hs | 26 +++++++++---------- .../src/Unison/Hashing/V2/Type.hs | 6 ++--- 12 files changed, 38 insertions(+), 38 deletions(-) diff --git a/codebase2/util-term/U/Util/Type.hs b/codebase2/util-term/U/Util/Type.hs index 7acf6a4c14..a8eccccf05 100644 --- a/codebase2/util-term/U/Util/Type.hs +++ b/codebase2/util-term/U/Util/Type.hs @@ -61,7 +61,7 @@ flattenEffects es = [es] generalize :: (Ord v) => [v] -> TypeR r v -> TypeR r v generalize vs t = foldr f t vs where - f v t = if Set.member v (ABT.freeVars t) then forall v t else t + f v t = if Set.member v (ABT.freeVars t) then forAll v t else t -- * Patterns @@ -80,8 +80,8 @@ pattern Effect1' e t <- ABT.Tm' (Effect e t) pattern Ref' :: r -> TypeR r v pattern Ref' r <- ABT.Tm' (Ref r) -forall :: (Ord v) => v -> TypeR r v -> TypeR r v -forall v body = ABT.tm () (Forall (ABT.abs () v body)) +forAll :: (Ord v) => v -> TypeR r v -> TypeR r v +forAll v body = ABT.tm () (Forall (ABT.abs () v body)) unforall' :: TypeR r v -> ([v], TypeR r v) unforall' (ForallsNamed' vs t) = (vs, t) diff --git a/parser-typechecker/src/Unison/Builtin.hs b/parser-typechecker/src/Unison/Builtin.hs index 0c7e0514bf..1a9477fa63 100644 --- a/parser-typechecker/src/Unison/Builtin.hs +++ b/parser-typechecker/src/Unison/Builtin.hs @@ -985,7 +985,7 @@ refPromiseBuiltins = forall1 :: Text -> (Type -> Type) -> Type forall1 name body = let a = Var.named name - in Type.forall () a (body $ Type.var () a) + in Type.forAll () a (body $ Type.var () a) forall2 :: Text -> Text -> (Type -> Type -> Type) -> Type diff --git a/parser-typechecker/src/Unison/Builtin/Decls.hs b/parser-typechecker/src/Unison/Builtin/Decls.hs index b48bc44830..9a30893513 100644 --- a/parser-typechecker/src/Unison/Builtin/Decls.hs +++ b/parser-typechecker/src/Unison/Builtin/Decls.hs @@ -596,7 +596,7 @@ builtinEffectDecls = Structural () [] - [ ((), v "Exception.raise", Type.forall () (v "x") (failureType () `arr` self (var "x"))) + [ ((), v "Exception.raise", Type.forAll () (v "x") (failureType () `arr` self (var "x"))) ] pattern UnitRef :: Reference diff --git a/parser-typechecker/src/Unison/Codebase/MainTerm.hs b/parser-typechecker/src/Unison/Codebase/MainTerm.hs index 9f99ae5599..4c48a8a95b 100644 --- a/parser-typechecker/src/Unison/Codebase/MainTerm.hs +++ b/parser-typechecker/src/Unison/Codebase/MainTerm.hs @@ -57,7 +57,7 @@ getMainTerm loadTypeOfTerm parseNames mainName mainType = do builtinMain :: (Var v) => a -> Type.Type v a builtinMain a = let result = Var.named "result" - in Type.forall a result (builtinMainWithResultType a (Type.var a result)) + in Type.forAll a result (builtinMainWithResultType a (Type.var a result)) -- '{io2.IO, Exception} res builtinMainWithResultType :: (Var v) => a -> Type.Type v a -> Type.Type v a diff --git a/parser-typechecker/src/Unison/Syntax/TypeParser.hs b/parser-typechecker/src/Unison/Syntax/TypeParser.hs index ff84f94cbe..7a143c7877 100644 --- a/parser-typechecker/src/Unison/Syntax/TypeParser.hs +++ b/parser-typechecker/src/Unison/Syntax/TypeParser.hs @@ -31,7 +31,7 @@ type TypeP v m = P v m (Type v Ann) -- the right of a function arrow: -- valueType ::= Int | Text | App valueType valueType | Arrow valueType computationType valueType :: (Monad m, Var v) => TypeP v m -valueType = forall type1 <|> type1 +valueType = forAll type1 <|> type1 -- Computation -- computationType ::= [{effect*}] valueType @@ -119,8 +119,8 @@ arrow rec = in chainr1 (effect <|> rec) (reserved "->" *> eff) -- "forall a b . List a -> List b -> Maybe Text" -forall :: (Var v) => TypeP v m -> TypeP v m -forall rec = do +forAll :: (Var v) => TypeP v m -> TypeP v m +forAll rec = do kw <- reserved "forall" <|> reserved "∀" vars <- fmap (fmap L.payload) . some $ prefixDefinitionName _ <- reserved "." diff --git a/parser-typechecker/src/Unison/Typechecker/Context.hs b/parser-typechecker/src/Unison/Typechecker/Context.hs index 11279cf898..f4932cd383 100644 --- a/parser-typechecker/src/Unison/Typechecker/Context.hs +++ b/parser-typechecker/src/Unison/Typechecker/Context.hs @@ -963,7 +963,7 @@ apply' solvedExistentials t = go t Type.Ann' v k -> Type.ann a (go v) k Type.Effect1' e t -> Type.effect1 a (go e) (go t) Type.Effects' es -> Type.effects a (map go es) - Type.ForallNamed' v t' -> Type.forall a v (go t') + Type.ForallNamed' v t' -> Type.forAll a v (go t') Type.IntroOuterNamed' v t' -> Type.introOuter a v (go t') _ -> error $ "Match error in Context.apply': " ++ show t where @@ -1059,7 +1059,7 @@ vectorConstructorOfArity loc arity = do let elementVar = Var.named "elem" args = replicate arity (loc, Type.var loc elementVar) resultType = Type.app loc (Type.list loc) (Type.var loc elementVar) - vt = Type.forall loc elementVar (Type.arrows args resultType) + vt = Type.forAll loc elementVar (Type.arrows args resultType) pure vt generalizeAndUnTypeVar :: (Var v) => Type v a -> Type.Type v a @@ -1984,7 +1984,7 @@ tweakEffects v0 t0 rewrite p ty | Type.ForallNamed' v t <- ty, v0 /= v = - second (Type.forall a v) <$> rewrite p t + second (Type.forAll a v) <$> rewrite p t | Type.Arrow' i o <- ty = do (vis, i) <- rewrite (not <$> p) i (vos, o) <- rewrite p o @@ -2097,7 +2097,7 @@ generalizeP p ctx0 ty = foldr gen (applyCtx ctx0 ty) ctx -- location of the forall is just the location of the input type -- and the location of each quantified variable is just inherited -- from its source location - Type.forall + Type.forAll (loc t) (TypeVar.Universal v) (ABT.substInheritAnnotation tv (universal' () v) t) diff --git a/parser-typechecker/tests/Unison/Test/DataDeclaration.hs b/parser-typechecker/tests/Unison/Test/DataDeclaration.hs index f7e66a7ada..425d9bd267 100644 --- a/parser-typechecker/tests/Unison/Test/DataDeclaration.hs +++ b/parser-typechecker/tests/Unison/Test/DataDeclaration.hs @@ -88,7 +88,7 @@ unhashComponentTest = inventedVarsFreshnessTest = let var = Type.var () app = Type.app () - forall = Type.forall () + forAll = Type.forAll () (-->) = Type.arrow () h = Hash.fromByteString (encodeUtf8 "abcd") ref = R.Id h 0 @@ -104,8 +104,8 @@ unhashComponentTest = annotation = (), bound = [], constructors' = - [ ((), nil, forall a (listType `app` var a)), - ((), cons, forall b (var b --> listType `app` var b --> listType `app` var b)) + [ ((), nil, forAll a (listType `app` var a)), + ((), cons, forAll b (var b --> listType `app` var b --> listType `app` var b)) ] } component :: Map R.Id (Decl Symbol ()) @@ -120,7 +120,7 @@ unhashComponentTest = in tests [ -- check that `nil` constructor's type did not collapse to `forall a. a a`, -- which would happen if the var invented for `listRef` was simply `Var.refNamed listRef` - expectEqual (forall z (listType' `app` var z)) nilType', + expectEqual (forAll z (listType' `app` var z)) nilType', -- check that the variable assigned to `listRef` is different from `cons`, -- which would happen if the var invented for `listRef` was simply `Var.refNamed listRef` expectNotEqual cons listVar diff --git a/parser-typechecker/tests/Unison/Test/Term.hs b/parser-typechecker/tests/Unison/Test/Term.hs index ae75589ac6..31122f5aac 100644 --- a/parser-typechecker/tests/Unison/Test/Term.hs +++ b/parser-typechecker/tests/Unison/Test/Term.hs @@ -33,7 +33,7 @@ test = Type.arrow () (tv "a") (tv "x") ) ) - (Type.forall () (v "a") (tv "a")) + (Type.forAll () (v "a") (tv "a")) tm' = Term.substTypeVar (v "x") (tv "a") tm expected = Term.ann @@ -45,7 +45,7 @@ test = Type.arrow () (Type.var () $ v1 "a") (tv "a") ) ) - (Type.forall () (v1 "a") (Type.var () $ v1 "a")) + (Type.forAll () (v1 "a") (Type.var () $ v1 "a")) note $ show tm' note $ show expected expect $ tm == tm diff --git a/parser-typechecker/tests/Unison/Test/Typechecker.hs b/parser-typechecker/tests/Unison/Test/Typechecker.hs index 6fe94b59be..ce39af19c2 100644 --- a/parser-typechecker/tests/Unison/Test/Typechecker.hs +++ b/parser-typechecker/tests/Unison/Test/Typechecker.hs @@ -18,12 +18,12 @@ test = isSubtypeTest :: Test () isSubtypeTest = let symbol i n = Symbol i (Var.User n) - forall v t = Type.forall () v t + forAll v t = Type.forAll () v t var v = Type.var () v a = symbol 0 "a" a_ i = symbol i "a" - lhs = forall a (var a) -- ∀a. a + lhs = forAll a (var a) -- ∀a. a rhs_ i = var (a_ i) -- a_i in -- check that `∀a. a <: a_i` (used to fail for i = 2, 3) tests [expectSubtype lhs (rhs_ i) | i <- [0 .. 5]] diff --git a/unison-core/src/Unison/Term.hs b/unison-core/src/Unison/Term.hs index 73df6fc3ae..c5f6193e1d 100644 --- a/unison-core/src/Unison/Term.hs +++ b/unison-core/src/Unison/Term.hs @@ -397,7 +397,7 @@ substTypeVar vt ty = go Set.empty t2 = ABT.bindInheritAnnotation body (Type.var () v2) in uncapture ((ABT.annotation t, v2) : vs) (renameTypeVar v v2 e) t2 uncapture vs e t0 = - let t = foldl (\body (loc, v) -> Type.forall loc v body) t0 vs + let t = foldl (\body (loc, v) -> Type.forAll loc v body) t0 vs bound' = case Type.unForalls (Type.stripIntroOuters t) of Nothing -> bound Just (vs, _) -> bound <> Set.fromList vs diff --git a/unison-core/src/Unison/Type.hs b/unison-core/src/Unison/Type.hs index 4e571ff761..d779aa7ce1 100644 --- a/unison-core/src/Unison/Type.hs +++ b/unison-core/src/Unison/Type.hs @@ -451,28 +451,28 @@ arrow' i o = arrow (ABT.annotation i <> ABT.annotation o) i o ann :: (Ord v) => a -> Type v a -> K.Kind -> Type v a ann a e t = ABT.tm' a (Ann e t) -forall :: (Ord v) => a -> v -> Type v a -> Type v a -forall a v body = ABT.tm' a (Forall (ABT.abs' a v body)) +forAll :: (Ord v) => a -> v -> Type v a -> Type v a +forAll a v body = ABT.tm' a (Forall (ABT.abs' a v body)) introOuter :: (Ord v) => a -> v -> Type v a -> Type v a introOuter a v body = ABT.tm' a (IntroOuter (ABT.abs' a v body)) iff :: (Var v) => Type v () -iff = forall () aa $ arrows (f <$> [boolean (), a, a]) a +iff = forAll () aa $ arrows (f <$> [boolean (), a, a]) a where aa = Var.named "a" a = var () aa f x = ((), x) iff' :: (Var v) => a -> Type v a -iff' loc = forall loc aa $ arrows (f <$> [boolean loc, a, a]) a +iff' loc = forAll loc aa $ arrows (f <$> [boolean loc, a, a]) a where aa = Var.named "a" a = var loc aa f x = (loc, x) iff2 :: (Var v) => a -> Type v a -iff2 loc = forall loc aa $ arrows (f <$> [a, a]) a +iff2 loc = forAll loc aa $ arrows (f <$> [a, a]) a where aa = Var.named "a" a = var loc aa @@ -498,11 +498,11 @@ v' s = ABT.var (Var.named s) av' :: (Var v) => a -> Text -> Type v a av' a s = ABT.annotatedVar a (Var.named s) -forall' :: (Var v) => a -> [Text] -> Type v a -> Type v a -forall' a vs body = foldr (forall a) body (Var.named <$> vs) +forAll' :: (Var v) => a -> [Text] -> Type v a -> Type v a +forAll' a vs body = foldr (forAll a) body (Var.named <$> vs) foralls :: (Ord v) => a -> [v] -> Type v a -> Type v a -foralls a vs body = foldr (forall a) body vs +foralls a vs body = foldr (forAll a) body vs -- Note: `a -> b -> c` parses as `a -> (b -> c)` -- the annotation associated with `b` will be the annotation for the `b -> c` @@ -545,7 +545,7 @@ stripEffect t = ([], t) -- The type of the flipped function application operator: -- `(a -> (a -> b) -> b)` flipApply :: (Var v) => Type v () -> Type v () -flipApply t = forall () b $ arrow () (arrow () t (var () b)) (var () b) +flipApply t = forAll () b $ arrow () (arrow () t (var () b)) (var () b) where b = ABT.fresh t (Var.named "b") @@ -554,12 +554,12 @@ generalize' k t = generalize vsk t where vsk = [v | v <- Set.toList (freeVars t), Var.typeOf v == k] --- | Bind the given variables with an outer `forall`, if they are used in `t`. +-- | Bind the given variables with an outer `forAll`, if they are used in `t`. generalize :: (Ord v) => [v] -> Type v a -> Type v a generalize vs t = foldr f t vs where f v t = - if Set.member v (ABT.freeVars t) then forall (ABT.annotation t) v t else t + if Set.member v (ABT.freeVars t) then forAll (ABT.annotation t) v t else t unforall :: Type v a -> Type v a unforall (ForallsNamed' _ t) = t @@ -755,7 +755,7 @@ functionResult = go False -- `.foo -> .foo` becomes `.foo -> .foo` (not changed) -- `.foo.bar -> blarrg.woot` becomes `.foo.bar -> blarrg.woot` (unchanged) generalizeLowercase :: (Var v) => Set v -> Type v a -> Type v a -generalizeLowercase except t = foldr (forall (ABT.annotation t)) t vars +generalizeLowercase except t = foldr (forAll (ABT.annotation t)) t vars where vars = [v | v <- Set.toList (ABT.freeVars t `Set.difference` except), Var.universallyQuantifyIfFree v] @@ -774,7 +774,7 @@ normalizeForallOrder tm0 = where step :: (a, v) -> Type v a -> Type v a step (a, v) body - | Set.member v (ABT.freeVars body) = forall a v body + | Set.member v (ABT.freeVars body) = forAll a v body | otherwise = body (body, vs0) = extract tm0 vs = sortOn (\(_, v) -> Map.lookup v ind) vs0 diff --git a/unison-hashing-v2/src/Unison/Hashing/V2/Type.hs b/unison-hashing-v2/src/Unison/Hashing/V2/Type.hs index 25a042e50c..14a5e0e809 100644 --- a/unison-hashing-v2/src/Unison/Hashing/V2/Type.hs +++ b/unison-hashing-v2/src/Unison/Hashing/V2/Type.hs @@ -103,15 +103,15 @@ charRef = ReferenceBuiltin "Char" listRef = ReferenceBuiltin "Sequence" effectRef = ReferenceBuiltin "Effect" -forall :: (Ord v) => a -> v -> Type v a -> Type v a -forall a v body = ABT.tm' a (TypeForall (ABT.abs' a v body)) +forAll :: (Ord v) => a -> v -> Type v a -> Type v a +forAll a v body = ABT.tm' a (TypeForall (ABT.abs' a v body)) -- | Bind the given variables with an outer `forall`, if they are used in `t`. generalize :: (Ord v) => [v] -> Type v a -> Type v a generalize vs t = foldr f t vs where f v t = - if Set.member v (ABT.freeVars t) then forall (ABT.annotation t) v t else t + if Set.member v (ABT.freeVars t) then forAll (ABT.annotation t) v t else t unforall' :: Type v a -> ([v], Type v a) unforall' (ForallsNamed' vs t) = (vs, t) From fff10977ec5ca9832a93ee9dbb01f19ee161d4ac Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Tue, 18 Jun 2024 23:45:34 +0100 Subject: [PATCH 63/81] move let block outside of inner function this caused a weird type error --- unison-cli/src/Unison/CommandLine/Completion.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unison-cli/src/Unison/CommandLine/Completion.hs b/unison-cli/src/Unison/CommandLine/Completion.hs index 28822ea6f8..39e1fd00a3 100644 --- a/unison-cli/src/Unison/CommandLine/Completion.hs +++ b/unison-cli/src/Unison/CommandLine/Completion.hs @@ -196,12 +196,6 @@ completeWithinNamespace compTypes query currentPath = do namesInBranch :: Int -> V2Branch.Branch Sqlite.Transaction -> Sqlite.Transaction [(CompletionType, Bool, Text)] namesInBranch hashLen b = do nonEmptyChildren <- V2Branch.nonEmptyChildren b - let textifyHQ :: (NameSegment -> r -> HQ'.HashQualified NameSegment) -> Map NameSegment (Map r metadata) -> [(Bool, Text)] - textifyHQ f xs = - xs - & hashQualifyCompletions f - & fmap (HQ'.toTextWith NameSegment.toEscapedText) - & fmap (True,) pure $ concat [ (NamespaceCompletion,False,) <$> (fmap NameSegment.toEscapedText . Map.keys $ nonEmptyChildren), @@ -216,6 +210,12 @@ completeWithinNamespace compTypes query currentPath = do (fmap ((PatchCompletion,True,) . NameSegment.toEscapedText) . Map.keys $ V2Branch.patches b) ] + textifyHQ :: (NameSegment -> r -> HQ'.HashQualified NameSegment) -> Map NameSegment (Map r metadata) -> [(Bool, Text)] + textifyHQ f xs = + xs + & hashQualifyCompletions f + & fmap (HQ'.toTextWith NameSegment.toEscapedText) + & fmap (True,) -- Regrettably there'shqFromNamedV2Referencenot a great spot to combinators for V2 references and shorthashes right now. hqFromNamedV2Referent :: Int -> NameSegment -> Referent.Referent -> HQ'.HashQualified NameSegment hqFromNamedV2Referent hashLen n r = HQ'.HashQualified n (Cv.referent2toshorthash1 (Just hashLen) r) From bbd11c9da76bc1ccd8fd423a3aef3107b01a0ce9 Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Tue, 18 Jun 2024 23:45:36 +0100 Subject: [PATCH 64/81] define non-trivial functor --- .../codebase-sqlite/U/Codebase/Sqlite/Patch/TermEdit.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TermEdit.hs b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TermEdit.hs index bc93dd166c..e588dc7540 100644 --- a/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TermEdit.hs +++ b/codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/TermEdit.hs @@ -22,6 +22,12 @@ type Referent' t h = Referent.Referent' (Reference' t h) (Reference' t h) data TermEdit' t h = Replace (Referent' t h) Typing | Deprecate deriving (Eq, Ord, Show) +instance Functor (TermEdit' t) where + fmap :: (a -> b) -> TermEdit' t a -> TermEdit' t b + fmap f (Replace (Referent.Ref termRef) typing) = Replace (Referent.Ref (fmap f termRef)) typing + fmap f (Replace (Referent.Con typeRef consId) typing) = Replace (Referent.Con (fmap f typeRef) consId) typing + fmap _ Deprecate = Deprecate + _Replace :: Prism (TermEdit' t h) (TermEdit' t' h') (Referent' t h, Typing) (Referent' t' h', Typing) _Replace = prism embed project where From 037ac60bf39c924a4c61014497d26b72f21c7b8e Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Tue, 18 Jun 2024 23:45:37 +0100 Subject: [PATCH 65/81] Use TypeOperators --- lib/unison-prelude/src/Unison/Util/Tuple.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/unison-prelude/src/Unison/Util/Tuple.hs b/lib/unison-prelude/src/Unison/Util/Tuple.hs index 613af47a36..2a9fbfb52d 100644 --- a/lib/unison-prelude/src/Unison/Util/Tuple.hs +++ b/lib/unison-prelude/src/Unison/Util/Tuple.hs @@ -1,5 +1,6 @@ {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE TypeOperators #-} -- | Tuple utils. module Unison.Util.Tuple From 4453fda57028cb3f2a71dda458f679f2f84ecdc5 Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Tue, 25 Jun 2024 09:26:49 +0100 Subject: [PATCH 66/81] fix redundant pattern match warning --- .../src/Unison/PatternMatchCoverage/Solve.hs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs index 5c10aa36ee..764d99266e 100644 --- a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs +++ b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs @@ -518,12 +518,9 @@ addConstraint con0 nc = do C.PosLit var pmlit -> let updateLiteral pos neg lit | Just lit1 <- pos, - lit1 == lit = case lit1 == lit of - -- we already have this positive constraint - True -> (pure (), Ignore) - -- contradicts positive info - False -> (contradiction, Ignore) - -- the constraint contradicts negative info + lit1 == lit = + (pure (), Ignore) -- we already have this positive constraint + -- the constraint contradicts negative info | Set.member lit neg = (contradiction, Ignore) | otherwise = (pure (), Update (Just lit, neg)) in modifyLiteralC var pmlit updateLiteral nc From 364b7790cad08534c216eb4bbb5bb8262faa48d8 Mon Sep 17 00:00:00 2001 From: Eduard Nicodei Date: Tue, 25 Jun 2024 12:29:13 +0100 Subject: [PATCH 67/81] more s/forall/forAll/ --- parser-typechecker/tests/Unison/Test/Type.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser-typechecker/tests/Unison/Test/Type.hs b/parser-typechecker/tests/Unison/Test/Type.hs index de22ec80bf..767addedcd 100644 --- a/parser-typechecker/tests/Unison/Test/Type.hs +++ b/parser-typechecker/tests/Unison/Test/Type.hs @@ -28,7 +28,7 @@ test = v2 = Var.named "b" vt = var () v vt2 = var () v2 - x = forall () v (nat () --> effect () [vt, builtin () "eff"] (nat ())) :: Type Symbol () - y = forall () v2 (nat () --> effect () [vt2] (nat ())) :: Type Symbol () + x = forAll () v (nat () --> effect () [vt, builtin () "eff"] (nat ())) :: Type Symbol () + y = forAll () v2 (nat () --> effect () [vt2] (nat ())) :: Type Symbol () expect . not $ Typechecker.isSubtype x y ] From ae54637bae36f8c44a667e26ccfffa881d38d9e8 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Tue, 25 Jun 2024 09:40:06 -0400 Subject: [PATCH 68/81] add transcript that demonstrates bug in merge --- unison-src/transcripts/merge.md | 65 ++++++++++ unison-src/transcripts/merge.output.md | 163 +++++++++++++++++++++++++ 2 files changed, 228 insertions(+) diff --git a/unison-src/transcripts/merge.md b/unison-src/transcripts/merge.md index 9436ae5232..debe548c5b 100644 --- a/unison-src/transcripts/merge.md +++ b/unison-src/transcripts/merge.md @@ -1518,3 +1518,68 @@ project/main> view Foo ```ucm:hide .> project.delete project ``` + +### Dependent that doesn't need to be in the file + +This test demonstrates a bug. + + +```ucm:hide +project/alice> builtins.mergeio lib.builtins +``` + +In the LCA, we have `foo` with dependent `bar`, and `baz`. + +```unison +foo : Nat +foo = 17 + +bar : Nat +bar = foo + foo + +baz : Text +baz = "lca" +``` + +```ucm +project/alice> add +project/alice> branch bob +``` + +On Bob, we update `baz` to "bob". + +```unison +baz : Text +baz = "bob" +``` + +```ucm +project/bob> update +``` + +On Alice, we update `baz` to "alice" (conflict), but also update `foo` (unconflicted), which propagates to `bar`. + +```unison +foo : Nat +foo = 18 + +baz : Text +baz = "alice" +``` + +```ucm +project/alice> update +``` + +When we try to merge Bob into Alice, we should see both versions of `baz`, with Alice's unconflicted `foo` and `bar` in +the underlying namespace. + +```ucm:error +project/alice> merge /bob +``` + +But `bar` was put into the scratch file instead. + +```ucm:hide +.> project.delete project +``` diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index 6334b362da..e2b7672de3 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -1793,3 +1793,166 @@ project/main> view Foo type Foo = Bar ``` +### Dependent that doesn't need to be in the file + +This test demonstrates a bug. + + +In the LCA, we have `foo` with dependent `bar`, and `baz`. + +```unison +foo : Nat +foo = 17 + +bar : Nat +bar = foo + foo + +baz : Text +baz = "lca" +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + bar : Nat + baz : Text + foo : Nat + +``` +```ucm +project/alice> add + + ⍟ I've added these definitions: + + bar : Nat + baz : Text + foo : Nat + +project/alice> branch bob + + Done. I've created the bob branch based off of alice. + + Tip: To merge your work back into the alice branch, first + `switch /alice` then `merge /bob`. + +``` +On Bob, we update `baz` to "bob". + +```unison +baz : Text +baz = "bob" +``` + +```ucm + + 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: + + ⍟ These names already exist. You can `update` them to your + new definition: + + baz : Text + +``` +```ucm +project/bob> update + + Okay, I'm searching the branch for code that needs to be + updated... + + Done. + +``` +On Alice, we update `baz` to "alice" (conflict), but also update `foo` (unconflicted), which propagates to `bar`. + +```unison +foo : Nat +foo = 18 + +baz : Text +baz = "alice" +``` + +```ucm + + 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: + + ⍟ These names already exist. You can `update` them to your + new definition: + + baz : Text + foo : Nat + +``` +```ucm +project/alice> update + + 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. + +``` +When we try to merge Bob into Alice, we should see both versions of `baz`, with Alice's unconflicted `foo` and `bar` in +the underlying namespace. + +```ucm +project/alice> merge /bob + + I couldn't automatically merge project/bob into project/alice. + However, I've added the definitions that need attention to the + top of scratch.u. + + When you're done, you can run + + merge.commit + + to merge your changes back into alice and delete the temporary + branch. Or, if you decide to cancel the merge instead, you can + run + + delete.branch /merge-bob-into-alice + + to delete the temporary branch and switch back to alice. + +``` +```unison:added-by-ucm scratch.u +-- project/alice +baz : Text +baz = "alice" + +-- project/bob +baz : Text +baz = "bob" + +-- The definitions below are not conflicted, but they each depend on one or more +-- conflicted definitions above. + +bar : Nat +bar = + use Nat + + foo + foo + + +``` + +But `bar` was put into the scratch file instead. + From 627acb7f3a3107ee5b31bba21429e1aee7815191 Mon Sep 17 00:00:00 2001 From: Arya Irani <538571+aryairani@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:33:22 -0400 Subject: [PATCH 69/81] Update parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs --- parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs index 764d99266e..9391aa693d 100644 --- a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs +++ b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs @@ -519,8 +519,8 @@ addConstraint con0 nc = do let updateLiteral pos neg lit | Just lit1 <- pos, lit1 == lit = - (pure (), Ignore) -- we already have this positive constraint - -- the constraint contradicts negative info + -- we already have this positive constraint + (pure (), Ignore) | Set.member lit neg = (contradiction, Ignore) | otherwise = (pure (), Update (Just lit, neg)) in modifyLiteralC var pmlit updateLiteral nc From 382cdacb9f687d79b3a4e9384cfce82fb50ffcf6 Mon Sep 17 00:00:00 2001 From: aryairani Date: Tue, 25 Jun 2024 19:33:39 +0000 Subject: [PATCH 70/81] automatically run ormolu --- parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs index 9391aa693d..b605750686 100644 --- a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs +++ b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs @@ -519,7 +519,7 @@ addConstraint con0 nc = do let updateLiteral pos neg lit | Just lit1 <- pos, lit1 == lit = - -- we already have this positive constraint + -- we already have this positive constraint (pure (), Ignore) | Set.member lit neg = (contradiction, Ignore) | otherwise = (pure (), Update (Just lit, neg)) From 34877da01c0bf3534613f20a56e25719b24bcd95 Mon Sep 17 00:00:00 2001 From: Dan Doel Date: Tue, 25 Jun 2024 15:54:10 -0400 Subject: [PATCH 71/81] Don't relax type when doing an instantiateL during subtyping This is the case of `a < T` for some structured T. By relaxing, we are actually allowing `a` to be a _supertype_ of T as far as abilities go, which is not correct. Seems like it was just erroneously mirrored from the opposite case. --- .../src/Unison/Typechecker/Context.hs | 3 +- unison-src/transcripts-using-base/fix5129.md | 45 ++++++++++++ .../transcripts-using-base/fix5129.output.md | 73 +++++++++++++++++++ unison-src/transcripts/fix2355.output.md | 2 +- 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 unison-src/transcripts-using-base/fix5129.md create mode 100644 unison-src/transcripts-using-base/fix5129.output.md diff --git a/parser-typechecker/src/Unison/Typechecker/Context.hs b/parser-typechecker/src/Unison/Typechecker/Context.hs index 11279cf898..7a4ecb8b59 100644 --- a/parser-typechecker/src/Unison/Typechecker/Context.hs +++ b/parser-typechecker/src/Unison/Typechecker/Context.hs @@ -2561,8 +2561,7 @@ subtype tx ty = scope (InSubtype tx ty) $ do go ctx (Type.Var' (TypeVar.Existential b v)) t -- `InstantiateL` | Set.member v (existentials ctx) && notMember v (Type.freeVars t) = do - e <- extendExistential Var.inferAbility - instantiateL b v (relax' False e t) + instantiateL b v t go ctx t (Type.Var' (TypeVar.Existential b v)) -- `InstantiateR` | Set.member v (existentials ctx) && notMember v (Type.freeVars t) = do diff --git a/unison-src/transcripts-using-base/fix5129.md b/unison-src/transcripts-using-base/fix5129.md new file mode 100644 index 0000000000..ccdd8bee41 --- /dev/null +++ b/unison-src/transcripts-using-base/fix5129.md @@ -0,0 +1,45 @@ +```ucm:hide +.> builtins.mergeio +``` + +Checks for some bad type checking behavior. Some ability subtyping was +too lenient when higher-order functions were involved. + +```unison:error +foreach : (a ->{g} ()) -> [a] ->{g} () +foreach f = cases + [] -> () + x +: xs -> + f x + foreach f xs + +forkIt : '{IO} () ->{IO} () +forkIt e = + _ = IO.forkComp e + () + +thunk : '{IO,Exception} () +thunk = do + raise (Failure (typeLink MiscFailure) "thunk" (Any ())) + +go = do + foreach forkIt [thunk] +``` + +This comes from issue #3513 + +```unison:error +(<<) : (b ->{e} c) -> (a ->{e} b) -> a ->{e} c +(<<) f g x = f (g x) + +catchAll.impl : '{IO, Exception} a ->{IO} Either Failure a +catchAll.impl thunk = + handle tryEval do catch thunk + with + cases + { x } -> x + {Exception.raise f -> _} -> Left f + +fancyTryEval : '{g, IO, Exception} a ->{g, IO, Exception} a +fancyTryEval = reraise << catchAll.impl +``` diff --git a/unison-src/transcripts-using-base/fix5129.output.md b/unison-src/transcripts-using-base/fix5129.output.md new file mode 100644 index 0000000000..af189d5a8f --- /dev/null +++ b/unison-src/transcripts-using-base/fix5129.output.md @@ -0,0 +1,73 @@ +Checks for some bad type checking behavior. Some ability subtyping was +too lenient when higher-order functions were involved. + +```unison +foreach : (a ->{g} ()) -> [a] ->{g} () +foreach f = cases + [] -> () + x +: xs -> + f x + foreach f xs + +forkIt : '{IO} () ->{IO} () +forkIt e = + _ = IO.forkComp e + () + +thunk : '{IO,Exception} () +thunk = do + raise (Failure (typeLink MiscFailure) "thunk" (Any ())) + +go = do + foreach forkIt [thunk] +``` + +```ucm + + Loading changes detected in scratch.u. + + I found an ability mismatch when checking the application + + 18 | foreach forkIt [thunk] + + + When trying to match [Unit ->{𝕖75, IO, Exception} Unit] with + [Unit ->{IO} Unit] the left hand side contained extra + abilities: {𝕖75, Exception} + + + +``` +This comes from issue #3513 + +```unison +(<<) : (b ->{e} c) -> (a ->{e} b) -> a ->{e} c +(<<) f g x = f (g x) + +catchAll.impl : '{IO, Exception} a ->{IO} Either Failure a +catchAll.impl thunk = + handle tryEval do catch thunk + with + cases + { x } -> x + {Exception.raise f -> _} -> Left f + +fancyTryEval : '{g, IO, Exception} a ->{g, IO, Exception} a +fancyTryEval = reraise << catchAll.impl +``` + +```ucm + + Loading changes detected in scratch.u. + + The expression in red + + needs the abilities: {g76} + but was assumed to only require: {IO, Exception} + + This is likely a result of using an un-annotated function as an argument with concrete abilities. Try adding an annotation to the function definition whose body is red. + + 13 | fancyTryEval = reraise << catchAll.impl + + +``` diff --git a/unison-src/transcripts/fix2355.output.md b/unison-src/transcripts/fix2355.output.md index ce2f06798e..0bc382663e 100644 --- a/unison-src/transcripts/fix2355.output.md +++ b/unison-src/transcripts/fix2355.output.md @@ -28,7 +28,7 @@ example = 'let The expression in red was inferred to require the ability: - {A t25 {𝕖39, 𝕖18}} + {A t25 {𝕖36, 𝕖18}} where `𝕖18` is its overall abilities. From e1b00d9c58af8f0806613258145557adad52b182 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 11:23:04 -0400 Subject: [PATCH 72/81] rename `alias.term.force` to `debug.alias.term.force` --- unison-cli/src/Unison/CommandLine/InputPatterns.hs | 8 +++++--- unison-src/transcripts/alias-term.md | 4 ++-- unison-src/transcripts/alias-term.output.md | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index 7f5bc46dc6..918340010b 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -1410,14 +1410,16 @@ aliasTerm = aliasTermForce :: InputPattern aliasTermForce = InputPattern - { patternName = "alias.term.force", + { patternName = "debug.alias.term.force", aliases = [], visibility = I.Hidden, args = [("term to alias", Required, exactDefinitionTermQueryArg), ("alias name", Required, newNameArg)], - help = "`alias.term.force foo bar` introduces `bar` with the same definition as `foo`.", + help = "`debug.alias.term.force foo bar` introduces `bar` with the same definition as `foo`.", parse = \case [oldName, newName] -> Input.AliasTermI True <$> handleShortHashOrHQSplit'Arg oldName <*> handleSplit'Arg newName - _ -> Left . warn $ P.wrap "`alias.term.force` takes two arguments, like `alias.term.force oldname newname`." + _ -> + Left . warn $ + P.wrap "`debug.alias.term.force` takes two arguments, like `debug.alias.term.force oldname newname`." } aliasType :: InputPattern diff --git a/unison-src/transcripts/alias-term.md b/unison-src/transcripts/alias-term.md index cd4f7454ae..1e1bb95ec6 100644 --- a/unison-src/transcripts/alias-term.md +++ b/unison-src/transcripts/alias-term.md @@ -19,9 +19,9 @@ project/main> alias.term lib.builtins.todo foo project/main> ls ``` -You can use `alias.term.force` for that. +You can use `debug.alias.term.force` for that. ```ucm -project/main> alias.term.force lib.builtins.todo foo +project/main> debug.alias.term.force lib.builtins.todo foo project/main> ls ``` diff --git a/unison-src/transcripts/alias-term.output.md b/unison-src/transcripts/alias-term.output.md index 733ff13849..d072506cb0 100644 --- a/unison-src/transcripts/alias-term.output.md +++ b/unison-src/transcripts/alias-term.output.md @@ -28,10 +28,10 @@ project/main> ls 2. lib/ (643 terms, 92 types) ``` -You can use `alias.term.force` for that. +You can use `debug.alias.term.force` for that. ```ucm -project/main> alias.term.force lib.builtins.todo foo +project/main> debug.alias.term.force lib.builtins.todo foo Done. From 267bfdf248960e5151c530895eb76414d9b9278f Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 11:26:37 -0400 Subject: [PATCH 73/81] remove fix2000.md --- unison-src/transcripts/fix2000.md | 44 ------ unison-src/transcripts/fix2000.output.md | 188 ----------------------- 2 files changed, 232 deletions(-) delete mode 100644 unison-src/transcripts/fix2000.md delete mode 100644 unison-src/transcripts/fix2000.output.md diff --git a/unison-src/transcripts/fix2000.md b/unison-src/transcripts/fix2000.md deleted file mode 100644 index 812ec10df0..0000000000 --- a/unison-src/transcripts/fix2000.md +++ /dev/null @@ -1,44 +0,0 @@ -Checks that squash and merge do the same thing, with nontrivial history that -includes a merge conflict. - -```ucm:hide -.> builtins.merge -``` - -```unison -x.a.p = "af" -x.a.q = "ef" -``` - -```ucm -.> add -.> fork x y -.> fork x s -.> fork x m -.> delete.verbose y.a.p -``` - -```unison -y.a.p = "fij" -``` - -```ucm -.> add -``` - -```unison -y.b.p = "wie" -``` - -Merge back into the ancestor. - -```ucm -.> add -.> merge.old y.b y.a -.> delete.term.verbose 1 -.> merge.old y m -.> merge.old.squash y s -.s> todo -.m> todo -``` - diff --git a/unison-src/transcripts/fix2000.output.md b/unison-src/transcripts/fix2000.output.md deleted file mode 100644 index cd388f7e55..0000000000 --- a/unison-src/transcripts/fix2000.output.md +++ /dev/null @@ -1,188 +0,0 @@ -Checks that squash and merge do the same thing, with nontrivial history that -includes a merge conflict. - -```unison -x.a.p = "af" -x.a.q = "ef" -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - x.a.p : Text - x.a.q : Text - -``` -```ucm -.> add - - ⍟ I've added these definitions: - - x.a.p : Text - x.a.q : Text - -.> fork x y - - Done. - -.> fork x s - - Done. - -.> fork x m - - Done. - -.> delete.verbose y.a.p - - Name changes: - - Original Changes - 1. m.a.p ┐ 2. y.a.p (removed) - 3. s.a.p │ - 4. x.a.p │ - 5. y.a.p ┘ - - Tip: You can use `undo` or `reflog` to undo this change. - -``` -```unison -y.a.p = "fij" -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - y.a.p : Text - -``` -```ucm -.> add - - ⍟ I've added these definitions: - - y.a.p : Text - -``` -```unison -y.b.p = "wie" -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - y.b.p : Text - -``` -Merge back into the ancestor. - -```ucm -.> add - - ⍟ I've added these definitions: - - y.b.p : Text - -.> merge.old y.b y.a - - Here's what's changed in y.a after the merge: - - New name conflicts: - - 1. p#l2mmpgn323 : Text - ↓ - 2. ┌ p#l2mmpgn323 : Text - 3. └ p#nm3omrdks9 : Text - - 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... - -.> delete.term.verbose 1 - - Resolved name conflicts: - - 1. ┌ y.a.p#l2mmpgn323 : Text - 2. └ y.a.p#nm3omrdks9 : Text - ↓ - 3. y.a.p#nm3omrdks9 : Text - - Tip: You can use `undo` or `reflog` to undo this change. - -.> merge.old y m - - Here's what's changed in m after the merge: - - Updates: - - 1. a.p : Text - ↓ - 2. a.p : Text - - Added definitions: - - 3. ┌ a.p : Text - 4. └ b.p : Text - - 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... - -.> merge.old.squash y s - - Here's what's changed in s after the merge: - - Updates: - - 1. a.p : Text - ↓ - 2. a.p : Text - - Added definitions: - - 3. ┌ a.p : Text - 4. └ b.p : Text - - 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... - -.s> todo - - - -.m> todo - - - -``` From 13558c91e8f27e68fa7c098ef39625b1b265ba5d Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 11:30:39 -0400 Subject: [PATCH 74/81] delete fix2004.md --- unison-src/transcripts/fix2004.md | 82 ------- unison-src/transcripts/fix2004.output.md | 267 ----------------------- 2 files changed, 349 deletions(-) delete mode 100644 unison-src/transcripts/fix2004.md delete mode 100644 unison-src/transcripts/fix2004.output.md diff --git a/unison-src/transcripts/fix2004.md b/unison-src/transcripts/fix2004.md deleted file mode 100644 index ab33da9e7f..0000000000 --- a/unison-src/transcripts/fix2004.md +++ /dev/null @@ -1,82 +0,0 @@ - -```ucm:hide -.> builtins.merge -``` - -Here's the scenario that can produce bad empty namespace LCAs: - -``` - deletes of v4 -j1: ... - v1 - v2 - v3 - v4 - v4a - v5 - v6 - v7 - / - - v5a - - adds of unrelated -j2: ... - v1 - v2 - v3 - v4 - x0 - x1 - x2 - x3 - / - - z1 - -``` - -So `j1` and `j2` have common history up through `v4`, then `j1` deletes some definitions while `j2` adds some definitions via shallow merges. These shallow merges then result in the LCA being the empty namespace rather than `v4`. - -First, we create some common history before a fork: - -```ucm -.> alias.term builtin.Nat.+ a.delete1 -.> alias.term builtin.Nat.* a.delete2 -.> alias.term builtin.Nat.drop a.delete3 -.> alias.type builtin.Nat a.Delete4 -``` - -Now we fork `a2` off of `a`. `a` continues on, deleting the terms it added previously and then adding one unrelated term via a merge with little history. It's this short history merge which will become a bad LCA of the empty namespace. - -```ucm -.> fork a a2 -.> delete.term.verbose a.delete1 -.> delete.term.verbose a.delete2 -.> delete.term.verbose a.delete3 -.> delete.type.verbose a.Delete4 -.> alias.term .builtin.Float.+ newbranchA.dontDelete -.> merge.old newbranchA a -.a> find -``` - -Meanwhile, `a2` adds some other unrelated terms, some via merging in namespaces with little history. When merging `a2` back into `a`, the deletes from their common history should be respected. - -```ucm -.> alias.term builtin.Text.take a2.keep1 -.> alias.term builtin.Text.take a2.keep2 -.> alias.term builtin.Text.take a2.keep3 -.> alias.term builtin.Text.take a2.keep4 -.> alias.term builtin.Text.take a2.keep5 -.> alias.term builtin.Text.take newbranchA2.keep6 -.> merge.old newbranchA2 a2 -.a2> find -``` - -```ucm -.> fork a asquash -.> merge.old a2 a -.> merge.old.squash a2 asquash -``` - -At this point, all the things that `a` has deleted (`delete1`, `delete2`, etc) should be deleted in both the merged and squashed results. Let's verify this: - -```ucm -.a> find -.asquash> find -``` - -```ucm:hide -.> view a.keep1 a.keep2 a.keep3 -.> view asquash.keep1 asquash.keep2 asquash.keep3 -``` - -```ucm:error -.> view a.Delete4 -``` - -```ucm:error -.> view asquash.delete1 -``` diff --git a/unison-src/transcripts/fix2004.output.md b/unison-src/transcripts/fix2004.output.md deleted file mode 100644 index c8216d5e89..0000000000 --- a/unison-src/transcripts/fix2004.output.md +++ /dev/null @@ -1,267 +0,0 @@ - -Here's the scenario that can produce bad empty namespace LCAs: - -```deletes -of v4 -j1: ... - v1 - v2 - v3 - v4 - v4a - v5 - v6 - v7 - / - - v5a - - adds of unrelated -j2: ... - v1 - v2 - v3 - v4 - x0 - x1 - x2 - x3 - / - - z1 - - -``` - -So `j1` and `j2` have common history up through `v4`, then `j1` deletes some definitions while `j2` adds some definitions via shallow merges. These shallow merges then result in the LCA being the empty namespace rather than `v4`. - -First, we create some common history before a fork: - -```ucm -.> alias.term builtin.Nat.+ a.delete1 - - Done. - -.> alias.term builtin.Nat.* a.delete2 - - Done. - -.> alias.term builtin.Nat.drop a.delete3 - - Done. - -.> alias.type builtin.Nat a.Delete4 - - Done. - -``` -Now we fork `a2` off of `a`. `a` continues on, deleting the terms it added previously and then adding one unrelated term via a merge with little history. It's this short history merge which will become a bad LCA of the empty namespace. - -```ucm -.> fork a a2 - - Done. - -.> delete.term.verbose a.delete1 - - Name changes: - - Original Changes - 1. a.delete1 ┐ 2. a.delete1 (removed) - 3. a2.delete1 │ - 4. builtin.Nat.+ ┘ - - Tip: You can use `undo` or `reflog` to undo this change. - -.> delete.term.verbose a.delete2 - - Name changes: - - Original Changes - 1. a.delete2 ┐ 2. a.delete2 (removed) - 3. a2.delete2 │ - 4. builtin.Nat.* ┘ - - Tip: You can use `undo` or `reflog` to undo this change. - -.> delete.term.verbose a.delete3 - - Name changes: - - Original Changes - 1. a.delete3 ┐ 2. a.delete3 (removed) - 3. a2.delete3 │ - 4. builtin.Nat.drop ┘ - - Tip: You can use `undo` or `reflog` to undo this change. - -.> delete.type.verbose a.Delete4 - - Name changes: - - Original Changes - 1. a.Delete4 ┐ 2. a.Delete4 (removed) - 3. a2.Delete4 │ - 4. builtin.Nat ┘ - - Tip: You can use `undo` or `reflog` to undo this change. - -.> alias.term .builtin.Float.+ newbranchA.dontDelete - - Done. - -.> merge.old newbranchA a - - Here's what's changed in a after the merge: - - Added definitions: - - 1. dontDelete : Float -> Float -> Float - - 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... - -.a> find - - 1. dontDelete : ##Float -> ##Float -> ##Float - - -``` -Meanwhile, `a2` adds some other unrelated terms, some via merging in namespaces with little history. When merging `a2` back into `a`, the deletes from their common history should be respected. - -```ucm -.> alias.term builtin.Text.take a2.keep1 - - Done. - -.> alias.term builtin.Text.take a2.keep2 - - Done. - -.> alias.term builtin.Text.take a2.keep3 - - Done. - -.> alias.term builtin.Text.take a2.keep4 - - Done. - -.> alias.term builtin.Text.take a2.keep5 - - Done. - -.> alias.term builtin.Text.take newbranchA2.keep6 - - Done. - -.> merge.old newbranchA2 a2 - - Here's what's changed in a2 after the merge: - - Name changes: - - Original Changes - 1. keep1 ┐ 2. keep6 (added) - 3. keep2 │ - 4. keep3 │ - 5. keep4 │ - 6. keep5 ┘ - - 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... - -.a2> find - - 1. delete1 : Delete4 -> Delete4 -> Delete4 - 2. delete2 : Delete4 -> Delete4 -> Delete4 - 3. delete3 : Delete4 -> Delete4 -> Delete4 - 4. builtin type Delete4 - 5. keep1 : Delete4 -> ##Text -> ##Text - 6. keep2 : Delete4 -> ##Text -> ##Text - 7. keep3 : Delete4 -> ##Text -> ##Text - 8. keep4 : Delete4 -> ##Text -> ##Text - 9. keep5 : Delete4 -> ##Text -> ##Text - 10. keep6 : Delete4 -> ##Text -> ##Text - - -``` -```ucm -.> fork a asquash - - Done. - -.> merge.old a2 a - - Here's what's changed in a after the merge: - - Added definitions: - - 1. ┌ keep1 : Delete4 -> Text -> Text - 2. │ keep2 : Delete4 -> Text -> Text - 3. │ keep3 : Delete4 -> Text -> Text - 4. │ keep4 : Delete4 -> Text -> Text - 5. │ keep5 : Delete4 -> Text -> Text - 6. └ keep6 : Delete4 -> Text -> Text - - 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... - -.> merge.old.squash a2 asquash - - Here's what's changed in asquash after the merge: - - Added definitions: - - 1. ┌ keep1 : Delete4 -> Text -> Text - 2. │ keep2 : Delete4 -> Text -> Text - 3. │ keep3 : Delete4 -> Text -> Text - 4. │ keep4 : Delete4 -> Text -> Text - 5. │ keep5 : Delete4 -> Text -> Text - 6. └ keep6 : Delete4 -> Text -> Text - - 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... - -``` -At this point, all the things that `a` has deleted (`delete1`, `delete2`, etc) should be deleted in both the merged and squashed results. Let's verify this: - -```ucm -.a> find - - 1. dontDelete : ##Float -> ##Float -> ##Float - 2. keep1 : ##Nat -> ##Text -> ##Text - 3. keep2 : ##Nat -> ##Text -> ##Text - 4. keep3 : ##Nat -> ##Text -> ##Text - 5. keep4 : ##Nat -> ##Text -> ##Text - 6. keep5 : ##Nat -> ##Text -> ##Text - 7. keep6 : ##Nat -> ##Text -> ##Text - - -.asquash> find - - 1. dontDelete : ##Float -> ##Float -> ##Float - 2. keep1 : ##Nat -> ##Text -> ##Text - 3. keep2 : ##Nat -> ##Text -> ##Text - 4. keep3 : ##Nat -> ##Text -> ##Text - 5. keep4 : ##Nat -> ##Text -> ##Text - 6. keep5 : ##Nat -> ##Text -> ##Text - 7. keep6 : ##Nat -> ##Text -> ##Text - - -``` -```ucm -.> view a.Delete4 - - ⚠️ - - The following names were not found in the codebase. Check your spelling. - a.Delete4 - -``` -```ucm -.> view asquash.delete1 - - ⚠️ - - The following names were not found in the codebase. Check your spelling. - asquash.delete1 - -``` From 93fc35e2c773b378e4bb5640da51b588a2a02996 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 11:32:06 -0400 Subject: [PATCH 75/81] delete child-namespace-history-merge.md --- .../child-namespace-history-merge.md | 99 ------ .../child-namespace-history-merge.output.md | 302 ------------------ 2 files changed, 401 deletions(-) delete mode 100644 unison-src/transcripts/child-namespace-history-merge.md delete mode 100644 unison-src/transcripts/child-namespace-history-merge.output.md diff --git a/unison-src/transcripts/child-namespace-history-merge.md b/unison-src/transcripts/child-namespace-history-merge.md deleted file mode 100644 index 6ed0e2400e..0000000000 --- a/unison-src/transcripts/child-namespace-history-merge.md +++ /dev/null @@ -1,99 +0,0 @@ -# Behaviour of namespace histories during a merge. - -Note: This is a descriptive test meant to capture the current behaviour of -branch histories during a merge. -It isn't prescriptive about how merges _should_ work with respect to child branches, -but I think we should at least notice if we change things by accident. - - -## Setting up some history - -```ucm:hide -.> builtins.merge -``` - -```unison:hide -parent.top = "top" -parent.child.thing = "parent.child.thing" -``` - -The child branch has a single history node representing the addition of `parent.child.thing`. - -```ucm -.> add -.> history parent.child -``` - -If we add another thing to the child namespace it should add another history node to both the child and parent. - -```unison:hide -parent.child.thing2 = "parent.child.thing2" -``` - -```ucm -.> add -.> history parent -.> history parent.child -``` - -## Forking off some history on a separate branch - -Now we fork the parent namespace to make some changes. - -```ucm -.> fork parent parent_fork -``` - -```unison:hide -parent_fork.child.thing3 = "parent_fork.child.thing3" -``` - -The child should have a new history node after adding `thing3` - -```ucm -.> add -.> history parent_fork.child -``` - -## Saving our parent state - -Split off two separate forks, one for testing squash merges, one for standard merges. - -```ucm:hide -.> fork parent parent_squash_base -.> fork parent parent_merge_base -``` - -## Squash merge - -For a squash merge, when I squash-merge back into parent, we expect `parent_fork.child.thing3` to be added. - -```ucm -.> merge.old.squash parent_fork parent_squash_base -.> history parent_squash_base -``` - -Notice that with the current behaviour, the history of `parent.child` is completely wiped out, containing nothing from the source OR destination. - -```ucm -.> history parent.child -.> history parent_fork.child -.> history parent_squash_base.child -``` - -## Standard merge - -For a standard merge, if I merge back into parent, we expect `parent_fork.child.thing3` to be added. - -```ucm -.> merge.old parent_fork parent_merge_base -.> history parent_merge_base -``` - -Child histories should also be *merged*. - -```ucm -.> history parent.child -.> history parent_fork.child -.> history parent_merge_base.child -``` diff --git a/unison-src/transcripts/child-namespace-history-merge.output.md b/unison-src/transcripts/child-namespace-history-merge.output.md deleted file mode 100644 index 18e080e093..0000000000 --- a/unison-src/transcripts/child-namespace-history-merge.output.md +++ /dev/null @@ -1,302 +0,0 @@ -# Behaviour of namespace histories during a merge. - -Note: This is a descriptive test meant to capture the current behaviour of -branch histories during a merge. -It isn't prescriptive about how merges _should_ work with respect to child branches, -but I think we should at least notice if we change things by accident. - - -## Setting up some history - -```unison -parent.top = "top" -parent.child.thing = "parent.child.thing" -``` - -The child branch has a single history node representing the addition of `parent.child.thing`. - -```ucm -.> add - - ⍟ I've added these definitions: - - parent.child.thing : Text - parent.top : Text - -.> history parent.child - - Note: The most recent namespace hash is immediately below this - message. - - - - □ 1. #0r73mam57g (start of history) - -``` -If we add another thing to the child namespace it should add another history node to both the child and parent. - -```unison -parent.child.thing2 = "parent.child.thing2" -``` - -```ucm -.> add - - ⍟ I've added these definitions: - - parent.child.thing2 : Text - -.> history parent - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #2hv7t9lp40 - - + Adds / updates: - - child.thing2 - - □ 2. #i9lji1bli0 (start of history) - -.> history parent.child - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #ggnrs01131 - - + Adds / updates: - - thing2 - - □ 2. #0r73mam57g (start of history) - -``` -## Forking off some history on a separate branch - -Now we fork the parent namespace to make some changes. - -```ucm -.> fork parent parent_fork - - Done. - -``` -```unison -parent_fork.child.thing3 = "parent_fork.child.thing3" -``` - -The child should have a new history node after adding `thing3` - -```ucm -.> add - - ⍟ I've added these definitions: - - parent_fork.child.thing3 : Text - -.> history parent_fork.child - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #9rcfgbsp81 - - + Adds / updates: - - thing3 - - ⊙ 2. #ggnrs01131 - - + Adds / updates: - - thing2 - - □ 3. #0r73mam57g (start of history) - -``` -## Saving our parent state - -Split off two separate forks, one for testing squash merges, one for standard merges. - -## Squash merge - -For a squash merge, when I squash-merge back into parent, we expect `parent_fork.child.thing3` to be added. - -```ucm -.> merge.old.squash parent_fork parent_squash_base - - Here's what's changed in parent_squash_base after the merge: - - Added definitions: - - 1. child.thing3 : Text - - 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... - -.> history parent_squash_base - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #594e0e1p39 - - + Adds / updates: - - child.thing3 - - ⊙ 2. #2hv7t9lp40 - - + Adds / updates: - - child.thing2 - - □ 3. #i9lji1bli0 (start of history) - -``` -Notice that with the current behaviour, the history of `parent.child` is completely wiped out, containing nothing from the source OR destination. - -```ucm -.> history parent.child - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #ggnrs01131 - - + Adds / updates: - - thing2 - - □ 2. #0r73mam57g (start of history) - -.> history parent_fork.child - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #9rcfgbsp81 - - + Adds / updates: - - thing3 - - ⊙ 2. #ggnrs01131 - - + Adds / updates: - - thing2 - - □ 3. #0r73mam57g (start of history) - -.> history parent_squash_base.child - - Note: The most recent namespace hash is immediately below this - message. - - - - □ 1. #19fd4mhpp4 (start of history) - -``` -## Standard merge - -For a standard merge, if I merge back into parent, we expect `parent_fork.child.thing3` to be added. - -```ucm -.> merge.old parent_fork parent_merge_base - - Here's what's changed in parent_merge_base after the merge: - - Added definitions: - - 1. child.thing3 : Text - - 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... - -.> history parent_merge_base - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #mtn8sha7gd - - + Adds / updates: - - child.thing3 - - ⊙ 2. #2hv7t9lp40 - - + Adds / updates: - - child.thing2 - - □ 3. #i9lji1bli0 (start of history) - -``` -Child histories should also be *merged*. - -```ucm -.> history parent.child - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #ggnrs01131 - - + Adds / updates: - - thing2 - - □ 2. #0r73mam57g (start of history) - -.> history parent_fork.child - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #9rcfgbsp81 - - + Adds / updates: - - thing3 - - ⊙ 2. #ggnrs01131 - - + Adds / updates: - - thing2 - - □ 3. #0r73mam57g (start of history) - -.> history parent_merge_base.child - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #9rcfgbsp81 - - + Adds / updates: - - thing3 - - ⊙ 2. #ggnrs01131 - - + Adds / updates: - - thing2 - - □ 3. #0r73mam57g (start of history) - -``` From 3556cb6ec8d226eb9dd5a8585d0f7d2be372dd46 Mon Sep 17 00:00:00 2001 From: etorreborre Date: Wed, 26 Jun 2024 17:34:45 +0200 Subject: [PATCH 76/81] fix: fix the textual representation of an ordinal number --- parser-typechecker/src/Unison/PrintError.hs | 9 +------- parser-typechecker/src/Unison/Util/Text.hs | 20 ++++++++++++++++ .../tests/Unison/Test/Util/Text.hs | 23 ++++++++++++++++++- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/parser-typechecker/src/Unison/PrintError.hs b/parser-typechecker/src/Unison/PrintError.hs index d6e50ebbb5..16ea2dc881 100644 --- a/parser-typechecker/src/Unison/PrintError.hs +++ b/parser-typechecker/src/Unison/PrintError.hs @@ -75,6 +75,7 @@ import Unison.Util.Monoid (intercalateMap) import Unison.Util.Pretty (ColorText, Pretty) import Unison.Util.Pretty qualified as Pr import Unison.Util.Range (Range (..), startingLine) +import Unison.Util.Text (ordinal) import Unison.Var (Var) import Unison.Var qualified as Var @@ -831,14 +832,6 @@ renderTypeError e env src = case e of let sz = length wrongs pl a b = if sz == 1 then a else b in mconcat [txt pl, intercalateMap "\n" (renderSuggestion env) wrongs] - ordinal :: (IsString s) => Int -> s - ordinal n = - fromString $ - show n ++ case last (show n) of - '1' -> "st" - '2' -> "nd" - '3' -> "rd" - _ -> "th" debugNoteLoc a = if Settings.debugNoteLoc then a else mempty debugSummary :: C.ErrorNote v loc -> Pretty ColorText debugSummary note = diff --git a/parser-typechecker/src/Unison/Util/Text.hs b/parser-typechecker/src/Unison/Util/Text.hs index 2c5bdf3c5b..16947d41f7 100644 --- a/parser-typechecker/src/Unison/Util/Text.hs +++ b/parser-typechecker/src/Unison/Util/Text.hs @@ -6,6 +6,7 @@ module Unison.Util.Text where import Data.Foldable (toList) import Data.List (foldl', unfoldr) +import Data.List qualified as L import Data.String (IsString (..)) import Data.Text qualified as T import Data.Text.Encoding qualified as T @@ -131,6 +132,25 @@ indexOf needle haystack = needle' = toLazyText needle haystack' = toLazyText haystack +-- | Return the ordinal representation of a number in English. +-- A number ending with '1' must finish with 'st' +-- A number ending with '2' must finish with 'nd' +-- A number ending with '3' must finish with 'rd' +-- _except_ for 11, 12, and 13 which must finish with 'th' +ordinal :: (IsString s) => Int -> s +ordinal n = do + let s = show n + fromString $ s ++ + case L.drop (L.length s - 2) s of + ['1', '1'] -> "th" + ['1', '2'] -> "th" + ['1', '3'] -> "th" + _ -> case last s of + '1' -> "st" + '2' -> "nd" + '3' -> "rd" + _ -> "th" + -- Drop with both a maximum size and a predicate. Yields actual number of -- dropped characters. -- diff --git a/parser-typechecker/tests/Unison/Test/Util/Text.hs b/parser-typechecker/tests/Unison/Test/Util/Text.hs index e5e13e9d55..083e042868 100644 --- a/parser-typechecker/tests/Unison/Test/Util/Text.hs +++ b/parser-typechecker/tests/Unison/Test/Util/Text.hs @@ -178,7 +178,28 @@ test = ) (P.Join [P.Capture (P.Literal "zzzaaa"), P.Capture (P.Literal "!")]) in P.run p "zzzaaa!!!" - ok + ok, + scope "ordinal" do + expectEqual (Text.ordinal 1) ("1st" :: String) + expectEqual (Text.ordinal 2) ("2nd" :: String) + expectEqual (Text.ordinal 3) ("3rd" :: String) + expectEqual (Text.ordinal 4) ("4th" :: String) + expectEqual (Text.ordinal 5) ("5th" :: String) + expectEqual (Text.ordinal 10) ("10th" :: String) + expectEqual (Text.ordinal 11) ("11th" :: String) + expectEqual (Text.ordinal 12) ("12th" :: String) + expectEqual (Text.ordinal 13) ("13th" :: String) + expectEqual (Text.ordinal 14) ("14th" :: String) + expectEqual (Text.ordinal 21) ("21st" :: String) + expectEqual (Text.ordinal 22) ("22nd" :: String) + expectEqual (Text.ordinal 23) ("23rd" :: String) + expectEqual (Text.ordinal 24) ("24th" :: String) + expectEqual (Text.ordinal 111) ("111th" :: String) + expectEqual (Text.ordinal 112) ("112th" :: String) + expectEqual (Text.ordinal 113) ("113th" :: String) + expectEqual (Text.ordinal 121) ("121st" :: String) + expectEqual (Text.ordinal 122) ("122nd" :: String) + expectEqual (Text.ordinal 123) ("123rd" :: String) ] where log2 :: Int -> Int From 596cb39b2e3936704d1ab7e480b6b32647df2005 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 12:13:18 -0400 Subject: [PATCH 77/81] port empty namespace merge test to new merge --- unison-src/transcripts/diff-namespace.md | 2 ++ unison-src/transcripts/empty-namespaces.md | 8 -------- .../transcripts/empty-namespaces.output.md | 20 ------------------- unison-src/transcripts/merge.md | 11 ++++++++++ unison-src/transcripts/merge.output.md | 17 ++++++++++++++++ 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/unison-src/transcripts/diff-namespace.md b/unison-src/transcripts/diff-namespace.md index 5e938a79a5..7db0bc898a 100644 --- a/unison-src/transcripts/diff-namespace.md +++ b/unison-src/transcripts/diff-namespace.md @@ -12,9 +12,11 @@ x = 23 .> fork b1 b2 .b2> alias.term x abc ``` + ```unison:hide fslkdjflskdjflksjdf = 663 ``` + ```ucm .b0> add .> merge.old b0 b1 diff --git a/unison-src/transcripts/empty-namespaces.md b/unison-src/transcripts/empty-namespaces.md index 223ab34ba9..d9497fc931 100644 --- a/unison-src/transcripts/empty-namespaces.md +++ b/unison-src/transcripts/empty-namespaces.md @@ -28,14 +28,6 @@ The history of the namespace should be empty. .> history mynamespace ``` -Merging an empty namespace should be a no-op - -```ucm:error -.empty> history -.empty> merge.old .mynamespace -.empty> history -``` - Add and then delete a term to add some history to a deleted namespace. ```unison:hide diff --git a/unison-src/transcripts/empty-namespaces.output.md b/unison-src/transcripts/empty-namespaces.output.md index 16d33046e1..7fb5de89ed 100644 --- a/unison-src/transcripts/empty-namespaces.output.md +++ b/unison-src/transcripts/empty-namespaces.output.md @@ -54,26 +54,6 @@ The history of the namespace should be empty. ☝️ The namespace .mynamespace is empty. -``` -Merging an empty namespace should be a no-op - -```ucm - ☝️ The namespace .empty is empty. - -.empty> history - - ☝️ The namespace .empty is empty. - -.empty> merge.old .mynamespace - - ⚠️ - - The namespace .mynamespace doesn't exist. - -.empty> history - - ☝️ The namespace .empty is empty. - ``` Add and then delete a term to add some history to a deleted namespace. diff --git a/unison-src/transcripts/merge.md b/unison-src/transcripts/merge.md index debe548c5b..d38895ce80 100644 --- a/unison-src/transcripts/merge.md +++ b/unison-src/transcripts/merge.md @@ -456,6 +456,17 @@ project/alice> merge /bob .> project.delete project ``` +## No-op merge: merge empty namespace into empty namespace + +```ucm +project/main> branch topic +project/main> merge /topic +``` + +```ucm:hide +.> project.delete project +``` + ## Merge failure: someone deleted something If either Alice or Bob delete something, so long as the other person didn't update it (in which case we ignore the delete, as explained above), then the delete goes through. diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index e2b7672de3..7e91d92c87 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -469,6 +469,23 @@ project/alice> merge /bob I fast-forward merged project/bob into project/alice. +``` +## No-op merge: merge empty namespace into empty namespace + +```ucm +project/main> branch topic + + Done. I've created the topic branch based off of main. + + Tip: To merge your work back into the main branch, first + `switch /main` then `merge /topic`. + +project/main> merge /topic + + 😶 + + project/main was already up-to-date with project/topic. + ``` ## Merge failure: someone deleted something From 25d505e78440f30b2bbf99a1f5cb59795eccc87d Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 12:24:00 -0400 Subject: [PATCH 78/81] port mergeloop.md over to use new merge --- unison-src/transcripts/merge.md | 51 +++++++ unison-src/transcripts/merge.output.md | 148 +++++++++++++++++++ unison-src/transcripts/mergeloop.md | 51 ------- unison-src/transcripts/mergeloop.output.md | 157 --------------------- 4 files changed, 199 insertions(+), 208 deletions(-) delete mode 100644 unison-src/transcripts/mergeloop.md delete mode 100644 unison-src/transcripts/mergeloop.output.md diff --git a/unison-src/transcripts/merge.md b/unison-src/transcripts/merge.md index d38895ce80..d2f1105a2f 100644 --- a/unison-src/transcripts/merge.md +++ b/unison-src/transcripts/merge.md @@ -1594,3 +1594,54 @@ But `bar` was put into the scratch file instead. ```ucm:hide .> project.delete project ``` + +### Merge loop test + +This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the +history. + +Let's make three identical namespaces with different histories: + +```unison +a = 1 +``` + +```ucm +project/alice> add +``` + +```unison +b = 2 +``` + +```ucm +project/alice> add +``` + +```unison +b = 2 +``` + +```ucm +project/bob> add +``` + +```unison +a = 1 +``` + +```ucm +project/bob> add +``` + +```unison +a = 1 +b = 2 +``` + +```ucm +project/carol> add +project/bob> merge /alice +project/carol> merge /bob +project/carol> history +``` diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index 7e91d92c87..258413502d 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -1973,3 +1973,151 @@ bar = But `bar` was put into the scratch file instead. +### Merge loop test + +This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the +history. + +Let's make three identical namespaces with different histories: + +```unison +a = 1 +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + a : ##Nat + +``` +```ucm +project/alice> add + + ⍟ I've added these definitions: + + a : ##Nat + +``` +```unison +b = 2 +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + b : ##Nat + +``` +```ucm +project/alice> add + + ⍟ I've added these definitions: + + b : ##Nat + +``` +```unison +b = 2 +``` + +```ucm + + Loading changes detected in scratch.u. + + I found and typechecked the definitions in scratch.u. This + file has been previously added to the codebase. + +``` +```ucm +project/bob> add + + ⍟ I've added these definitions: + + b : ##Nat + +``` +```unison +a = 1 +``` + +```ucm + + 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: + + ⍟ These new definitions are ok to `add`: + + a : ##Nat + +``` +```ucm +project/bob> add + + ⍟ I've added these definitions: + + a : ##Nat + +``` +```unison +a = 1 +b = 2 +``` + +```ucm + + Loading changes detected in scratch.u. + + I found and typechecked the definitions in scratch.u. This + file has been previously added to the codebase. + +``` +```ucm +project/carol> add + + ⍟ I've added these definitions: + + a : ##Nat + b : ##Nat + +project/bob> merge /alice + + I merged project/alice into project/bob. + +project/carol> merge /bob + + I merged project/bob into project/carol. + +project/carol> history + + Note: The most recent namespace hash is immediately below this + message. + + + + This segment of history starts with a merge. Use + `history #som3n4m3space` to view history starting from a given + namespace hash. + + ⊙ 1. #b7fr6ifj87 + ⑃ + 2. #9npggauqo9 + 3. #dm4u1eokg1 + +``` diff --git a/unison-src/transcripts/mergeloop.md b/unison-src/transcripts/mergeloop.md deleted file mode 100644 index fd1b25fa8e..0000000000 --- a/unison-src/transcripts/mergeloop.md +++ /dev/null @@ -1,51 +0,0 @@ -# Merge loop test - -This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the history. - -Let's make three identical namespaces with different histories: - -```unison -a = 1 -``` - -```ucm -.x> add -``` - -```unison -b = 2 -``` - -```ucm -.x> add -``` - -```unison -b = 2 -``` - -```ucm -.y> add -``` - -```unison -a = 1 -``` - -```ucm -.y> add -``` - -```unison -a = 1 -b = 2 -``` - -```ucm -.z> add -.> merge.old x y -.> merge.old y z -.> history z -``` - - diff --git a/unison-src/transcripts/mergeloop.output.md b/unison-src/transcripts/mergeloop.output.md deleted file mode 100644 index faa084764b..0000000000 --- a/unison-src/transcripts/mergeloop.output.md +++ /dev/null @@ -1,157 +0,0 @@ -# Merge loop test - -This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the history. - -Let's make three identical namespaces with different histories: - -```unison -a = 1 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - a : ##Nat - -``` -```ucm - ☝️ The namespace .x is empty. - -.x> add - - ⍟ I've added these definitions: - - a : ##Nat - -``` -```unison -b = 2 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - b : ##Nat - -``` -```ucm -.x> add - - ⍟ I've added these definitions: - - b : ##Nat - -``` -```unison -b = 2 -``` - -```ucm - - Loading changes detected in scratch.u. - - I found and typechecked the definitions in scratch.u. This - file has been previously added to the codebase. - -``` -```ucm - ☝️ The namespace .y is empty. - -.y> add - - ⍟ I've added these definitions: - - b : ##Nat - -``` -```unison -a = 1 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - a : ##Nat - -``` -```ucm -.y> add - - ⍟ I've added these definitions: - - a : ##Nat - -``` -```unison -a = 1 -b = 2 -``` - -```ucm - - Loading changes detected in scratch.u. - - I found and typechecked the definitions in scratch.u. This - file has been previously added to the codebase. - -``` -```ucm - ☝️ The namespace .z is empty. - -.z> add - - ⍟ I've added these definitions: - - a : ##Nat - b : ##Nat - -.> merge.old x y - - Nothing changed as a result of the merge. - - Applying changes from patch... - -.> merge.old y z - - Nothing changed as a result of the merge. - - Applying changes from patch... - -.> history z - - Note: The most recent namespace hash is immediately below this - message. - - - - This segment of history starts with a merge. Use - `history #som3n4m3space` to view history starting from a given - namespace hash. - - ⊙ 1. #b7fr6ifj87 - ⑃ - 2. #9npggauqo9 - 3. #dm4u1eokg1 - -``` From 19da19b2597f41253b8d08b23c68c408071dc119 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 12:25:24 -0400 Subject: [PATCH 79/81] delete merges.md --- unison-src/transcripts/merges.md | 121 --------- unison-src/transcripts/merges.output.md | 312 ------------------------ 2 files changed, 433 deletions(-) delete mode 100644 unison-src/transcripts/merges.md delete mode 100644 unison-src/transcripts/merges.output.md diff --git a/unison-src/transcripts/merges.md b/unison-src/transcripts/merges.md deleted file mode 100644 index 330e46857b..0000000000 --- a/unison-src/transcripts/merges.md +++ /dev/null @@ -1,121 +0,0 @@ -# Forking and merging namespaces in `ucm` - -```ucm:hide -.master> builtins.merge -``` - -The Unison namespace is a versioned tree of names that map to Unison definitions. You can change this namespace and fork and merge subtrees of it. Let's start by introducing a few definitions into a new namespace, `foo`: - -```unison -x = 42 -``` - -```ucm -.> add -``` - -Let's move `x` into a new namespace, `master`: - -```ucm -.> rename.term x master.x -``` - -If you want to do some experimental work in a namespace without disturbing anyone else, you can `fork` it (which is a shorthand for `copy.namespace`). This creates a copy of it, preserving its history. - -> __Note:__ these copies are very efficient to create as they just have pointers into the same underlying definitions. Create as many as you like. - -Let's go ahead and do this: - -``` -.> fork master feature1 -.> view master.x -.> view feature1.x -``` - -Great! We can now do some further work in the `feature1` branch, then merge it back into `master` when we're ready. - -```unison -y = "hello" -``` - -```ucm -.feature1> add -.master> merge.old .feature1 -.master> view y -``` - -> Note: `merge src`, with one argument, merges `src` into the current namespace. You can also do `merge src dest` to merge into any destination namespace. - -Notice that `master` now has the definition of `y` we wrote. - -We can also delete the fork if we're done with it. (Don't worry, even though the history at that path is now empty, -it's still in the `history` of the parent namespace and can be resurrected at any time.) - -```ucm -.> delete.namespace feature1 -.> history .feature1 -.> history -``` - -To resurrect an old version of a namespace, you can learn its hash via the `history` command, then use `fork #namespacehash .newname`. - -## Concurrent edits and merges - -In the above scenario the destination namespace (`master`) was strictly behind the source namespace, so the merge didn't have anything interesting to do (Git would call this a "fast forward" merge). In other cases, the source and destination namespaces will each have changes the other doesn't know about, and the merge needs to something more interesting. That's okay too, and Unison will merge those results, using a 3-way merge algorithm. - -> __Note:__ When merging nested namespaces, Unison actually uses a recursive 3-way merge, so it finds a different (and possibly closer) common ancestor at each level of the tree. - -Let's see how this works. We are going to create a copy of `master`, add and delete some definitions in `master` and in the fork, then merge. - -```ucm -.> fork master feature2 -``` - -Here's one fork, we add `z` and delete `x`: - -```unison -z = 99 -``` - -```ucm -.feature2> add -.feature2> delete.term.verbose x -``` - -And here's the other fork, where we update `y` and add a new definition, `frobnicate`: - -```unison -master.y = "updated y" -master.frobnicate n = n + 1 -``` - -```ucm -.> update -.> view master.y -.> view master.frobnicate -``` - -At this point, `master` and `feature2` both have some changes the other doesn't know about. Let's merge them. - -```ucm -.> merge.old feature2 master -``` - -Notice that `x` is deleted in the merged branch (it was deleted in `feature2` and untouched by `master`): - -```ucm:error -.> view master.x -``` - -And notice that `y` has the most recent value, and that `z` and `frobnicate` both exist as well: - -```ucm -.> view master.y -.> view master.z -.> view master.frobnicate -``` - -## FAQ - -* What happens if namespace1 deletes a name that namespace2 has updated? A: ??? -* ... diff --git a/unison-src/transcripts/merges.output.md b/unison-src/transcripts/merges.output.md deleted file mode 100644 index 8bfbb170fb..0000000000 --- a/unison-src/transcripts/merges.output.md +++ /dev/null @@ -1,312 +0,0 @@ -# Forking and merging namespaces in `ucm` - -The Unison namespace is a versioned tree of names that map to Unison definitions. You can change this namespace and fork and merge subtrees of it. Let's start by introducing a few definitions into a new namespace, `foo`: - -```unison -x = 42 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - x : Nat - -``` -```ucm -.> add - - ⍟ I've added these definitions: - - x : Nat - -``` -Let's move `x` into a new namespace, `master`: - -```ucm -.> rename.term x master.x - - Done. - -``` -If you want to do some experimental work in a namespace without disturbing anyone else, you can `fork` it (which is a shorthand for `copy.namespace`). This creates a copy of it, preserving its history. - -> __Note:__ these copies are very efficient to create as they just have pointers into the same underlying definitions. Create as many as you like. - -Let's go ahead and do this: - -``` -.> fork master feature1 -.> view master.x -.> view feature1.x - -``` - -Great! We can now do some further work in the `feature1` branch, then merge it back into `master` when we're ready. - -```unison -y = "hello" -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - y : Text - -``` -```ucm - ☝️ The namespace .feature1 is empty. - -.feature1> add - - ⍟ I've added these definitions: - - y : ##Text - -.master> merge.old .feature1 - - Here's what's changed in the current namespace after the - merge: - - Added definitions: - - 1. y : Text - - 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... - -.master> view y - - y : Text - y = "hello" - -``` -> Note: `merge src`, with one argument, merges `src` into the current namespace. You can also do `merge src dest` to merge into any destination namespace. - -Notice that `master` now has the definition of `y` we wrote. - -We can also delete the fork if we're done with it. (Don't worry, even though the history at that path is now empty, -it's still in the `history` of the parent namespace and can be resurrected at any time.) - -```ucm -.> delete.namespace feature1 - - Done. - -.> history .feature1 - - ☝️ The namespace .feature1 is empty. - -.> history - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #6j9omad7mv - - - Deletes: - - feature1.y - - ⊙ 2. #59u4sdgodu - - + Adds / updates: - - master.y - - = Copies: - - Original name New name(s) - feature1.y master.y - - ⊙ 3. #0je96at36h - - + Adds / updates: - - feature1.y - - ⊙ 4. #cnv4gjntbl - - > Moves: - - Original name New name - x master.x - - ⊙ 5. #tp0bn8ulih - - + Adds / updates: - - x - - □ 6. #cujaete914 (start of history) - -``` -To resurrect an old version of a namespace, you can learn its hash via the `history` command, then use `fork #namespacehash .newname`. - -## Concurrent edits and merges - -In the above scenario the destination namespace (`master`) was strictly behind the source namespace, so the merge didn't have anything interesting to do (Git would call this a "fast forward" merge). In other cases, the source and destination namespaces will each have changes the other doesn't know about, and the merge needs to something more interesting. That's okay too, and Unison will merge those results, using a 3-way merge algorithm. - -> __Note:__ When merging nested namespaces, Unison actually uses a recursive 3-way merge, so it finds a different (and possibly closer) common ancestor at each level of the tree. - -Let's see how this works. We are going to create a copy of `master`, add and delete some definitions in `master` and in the fork, then merge. - -```ucm -.> fork master feature2 - - Done. - -``` -Here's one fork, we add `z` and delete `x`: - -```unison -z = 99 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - z : Nat - -``` -```ucm -.feature2> add - - ⍟ I've added these definitions: - - z : Nat - -.feature2> delete.term.verbose x - - Removed definitions: - - 1. x : Nat - - Tip: You can use `undo` or `reflog` to undo this change. - -``` -And here's the other fork, where we update `y` and add a new definition, `frobnicate`: - -```unison -master.y = "updated y" -master.frobnicate n = n + 1 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - master.frobnicate : Nat -> Nat - master.y : Text - -``` -```ucm -.> update - - Okay, I'm searching the branch for code that needs to be - updated... - - Done. - -.> view master.y - - master.y : Text - master.y = "updated y" - -.> view master.frobnicate - - master.frobnicate : Nat -> Nat - master.frobnicate n = - use Nat + - n + 1 - -``` -At this point, `master` and `feature2` both have some changes the other doesn't know about. Let's merge them. - -```ucm -.> merge.old feature2 master - - Here's what's changed in master after the merge: - - Added definitions: - - 1. z : Nat - - Removed definitions: - - 2. x : 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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -``` -Notice that `x` is deleted in the merged branch (it was deleted in `feature2` and untouched by `master`): - -```ucm -.> view master.x - - ⚠️ - - The following names were not found in the codebase. Check your spelling. - master.x - -``` -And notice that `y` has the most recent value, and that `z` and `frobnicate` both exist as well: - -```ucm -.> view master.y - - master.y : Text - master.y = "updated y" - -.> view master.z - - master.z : Nat - master.z = 99 - -.> view master.frobnicate - - master.frobnicate : Nat -> Nat - master.frobnicate n = - use Nat + - n + 1 - -``` -## FAQ - -* What happens if namespace1 deletes a name that namespace2 has updated? A: ??? -* ... From c5f4d6d5b7dff2e5ced8f9a1d0419a3a8c6a2518 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 12:26:14 -0400 Subject: [PATCH 80/81] delete project-merge.md --- unison-src/transcripts/project-merge.md | 39 ---- .../transcripts/project-merge.output.md | 193 ------------------ 2 files changed, 232 deletions(-) delete mode 100644 unison-src/transcripts/project-merge.md delete mode 100644 unison-src/transcripts/project-merge.output.md diff --git a/unison-src/transcripts/project-merge.md b/unison-src/transcripts/project-merge.md deleted file mode 100644 index d18fd89cfd..0000000000 --- a/unison-src/transcripts/project-merge.md +++ /dev/null @@ -1,39 +0,0 @@ -# projects merge - -```ucm -.> builtins.merge -``` - -```unison -zonk = 0 -``` - -```ucm -.foo> add -.> project.create-empty foo -.> merge.old foo foo/main -``` - -```unison -bonk = 2 -``` - -```ucm -foo/main> add -``` - -```ucm -.> project.create-empty bar -bar/main> merge.old foo/main -bar/main> branch /topic -``` - -```unison -xonk = 1 -``` - -```ucm -bar/main> add -bar/topic> merge.old /main -.bar> merge.old foo/main -``` diff --git a/unison-src/transcripts/project-merge.output.md b/unison-src/transcripts/project-merge.output.md deleted file mode 100644 index 98f20e79d7..0000000000 --- a/unison-src/transcripts/project-merge.output.md +++ /dev/null @@ -1,193 +0,0 @@ -# projects merge - -```ucm -.> builtins.merge - - Done. - -``` -```unison -zonk = 0 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - zonk : Nat - -``` -```ucm - ☝️ The namespace .foo is empty. - -.foo> add - - ⍟ I've added these definitions: - - zonk : ##Nat - -.> project.create-empty foo - - 🎉 I've created the project foo. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - -.> merge.old foo foo/main - - Here's what's changed in foo/main after the merge: - - Added definitions: - - 1. zonk : 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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -``` -```unison -bonk = 2 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - bonk : Nat - -``` -```ucm -foo/main> add - - ⍟ I've added these definitions: - - bonk : ##Nat - -``` -```ucm -.> project.create-empty bar - - 🎉 I've created the project bar. - - 🎨 Type `ui` to explore this project's code in your browser. - 🔭 Discover libraries at https://share.unison-lang.org - 📖 Use `help-topic projects` to learn more about projects. - - Write your first Unison code with UCM: - - 1. Open scratch.u. - 2. Write some Unison code and save the file. - 3. In UCM, type `add` to save it to your new project. - - 🎉 🥳 Happy coding! - -bar/main> merge.old foo/main - - Here's what's changed in the current namespace after the - merge: - - Added definitions: - - 1. bonk : ##Nat - 2. zonk : ##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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -bar/main> branch /topic - - Done. I've created the topic branch based off of main. - - Tip: To merge your work back into the main branch, first - `switch /main` then `merge /topic`. - -``` -```unison -xonk = 1 -``` - -```ucm - - 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: - - ⍟ These new definitions are ok to `add`: - - xonk : ##Nat - -``` -```ucm -bar/main> add - - ⍟ I've added these definitions: - - xonk : ##Nat - -bar/topic> merge.old /main - - Here's what's changed in the current namespace after the - merge: - - Added definitions: - - 1. xonk : ##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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - - ☝️ The namespace .bar is empty. - -.bar> merge.old foo/main - - Here's what's changed in the current namespace after the - merge: - - Added definitions: - - 1. bonk : ##Nat - 2. zonk : ##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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -``` From 94580cc2185e04b90855a67631670cad940221b6 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 12:27:49 -0400 Subject: [PATCH 81/81] delete squash.md --- unison-src/transcripts/squash.md | 157 ------- unison-src/transcripts/squash.output.md | 529 ------------------------ 2 files changed, 686 deletions(-) delete mode 100644 unison-src/transcripts/squash.md delete mode 100644 unison-src/transcripts/squash.output.md diff --git a/unison-src/transcripts/squash.md b/unison-src/transcripts/squash.md deleted file mode 100644 index f3b010944a..0000000000 --- a/unison-src/transcripts/squash.md +++ /dev/null @@ -1,157 +0,0 @@ - -```ucm:hide -.> builtins.merge -``` - -# Squash merges - -`squash src dest` merges can be used to merge from `src` to `dest`, discarding the history of `src`. It's useful when the source namespace history is irrelevant or has a bunch of churn you wish to discard. Often when merging small pull requests, you'll use a squash merge. - -Let's look at some examples. We'll start with a namespace with just the builtins. Let's take a look at the hash of this namespace: - -```ucm -.> history builtin -.> fork builtin builtin2 -``` - -(We make a copy of `builtin` for use later in this transcript.) - -Now suppose we `fork` a copy of builtin, then rename `Nat.+` to `frobnicate`, then rename it back. Notice this produces multiple entries in the history: - -```ucm -.> fork builtin mybuiltin -.mybuiltin> rename.term Nat.+ Nat.frobnicate -.mybuiltin> rename.term Nat.frobnicate Nat.+ -.mybuiltin> history -``` - -If we merge that back into `builtin`, we get that same chain of history: - -```ucm -.> merge.old mybuiltin builtin -.> history builtin -``` - -Let's try again, but using a `merge.squash` (or just `squash`) instead. The history will be unchanged: - -```ucm -.> merge.old.squash mybuiltin builtin2 -.> history builtin2 -``` - -The churn that happened in `mybuiltin` namespace ended up back in the same spot, so the squash merge of that namespace with our original namespace had no effect. - -## Another example - -Let's look at a more interesting example, where the two namespaces have diverged a bit. Here's our starting namespace: - -```unison:hide -x = 1 -``` - -```ucm -.trunk> add -.> fork trunk alice -.> fork trunk bob -``` - -Alice now does some hacking: - -```unison:hide -radNumber = 348 -bodaciousNumero = 2394 -neatoFun x = x -``` - -```ucm -.alice> add -.alice> rename.term radNumber superRadNumber -.alice> rename.term neatoFun productionReadyId -``` - -Meanwhile, Bob does his own hacking: - -```unison:hide -whatIsLove = "?" -babyDon'tHurtMe = ".. Don't hurt me..." -no more = no more -``` - -```ucm -.bob> add -``` - -At this point, Alice and Bob both have some history beyond what's in trunk: - -```ucm -.> history trunk -.> history alice -.> history bob -``` - -Alice then squash merges into `trunk`, as does Bob. It's as if Alice and Bob both made their changes in one single commit. - -```ucm -.> merge.old.squash alice trunk -.> history trunk -.> merge.old.squash bob trunk -.> history trunk -``` - -Since squash merges don't produce any merge nodes, we can `undo` a couple times to get back to our starting state: - -```ucm -.> undo -.> undo -.> history trunk -``` - -This time, we'll first squash Alice and Bob's changes together before squashing their combined changes into `trunk`. The resulting `trunk` will have just a single entry in it, combining both Alice and Bob's changes: - -```ucm -.> merge.old.squash alice bob -.> merge.old.squash bob trunk -.> history trunk -``` - -So, there you have it. With squashing, you can control the granularity of your history. - -## Throwing out all history - -Another thing we can do is `squash` into an empty namespace. This effectively makes a copy of the namespace, but without any of its history: - -```ucm -.> merge.old.squash alice nohistoryalice -.> history nohistoryalice -``` - -There's nothing really special here, `squash src dest` discards `src` history that comes after the LCA of `src` and `dest`, it's just that in the case of an empty namespace, that LCA is the beginning of time (the empty namespace), so all the history of `src` is discarded. - -## Checking for handling of deletes - -This checks to see that squashing correctly preserves deletions: - -```ucm -.delete> builtins.merge -.delete> fork builtin builtin2 -.delete> delete.term.verbose builtin2.Nat.+ -.delete> delete.term.verbose builtin2.Nat.* -.delete> merge.old.squash builtin2 builtin -.delete> history builtin -``` - -Notice that `Nat.+` and `Nat.*` are deleted by the squash, and we see them deleted in one atomic step in the history. - -Just confirming that those two definitions are in fact removed: - -```ucm:error -.delete> view .delete.builtin.Nat.+ -``` - -```ucm:error -.delete> view .delete.builtin.Nat.* -``` - -## Caveats - -If you `squash mystuff trunk`, you're discarding any history of `mystuff` and just cons'ing onto the history of `trunk`. Thus, don't expect to be able to `merge trunk mystuff` later and get great results. Squashing should only be used when you don't care about the history (and you know others haven't pulled and built on your line of history being discarded, so they don't care about the history either). diff --git a/unison-src/transcripts/squash.output.md b/unison-src/transcripts/squash.output.md deleted file mode 100644 index 3698fdfe6a..0000000000 --- a/unison-src/transcripts/squash.output.md +++ /dev/null @@ -1,529 +0,0 @@ - -# Squash merges - -`squash src dest` merges can be used to merge from `src` to `dest`, discarding the history of `src`. It's useful when the source namespace history is irrelevant or has a bunch of churn you wish to discard. Often when merging small pull requests, you'll use a squash merge. - -Let's look at some examples. We'll start with a namespace with just the builtins. Let's take a look at the hash of this namespace: - -```ucm -.> history builtin - - Note: The most recent namespace hash is immediately below this - message. - - - - □ 1. #i3vp9o9btm (start of history) - -.> fork builtin builtin2 - - Done. - -``` -(We make a copy of `builtin` for use later in this transcript.) - -Now suppose we `fork` a copy of builtin, then rename `Nat.+` to `frobnicate`, then rename it back. Notice this produces multiple entries in the history: - -```ucm -.> fork builtin mybuiltin - - Done. - -.mybuiltin> rename.term Nat.+ Nat.frobnicate - - Done. - -.mybuiltin> rename.term Nat.frobnicate Nat.+ - - Done. - -.mybuiltin> history - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #tpkjb488ei - - > Moves: - - Original name New name - Nat.frobnicate Nat.+ - - ⊙ 2. #334ak3epqt - - > Moves: - - Original name New name - Nat.+ Nat.frobnicate - - □ 3. #i3vp9o9btm (start of history) - -``` -If we merge that back into `builtin`, we get that same chain of history: - -```ucm -.> merge.old mybuiltin builtin - - Nothing changed as a result of the merge. - - Applying changes from patch... - -.> history builtin - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #tpkjb488ei - - > Moves: - - Original name New name - Nat.frobnicate Nat.+ - - ⊙ 2. #334ak3epqt - - > Moves: - - Original name New name - Nat.+ Nat.frobnicate - - □ 3. #i3vp9o9btm (start of history) - -``` -Let's try again, but using a `merge.squash` (or just `squash`) instead. The history will be unchanged: - -```ucm -.> merge.old.squash mybuiltin builtin2 - - Nothing changed as a result of the merge. - - 😶 - - builtin2 was already up-to-date with mybuiltin. - -.> history builtin2 - - Note: The most recent namespace hash is immediately below this - message. - - - - □ 1. #i3vp9o9btm (start of history) - -``` -The churn that happened in `mybuiltin` namespace ended up back in the same spot, so the squash merge of that namespace with our original namespace had no effect. - -## Another example - -Let's look at a more interesting example, where the two namespaces have diverged a bit. Here's our starting namespace: - -```unison -x = 1 -``` - -```ucm - ☝️ The namespace .trunk is empty. - -.trunk> add - - ⍟ I've added these definitions: - - x : ##Nat - -.> fork trunk alice - - Done. - -.> fork trunk bob - - Done. - -``` -Alice now does some hacking: - -```unison -radNumber = 348 -bodaciousNumero = 2394 -neatoFun x = x -``` - -```ucm -.alice> add - - ⍟ I've added these definitions: - - bodaciousNumero : ##Nat - neatoFun : x -> x - radNumber : ##Nat - -.alice> rename.term radNumber superRadNumber - - Done. - -.alice> rename.term neatoFun productionReadyId - - Done. - -``` -Meanwhile, Bob does his own hacking: - -```unison -whatIsLove = "?" -babyDon'tHurtMe = ".. Don't hurt me..." -no more = no more -``` - -```ucm -.bob> add - - ⍟ I've added these definitions: - - babyDon'tHurtMe : ##Text - no : more -> r - whatIsLove : ##Text - -``` -At this point, Alice and Bob both have some history beyond what's in trunk: - -```ucm -.> history trunk - - Note: The most recent namespace hash is immediately below this - message. - - - - □ 1. #i52j9fd57b (start of history) - -.> history alice - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #e9jd55555o - - > Moves: - - Original name New name - neatoFun productionReadyId - - ⊙ 2. #l5ocoo2eac - - > Moves: - - Original name New name - radNumber superRadNumber - - ⊙ 3. #i1vq05628n - - + Adds / updates: - - bodaciousNumero neatoFun radNumber - - □ 4. #i52j9fd57b (start of history) - -.> history bob - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #brr4400742 - - + Adds / updates: - - babyDon'tHurtMe no whatIsLove - - □ 2. #i52j9fd57b (start of history) - -``` -Alice then squash merges into `trunk`, as does Bob. It's as if Alice and Bob both made their changes in one single commit. - -```ucm -.> merge.old.squash alice trunk - - Here's what's changed in trunk after the merge: - - Added definitions: - - 1. bodaciousNumero : Nat - 2. productionReadyId : x -> x - 3. superRadNumber : 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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -.> history trunk - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #f9lvm9gd2k - - + Adds / updates: - - bodaciousNumero productionReadyId superRadNumber - - □ 2. #i52j9fd57b (start of history) - -.> merge.old.squash bob trunk - - Here's what's changed in trunk after the merge: - - Added definitions: - - 1. babyDon'tHurtMe : Text - 2. no : more -> r - 3. whatIsLove : Text - - 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... - -.> history trunk - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #dbp78ts6q3 - - + Adds / updates: - - babyDon'tHurtMe no whatIsLove - - ⊙ 2. #f9lvm9gd2k - - + Adds / updates: - - bodaciousNumero productionReadyId superRadNumber - - □ 3. #i52j9fd57b (start of history) - -``` -Since squash merges don't produce any merge nodes, we can `undo` a couple times to get back to our starting state: - -```ucm -.> undo - - Here are the changes I undid - - Name changes: - - Original Changes - 1. bob.babyDon'tHurtMe 2. trunk.babyDon'tHurtMe (added) - - 3. bob.no 4. trunk.no (added) - - 5. bob.whatIsLove 6. trunk.whatIsLove (added) - -.> undo - - Here are the changes I undid - - Name changes: - - Original Changes - 1. alice.bodaciousNumero 2. trunk.bodaciousNumero (added) - - 3. alice.productionReadyId 4. trunk.productionReadyId (added) - - 5. alice.superRadNumber 6. trunk.superRadNumber (added) - -.> history trunk - - Note: The most recent namespace hash is immediately below this - message. - - - - □ 1. #i52j9fd57b (start of history) - -``` -This time, we'll first squash Alice and Bob's changes together before squashing their combined changes into `trunk`. The resulting `trunk` will have just a single entry in it, combining both Alice and Bob's changes: - -```ucm -.> merge.old.squash alice bob - - Here's what's changed in bob after the merge: - - Added definitions: - - 1. bodaciousNumero : Nat - 2. productionReadyId : x -> x - 3. superRadNumber : 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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -.> merge.old.squash bob trunk - - Here's what's changed in trunk after the merge: - - Added definitions: - - 1. babyDon'tHurtMe : Text - 2. bodaciousNumero : Nat - 3. no : more -> r - 4. productionReadyId : x -> x - 5. superRadNumber : Nat - 6. whatIsLove : Text - - 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... - -.> history trunk - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #qtotqgds4i - - + Adds / updates: - - babyDon'tHurtMe bodaciousNumero no productionReadyId - superRadNumber whatIsLove - - □ 2. #i52j9fd57b (start of history) - -``` -So, there you have it. With squashing, you can control the granularity of your history. - -## Throwing out all history - -Another thing we can do is `squash` into an empty namespace. This effectively makes a copy of the namespace, but without any of its history: - -```ucm -.> merge.old.squash alice nohistoryalice - - Here's what's changed in nohistoryalice after the merge: - - Added definitions: - - 1. bodaciousNumero : Nat - 2. productionReadyId : x -> x - 3. superRadNumber : Nat - 4. x : 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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -.> history nohistoryalice - - Note: The most recent namespace hash is immediately below this - message. - - - - □ 1. #1d9haupn3d (start of history) - -``` -There's nothing really special here, `squash src dest` discards `src` history that comes after the LCA of `src` and `dest`, it's just that in the case of an empty namespace, that LCA is the beginning of time (the empty namespace), so all the history of `src` is discarded. - -## Checking for handling of deletes - -This checks to see that squashing correctly preserves deletions: - -```ucm - ☝️ The namespace .delete is empty. - -.delete> builtins.merge - - Done. - -.delete> fork builtin builtin2 - - Done. - -.delete> delete.term.verbose builtin2.Nat.+ - - Name changes: - - Original Changes - 1. builtin.Nat.+ ┐ 2. builtin2.Nat.+ (removed) - 3. builtin2.Nat.+ ┘ - - Tip: You can use `undo` or `reflog` to undo this change. - -.delete> delete.term.verbose builtin2.Nat.* - - Name changes: - - Original Changes - 1. builtin.Nat.* ┐ 2. builtin2.Nat.* (removed) - 3. builtin2.Nat.* ┘ - - Tip: You can use `undo` or `reflog` to undo this change. - -.delete> merge.old.squash builtin2 builtin - - Here's what's changed in builtin after the merge: - - Removed definitions: - - 1. Nat.* : Nat -> Nat -> Nat - 2. Nat.+ : Nat -> Nat -> 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 - can use `undo` or `reflog` to undo the results of this - merge. - - Applying changes from patch... - -.delete> history builtin - - Note: The most recent namespace hash is immediately below this - message. - - ⊙ 1. #dv00hf6vmg - - - Deletes: - - Nat.* Nat.+ - - □ 2. #i3vp9o9btm (start of history) - -``` -Notice that `Nat.+` and `Nat.*` are deleted by the squash, and we see them deleted in one atomic step in the history. - -Just confirming that those two definitions are in fact removed: - -```ucm -.delete> view .delete.builtin.Nat.+ - - ⚠️ - - The following names were not found in the codebase. Check your spelling. - .delete.builtin.Nat.+ - -``` -```ucm -.delete> view .delete.builtin.Nat.* - - ⚠️ - - The following names were not found in the codebase. Check your spelling. - .delete.builtin.Nat.* - -``` -## Caveats - -If you `squash mystuff trunk`, you're discarding any history of `mystuff` and just cons'ing onto the history of `trunk`. Thus, don't expect to be able to `merge trunk mystuff later and get great results. Squashing should only be used when you don't care about the history (and you know others haven't pulled and built on your line of history being discarded, so they don't care about the history either).