Skip to content

Commit 2aafc05

Browse files
committed
fix: pass exising test cases
1 parent 43d2551 commit 2aafc05

File tree

3 files changed

+91
-95
lines changed

3 files changed

+91
-95
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub trait Completer {
2020
) -> Vec<SemanticSuggestion>;
2121
}
2222

23-
#[derive(Clone, Debug, Default, PartialEq)]
23+
#[derive(Debug, Default, PartialEq)]
2424
pub struct SemanticSuggestion {
2525
pub suggestion: Suggestion,
2626
pub kind: Option<SuggestionKind>,

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl Completer for CommandCompletion {
126126
);
127127
}
128128

129-
let external_suggs = if self.find_externals {
129+
let mut external_suggs = if self.find_externals {
130130
self.external_command_completion(
131131
working_set,
132132
sugg_span,
@@ -137,10 +137,15 @@ impl Completer for CommandCompletion {
137137
HashMap::new()
138138
};
139139

140-
internal_suggs
141-
.values()
142-
.chain(external_suggs.values())
143-
.cloned()
144-
.collect()
140+
let mut res = Vec::new();
141+
for cmd_name in matcher.results() {
142+
if let Some(sugg) = internal_suggs
143+
.remove(&cmd_name)
144+
.or_else(|| external_suggs.remove(&cmd_name))
145+
{
146+
res.push(sugg);
147+
}
148+
}
149+
res
145150
}
146151
}

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

Lines changed: 79 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,27 @@ fn strip_placeholder<'a>(working_set: &'a StateWorkingSet, span: &Span) -> (Span
7575
(new_span, prefix)
7676
}
7777

78+
/// Given a span with noise,
79+
/// 1. Call `rsplit` to get the last token
80+
/// 2. Strip the last placeholder from the token
81+
fn strip_placeholder_with_rsplit<'a>(
82+
working_set: &'a StateWorkingSet,
83+
span: &Span,
84+
predicate: impl FnMut(&u8) -> bool,
85+
) -> (Span, &'a [u8]) {
86+
let flag_equals_str = working_set.get_span_contents(*span);
87+
let mut true_prefix = flag_equals_str
88+
.rsplit(predicate)
89+
.next()
90+
.unwrap_or(flag_equals_str);
91+
let true_start = span.end.saturating_sub(true_prefix.len());
92+
if !true_prefix.is_empty() {
93+
true_prefix = &true_prefix[..true_prefix.len() - 1];
94+
}
95+
let true_end = true_start + true_prefix.len();
96+
(Span::new(true_start, true_end), true_prefix)
97+
}
98+
7899
#[derive(Clone)]
79100
pub struct NuCompleter {
80101
engine_state: Arc<EngineState>,
@@ -267,6 +288,8 @@ impl NuCompleter {
267288
return vec![];
268289
};
269290

291+
let mut suggestions: Vec<SemanticSuggestion> = vec![];
292+
270293
match &element_expression.expr {
271294
Expr::Var(_) => {
272295
return self.variable_names_completion_helper(
@@ -314,36 +337,33 @@ impl NuCompleter {
314337
}
315338
}
316339
}
340+
// NOTE: user defined internal commands can have any length
341+
// e.g. `def "foo -f --ff bar"`, complete by line text
342+
// instead of relying on the parsing result in that case
343+
Expr::Call(_) | Expr::ExternalCall(_, _) => {
344+
suggestions.extend(self.command_completion_helper(
345+
&working_set,
346+
element_expression.span,
347+
fake_offset,
348+
true,
349+
))
350+
}
351+
_ => (),
352+
}
353+
354+
// unfinished argument completion for commands
355+
match &element_expression.expr {
317356
Expr::Call(call) => {
318-
// at command head, e.g. `l<tab>`
319-
// still not sure user want internal or external
320-
// complete both internal and external commands
321-
if call.head.contains(pos) {
322-
return self.command_completion_helper(
323-
&working_set,
324-
call.head,
325-
fake_offset,
326-
true,
327-
);
328-
}
329-
for (i, arg) in call.arguments.iter().enumerate() {
357+
for arg in call.arguments.iter() {
330358
let span = arg.span();
331359
if span.contains(pos) {
332360
// if customized completion specified, it has highest priority
333361
if let Some(decl_id) = arg.expr().and_then(|e| e.custom_completion) {
334362
// for `--foo <tab>` and `--foo=<tab>`, the arg span should be trimmed
335363
let (new_span, prefix) = if matches!(arg, Argument::Named(_)) {
336-
let flag_equals_str = working_set.get_span_contents(span);
337-
let mut true_prefix = flag_equals_str
338-
.rsplit(|c| *c == b'=' || *c == b' ')
339-
.next()
340-
.unwrap_or(flag_equals_str);
341-
let true_start = span.end.saturating_sub(true_prefix.len());
342-
if !true_prefix.is_empty() {
343-
true_prefix = &true_prefix[..true_prefix.len() - 1];
344-
}
345-
let true_end = true_start + true_prefix.len();
346-
(Span::new(true_start, true_end), true_prefix)
364+
strip_placeholder_with_rsplit(&working_set, &span, |b| {
365+
*b == b'=' || *b == b' '
366+
})
347367
} else {
348368
strip_placeholder(&working_set, &span)
349369
};
@@ -355,14 +375,15 @@ impl NuCompleter {
355375
FileCompletion::new(),
356376
);
357377

358-
return self.process_completion(
378+
suggestions.extend(self.process_completion(
359379
&mut completer,
360380
&working_set,
361381
prefix,
362382
new_span,
363383
fake_offset,
364384
pos,
365-
);
385+
));
386+
break;
366387
}
367388
// complete module file/directory
368389
// TODO: if module file already specified,
@@ -374,7 +395,7 @@ impl NuCompleter {
374395
|| command_head == b"source-env"
375396
{
376397
let mut completer = DotNuCompletion::new();
377-
let (new_span, prefix) = strip_placeholder(&working_set, &call.head);
398+
let (new_span, prefix) = strip_placeholder(&working_set, &span);
378399
return self.process_completion(
379400
&mut completer,
380401
&working_set,
@@ -384,89 +405,55 @@ impl NuCompleter {
384405
pos,
385406
);
386407
}
387-
// if at the first arguments, e.g. `help co<tab>`
388-
// still need to complete internal commands if any
389-
if i == 0 {
390-
let subcommands = self.command_completion_helper(
391-
&working_set,
392-
call.head.merge(span),
393-
offset,
394-
false,
395-
);
396-
if !subcommands.is_empty() {
397-
return subcommands;
398-
}
399-
}
400408
// normal arguments completion
401-
match arg {
409+
suggestions.extend(match arg {
402410
// flags
403411
Argument::Named(_) => {
404412
let mut flag_completions = FlagCompletion {
405413
decl_id: call.decl_id,
406414
};
407415
let (new_span, prefix) = strip_placeholder(&working_set, &span);
408-
return self.process_completion(
416+
self.process_completion(
409417
&mut flag_completions,
410418
&working_set,
411419
prefix,
412420
new_span,
413421
fake_offset,
414422
pos,
415-
);
423+
)
416424
}
417425
// complete according to expression type
418-
Argument::Positional(expr) => {
419-
return self.argument_completion_helper(
420-
expr,
421-
&working_set,
422-
span,
423-
fake_offset,
424-
);
425-
}
426+
Argument::Positional(expr) => self.argument_completion_helper(
427+
expr,
428+
&working_set,
429+
span,
430+
fake_offset,
431+
),
426432
Argument::Unknown(_) => {
427433
let (new_span, prefix) = strip_placeholder(&working_set, &span);
428434
if prefix.starts_with(b"-") {
429435
let mut flag_completions = FlagCompletion {
430436
decl_id: call.decl_id,
431437
};
432-
return self.process_completion(
438+
self.process_completion(
433439
&mut flag_completions,
434440
&working_set,
435441
prefix,
436442
new_span,
437443
fake_offset,
438444
pos,
439-
);
445+
)
440446
} else {
441-
let mut completer = FileCompletion::new();
442-
return self.process_completion(
443-
&mut completer,
444-
&working_set,
445-
prefix,
446-
new_span,
447-
offset,
448-
0,
449-
);
447+
vec![]
450448
}
451449
}
452-
_ => (),
453-
};
450+
_ => vec![],
451+
});
454452
break;
455453
}
456454
}
457455
}
458456
Expr::ExternalCall(head, arguments) => {
459-
// at command head, e.g. `l<tab>`
460-
// still not sure user want internal or external
461-
// complete both internal and external commands
462-
if head.span.contains(pos) {
463-
return self.command_completion_helper(
464-
&working_set,
465-
head.span,
466-
fake_offset,
467-
true,
468-
);
469-
}
470457
for (i, arg) in arguments.iter().enumerate() {
471458
let span = arg.expr().span;
472459
if span.contains(pos) {
@@ -504,19 +491,8 @@ impl NuCompleter {
504491
fake_offset,
505492
Span::new(span.start, span.end.saturating_sub(1)),
506493
) {
507-
return external_result;
508-
} else {
509-
// fallback to file completion
510-
let mut file_completer = FileCompletion::new();
511-
let (new_span, prefix) = strip_placeholder(&working_set, &span);
512-
return self.process_completion(
513-
&mut file_completer,
514-
&working_set,
515-
prefix,
516-
new_span,
517-
fake_offset,
518-
pos,
519-
);
494+
suggestions.extend(external_result);
495+
return suggestions;
520496
}
521497
}
522498
break;
@@ -526,7 +502,22 @@ impl NuCompleter {
526502
_ => (),
527503
}
528504

529-
vec![]
505+
if suggestions.is_empty() {
506+
let mut file_completions = FileCompletion::new();
507+
let (new_span, prefix) =
508+
strip_placeholder_with_rsplit(&working_set, &element_expression.span, |c| {
509+
*c == b' '
510+
});
511+
suggestions.extend(self.process_completion(
512+
&mut file_completions,
513+
&working_set,
514+
prefix,
515+
new_span,
516+
fake_offset,
517+
pos,
518+
));
519+
}
520+
suggestions
530521
}
531522
}
532523

0 commit comments

Comments
 (0)