Skip to content

Commit 253a894

Browse files
committed
fix(completion): file completion for redirection target
1 parent 5c251ea commit 253a894

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

crates/nu-cli/src/completions/completer.rs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
88
use nu_parser::{parse, parse_module_file_or_dir};
99
use nu_protocol::{
1010
CommandWideCompleter, Completion, Span, Type, Value,
11-
ast::{Argument, Block, Expr, Expression, FindMapResult, ListItem, Traverse},
11+
ast::{
12+
Argument, Block, Expr, Expression, FindMapResult, ListItem, PipelineRedirection,
13+
RedirectionTarget, Traverse,
14+
},
1215
engine::{EngineState, Stack, StateWorkingSet},
1316
};
1417
use reedline::{Completer as ReedlineCompleter, Suggestion};
@@ -78,6 +81,21 @@ fn find_pipeline_element_by_position<'a>(
7881
}
7982
}
8083

84+
/// For redirection target completion
85+
/// https://github.com/nushell/nushell/issues/16827
86+
fn redirection_target_expression(target: &RedirectionTarget, pos: usize) -> Option<&Expression> {
87+
let expr = target.expr();
88+
expr.and_then(|expression| {
89+
if let Expr::String(_) = expression.expr
90+
&& expression.span.contains(pos)
91+
{
92+
expr
93+
} else {
94+
None
95+
}
96+
})
97+
}
98+
8199
/// Before completion, an additional character `a` is added to the source as a placeholder for correct parsing results.
82100
/// This function helps to strip it
83101
fn strip_placeholder_if_any<'a>(
@@ -218,9 +236,26 @@ impl NuCompleter {
218236
if !extra_placeholder {
219237
pos_to_search = pos_to_search.saturating_sub(1);
220238
}
221-
let Some(element_expression) = block.find_map(working_set, &|expr: &Expression| {
222-
find_pipeline_element_by_position(expr, working_set, pos_to_search)
223-
}) else {
239+
let Some(element_expression) = block
240+
.find_map(working_set, &|expr: &Expression| {
241+
find_pipeline_element_by_position(expr, working_set, pos_to_search)
242+
})
243+
.or_else(|| {
244+
block.pipelines.iter().find_map(|pipeline| {
245+
pipeline.elements.iter().find_map(|element| {
246+
element.redirection.as_ref().and_then(|redir| match redir {
247+
PipelineRedirection::Single { target, .. } => {
248+
redirection_target_expression(target, pos_to_search)
249+
}
250+
PipelineRedirection::Separate { out, err } => {
251+
redirection_target_expression(out, pos_to_search)
252+
.or_else(|| redirection_target_expression(err, pos_to_search))
253+
}
254+
})
255+
})
256+
})
257+
})
258+
else {
224259
return vec![];
225260
};
226261

@@ -259,6 +294,16 @@ impl NuCompleter {
259294
let mut suggestions: Vec<SemanticSuggestion> = vec![];
260295

261296
match &element_expression.expr {
297+
// WARN: Expr::String should only match redirection targets.
298+
// We choose to handle it explicitly here because of a legacy issue: fallback file
299+
// completion doesn't work well with filepath with spaces
300+
// https://github.com/nushell/nushell/issues/16712
301+
Expr::String(_) => {
302+
let span = element_expression.span;
303+
let (new_span, prefix) = strip_placeholder_if_any(working_set, &span, strip);
304+
let ctx = Context::new(working_set, new_span, prefix, offset);
305+
return self.process_completion(&mut FileCompletion, &ctx);
306+
}
262307
Expr::Var(_) => {
263308
return self.variable_names_completion_helper(
264309
working_set,

crates/nu-cli/tests/completions/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,6 +2498,21 @@ fn filecompletions_triggers_after_cursor() {
24982498
match_suggestions(&expected_paths, &suggestions);
24992499
}
25002500

2501+
#[test]
2502+
fn filecompletions_for_redirection_target() {
2503+
let (_, _, engine, stack) = new_engine_helper(fs::fixtures().join("external_completions"));
2504+
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
2505+
2506+
let expected = vec!["`dir with space/bar baz`", "`dir with space/foo`"];
2507+
let command = "echo 'foo' o+e> `dir with space/`";
2508+
let suggestions = completer.complete(command, command.len());
2509+
match_suggestions(&expected, &suggestions);
2510+
2511+
let command = "echo 'foo' o> foo e> `dir with space/`";
2512+
let suggestions = completer.complete(command, command.len());
2513+
match_suggestions(&expected, &suggestions);
2514+
}
2515+
25012516
#[rstest]
25022517
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
25032518
let suggestions = extern_completer.complete("spam ", 5);

0 commit comments

Comments
 (0)