mirror of
https://github.com/nushell/nushell.git
synced 2025-01-11 08:48:23 +01:00
Improve completions with no starting characters (#4433)
* Improve completions with no starting characters * Fix subexpressions, crashes, and differentiate externals
This commit is contained in:
parent
0256e42e3b
commit
cc171b6ad4
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{flatten_expression, parse, trim_quotes};
|
use nu_parser::{flatten_expression, parse, trim_quotes, FlatShape};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Expr, Statement},
|
ast::{Expr, Statement},
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
@ -121,7 +121,7 @@ impl NuCompleter {
|
|||||||
) -> Vec<(reedline::Span, String)> {
|
) -> Vec<(reedline::Span, String)> {
|
||||||
let prefix = working_set.get_span_contents(span);
|
let prefix = working_set.get_span_contents(span);
|
||||||
|
|
||||||
let results = working_set
|
let mut results = working_set
|
||||||
.find_commands_by_prefix(prefix)
|
.find_commands_by_prefix(prefix)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| {
|
.map(move |x| {
|
||||||
@ -132,7 +132,8 @@ impl NuCompleter {
|
|||||||
},
|
},
|
||||||
String::from_utf8_lossy(&x).to_string(),
|
String::from_utf8_lossy(&x).to_string(),
|
||||||
)
|
)
|
||||||
});
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let prefix = working_set.get_span_contents(span);
|
let prefix = working_set.get_span_contents(span);
|
||||||
let prefix = String::from_utf8_lossy(prefix).to_string();
|
let prefix = String::from_utf8_lossy(prefix).to_string();
|
||||||
@ -149,31 +150,45 @@ impl NuCompleter {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for external in results_external {
|
||||||
|
if results.contains(&external) {
|
||||||
|
results.push((external.0, format!("^{}", external.1)))
|
||||||
|
} else {
|
||||||
|
results.push(external)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
results
|
results
|
||||||
.into_iter()
|
|
||||||
.chain(results_external.into_iter())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion_helper(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
fn completion_helper(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let offset = working_set.next_span_start();
|
let offset = working_set.next_span_start();
|
||||||
|
let mut line = line.to_string();
|
||||||
|
line.insert(pos, 'a');
|
||||||
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);
|
||||||
|
|
||||||
for stmt in output.stmts.into_iter() {
|
for stmt in output.stmts.into_iter() {
|
||||||
if let Statement::Pipeline(pipeline) = stmt {
|
if let Statement::Pipeline(pipeline) = stmt {
|
||||||
for expr in pipeline.expressions {
|
for expr in pipeline.expressions {
|
||||||
let flattened = flatten_expression(&working_set, &expr);
|
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||||
for (flat_idx, flat) in flattened.into_iter().enumerate() {
|
|
||||||
if pos >= flat.0.start && pos <= flat.0.end {
|
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||||
let prefix = working_set.get_span_contents(flat.0);
|
if pos >= flat.0.start && pos < flat.0.end {
|
||||||
|
let new_span = Span {
|
||||||
|
start: flat.0.start,
|
||||||
|
end: flat.0.end - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
|
prefix.remove(pos - flat.0.start);
|
||||||
|
|
||||||
if prefix.starts_with(b"$") {
|
if prefix.starts_with(b"$") {
|
||||||
return self.complete_variables(
|
return self.complete_variables(
|
||||||
&working_set,
|
&working_set,
|
||||||
prefix,
|
&prefix,
|
||||||
flat.0,
|
new_span,
|
||||||
offset,
|
offset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -189,11 +204,11 @@ impl NuCompleter {
|
|||||||
let mut named = named.long.as_bytes().to_vec();
|
let mut named = named.long.as_bytes().to_vec();
|
||||||
named.insert(0, b'-');
|
named.insert(0, b'-');
|
||||||
named.insert(0, b'-');
|
named.insert(0, b'-');
|
||||||
if named.starts_with(prefix) {
|
if named.starts_with(&prefix) {
|
||||||
output.push((
|
output.push((
|
||||||
reedline::Span {
|
reedline::Span {
|
||||||
start: flat.0.start - offset,
|
start: new_span.start - offset,
|
||||||
end: flat.0.end - offset,
|
end: new_span.end - offset,
|
||||||
},
|
},
|
||||||
String::from_utf8_lossy(&named).to_string(),
|
String::from_utf8_lossy(&named).to_string(),
|
||||||
));
|
));
|
||||||
@ -205,7 +220,7 @@ impl NuCompleter {
|
|||||||
|
|
||||||
match &flat.1 {
|
match &flat.1 {
|
||||||
nu_parser::FlatShape::Custom(custom_completion) => {
|
nu_parser::FlatShape::Custom(custom_completion) => {
|
||||||
let prefix = working_set.get_span_contents(flat.0).to_vec();
|
//let prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
|
|
||||||
let (block, ..) = parse(
|
let (block, ..) = parse(
|
||||||
&mut working_set,
|
&mut working_set,
|
||||||
@ -228,24 +243,25 @@ impl NuCompleter {
|
|||||||
&self.engine_state,
|
&self.engine_state,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
&block,
|
&block,
|
||||||
PipelineData::new(flat.0),
|
PipelineData::new(new_span),
|
||||||
);
|
);
|
||||||
|
|
||||||
let v: Vec<_> = match result {
|
let v: Vec<_> = match result {
|
||||||
Ok(pd) => pd
|
Ok(pd) => pd
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| {
|
.filter_map(move |x| {
|
||||||
let s = x.as_string().expect(
|
let s = x.as_string();
|
||||||
"FIXME: better error handling for custom completions",
|
|
||||||
);
|
|
||||||
|
|
||||||
(
|
match s {
|
||||||
reedline::Span {
|
Ok(s) => Some((
|
||||||
start: flat.0.start - offset,
|
reedline::Span {
|
||||||
end: flat.0.end - offset,
|
start: new_span.start - offset,
|
||||||
},
|
end: new_span.end - offset,
|
||||||
s,
|
},
|
||||||
)
|
s,
|
||||||
|
)),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.filter(|x| x.1.as_bytes().starts_with(&prefix))
|
.filter(|x| x.1.as_bytes().starts_with(&prefix))
|
||||||
.collect(),
|
.collect(),
|
||||||
@ -254,15 +270,50 @@ impl NuCompleter {
|
|||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
_ => {
|
flat_shape => {
|
||||||
let subcommands = self.complete_commands(
|
let commands =
|
||||||
&working_set,
|
if matches!(flat_shape, nu_parser::FlatShape::External)
|
||||||
Span {
|
|| matches!(
|
||||||
start: expr.span.start,
|
flat_shape,
|
||||||
end: pos,
|
nu_parser::FlatShape::InternalCall
|
||||||
},
|
)
|
||||||
offset,
|
|| ((new_span.end - new_span.start) == 0)
|
||||||
);
|
{
|
||||||
|
// we're in a gap or at a command
|
||||||
|
self.complete_commands(&working_set, new_span, offset)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
let last = flattened
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.skip_while(|x| x.0.end > pos)
|
||||||
|
.take_while(|x| {
|
||||||
|
matches!(
|
||||||
|
x.1,
|
||||||
|
FlatShape::InternalCall
|
||||||
|
| FlatShape::External
|
||||||
|
| FlatShape::ExternalArg
|
||||||
|
| FlatShape::Literal
|
||||||
|
| FlatShape::String
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.last();
|
||||||
|
|
||||||
|
// The last item here would be the earliest shape that could possible by part of this subcommand
|
||||||
|
let subcommands = if let Some(last) = last {
|
||||||
|
self.complete_commands(
|
||||||
|
&working_set,
|
||||||
|
Span {
|
||||||
|
start: last.0.start,
|
||||||
|
end: pos,
|
||||||
|
},
|
||||||
|
offset,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
let cwd = if let Some(d) = self.engine_state.env_vars.get("PWD")
|
let cwd = if let Some(d) = self.engine_state.env_vars.get("PWD")
|
||||||
{
|
{
|
||||||
@ -274,19 +325,19 @@ impl NuCompleter {
|
|||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let preceding_byte = if flat.0.start > offset {
|
let preceding_byte = if new_span.start > offset {
|
||||||
working_set
|
working_set
|
||||||
.get_span_contents(Span {
|
.get_span_contents(Span {
|
||||||
start: flat.0.start - 1,
|
start: new_span.start - 1,
|
||||||
end: flat.0.start,
|
end: new_span.start,
|
||||||
})
|
})
|
||||||
.to_vec()
|
.to_vec()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
let prefix = working_set.get_span_contents(flat.0);
|
// let prefix = working_set.get_span_contents(flat.0);
|
||||||
let prefix = String::from_utf8_lossy(prefix).to_string();
|
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||||
return file_path_completion(flat.0, &prefix, &cwd)
|
let mut output = file_path_completion(new_span, &prefix, &cwd)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| {
|
.map(move |x| {
|
||||||
if flat_idx == 0 {
|
if flat_idx == 0 {
|
||||||
@ -326,25 +377,14 @@ impl NuCompleter {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.chain(subcommands.into_iter())
|
.chain(subcommands.into_iter())
|
||||||
.collect();
|
.chain(commands.into_iter())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
output.dedup_by(|a, b| a.1 == b.1);
|
||||||
|
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, let's just check to see if we can complete a subcommand
|
|
||||||
// Check for subcommands
|
|
||||||
let subcommands = self.complete_commands(
|
|
||||||
&working_set,
|
|
||||||
Span {
|
|
||||||
start: expr.span.start,
|
|
||||||
end: pos,
|
|
||||||
},
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !subcommands.is_empty() {
|
|
||||||
return subcommands;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ pub fn flatten_expression(
|
|||||||
start: last.0.end,
|
start: last.0.end,
|
||||||
end: outer_span.end,
|
end: outer_span.end,
|
||||||
},
|
},
|
||||||
FlatShape::Table,
|
FlatShape::Block,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
Loading…
Reference in New Issue
Block a user