From aeebfe3d53ddc1ddcccb2409957546cc2673ea2a Mon Sep 17 00:00:00 2001 From: Arya Irani Date: Mon, 2 Mar 2020 11:18:34 +0100 Subject: [PATCH 1/5] implements the `back` command using a stack of currentNamespace --- .../src/Unison/Codebase/Editor/HandleInput.hs | 19 ++++++++++++++++--- .../src/Unison/Codebase/Editor/Input.hs | 1 + .../src/Unison/Codebase/Editor/Output.hs | 2 ++ .../src/Unison/Codebase/TranscriptParser.hs | 3 ++- .../src/Unison/CommandLine/InputPatterns.hs | 12 ++++++++++++ .../src/Unison/CommandLine/Main.hs | 3 ++- .../src/Unison/CommandLine/OutputMessages.hs | 3 ++- 7 files changed, 37 insertions(+), 6 deletions(-) diff --git a/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs b/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs index b6c706b55..2698e6a6e 100644 --- a/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs +++ b/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs @@ -16,7 +16,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE EmptyCase #-} -module Unison.Codebase.Editor.HandleInput (loop, loopState0, LoopState(..), parseSearchType) where +module Unison.Codebase.Editor.HandleInput (loop, loopState0, LoopState(..), currentPath, parseSearchType) where import Unison.Prelude @@ -126,6 +126,8 @@ import qualified Unison.CommandLine.DisplayValues as DisplayValues import qualified Control.Error.Util as ErrorUtil import Unison.Codebase.GitError (GitError) import Unison.Util.Monoid (intercalateMap) +import Data.List.NonEmpty (NonEmpty) +import qualified Data.List.NonEmpty as Nel type F m i v = Free (Command m i v) type Term v a = Term.AnnotatedTerm v a @@ -140,7 +142,7 @@ data LoopState m v = LoopState { _root :: Branch m -- the current position in the namespace - , _currentPath :: Path.Absolute + , _currentPathStack :: NonEmpty Path.Absolute -- TBD -- , _activeEdits :: Set Branch.EditGuid @@ -166,8 +168,14 @@ type InputDescription = Text makeLenses ''LoopState +-- replacing the old read/write scalar lens with peek/push lens +peekPushNel :: Lens (NonEmpty Path.Absolute) (NonEmpty Path.Absolute) Path.Absolute Path.Absolute +peekPushNel = lens Nel.head (flip Nel.cons) +currentPath :: Lens (LoopState m v) (LoopState m v) Path.Absolute Path.Absolute +currentPath = currentPathStack . peekPushNel + loopState0 :: Branch m -> Path.Absolute -> LoopState m v -loopState0 b p = LoopState b p Nothing Nothing Nothing [] +loopState0 b p = LoopState b (pure p) Nothing Nothing Nothing [] type Action' m v = Action m (Either Event Input) v @@ -349,6 +357,7 @@ loop = do PreviewMergeLocalBranchI{} -> wat DiffNamespaceI{} -> wat SwitchBranchI{} -> wat + PopBranchI{} -> wat NamesI{} -> wat TodoI{} -> wat ListEditsI{} -> wat @@ -682,6 +691,10 @@ loop = do branch' <- getAt path when (Branch.isEmpty branch') (respond $ CreatedNewBranch path) + PopBranchI -> use (currentPathStack . to Nel.uncons) >>= \case + (_, Nothing) -> respond StartOfCurrentPathHistory + (_, Just t) -> currentPathStack .= t + HistoryI resultsCap diffCap from -> case from of Left hash -> resolveShortBranchHash hash >>= \case Left output -> respond output diff --git a/parser-typechecker/src/Unison/Codebase/Editor/Input.hs b/parser-typechecker/src/Unison/Codebase/Editor/Input.hs index 765ede4ff..868fc7845 100644 --- a/parser-typechecker/src/Unison/Codebase/Editor/Input.hs +++ b/parser-typechecker/src/Unison/Codebase/Editor/Input.hs @@ -54,6 +54,7 @@ data Input -- Does it make sense to fork from not-the-root of a Github repo? -- change directory | SwitchBranchI Path' + | PopBranchI -- > names foo -- > names foo.bar -- > names .foo.bar diff --git a/parser-typechecker/src/Unison/Codebase/Editor/Output.hs b/parser-typechecker/src/Unison/Codebase/Editor/Output.hs index 2e625e420..ec3306b17 100644 --- a/parser-typechecker/src/Unison/Codebase/Editor/Output.hs +++ b/parser-typechecker/src/Unison/Codebase/Editor/Output.hs @@ -181,6 +181,7 @@ data Output v | PatchNeedsToBeConflictFree | PatchInvolvesExternalDependents PPE.PrettyPrintEnv (Set Reference) | WarnIncomingRootBranch (Set ShortBranchHash) + | StartOfCurrentPathHistory | History (Maybe Int) [(ShortBranchHash, Names.Diff)] HistoryTail | ShowReflog [ReflogEntry] | PullAlreadyUpToDate RemoteNamespace Path' @@ -305,6 +306,7 @@ isFailure o = case o of NothingToPatch{} -> False WarnIncomingRootBranch{} -> False History{} -> False + StartOfCurrentPathHistory -> True NotImplemented -> True DumpNumberedArgs{} -> False DumpBitBooster{} -> False diff --git a/parser-typechecker/src/Unison/Codebase/TranscriptParser.hs b/parser-typechecker/src/Unison/Codebase/TranscriptParser.hs index 2a36ea78e..0d90bc307 100644 --- a/parser-typechecker/src/Unison/Codebase/TranscriptParser.hs +++ b/parser-typechecker/src/Unison/Codebase/TranscriptParser.hs @@ -44,6 +44,7 @@ import qualified Unison.Runtime.Rt1IO as Rt1 import qualified Unison.Util.Pretty as P import qualified Unison.Util.TQueue as Q import qualified Unison.Codebase.Editor.Output as Output +import Control.Lens (view) type ExpectingError = Bool data Hidden = Shown | HideOutput | HideAll @@ -254,7 +255,7 @@ run dir configFile stanzas codebase = do "Run `ucm -codebase " <> Text.pack dir <> "` " <> "to do more work with it."] loop state = do - writeIORef pathRef (HandleInput._currentPath state) + writeIORef pathRef (view HandleInput.currentPath state) let free = runStateT (runMaybeT HandleInput.loop) state rng i = pure $ Random.drgNewSeed (Random.seedFromInteger (fromIntegral i)) (o, state') <- HandleCommand.commandLine config awaitInput diff --git a/parser-typechecker/src/Unison/CommandLine/InputPatterns.hs b/parser-typechecker/src/Unison/CommandLine/InputPatterns.hs index b12020487..87dbabb0e 100644 --- a/parser-typechecker/src/Unison/CommandLine/InputPatterns.hs +++ b/parser-typechecker/src/Unison/CommandLine/InputPatterns.hs @@ -474,6 +474,17 @@ cd = InputPattern "namespace" ["cd", "j"] [(Required, pathArg)] _ -> Left (I.help cd) ) +back :: InputPattern +back = InputPattern "back" ["popd"] [] + (P.wrapColumn2 + [ (makeExample back [], + "undoes the last" <> makeExample' cd <> "command.") + ]) + (\case + [] -> pure Input.PopBranchI + _ -> Left (I.help cd) + ) + deleteBranch :: InputPattern deleteBranch = InputPattern "delete.namespace" [] [(Required, pathArg)] "`delete.namespace ` deletes the namespace `foo`" @@ -1136,6 +1147,7 @@ validInputs = , createPullRequest , loadPullRequest , cd + , back , deleteBranch , renameBranch , deletePatch diff --git a/parser-typechecker/src/Unison/CommandLine/Main.hs b/parser-typechecker/src/Unison/CommandLine/Main.hs index aaa9e5afa..3d1a04e63 100644 --- a/parser-typechecker/src/Unison/CommandLine/Main.hs +++ b/parser-typechecker/src/Unison/CommandLine/Main.hs @@ -45,6 +45,7 @@ import qualified Unison.CommandLine.InputPattern as IP import qualified Unison.Util.Pretty as P import qualified Unison.Util.TQueue as Q import Text.Regex.TDFA +import Control.Lens (view) -- Expand a numeric argument like `1` or a range like `3-9` expandNumber :: [String] -> String -> [String] @@ -232,7 +233,7 @@ main dir initialPath configFile initialInputs startRuntime codebase = do cancelFileSystemWatch cancelWatchBranchUpdates loop state = do - writeIORef pathRef (HandleInput._currentPath state) + writeIORef pathRef (view HandleInput.currentPath state) let free = runStateT (runMaybeT HandleInput.loop) state (o, state') <- HandleCommand.commandLine config awaitInput diff --git a/parser-typechecker/src/Unison/CommandLine/OutputMessages.hs b/parser-typechecker/src/Unison/CommandLine/OutputMessages.hs index 3903741cf..cdac1f6af 100644 --- a/parser-typechecker/src/Unison/CommandLine/OutputMessages.hs +++ b/parser-typechecker/src/Unison/CommandLine/OutputMessages.hs @@ -873,7 +873,8 @@ notifyUser dir o = case o of ] ex = "Use" <> IP.makeExample IP.history ["#som3n4m3space"] <> "to view history starting from a given namespace hash." - + StartOfCurrentPathHistory -> pure $ + P.wrap "You're already at the very beginning! 🙂" PullAlreadyUpToDate ns dest -> pure . P.callout "😶" $ P.wrap $ prettyPath' dest <> "was already up-to-date with" <> P.group (prettyRemoteNamespace ns <> ".") From 3846bf7da978590e90d562c5d89d5751a30b1c7b Mon Sep 17 00:00:00 2001 From: Arya Irani Date: Mon, 2 Mar 2020 11:34:35 +0100 Subject: [PATCH 2/5] move peekPushNel to a where clause --- .../src/Unison/Codebase/Editor/HandleInput.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs b/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs index 2698e6a6e..2dd2ff72a 100644 --- a/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs +++ b/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs @@ -169,10 +169,10 @@ type InputDescription = Text makeLenses ''LoopState -- replacing the old read/write scalar lens with peek/push lens -peekPushNel :: Lens (NonEmpty Path.Absolute) (NonEmpty Path.Absolute) Path.Absolute Path.Absolute -peekPushNel = lens Nel.head (flip Nel.cons) currentPath :: Lens (LoopState m v) (LoopState m v) Path.Absolute Path.Absolute -currentPath = currentPathStack . peekPushNel +currentPath = currentPathStack . peekPushNel where + peekPushNel :: Lens (NonEmpty a) (NonEmpty a) a a + peekPushNel = lens Nel.head (flip Nel.cons) loopState0 :: Branch m -> Path.Absolute -> LoopState m v loopState0 b p = LoopState b (pure p) Nothing Nothing Nothing [] From 7b36b19283e353605d60efeb783693ae5337922c Mon Sep 17 00:00:00 2001 From: Arya Irani Date: Mon, 2 Mar 2020 12:12:58 +0100 Subject: [PATCH 3/5] change `currentPath` from a Lens to a Getter --- .../src/Unison/Codebase/Editor/HandleInput.hs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs b/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs index 2dd2ff72a..9618dfd69 100644 --- a/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs +++ b/parser-typechecker/src/Unison/Codebase/Editor/HandleInput.hs @@ -168,11 +168,9 @@ type InputDescription = Text makeLenses ''LoopState --- replacing the old read/write scalar lens with peek/push lens -currentPath :: Lens (LoopState m v) (LoopState m v) Path.Absolute Path.Absolute -currentPath = currentPathStack . peekPushNel where - peekPushNel :: Lens (NonEmpty a) (NonEmpty a) a a - peekPushNel = lens Nel.head (flip Nel.cons) +-- replacing the old read/write scalar Lens with "peek" Getter for the NonEmpty +currentPath :: Getter (LoopState m v) Path.Absolute +currentPath = currentPathStack . to Nel.head loopState0 :: Branch m -> Path.Absolute -> LoopState m v loopState0 b p = LoopState b (pure p) Nothing Nothing Nothing [] @@ -687,7 +685,7 @@ loop = do SwitchBranchI path' -> do let path = resolveToAbsolute path' - currentPath .= path + currentPathStack %= Nel.cons path branch' <- getAt path when (Branch.isEmpty branch') (respond $ CreatedNewBranch path) From 0af4df95b810232df63850190d4fe470ee2ac871 Mon Sep 17 00:00:00 2001 From: Arya Irani Date: Mon, 2 Mar 2020 18:50:49 +0100 Subject: [PATCH 4/5] add transcripts --- unison-src/transcripts/cd-back.md | 44 ++++++++++++++++++++++++ unison-src/transcripts/cd-back.output.md | 19 ++++++++++ 2 files changed, 63 insertions(+) create mode 100644 unison-src/transcripts/cd-back.md create mode 100644 unison-src/transcripts/cd-back.output.md diff --git a/unison-src/transcripts/cd-back.md b/unison-src/transcripts/cd-back.md new file mode 100644 index 000000000..20fb5c1cb --- /dev/null +++ b/unison-src/transcripts/cd-back.md @@ -0,0 +1,44 @@ +## Switching between namespaces / projects / branches / modules + +Unison uses the same organizational element to represent directories, projects, sub-projects, forks, modules, etc.; currently called a "namespace". + +Namespaces are trees that contain definitions of "types" and "terms", "patches", and other child namespaces. + +We're still working out what a nice codebase layout might be (feel free to write up a blog post if you find one that works well for you), but in this example, we have these, along with their children (not shown): + +> .libs.base +> .libs.megaparser.master +> .libs.megaparser.v1 +> .libs.megaparser.v2 +> .arya.base +> .arya.myproject +> .pullrequests.runarorama.base_3.base +> .pullrequests.runarorama.base_3.head +> .pullrequests.runarorama.base_3.merged +> .temp + +```ucm:hide +.> builtins.merge +.> move.namespace builtin .arya.base +``` + +```ucm +.> cd arya.base +.arya.base> find Boolean +.arya.base> cd arya.myproject +``` + +blah blah blah more stuff about project management and patches and the value of working from the appropriate namespace, and what that is in any given case + +We can pop back to the previous namespace with the `back` command. + +```ucm +.arya.myproject> back +``` +```ucm +.arya.base> back +``` +```ucm:error +.> back +``` +😬 Right, ok. diff --git a/unison-src/transcripts/cd-back.output.md b/unison-src/transcripts/cd-back.output.md new file mode 100644 index 000000000..8f9c4d67a --- /dev/null +++ b/unison-src/transcripts/cd-back.output.md @@ -0,0 +1,19 @@ +## Switching between namespaces / projects / branches / modules + +Unison uses the same organizational element to represent directories, projects, sub-projects, forks, modules, etc.; currently called a "namespace". + +Namespaces are trees that contain definitions of "types" and "terms", "patches", and other child namespaces. + +We're still working out what a nice codebase layout might be (feel free to write up a blog post if you find one that works well for you), but in this example, we have these, along with their children (not shown): + +> .libs.base +> .libs.megaparser.master +> .libs.megaparser.v1 +> .libs.megaparser.v2 +> .arya.base +> .arya.myproject +> .pullrequests.runarorama.base_3.base +> .pullrequests.runarorama.base_3.head +> .pullrequests.runarorama.base_3.merged +> .temp + From a252a6eafccfb3f52384122852389f61b46f097c Mon Sep 17 00:00:00 2001 From: Arya Irani Date: Mon, 2 Mar 2020 19:49:37 +0100 Subject: [PATCH 5/5] add transcripts --- unison-src/transcripts/cd-back.md | 8 +++++--- unison-src/transcripts/cd-back.output.md | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/unison-src/transcripts/cd-back.md b/unison-src/transcripts/cd-back.md index 20fb5c1cb..7a7df60eb 100644 --- a/unison-src/transcripts/cd-back.md +++ b/unison-src/transcripts/cd-back.md @@ -25,17 +25,19 @@ We're still working out what a nice codebase layout might be (feel free to write ```ucm .> cd arya.base .arya.base> find Boolean -.arya.base> cd arya.myproject +``` +```ucm:hide +.arya.base> cd .arya.myproject ``` blah blah blah more stuff about project management and patches and the value of working from the appropriate namespace, and what that is in any given case We can pop back to the previous namespace with the `back` command. -```ucm +```ucm:hide .arya.myproject> back ``` -```ucm +```ucm:hide .arya.base> back ``` ```ucm:error diff --git a/unison-src/transcripts/cd-back.output.md b/unison-src/transcripts/cd-back.output.md index 8f9c4d67a..9b89c2335 100644 --- a/unison-src/transcripts/cd-back.output.md +++ b/unison-src/transcripts/cd-back.output.md @@ -17,3 +17,24 @@ We're still working out what a nice codebase layout might be (feel free to write > .pullrequests.runarorama.base_3.merged > .temp +```ucm +.> cd arya.base + +.arya.base> find Boolean + + 1. builtin type Boolean + 2. Boolean.not : Boolean -> Boolean + + +``` +blah blah blah more stuff about project management and patches and the value of working from the appropriate namespace, and what that is in any given case + +We can pop back to the previous namespace with the `back` command. + +```ucm +.> back + + You're already at the very beginning! 🙂 + +``` +😬 Right, ok.