Skip to content

Commit fd98600

Browse files
committed
refactor: implicit match_indices assignment in matcher::add_semantic_suggestion
1 parent 59f8621 commit fd98600

15 files changed

+245
-191
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ impl Completer for AttributeCompletion {
2525
let mut matcher = NuMatcher::new(prefix, options);
2626

2727
let attr_commands =
28-
working_set.find_commands_by_predicate(|s| s.starts_with(b"attr "), true);
28+
working_set.find_commands_by_predicate(|s| s.starts_with("attr "), true);
2929

3030
for (decl_id, name, desc, ty) in attr_commands {
31-
let name = name.strip_prefix(b"attr ").unwrap_or(&name);
31+
let name = name.strip_prefix("attr ").unwrap_or(&name);
3232
matcher.add_semantic_suggestion(SemanticSuggestion {
3333
suggestion: Suggestion {
34-
value: String::from_utf8_lossy(name).into_owned(),
34+
value: name.to_string(),
3535
description: desc,
3636
span: reedline::Span {
3737
start: span.start - offset,
@@ -44,7 +44,7 @@ impl Completer for AttributeCompletion {
4444
});
4545
}
4646

47-
matcher.suggestion_results()
47+
matcher.results()
4848
}
4949
}
5050

@@ -80,6 +80,6 @@ impl Completer for AttributableCompletion {
8080
});
8181
}
8282

83-
matcher.suggestion_results()
83+
matcher.results()
8484
}
8585
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl Completer for CellPathCompletion<'_> {
7272
for suggestion in get_suggestions_by_value(&value, current_span) {
7373
matcher.add_semantic_suggestion(suggestion);
7474
}
75-
matcher.suggestion_results()
75+
matcher.results()
7676
}
7777
}
7878

Lines changed: 43 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::HashSet;
22

33
use crate::{
44
SuggestionKind,
@@ -26,9 +26,9 @@ impl CommandCompletion {
2626
working_set: &StateWorkingSet,
2727
sugg_span: reedline::Span,
2828
matched_internal: impl Fn(&str) -> bool,
29-
matcher: &mut NuMatcher<String>,
30-
) -> HashMap<String, SemanticSuggestion> {
31-
let mut suggs = HashMap::new();
29+
matcher: &mut NuMatcher<SemanticSuggestion>,
30+
) {
31+
let mut external_commands = HashSet::new();
3232

3333
let paths = working_set.permanent_state.get_env_var_insensitive("path");
3434

@@ -46,50 +46,43 @@ impl CommandCompletion {
4646
.completions
4747
.external
4848
.max_results
49-
<= suggs.len() as i64
49+
<= external_commands.len() as i64
5050
{
5151
break;
5252
}
5353
let Ok(name) = item.file_name().into_string() else {
5454
continue;
5555
};
56+
// If there's an internal command with the same name, adds ^cmd to the
57+
// matcher so that both the internal and external command are included
5658
let value = if matched_internal(&name) {
5759
format!("^{name}")
5860
} else {
5961
name.clone()
6062
};
61-
if suggs.contains_key(&value) {
63+
// If a command of the same name already exists, skip
64+
if external_commands.contains(&value) {
6265
continue;
6366
}
6467
// TODO: check name matching before a relative heavy IO involved
6568
// `is_executable` for performance consideration, should avoid
6669
// duplicated `match_aux` call for matched items in the future
67-
if matcher.matches(&name) && is_executable::is_executable(item.path()) {
68-
// If there's an internal command with the same name, adds ^cmd to the
69-
// matcher so that both the internal and external command are included
70-
matcher.add(&name, value.clone());
71-
suggs.insert(
72-
value.clone(),
73-
SemanticSuggestion {
74-
suggestion: Suggestion {
75-
value,
76-
span: sugg_span,
77-
append_whitespace: true,
78-
..Default::default()
79-
},
80-
kind: Some(SuggestionKind::Command(
81-
CommandType::External,
82-
None,
83-
)),
70+
if matcher.matches(&name).0 && is_executable::is_executable(item.path()) {
71+
external_commands.insert(value.clone());
72+
matcher.add_semantic_suggestion(SemanticSuggestion {
73+
suggestion: Suggestion {
74+
value,
75+
span: sugg_span,
76+
append_whitespace: true,
77+
..Default::default()
8478
},
85-
);
79+
kind: Some(SuggestionKind::Command(CommandType::External, None)),
80+
});
8681
}
8782
}
8883
}
8984
}
9085
}
91-
92-
suggs
9386
}
9487
}
9588

@@ -103,58 +96,42 @@ impl Completer for CommandCompletion {
10396
offset: usize,
10497
options: &CompletionOptions,
10598
) -> Vec<SemanticSuggestion> {
106-
let mut matcher = NuMatcher::new(prefix, options);
99+
let mut matcher = NuMatcher::new(prefix.as_ref(), options);
107100

108101
let sugg_span = reedline::Span::new(span.start - offset, span.end - offset);
109102

110-
let mut internal_suggs = HashMap::new();
103+
let mut internal_commands = HashSet::new();
111104
if self.internals {
112-
let filtered_commands = working_set.find_commands_by_predicate(
113-
|name| {
114-
let name = String::from_utf8_lossy(name);
115-
matcher.add(&name, name.to_string())
116-
},
117-
true,
118-
);
105+
let filtered_commands =
106+
working_set.find_commands_by_predicate(|name| matcher.matches(name).0, true);
119107
for (decl_id, name, description, typ) in filtered_commands {
120-
let name = String::from_utf8_lossy(&name);
121-
internal_suggs.insert(
122-
name.to_string(),
123-
SemanticSuggestion {
124-
suggestion: Suggestion {
125-
value: name.to_string(),
126-
description,
127-
span: sugg_span,
128-
append_whitespace: true,
129-
..Suggestion::default()
130-
},
131-
kind: Some(SuggestionKind::Command(typ, Some(decl_id))),
108+
matcher.add_semantic_suggestion(SemanticSuggestion {
109+
suggestion: Suggestion {
110+
value: name.to_string(),
111+
description,
112+
span: sugg_span,
113+
append_whitespace: true,
114+
..Suggestion::default()
132115
},
133-
);
116+
kind: Some(SuggestionKind::Command(typ, Some(decl_id))),
117+
});
118+
internal_commands.insert(name);
134119
}
135120
}
136121

137-
let mut external_suggs = if self.externals {
122+
let mut ans = matcher.results();
123+
if self.externals {
124+
// Create another matcher so that externals always come after internals
125+
let mut external_matcher =
126+
NuMatcher::new_with_customized_trimming(prefix.as_ref(), options, &['^']);
138127
self.external_command_completion(
139128
working_set,
140129
sugg_span,
141-
|name| internal_suggs.contains_key(name),
142-
&mut matcher,
143-
)
144-
} else {
145-
HashMap::new()
146-
};
147-
148-
let mut res = Vec::new();
149-
for (cmd_name, indices) in matcher.results() {
150-
if let Some(mut sugg) = internal_suggs
151-
.remove(&cmd_name)
152-
.or_else(|| external_suggs.remove(&cmd_name))
153-
{
154-
sugg.suggestion.match_indices = indices;
155-
res.push(sugg);
156-
}
130+
|name| internal_commands.contains(name),
131+
&mut external_matcher,
132+
);
133+
ans.extend(external_matcher.results());
157134
}
158-
res
135+
ans
159136
}
160137
}

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ fn complete_rec(
104104
}
105105
}
106106

107+
// TODO: accurate `match_indices` for `substring`/`fuzzy` matching, #15887
107108
matcher.add(entry_name, built);
108109
}
109110
}
@@ -123,7 +124,7 @@ fn complete_rec(
123124

124125
if has_more {
125126
let mut completions = vec![];
126-
for (built, _) in matcher.results() {
127+
for built in matcher.results() {
127128
completions.extend(complete_rec(
128129
&partial[1..],
129130
&[built],
@@ -135,11 +136,7 @@ fn complete_rec(
135136
}
136137
completions
137138
} else {
138-
matcher
139-
.results()
140-
.into_iter()
141-
.map(|(built, _)| built)
142-
.collect()
139+
matcher.results()
143140
}
144141
}
145142

0 commit comments

Comments
 (0)