Skip to content

Commit

Permalink
feat(cli): names command can search for multiple names
Browse files Browse the repository at this point in the history
* uses a new, more compact 3-column table for names
* fixes insufficient indentation of 3rd column in Unison.Util.Pretty.column3Header
  • Loading branch information
xmbhasin committed Jan 18, 2025
1 parent 14c73f1 commit d4f60a5
Show file tree
Hide file tree
Showing 16 changed files with 430 additions and 160 deletions.
4 changes: 2 additions & 2 deletions lib/unison-pretty-printer/src/Unison/Util/Pretty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -695,8 +695,8 @@ column2UnzippedM bottomPadding left right =
column3sep ::
(LL.ListLike s Char, IsString s) => Pretty s -> [(Pretty s, Pretty s, Pretty s)] -> Pretty s
column3sep sep rows =
let bc = align [(b, sep <> c) | (_, b, c) <- rows]
abc = group <$> align [(a, sep <> bc) | ((a, _, _), bc) <- rows `zip` bc]
let bc = align $ [(b, indent sep c) | (_, b, c) <- rows]
abc = group <$> align [(a, indent sep bc) | ((a, _, _), bc) <- rows `zip` bc]
in lines abc

-- | Creates an aligned table with an arbitrary number of columns separated by `sep`
Expand Down
28 changes: 4 additions & 24 deletions unison-cli/src/Unison/Codebase/Editor/HandleInput.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ import Unison.Codebase.Editor.HandleInput.DeleteBranch (handleDeleteBranch)
import Unison.Codebase.Editor.HandleInput.DeleteNamespace (getEndangeredDependents, handleDeleteNamespace)
import Unison.Codebase.Editor.HandleInput.DeleteProject (handleDeleteProject)
import Unison.Codebase.Editor.HandleInput.Dependents (handleDependents)
import Unison.Codebase.Editor.HandleInput.EditNamespace (handleEditNamespace)
import Unison.Codebase.Editor.HandleInput.EditDependents (handleEditDependents)
import Unison.Codebase.Editor.HandleInput.EditNamespace (handleEditNamespace)
import Unison.Codebase.Editor.HandleInput.FindAndReplace (handleStructuredFindI, handleStructuredFindReplaceI, handleTextFindI)
import Unison.Codebase.Editor.HandleInput.FormatFile qualified as Format
import Unison.Codebase.Editor.HandleInput.Global qualified as Global
Expand All @@ -73,6 +73,7 @@ import Unison.Codebase.Editor.HandleInput.MoveAll (handleMoveAll)
import Unison.Codebase.Editor.HandleInput.MoveBranch (doMoveBranch)
import Unison.Codebase.Editor.HandleInput.MoveTerm (doMoveTerm)
import Unison.Codebase.Editor.HandleInput.MoveType (doMoveType)
import Unison.Codebase.Editor.HandleInput.Names (handleNames)
import Unison.Codebase.Editor.HandleInput.NamespaceDependencies (handleNamespaceDependencies)
import Unison.Codebase.Editor.HandleInput.NamespaceDiffUtils (diffHelper)
import Unison.Codebase.Editor.HandleInput.ProjectClone (handleClone)
Expand Down Expand Up @@ -497,29 +498,8 @@ loop e = do

fixupOutput :: Path.HQSplit -> HQ.HashQualified Name
fixupOutput = HQ'.toHQ . Path.nameFromHQSplit
NamesI global query -> do
hqLength <- Cli.runTransaction Codebase.hashLength
let searchNames names = do
let pped = PPED.makePPED (PPE.hqNamer 10 names) (PPE.suffixifyByHash names)
unsuffixifiedPPE = PPED.unsuffixifiedPPE pped
terms = Names.lookupHQTerm Names.IncludeSuffixes query names
types = Names.lookupHQType Names.IncludeSuffixes query names
terms' :: [(Referent, [HQ'.HashQualified Name])]
terms' = map (\r -> (r, PPE.allTermNames unsuffixifiedPPE r)) (Set.toList terms)
types' :: [(Reference, [HQ'.HashQualified Name])]
types' = map (\r -> (r, PPE.allTypeNames unsuffixifiedPPE r)) (Set.toList types)
pure (terms', types')
if global
then do
Global.forAllProjectBranches \(projBranchNames, _ids) branch -> do
let names = Branch.toNames . Branch.head $ branch
(terms, types) <- searchNames names
when (not (null terms) || not (null types)) do
Cli.respond $ GlobalListNames projBranchNames hqLength types terms
else do
names <- Cli.currentNames
(terms, types) <- searchNames names
Cli.respond $ ListNames hqLength types terms
NamesI global queries -> do
mapM_ (handleNames global) queries
DocsI srcs -> do
for_ srcs docsI
CreateAuthorI authorNameSegment authorFullName -> do
Expand Down
70 changes: 70 additions & 0 deletions unison-cli/src/Unison/Codebase/Editor/HandleInput/Names.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module Unison.Codebase.Editor.HandleInput.Names (handleNames) where

import Control.Monad (when)
import Data.Set qualified as Set
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.NamesUtils 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.HandleInput.Global qualified as Global
import Unison.Codebase.Editor.Input (ErrorMessageOrName, RawQuery)
import Unison.Codebase.Editor.Output (Output (..))
import Unison.HashQualifiedPrime qualified as HQ'
import Unison.Name (Name)
import Unison.NamesWithHistory qualified as Names
import Unison.PrettyPrintEnv qualified as PPE
import Unison.PrettyPrintEnv.Names qualified as PPE
import Unison.PrettyPrintEnvDecl qualified as PPED
import Unison.PrettyPrintEnvDecl.Names qualified as PPED
import Unison.Reference (Reference)
import Unison.Referent (Referent)
import Unison.Util.Pretty qualified as P

-- | Handles a single @NamesI@ input query returning terms that match a given name.
--
-- Parameters:
--
-- * @global :: Bool@
-- ** If @True@, search all projects and branches.
-- ** If @False@, search only the current branch.
--
-- * @query :: (RawQuery, ErrorMessageOrName)@
-- ** The first member is the raw @nameQuery@ being handled.
-- ** The second member is the parsed @nameQuery@ that is either an error message
-- to be printed or a name that can be looked up in the codebase.
handleNames ::
Bool ->
(RawQuery, ErrorMessageOrName) ->
Cli ()
handleNames _ (nameQuery, Left errMsg) = do
Cli.respond $
PrintMessage $
P.lines [prettyNameQuery, errMsg]
where
prettyNameQuery =
P.red (P.bold $ P.string nameQuery) <> ":"
handleNames global (nameQuery, Right query) = do
hqLength <- Cli.runTransaction Codebase.hashLength
let searchNames names = do
let pped = PPED.makePPED (PPE.hqNamer 10 names) (PPE.suffixifyByHash names)
unsuffixifiedPPE = PPED.unsuffixifiedPPE pped
terms = Names.lookupHQTerm Names.IncludeSuffixes query names
types = Names.lookupHQType Names.IncludeSuffixes query names
terms' :: [(Referent, [HQ'.HashQualified Name])]
terms' = map (\r -> (r, PPE.allTermNames unsuffixifiedPPE r)) (Set.toList terms)
types' :: [(Reference, [HQ'.HashQualified Name])]
types' = map (\r -> (r, PPE.allTypeNames unsuffixifiedPPE r)) (Set.toList types)
pure (terms', types')
if global
then do
Global.forAllProjectBranches \(projBranchNames, _ids) branch -> do
let names = Branch.toNames . Branch.head $ branch
(terms, types) <- searchNames names
when (not (null terms) || not (null types)) do
Cli.respond $ GlobalListNames nameQuery projBranchNames hqLength types terms
else do
names <- Cli.currentNames
(terms, types) <- searchNames names
Cli.respond $ ListNames nameQuery hqLength types terms
13 changes: 12 additions & 1 deletion unison-cli/src/Unison/Codebase/Editor/Input.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ module Unison.Codebase.Editor.Input
IsGlobal,
DeleteOutput (..),
DeleteTarget (..),

-- * Type aliases
ErrorMessageOrName,
RawQuery,
)
where

Expand Down Expand Up @@ -61,6 +65,12 @@ type SourceName = Text -- "foo.u" or "buffer 7"

type PatchPath = Path.Split'

type ErrorMessageOrValue a = Either (P.Pretty P.ColorText) a

type ErrorMessageOrName = ErrorMessageOrValue (HQ.HashQualified Name)

type RawQuery = String

data OptionalPatch = NoPatch | DefaultPatch | UsePatch PatchPath
deriving (Eq, Ord, Show)

Expand Down Expand Up @@ -141,7 +151,8 @@ data Input
-- > names .foo.bar
-- > names .foo.bar#asdflkjsdf
-- > names #sdflkjsdfhsdf
NamesI IsGlobal (HQ.HashQualified Name)
-- > names foo.bar foo.baz #sdflkjsdfhsdf
NamesI IsGlobal [(RawQuery, ErrorMessageOrName)]
| AliasTermI !Bool HashOrHQSplit' Path.Split' -- bool = force?
| AliasTypeI !Bool HashOrHQSplit' Path.Split' -- bool = force?
| AliasManyI [Path.HQSplit] Path'
Expand Down
4 changes: 3 additions & 1 deletion unison-cli/src/Unison/Codebase/Editor/Output.hs
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,12 @@ data Output
| MovedOverExistingBranch Path'
| DeletedEverything
| ListNames
String -- input namesQuery for which this output is being produced
Int -- hq length to print References
[(Reference, [HQ'.HashQualified Name])] -- type match, type names
[(Referent, [HQ'.HashQualified Name])] -- term match, term names
| GlobalListNames
String -- input namesQuery for which this output is being produced
(ProjectAndBranch ProjectName ProjectBranchName)
Int -- hq length to print References
[(Reference, [HQ'.HashQualified Name])] -- type match, type names
Expand Down Expand Up @@ -547,7 +549,7 @@ isFailure o = case o of
MoveRootBranchConfirmation -> False
MovedOverExistingBranch {} -> False
DeletedEverything -> False
ListNames _ tys tms -> null tms && null tys
ListNames _ _ tys tms -> null tms && null tys
GlobalListNames {} -> False
ListOfDefinitions _ _ _ ds -> null ds
GlobalFindBranchResults _ _ _ _ -> False
Expand Down
34 changes: 26 additions & 8 deletions unison-cli/src/Unison/CommandLine/InputPatterns.hs
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,13 @@ formatStructuredArgument schLength = \case
else "." <> s
pathArgStr = Text.pack $ show pathArg

-- | Converts an arbitrary argument to a `String`. This is for cases where the
-- | Converts an arbitrary argument to a `String`.
--
-- This is for cases where the
-- command /should/ accept a structured argument of some type, but currently
-- wants a `String`.
--
-- This can also be used where the input argument needs to be included in the output.
unifyArgument :: I.Argument -> String
unifyArgument = either id (Text.unpack . formatStructuredArgument Nothing)

Expand Down Expand Up @@ -2687,16 +2691,30 @@ names isGlobal =
cmdName
[]
I.Visible
[("name or hash", Required, definitionQueryArg)]
(P.wrap $ makeExample (names isGlobal) ["foo"] <> description)
[("name or hash", OnePlus, definitionQueryArg)]
description
$ \case
[thing] -> Input.NamesI isGlobal <$> handleHashQualifiedNameArg thing
args -> wrongArgsLength "exactly one argument" args
[] -> wrongArgsLength "at least one argument" []
[rawArg] -> do
let arg = handleArg rawArg
case arg of
(_, Left errMsg) -> Left errMsg
(argString, Right name) -> pure $ Input.NamesI isGlobal [(argString, Right name)]
rawArgs -> do
let args = handleArg <$> rawArgs
pure $ Input.NamesI isGlobal args
where
description
| isGlobal = "Iteratively search across all projects and branches for names matching `foo`. Note that this is expected to be quite slow and is primarily for debugging issues with your codebase."
| otherwise = "List all known names for `foo` in the current branch."
isGlobalPreamble = "Iteratively search names or hashes across all projects and branches."
isNotGlobalPreamble = "Search names or hashes in the current branch."
cmdName = if isGlobal then "debug.names.global" else "names"
description =
P.lines
[ if isGlobal then isGlobalPreamble else isNotGlobalPreamble,
P.wrap $ makeExample (names isGlobal) ["foo"] <> "List all known names for `foo`.",
P.wrap $ makeExample (names isGlobal) ["foo", "#bar"] <> "List all known names for the name `foo` and for the hash `#bar`.",
P.wrap $ makeExample (names isGlobal) [] <> "without arguments invokes a search to select names/hashes to list, which requires that `fzf` can be found within your PATH."
]
handleArg arg = (unifyArgument arg, handleHashQualifiedNameArg arg)

dependents, dependencies :: InputPattern
dependents =
Expand Down
Loading

0 comments on commit d4f60a5

Please sign in to comment.