Add in variable and sub-command completions (#480)

* WIP

* wip

* Add in variable and subcommand completions

* clippy
This commit is contained in:
JT 2021-12-13 12:18:31 +13:00 committed by GitHub
parent d1d1402512
commit bee7ef21eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 164 additions and 111 deletions

View File

@ -1,8 +1,9 @@
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::{flatten_block, parse}; use nu_parser::{flatten_expression, parse};
use nu_protocol::{ use nu_protocol::{
ast::Statement,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
PipelineData, PipelineData, Span,
}; };
use reedline::Completer; use reedline::Completer;
@ -26,121 +27,172 @@ impl Completer for NuCompleter {
let pos = offset + pos; let pos = offset + pos;
let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false); let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
let flattened = flatten_block(&working_set, &output); for stmt in output.stmts.into_iter() {
if let Statement::Pipeline(pipeline) = stmt {
for expr in pipeline.expressions {
if pos >= expr.span.start
&& (pos <= (line.len() + offset) || pos <= expr.span.end)
{
let possible_cmd = working_set.get_span_contents(Span {
start: expr.span.start,
end: pos,
});
for flat in flattened { let results = working_set.find_commands_by_prefix(possible_cmd);
if pos >= flat.0.start && pos <= flat.0.end {
let prefix = working_set.get_span_contents(flat.0);
if prefix.starts_with(b"$") {
let mut output = vec![];
for scope in &working_set.delta.scope { if !results.is_empty() {
for v in &scope.vars { return results
if v.0.starts_with(prefix) {
output.push((
reedline::Span {
start: flat.0.start - offset,
end: flat.0.end - offset,
},
String::from_utf8_lossy(v.0).to_string(),
));
}
}
}
for scope in &self.engine_state.scope {
for v in &scope.vars {
if v.0.starts_with(prefix) {
output.push((
reedline::Span {
start: flat.0.start - offset,
end: flat.0.end - offset,
},
String::from_utf8_lossy(v.0).to_string(),
));
}
}
}
return output;
}
match &flat.1 {
nu_parser::FlatShape::Custom(custom_completion) => {
let prefix = working_set.get_span_contents(flat.0).to_vec();
let (block, ..) =
parse(&mut working_set, None, custom_completion.as_bytes(), false);
let mut stack = Stack::default();
let result = eval_block(
&self.engine_state,
&mut stack,
&block,
PipelineData::new(flat.0),
);
let v: Vec<_> = match result {
Ok(pd) => pd
.into_iter() .into_iter()
.map(move |x| { .map(move |x| {
let s = x.as_string().expect( (
reedline::Span {
start: expr.span.start - offset,
end: pos - offset,
},
String::from_utf8_lossy(&x).to_string(),
)
})
.collect();
}
}
let flattened = flatten_expression(&working_set, &expr);
for flat in flattened {
if pos >= flat.0.start && pos <= flat.0.end {
match &flat.1 {
nu_parser::FlatShape::Custom(custom_completion) => {
let prefix = working_set.get_span_contents(flat.0).to_vec();
let (block, ..) = parse(
&mut working_set,
None,
custom_completion.as_bytes(),
false,
);
let mut stack = Stack::default();
let result = eval_block(
&self.engine_state,
&mut stack,
&block,
PipelineData::new(flat.0),
);
let v: Vec<_> = match result {
Ok(pd) => pd
.into_iter()
.map(move |x| {
let s = x.as_string().expect(
"FIXME: better error handling for custom completions", "FIXME: better error handling for custom completions",
); );
( (
reedline::Span { reedline::Span {
start: flat.0.start - offset, start: flat.0.start - offset,
end: flat.0.end - offset, end: flat.0.end - offset,
}, },
s, s,
) )
}) })
.filter(|x| x.1.as_bytes().starts_with(&prefix)) .filter(|x| x.1.as_bytes().starts_with(&prefix))
.collect(), .collect(),
_ => vec![], _ => vec![],
}; };
return v; return v;
}
nu_parser::FlatShape::External
| nu_parser::FlatShape::InternalCall
| nu_parser::FlatShape::String => {
let prefix = working_set.get_span_contents(flat.0);
let results = working_set.find_commands_by_prefix(prefix);
let prefix = String::from_utf8_lossy(prefix).to_string();
let results2 = file_path_completion(flat.0, &prefix)
.into_iter()
.map(move |x| {
(
reedline::Span {
start: x.0.start - offset,
end: x.0.end - offset,
},
x.1,
)
});
return results
.into_iter()
.map(move |x| {
(
reedline::Span {
start: flat.0.start - offset,
end: flat.0.end - offset,
},
String::from_utf8_lossy(&x).to_string(),
)
})
.chain(results2.into_iter())
.collect();
}
nu_parser::FlatShape::Filepath
| nu_parser::FlatShape::GlobPattern
| nu_parser::FlatShape::ExternalArg => {
let prefix = working_set.get_span_contents(flat.0);
let prefix = String::from_utf8_lossy(prefix).to_string();
let results = file_path_completion(flat.0, &prefix);
return results
.into_iter()
.map(move |x| {
(
reedline::Span {
start: x.0.start - offset,
end: x.0.end - offset,
},
x.1,
)
})
.collect();
}
_ => {
let prefix = working_set.get_span_contents(flat.0);
if prefix.starts_with(b"$") {
let mut output = vec![];
for scope in &working_set.delta.scope {
for v in &scope.vars {
if v.0.starts_with(prefix) {
output.push((
reedline::Span {
start: flat.0.start - offset,
end: flat.0.end - offset,
},
String::from_utf8_lossy(v.0).to_string(),
));
}
}
}
for scope in &self.engine_state.scope {
for v in &scope.vars {
if v.0.starts_with(prefix) {
output.push((
reedline::Span {
start: flat.0.start - offset,
end: flat.0.end - offset,
},
String::from_utf8_lossy(v.0).to_string(),
));
}
}
}
return output;
}
}
}
}
} }
nu_parser::FlatShape::External | nu_parser::FlatShape::InternalCall => {
let prefix = working_set.get_span_contents(flat.0);
let results = working_set.find_commands_by_prefix(prefix);
return results
.into_iter()
.map(move |x| {
(
reedline::Span {
start: flat.0.start - offset,
end: flat.0.end - offset,
},
String::from_utf8_lossy(&x).to_string(),
)
})
.collect();
}
nu_parser::FlatShape::Filepath
| nu_parser::FlatShape::GlobPattern
| nu_parser::FlatShape::ExternalArg => {
let prefix = working_set.get_span_contents(flat.0);
let prefix = String::from_utf8_lossy(prefix).to_string();
let results = file_path_completion(flat.0, &prefix);
return results
.into_iter()
.map(move |x| {
(
reedline::Span {
start: x.0.start - offset,
end: x.0.end - offset,
},
x.1,
)
})
.collect();
}
_ => {}
} }
} }
} }

View File

@ -7,7 +7,9 @@ mod parser;
mod type_check; mod type_check;
pub use errors::ParseError; pub use errors::ParseError;
pub use flatten::{flatten_block, FlatShape}; pub use flatten::{
flatten_block, flatten_expression, flatten_pipeline, flatten_statement, FlatShape,
};
pub use lex::{lex, Token, TokenContents}; pub use lex::{lex, Token, TokenContents};
pub use lite_parse::{lite_parse, LiteBlock}; pub use lite_parse::{lite_parse, LiteBlock};
pub use parse_keywords::{ pub use parse_keywords::{

View File

@ -225,7 +225,6 @@ fn main() -> Result<()> {
} else { } else {
use reedline::{FileBackedHistory, Reedline, Signal}; use reedline::{FileBackedHistory, Reedline, Signal};
let completer = NuCompleter::new(engine_state.clone());
let mut entry_num = 0; let mut entry_num = 0;
let default_prompt = DefaultPrompt::new(1); let default_prompt = DefaultPrompt::new(1);
@ -298,7 +297,7 @@ fn main() -> Result<()> {
let line_editor = Reedline::create() let line_editor = Reedline::create()
.into_diagnostic()? .into_diagnostic()?
.with_completion_action_handler(Box::new(FuzzyCompletion { .with_completion_action_handler(Box::new(FuzzyCompletion {
completer: Box::new(completer.clone()), completer: Box::new(NuCompleter::new(engine_state.clone())),
})) }))
.with_highlighter(Box::new(NuHighlighter { .with_highlighter(Box::new(NuHighlighter {
engine_state: engine_state.clone(), engine_state: engine_state.clone(),