Skip to content

Add commandline complete to invoke nushell completions#17941

Open
ian-h-chamberlain wants to merge 7 commits into
nushell:mainfrom
ian-h-chamberlain:feature/commandline-complete
Open

Add commandline complete to invoke nushell completions#17941
ian-h-chamberlain wants to merge 7 commits into
nushell:mainfrom
ian-h-chamberlain:feature/commandline-complete

Conversation

@ian-h-chamberlain
Copy link
Copy Markdown
Contributor

@ian-h-chamberlain ian-h-chamberlain commented Mar 31, 2026

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:

    let completer = NuCompleter::new(Arc::new(engine_state.clone()), Arc::new(stack.clone()));

    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 -> NuStyle is incomplete; the default LS_COLORS are Fixed(_) 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).

    • After some more thorough testing, I think I got this right now?
  • adding impl TryIntoValue for SuggestionKind could 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 or nu-cli instead?

    • I moved this logic into SemanticSuggestion::into_value, avoids locking in any specific JSON representation. Still not 100% sure about my choices for the table format though
  • bikeshed on the name for --detailed(-d) (mirroring describe)? This felt more descriptive than --long (from ps, ls) to me, not sure if there are other builtins with similar flags for examples

Release notes summary - What our users need to know

Add commandline complete command. 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 pressing Tab)
  • './a' | commandline complete --type directory — return directory paths relative to the current directory that start with a
  • '%ls -' | commandline complete --detailed — return all flags the builtin ls command 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

Comment thread tests/repl/test_commandline.rs
Comment thread crates/nu-cli/src/commands/commandline/complete.rs Outdated
@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Mar 31, 2026

This sounds very cool!
/cc @ysthakur

@Juhan280
Copy link
Copy Markdown
Contributor

There should be dedicated flags for the default completions like commandline complete --directory or --path or something similar

Comment thread crates/nu-cli/src/commands/commandline/complete.rs
@ian-h-chamberlain ian-h-chamberlain force-pushed the feature/commandline-complete branch 2 times, most recently from 6c59acc to 7533646 Compare May 12, 2026 07:24
@ian-h-chamberlain ian-h-chamberlain force-pushed the feature/commandline-complete branch from 7533646 to 592b44f Compare May 12, 2026 07:26
@ian-h-chamberlain ian-h-chamberlain force-pushed the feature/commandline-complete branch from e72b9d6 to e415611 Compare May 15, 2026 07:37
@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented May 15, 2026

Github CI is just puking. I kept re-running it and it finally all passed.

@ian-h-chamberlain ian-h-chamberlain marked this pull request as ready for review May 15, 2026 15:52
@ian-h-chamberlain
Copy link
Copy Markdown
Contributor Author

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:

  • Added --type flag which supports completing path, glob, directory

    • path and glob are the same for now (like they are for @completer) so maybe we should just drop glob for now? I wanted to mirror shape names but if Nushell ever adds e.g. **, * as completion options it might make sense to separate these
  • Changed how completion works for strings, by adding the input string contents as a new virtual file so that the span passed into completers is correct. This is also how the top-level completer works.
    Something notable here: this means the output spans are always relative to the input buffer, which I think is the only reasonable behavior. completions: Document filter option and span and display_override fields nushell.github.io#2140 doc change would probably be nice to have here to help explain how these spans work.

    This means that completion implementers may need to do one of the following:

    • reject span and let the existing logic add the correct span
    • offset the span start/end with something like update cells { $in + $offset }.

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.

@fdncred fdncred requested a review from Copilot May 15, 2026 16:21
@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented May 15, 2026

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?

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 complete builtin in nu-cli, including registration in the default CLI context.
  • New IntoValue conversion for SemanticSuggestion (and a derived IntoValue on NuStyle) 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.

Comment thread crates/nu-cli/src/commands/commandline/complete.rs Outdated
Comment thread crates/nu-cli/src/commands/commandline/complete.rs Outdated
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()),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me

Comment thread crates/nu-cli/src/completions/base.rs Outdated
Comment thread crates/nu-color-config/src/nu_style.rs Outdated
Most of these were pretty reasonable suggestions, and one was a pretty
clear potential deadlock which is now covered by an additional test case.
Comment on lines +72 to +92
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));
}
Copy link
Copy Markdown
Contributor

@Juhan280 Juhan280 May 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nitpick - I was wondering if we can do this instead of using map/and_then with if let

Suggested change
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));
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable directly invoking the default path completions

4 participants