Skip to content

Commit 864b5ac

Browse files
committed
refactor(completion): expression based variable/cell_path completion
1 parent 2ce5de5 commit 864b5ac

File tree

3 files changed

+135
-221
lines changed

3 files changed

+135
-221
lines changed

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

Lines changed: 69 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::completions::{
2-
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
3-
DotNuCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
2+
CellPathCompletion, CommandCompletion, Completer, CompletionOptions, CustomCompletion,
3+
DirectoryCompletion, DotNuCompletion, FileCompletion, FlagCompletion, OperatorCompletion,
4+
VariableNameCompletion,
45
};
56
use log::debug;
67
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
@@ -17,6 +18,10 @@ use std::{str, sync::Arc};
1718

1819
use super::base::{SemanticSuggestion, SuggestionKind};
1920

21+
/// Used as the function `f` in find_map Traverse
22+
///
23+
/// returns the inner-most pipeline_element of interest
24+
/// i.e. the one that contains given position and needs completion
2025
fn find_pipeline_element_by_position<'a>(
2126
expr: &'a Expression,
2227
working_set: &'a StateWorkingSet,
@@ -62,6 +67,15 @@ fn find_pipeline_element_by_position<'a>(
6267
}
6368
}
6469

70+
/// Before completion, an additional character `a` is added to the source as a placeholder for correct parsing results.
71+
/// This function helps to strip it
72+
fn strip_placeholder<'a>(working_set: &'a StateWorkingSet, span: &Span) -> (Span, &'a [u8]) {
73+
let new_end = std::cmp::max(span.end - 1, span.start);
74+
let new_span = Span::new(span.start, new_end);
75+
let prefix = working_set.get_span_contents(new_span);
76+
(new_span, prefix)
77+
}
78+
6579
#[derive(Clone)]
6680
pub struct NuCompleter {
6781
engine_state: Arc<EngineState>,
@@ -80,6 +94,28 @@ impl NuCompleter {
8094
self.completion_helper(line, pos)
8195
}
8296

97+
fn variable_names_completion_helper(
98+
&self,
99+
working_set: &StateWorkingSet,
100+
span: Span,
101+
offset: usize,
102+
) -> Vec<SemanticSuggestion> {
103+
let (new_span, prefix) = strip_placeholder(working_set, &span);
104+
if !prefix.starts_with(b"$") {
105+
return vec![];
106+
}
107+
let mut variable_names_completer = VariableNameCompletion {};
108+
self.process_completion(
109+
&mut variable_names_completer,
110+
working_set,
111+
prefix,
112+
new_span,
113+
offset,
114+
// pos is not required
115+
0,
116+
)
117+
}
118+
83119
// Process the completion for a given completer
84120
fn process_completion<T: Completer>(
85121
&self,
@@ -193,6 +229,37 @@ impl NuCompleter {
193229
return vec![];
194230
};
195231

232+
match &element_expression.expr {
233+
Expr::Var(_) => {
234+
return self.variable_names_completion_helper(
235+
&working_set,
236+
element_expression.span,
237+
fake_offset,
238+
);
239+
}
240+
Expr::FullCellPath(full_cell_path) => {
241+
// e.g. `$e<tab>` parsed as FullCellPath
242+
if full_cell_path.tail.is_empty() {
243+
return self.variable_names_completion_helper(
244+
&working_set,
245+
element_expression.span,
246+
fake_offset,
247+
);
248+
} else {
249+
let mut cell_path_completer = CellPathCompletion { full_cell_path };
250+
return self.process_completion(
251+
&mut cell_path_completer,
252+
&working_set,
253+
&[],
254+
element_expression.span,
255+
fake_offset,
256+
pos,
257+
);
258+
}
259+
}
260+
_ => (),
261+
}
262+
196263
let flattened = flatten_expression(&working_set, element_expression);
197264
let mut spans: Vec<String> = vec![];
198265

@@ -223,46 +290,13 @@ impl NuCompleter {
223290

224291
// Complete based on the last span
225292
if is_last_span {
226-
// Context variables
227-
let most_left_var = most_left_variable(flat_idx, &working_set, flattened.clone());
228-
229293
// Create a new span
230294
let new_span = Span::new(span.start, span.end - 1);
231295

232296
// Parses the prefix. Completion should look up to the cursor position, not after.
233297
let index = pos - span.start;
234298
let prefix = &current_span[..index];
235299

236-
// Variables completion
237-
if prefix.starts_with(b"$") || most_left_var.is_some() {
238-
let mut variable_names_completer =
239-
VariableCompletion::new(most_left_var.unwrap_or((vec![], vec![])));
240-
241-
let mut variable_completions = self.process_completion(
242-
&mut variable_names_completer,
243-
&working_set,
244-
prefix,
245-
new_span,
246-
fake_offset,
247-
pos,
248-
);
249-
250-
let mut variable_operations_completer =
251-
OperatorCompletion::new(element_expression.clone());
252-
253-
let mut variable_operations_completions = self.process_completion(
254-
&mut variable_operations_completer,
255-
&working_set,
256-
prefix,
257-
new_span,
258-
fake_offset,
259-
pos,
260-
);
261-
262-
variable_completions.append(&mut variable_operations_completions);
263-
return variable_completions;
264-
}
265-
266300
// Flags completion
267301
if prefix.starts_with(b"-") {
268302
// Try to complete flag internally
@@ -474,56 +508,6 @@ impl ReedlineCompleter for NuCompleter {
474508
}
475509
}
476510

477-
// reads the most left variable returning it's name (e.g: $myvar)
478-
// and the depth (a.b.c)
479-
fn most_left_variable(
480-
idx: usize,
481-
working_set: &StateWorkingSet<'_>,
482-
flattened: Vec<(Span, FlatShape)>,
483-
) -> Option<(Vec<u8>, Vec<Vec<u8>>)> {
484-
// Reverse items to read the list backwards and truncate
485-
// because the only items that matters are the ones before the current index
486-
let mut rev = flattened;
487-
rev.truncate(idx);
488-
rev = rev.into_iter().rev().collect();
489-
490-
// Store the variables and sub levels found and reverse to correct order
491-
let mut variables_found: Vec<Vec<u8>> = vec![];
492-
let mut found_var = false;
493-
for item in rev.clone() {
494-
let result = working_set.get_span_contents(item.0).to_vec();
495-
496-
match item.1 {
497-
FlatShape::Variable(_) => {
498-
variables_found.push(result);
499-
found_var = true;
500-
501-
break;
502-
}
503-
FlatShape::String => {
504-
variables_found.push(result);
505-
}
506-
_ => {
507-
break;
508-
}
509-
}
510-
}
511-
512-
// If most left var was not found
513-
if !found_var {
514-
return None;
515-
}
516-
517-
// Reverse the order back
518-
variables_found = variables_found.into_iter().rev().collect();
519-
520-
// Extract the variable and the sublevels
521-
let var = variables_found.first().unwrap_or(&vec![]).to_vec();
522-
let sublevels: Vec<Vec<u8>> = variables_found.into_iter().skip(1).collect();
523-
524-
Some((var, sublevels))
525-
}
526-
527511
pub fn map_value_completions<'a>(
528512
list: impl Iterator<Item = &'a Value>,
529513
span: Span,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ pub use dotnu_completions::DotNuCompletion;
2121
pub use file_completions::{file_path_completion, FileCompletion};
2222
pub use flag_completions::FlagCompletion;
2323
pub use operator_completions::OperatorCompletion;
24-
pub use variable_completions::VariableCompletion;
24+
pub use variable_completions::{CellPathCompletion, VariableNameCompletion};

0 commit comments

Comments
 (0)