Add commandline complete to invoke nushell completions#17941
Add commandline complete to invoke nushell completions#17941ian-h-chamberlain wants to merge 7 commits into
commandline complete to invoke nushell completions#17941Conversation
|
This sounds very cool! |
|
There should be dedicated flags for the default completions like |
6c59acc to
7533646
Compare
7533646 to
592b44f
Compare
e72b9d6 to
e415611
Compare
|
Github CI is just puking. I kept re-running it and it finally all passed. |
Awesome, thank you for looking into that! I noticed it failed last night and decided to go to bed instead of investigating further 😄 With that, I think this PR is finally in a state where it's ready for review — some info about the change since I first pushed:
I tried to add as much testing around the span logic as I could but found it a little tricky, so if you see ways to improve those tests please let me know! To some extend I suspect this command needs a bit of manual testing in the wild to see what issues crop up. |
|
Thanks. Let's see what copilot thinks. Can you take a look at what copilot is saying and see if it makes since to implement it's changes? |
There was a problem hiding this comment.
Pull request overview
Adds a new commandline complete command that exposes Nushell's built-in completer to scripts and custom completers. It accepts the current REPL buffer or a piped string, optionally restricts results by completion type (directory/path/glob), and can return either a list of strings or detailed records (via --detailed) compatible with the custom-completion record format.
Changes:
- New
commandline completebuiltin innu-cli, including registration in the default CLI context. - New
IntoValueconversion forSemanticSuggestion(and a derivedIntoValueonNuStyle) so completion results can round-trip into Nu records. - Tests for the new command (REPL + completion integration tests covering wrapping the builtin from custom and external completers).
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/nu-cli/src/commands/commandline/complete.rs | New commandline complete command implementation. |
| crates/nu-cli/src/commands/commandline/mod.rs | Re-exports the new CommandlineComplete command. |
| crates/nu-cli/src/commands/mod.rs | Re-exports CommandlineComplete from the commands module. |
| crates/nu-cli/src/commands/default_context.rs | Registers CommandlineComplete in the CLI context. |
| crates/nu-cli/src/completions/base.rs | Adds IntoValue impl for SemanticSuggestion and helper for span records. |
| crates/nu-cli/src/completions/mod.rs | Re-exports Context for crate-internal use by the new command. |
| crates/nu-color-config/src/nu_style.rs | Derives IntoValue on NuStyle; minor TODO note for Color::Fixed. |
| crates/nu-cli/tests/completions/mod.rs | Adds tests wrapping commandline complete from custom/external completers; sets REPL buffer in helper. |
| crates/nu-cli/tests/completions/support/completions_helpers.rs | Adds CLI context to test engine; #[track_caller] on suggestion matchers. |
| tests/repl/test_commandline.rs | Adds REPL tests for commandline complete (input, flags, detailed, errors). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let repl = engine_state.repl_state.lock().expect("repl state mutex"); | ||
| let (buffer, cursor_pos) = match &input { | ||
| PipelineData::Empty => (repl.buffer.as_str(), repl.cursor_pos), | ||
| PipelineData::Value(Value::String { val, .. }, _) => (val.as_str(), val.len()), |
There was a problem hiding this comment.
Explicit --cursor positioning feels like a pretty niche use case, and should be possible to add later if there's demand for it. For now I think keeping this simpler makes sense, so I'll add a sentence about this to the extra_description.
Most of these were pretty reasonable suggestions, and one was a pretty clear potential deadlock which is now covered by an additional test case.
| if let Some(kind) = self.kind.as_ref().map(|kind| match kind { | ||
| SuggestionKind::Command(..) => "command", | ||
| SuggestionKind::Value(_) => "value", | ||
| SuggestionKind::CellPath => "cell-path", | ||
| SuggestionKind::Directory => "directory", | ||
| SuggestionKind::File => "file", | ||
| SuggestionKind::Flag => "flag", | ||
| SuggestionKind::Module => "module", | ||
| SuggestionKind::Operator => "operator", | ||
| SuggestionKind::Variable => "variable", | ||
| }) { | ||
| record.insert("kind", kind.into_value(span)); | ||
| } | ||
|
|
||
| if let Some(ty) = self.kind.as_ref().and_then(|kind| match kind { | ||
| SuggestionKind::Command(ty, _) => Some(ty.to_string()), | ||
| SuggestionKind::Value(ty) => Some(ty.to_string()), | ||
| _ => None, | ||
| }) { | ||
| record.insert("type", ty.into_value(span)); | ||
| } |
There was a problem hiding this comment.
Small nitpick - I was wondering if we can do this instead of using map/and_then with if let
| if let Some(kind) = self.kind.as_ref().map(|kind| match kind { | |
| SuggestionKind::Command(..) => "command", | |
| SuggestionKind::Value(_) => "value", | |
| SuggestionKind::CellPath => "cell-path", | |
| SuggestionKind::Directory => "directory", | |
| SuggestionKind::File => "file", | |
| SuggestionKind::Flag => "flag", | |
| SuggestionKind::Module => "module", | |
| SuggestionKind::Operator => "operator", | |
| SuggestionKind::Variable => "variable", | |
| }) { | |
| record.insert("kind", kind.into_value(span)); | |
| } | |
| if let Some(ty) = self.kind.as_ref().and_then(|kind| match kind { | |
| SuggestionKind::Command(ty, _) => Some(ty.to_string()), | |
| SuggestionKind::Value(ty) => Some(ty.to_string()), | |
| _ => None, | |
| }) { | |
| record.insert("type", ty.into_value(span)); | |
| } | |
| if let Some(kind) = &self.kind { | |
| let (kind_str, ty) = match kind { | |
| SuggestionKind::Command(ty, _) => ("command", Some(ty.to_string())), | |
| SuggestionKind::Value(ty) => ("value", Some(ty.to_string())), | |
| SuggestionKind::CellPath => ("cell-path", None), | |
| SuggestionKind::Directory => ("directory", None), | |
| SuggestionKind::File => ("file", None), | |
| SuggestionKind::Flag => ("flag", None), | |
| SuggestionKind::Module => ("module", None), | |
| SuggestionKind::Operator => ("operator", None), | |
| SuggestionKind::Variable => ("variable", None), | |
| }; | |
| record.insert("kind", kind_str.into_value(span)); | |
| if let Some(ty) = ty { | |
| record.insert("type", ty.into_value(span)); | |
| } | |
| } |
Closes #16951
There are a couple things that would be nice to have, but I think this PR stands on its own without them. I can probably do some followup PRs if they seem desirable:
This line should probably not clone the world just to run this command:
It definitely is possible to make a
NuCompleterRef<'_>type or something to avoid this, but it would be a bigger refactor and this PR already feels big, so I intend to do that as a followup.Color conversion back from
Style->NuStyleis incomplete; the defaultLS_COLORSareFixed(_)so they don't convert cleanly back to strings yet. This means file completions won't roundtrip properly when passed into a completer, but it should be easy to make that work.Questions:
Am I using spans appropriately here? Particularly converting between reedline span + nushell span, I'm not sure what the "source" for these completions really should be (or if it needs to be offset according to the cursor position).addingimpl TryIntoValue for SuggestionKindcould be a breaking change, and I'm not sure it's the right semantics either... should I mvoe this into a custom wrapper in the command itself ornu-cliinstead?SemanticSuggestion::into_value, avoids locking in any specific JSON representation. Still not 100% sure about my choices for the table format thoughbikeshed on the name for
--detailed(-d)(mirroringdescribe)? This felt more descriptive than--long(fromps,ls) to me, not sure if there are other builtins with similar flags for examplesRelease notes summary - What our users need to know
Add
commandline completecommand. This can be used to obtain the suggestions that nushell itself would normally suggest. Some examples:commandline complete— return completions based on the current commandline contents (i.e. what would be suggested when pressingTab)'./a' | commandline complete --type directory— return directory paths relative to the current directory that start witha'%ls -' | commandline complete --detailed— return all flags the builtinlscommand accepts, including their descriptions.These completions can be used in custom command completions, for example to wrap a builtin command with additional options, or to obtain a list of suggested paths for further filtering or processing.
Tasks after submitting