From b96044c2692cbb38c8c2202ffde98c31c37693a4 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Fri, 21 Jun 2024 15:37:56 -0400 Subject: [PATCH 1/4] 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 2/4] 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 e1b00d9c58af8f0806613258145557adad52b182 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Wed, 26 Jun 2024 11:23:04 -0400 Subject: [PATCH 3/4] 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 3556cb6ec8d226eb9dd5a8585d0f7d2be372dd46 Mon Sep 17 00:00:00 2001 From: etorreborre Date: Wed, 26 Jun 2024 17:34:45 +0200 Subject: [PATCH 4/4] 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