Skip to content

Commit 43d2551

Browse files
committed
stash
1 parent 6e88b3f commit 43d2551

File tree

5 files changed

+296
-500
lines changed

5 files changed

+296
-500
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(Debug, Default, PartialEq)]
23+
#[derive(Clone, 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: 30 additions & 221 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,19 @@ use crate::{
44
completions::{Completer, CompletionOptions},
55
SuggestionKind,
66
};
7-
use nu_parser::FlatShape;
87
use nu_protocol::{
9-
engine::{CachedFile, Stack, StateWorkingSet},
8+
engine::{Stack, StateWorkingSet},
109
Span,
1110
};
1211
use reedline::Suggestion;
1312

1413
use super::{completion_options::NuMatcher, SemanticSuggestion};
1514

1615
pub struct CommandCompletion {
17-
flattened: Vec<(Span, FlatShape)>,
18-
flat_shape: FlatShape,
19-
force_completion_after_space: bool,
16+
pub find_externals: bool,
2017
}
2118

2219
impl CommandCompletion {
23-
pub fn new(
24-
flattened: Vec<(Span, FlatShape)>,
25-
flat_shape: FlatShape,
26-
force_completion_after_space: bool,
27-
) -> Self {
28-
Self {
29-
flattened,
30-
flat_shape,
31-
force_completion_after_space,
32-
}
33-
}
34-
3520
fn external_command_completion(
3621
&self,
3722
working_set: &StateWorkingSet,
@@ -71,23 +56,24 @@ impl CommandCompletion {
7156
if suggs.contains_key(&value) {
7257
continue;
7358
}
74-
if matcher.matches(&name) && is_executable::is_executable(item.path()) {
59+
if is_executable::is_executable(item.path()) {
7560
// If there's an internal command with the same name, adds ^cmd to the
7661
// matcher so that both the internal and external command are included
77-
matcher.add(&name, value.clone());
78-
suggs.insert(
79-
value.clone(),
80-
SemanticSuggestion {
81-
suggestion: Suggestion {
82-
value,
83-
span: sugg_span,
84-
append_whitespace: true,
85-
..Default::default()
62+
if matcher.add(&name, value.clone()) {
63+
suggs.insert(
64+
value.clone(),
65+
SemanticSuggestion {
66+
suggestion: Suggestion {
67+
value,
68+
span: sugg_span,
69+
append_whitespace: true,
70+
..Default::default()
71+
},
72+
// TODO: is there a way to create a test?
73+
kind: None,
8674
},
87-
// TODO: is there a way to create a test?
88-
kind: None,
89-
},
90-
);
75+
);
76+
}
9177
}
9278
}
9379
}
@@ -97,13 +83,17 @@ impl CommandCompletion {
9783

9884
suggs
9985
}
86+
}
10087

101-
fn complete_commands(
102-
&self,
88+
impl Completer for CommandCompletion {
89+
fn fetch(
90+
&mut self,
10391
working_set: &StateWorkingSet,
92+
_stack: &Stack,
93+
_prefix: &[u8],
10494
span: Span,
10595
offset: usize,
106-
find_externals: bool,
96+
_pos: usize,
10797
options: &CompletionOptions,
10898
) -> Vec<SemanticSuggestion> {
10999
let partial = working_set.get_span_contents(span);
@@ -136,7 +126,7 @@ impl CommandCompletion {
136126
);
137127
}
138128

139-
let mut external_suggs = if find_externals {
129+
let external_suggs = if self.find_externals {
140130
self.external_command_completion(
141131
working_set,
142132
sugg_span,
@@ -147,191 +137,10 @@ impl CommandCompletion {
147137
HashMap::new()
148138
};
149139

150-
let mut res = Vec::new();
151-
for cmd_name in matcher.results() {
152-
if let Some(sugg) = internal_suggs
153-
.remove(&cmd_name)
154-
.or_else(|| external_suggs.remove(&cmd_name))
155-
{
156-
res.push(sugg);
157-
}
158-
}
159-
res
160-
}
161-
}
162-
163-
impl Completer for CommandCompletion {
164-
fn fetch(
165-
&mut self,
166-
working_set: &StateWorkingSet,
167-
_stack: &Stack,
168-
_prefix: &[u8],
169-
span: Span,
170-
offset: usize,
171-
pos: usize,
172-
options: &CompletionOptions,
173-
) -> Vec<SemanticSuggestion> {
174-
let last = self
175-
.flattened
176-
.iter()
177-
.rev()
178-
.skip_while(|x| x.0.end > pos)
179-
.take_while(|x| {
180-
matches!(
181-
x.1,
182-
FlatShape::InternalCall(_)
183-
| FlatShape::External
184-
| FlatShape::ExternalArg
185-
| FlatShape::Literal
186-
| FlatShape::String
187-
)
188-
})
189-
.last();
190-
191-
// The last item here would be the earliest shape that could possible by part of this subcommand
192-
let subcommands = if let Some(last) = last {
193-
self.complete_commands(
194-
working_set,
195-
Span::new(last.0.start, pos),
196-
offset,
197-
false,
198-
options,
199-
)
200-
} else {
201-
vec![]
202-
};
203-
204-
if !subcommands.is_empty() {
205-
return subcommands;
206-
}
207-
208-
let config = working_set.get_config();
209-
if matches!(self.flat_shape, nu_parser::FlatShape::External)
210-
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall(_))
211-
|| ((span.end - span.start) == 0)
212-
|| is_passthrough_command(working_set.delta.get_file_contents())
213-
{
214-
// we're in a gap or at a command
215-
if working_set.get_span_contents(span).is_empty() && !self.force_completion_after_space
216-
{
217-
return vec![];
218-
}
219-
self.complete_commands(
220-
working_set,
221-
span,
222-
offset,
223-
config.completions.external.enable,
224-
options,
225-
)
226-
} else {
227-
vec![]
228-
}
229-
}
230-
}
231-
232-
pub fn find_non_whitespace_index(contents: &[u8], start: usize) -> usize {
233-
match contents.get(start..) {
234-
Some(contents) => {
235-
contents
236-
.iter()
237-
.take_while(|x| x.is_ascii_whitespace())
238-
.count()
239-
+ start
240-
}
241-
None => start,
242-
}
243-
}
244-
245-
pub fn is_passthrough_command(working_set_file_contents: &[CachedFile]) -> bool {
246-
for cached_file in working_set_file_contents {
247-
let contents = &cached_file.content;
248-
let last_pipe_pos_rev = contents.iter().rev().position(|x| x == &b'|');
249-
let last_pipe_pos = last_pipe_pos_rev.map(|x| contents.len() - x).unwrap_or(0);
250-
251-
let cur_pos = find_non_whitespace_index(contents, last_pipe_pos);
252-
253-
let result = match contents.get(cur_pos..) {
254-
Some(contents) => contents.starts_with(b"sudo ") || contents.starts_with(b"doas "),
255-
None => false,
256-
};
257-
if result {
258-
return true;
259-
}
260-
}
261-
false
262-
}
263-
264-
#[cfg(test)]
265-
mod command_completions_tests {
266-
use super::*;
267-
use nu_protocol::engine::EngineState;
268-
use std::sync::Arc;
269-
270-
#[test]
271-
fn test_find_non_whitespace_index() {
272-
let commands = [
273-
(" hello", 4),
274-
("sudo ", 0),
275-
(" sudo ", 2),
276-
(" sudo ", 2),
277-
(" hello ", 1),
278-
(" hello ", 3),
279-
(" hello | sudo ", 4),
280-
(" sudo|sudo", 5),
281-
("sudo | sudo ", 0),
282-
(" hello sud", 1),
283-
];
284-
for (idx, ele) in commands.iter().enumerate() {
285-
let index = find_non_whitespace_index(ele.0.as_bytes(), 0);
286-
assert_eq!(index, ele.1, "Failed on index {}", idx);
287-
}
288-
}
289-
290-
#[test]
291-
fn test_is_last_command_passthrough() {
292-
let commands = [
293-
(" hello", false),
294-
(" sudo ", true),
295-
("sudo ", true),
296-
(" hello", false),
297-
(" sudo", false),
298-
(" sudo ", true),
299-
(" sudo ", true),
300-
(" sudo ", true),
301-
(" hello ", false),
302-
(" hello | sudo ", true),
303-
(" sudo|sudo", false),
304-
("sudo | sudo ", true),
305-
(" hello sud", false),
306-
(" sudo | sud ", false),
307-
(" sudo|sudo ", true),
308-
(" sudo | sudo ls | sudo ", true),
309-
];
310-
for (idx, ele) in commands.iter().enumerate() {
311-
let input = ele.0.as_bytes();
312-
313-
let mut engine_state = EngineState::new();
314-
engine_state.add_file("test.nu".into(), Arc::new([]));
315-
316-
let delta = {
317-
let mut working_set = StateWorkingSet::new(&engine_state);
318-
let _ = working_set.add_file("child.nu".into(), input);
319-
working_set.render()
320-
};
321-
322-
let result = engine_state.merge_delta(delta);
323-
assert!(
324-
result.is_ok(),
325-
"Merge delta has failed: {}",
326-
result.err().unwrap()
327-
);
328-
329-
let is_passthrough_command = is_passthrough_command(engine_state.get_file_contents());
330-
assert_eq!(
331-
is_passthrough_command, ele.1,
332-
"index for '{}': {}",
333-
ele.0, idx
334-
);
335-
}
140+
internal_suggs
141+
.values()
142+
.chain(external_suggs.values())
143+
.cloned()
144+
.collect()
336145
}
337146
}

0 commit comments

Comments
 (0)