mirror of
https://github.com/nushell/nushell
synced 2024-11-10 23:24:14 +00:00
Add MatchAlgorithm for completion suggestions (#5244)
* Pass completion options to each fetch() call * Add MatchAlgorithm to CompletionOptions * Add unit test for MatchAlgorithm * Pass completion options to directory completer
This commit is contained in:
parent
667eb27d1b
commit
e6a70f9846
12 changed files with 151 additions and 64 deletions
|
@ -1,4 +1,4 @@
|
|||
use crate::completions::SortBy;
|
||||
use crate::completions::{CompletionOptions, SortBy};
|
||||
use nu_protocol::{engine::StateWorkingSet, levenshtein_distance, Span};
|
||||
use reedline::Suggestion;
|
||||
|
||||
|
@ -12,6 +12,7 @@ pub trait Completer {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion>;
|
||||
|
||||
fn get_sort_by(&self) -> SortBy {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::completions::{file_completions::file_path_completion, Completer, SortBy};
|
||||
use crate::completions::{
|
||||
file_completions::file_path_completion, Completer, CompletionOptions, MatchAlgorithm, SortBy,
|
||||
};
|
||||
use nu_parser::{trim_quotes, FlatShape};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
|
@ -30,7 +32,11 @@ impl CommandCompletion {
|
|||
}
|
||||
}
|
||||
|
||||
fn external_command_completion(&self, prefix: &str) -> Vec<String> {
|
||||
fn external_command_completion(
|
||||
&self,
|
||||
prefix: &str,
|
||||
match_algorithm: MatchAlgorithm,
|
||||
) -> Vec<String> {
|
||||
let mut executables = vec![];
|
||||
|
||||
let paths = self.engine_state.env_vars.get("PATH");
|
||||
|
@ -51,7 +57,8 @@ impl CommandCompletion {
|
|||
) && matches!(
|
||||
item.path()
|
||||
.file_name()
|
||||
.map(|x| x.to_string_lossy().starts_with(prefix)),
|
||||
.map(|x| match_algorithm
|
||||
.matches_str(&x.to_string_lossy(), prefix)),
|
||||
Some(true)
|
||||
) && is_executable::is_executable(&item.path())
|
||||
{
|
||||
|
@ -74,11 +81,14 @@ impl CommandCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
find_externals: bool,
|
||||
match_algorithm: MatchAlgorithm,
|
||||
) -> Vec<Suggestion> {
|
||||
let prefix = working_set.get_span_contents(span);
|
||||
let partial = working_set.get_span_contents(span);
|
||||
|
||||
let filter_predicate = |command: &[u8]| match_algorithm.matches_u8(command, partial);
|
||||
|
||||
let results = working_set
|
||||
.find_commands_by_prefix(prefix)
|
||||
.find_commands_by_predicate(filter_predicate)
|
||||
.into_iter()
|
||||
.map(move |x| Suggestion {
|
||||
value: String::from_utf8_lossy(&x.0).to_string(),
|
||||
|
@ -90,12 +100,29 @@ impl CommandCompletion {
|
|||
},
|
||||
});
|
||||
|
||||
let results_aliases =
|
||||
working_set
|
||||
.find_aliases_by_prefix(prefix)
|
||||
let results_aliases = working_set
|
||||
.find_aliases_by_predicate(filter_predicate)
|
||||
.into_iter()
|
||||
.map(move |x| Suggestion {
|
||||
value: String::from_utf8_lossy(&x).to_string(),
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
});
|
||||
|
||||
let mut results = results.chain(results_aliases).collect::<Vec<_>>();
|
||||
|
||||
let partial = working_set.get_span_contents(span);
|
||||
let partial = String::from_utf8_lossy(partial).to_string();
|
||||
let results = if find_externals {
|
||||
let results_external = self
|
||||
.external_command_completion(&partial, match_algorithm)
|
||||
.into_iter()
|
||||
.map(move |x| Suggestion {
|
||||
value: String::from_utf8_lossy(&x).to_string(),
|
||||
value: x,
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
|
@ -104,24 +131,6 @@ impl CommandCompletion {
|
|||
},
|
||||
});
|
||||
|
||||
let mut results = results.chain(results_aliases).collect::<Vec<_>>();
|
||||
|
||||
let prefix = working_set.get_span_contents(span);
|
||||
let prefix = String::from_utf8_lossy(prefix).to_string();
|
||||
let results = if find_externals {
|
||||
let results_external =
|
||||
self.external_command_completion(&prefix)
|
||||
.into_iter()
|
||||
.map(move |x| Suggestion {
|
||||
value: x,
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
});
|
||||
|
||||
for external in results_external {
|
||||
if results.contains(&external) {
|
||||
results.push(Suggestion {
|
||||
|
@ -152,6 +161,7 @@ impl Completer for CommandCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let last = self
|
||||
.flattened
|
||||
|
@ -180,6 +190,7 @@ impl Completer for CommandCompletion {
|
|||
},
|
||||
offset,
|
||||
false,
|
||||
options.match_algorithm,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
|
@ -194,7 +205,7 @@ impl Completer for CommandCompletion {
|
|||
|| ((span.end - span.start) == 0)
|
||||
{
|
||||
// we're in a gap or at a command
|
||||
self.complete_commands(working_set, span, offset, true)
|
||||
self.complete_commands(working_set, span, offset, true, options.match_algorithm)
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
@ -221,7 +232,7 @@ impl Completer for CommandCompletion {
|
|||
// let prefix = working_set.get_span_contents(flat.0);
|
||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||
|
||||
file_path_completion(span, &prefix, &cwd)
|
||||
file_path_completion(span, &prefix, &cwd, options.match_algorithm)
|
||||
.into_iter()
|
||||
.map(move |x| {
|
||||
if self.flat_idx == 0 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::completions::{
|
||||
CommandCompletion, Completer, CustomCompletion, DirectoryCompletion, DotNuCompletion,
|
||||
FileCompletion, FlagCompletion, VariableCompletion,
|
||||
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
||||
DotNuCompletion, FileCompletion, FlagCompletion, VariableCompletion,
|
||||
};
|
||||
use nu_parser::{flatten_expression, parse, FlatShape};
|
||||
use nu_protocol::{
|
||||
|
@ -35,8 +35,11 @@ impl NuCompleter {
|
|||
offset: usize,
|
||||
pos: usize,
|
||||
) -> Vec<Suggestion> {
|
||||
let options = CompletionOptions::default();
|
||||
|
||||
// Fetch
|
||||
let mut suggestions = completer.fetch(working_set, prefix.clone(), new_span, offset, pos);
|
||||
let mut suggestions =
|
||||
completer.fetch(working_set, prefix.clone(), new_span, offset, pos, &options);
|
||||
|
||||
// Sort
|
||||
suggestions = completer.sort(suggestions, prefix);
|
||||
|
|
|
@ -5,11 +5,38 @@ pub enum SortBy {
|
|||
None,
|
||||
}
|
||||
|
||||
/// Describes how suggestions should be matched.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum MatchAlgorithm {
|
||||
/// Only show suggestions which begin with the given input
|
||||
///
|
||||
/// Example:
|
||||
/// "git switch" is matched by "git sw"
|
||||
Prefix,
|
||||
}
|
||||
|
||||
impl MatchAlgorithm {
|
||||
/// Returns whether the `needle` search text matches the given `haystack`.
|
||||
pub fn matches_str(&self, haystack: &str, needle: &str) -> bool {
|
||||
match *self {
|
||||
MatchAlgorithm::Prefix => haystack.starts_with(needle),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the `needle` search text matches the given `haystack`.
|
||||
pub fn matches_u8(&self, haystack: &[u8], needle: &[u8]) -> bool {
|
||||
match *self {
|
||||
MatchAlgorithm::Prefix => haystack.starts_with(needle),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CompletionOptions {
|
||||
pub case_sensitive: bool,
|
||||
pub positional: bool,
|
||||
pub sort_by: SortBy,
|
||||
pub match_algorithm: MatchAlgorithm,
|
||||
}
|
||||
|
||||
impl Default for CompletionOptions {
|
||||
|
@ -18,6 +45,25 @@ impl Default for CompletionOptions {
|
|||
case_sensitive: true,
|
||||
positional: true,
|
||||
sort_by: SortBy::Ascending,
|
||||
match_algorithm: MatchAlgorithm::Prefix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::MatchAlgorithm;
|
||||
|
||||
#[test]
|
||||
fn match_algorithm_prefix() {
|
||||
let algorithm = MatchAlgorithm::Prefix;
|
||||
|
||||
assert!(algorithm.matches_str("example text", ""));
|
||||
assert!(algorithm.matches_str("example text", "examp"));
|
||||
assert!(!algorithm.matches_str("example text", "text"));
|
||||
|
||||
assert!(algorithm.matches_u8(&[1, 2, 3], &[]));
|
||||
assert!(algorithm.matches_u8(&[1, 2, 3], &[1, 2]));
|
||||
assert!(!algorithm.matches_u8(&[1, 2, 3], &[2, 3]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::completions::{Completer, CompletionOptions, SortBy};
|
||||
use crate::completions::{Completer, CompletionOptions, MatchAlgorithm, SortBy};
|
||||
use nu_engine::eval_call;
|
||||
use nu_protocol::{
|
||||
ast::{Argument, Call, Expr, Expression},
|
||||
|
@ -97,6 +97,7 @@ impl Completer for CustomCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
pos: usize,
|
||||
_options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
// Line position
|
||||
let line_pos = pos - offset;
|
||||
|
@ -169,6 +170,7 @@ impl Completer for CustomCompletion {
|
|||
} else {
|
||||
SortBy::None
|
||||
},
|
||||
match_algorithm: MatchAlgorithm::Prefix,
|
||||
}
|
||||
} else {
|
||||
CompletionOptions::default()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::completions::{file_path_completion, Completer};
|
||||
use crate::completions::{file_path_completion, Completer, CompletionOptions};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
levenshtein_distance, Span,
|
||||
|
@ -28,6 +28,7 @@ impl Completer for DirectoryCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let cwd = if let Some(d) = self.engine_state.env_vars.get("PWD") {
|
||||
match d.as_string() {
|
||||
|
@ -37,10 +38,10 @@ impl Completer for DirectoryCompletion {
|
|||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||
let partial = String::from_utf8_lossy(&prefix).to_string();
|
||||
|
||||
// Filter only the folders
|
||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd)
|
||||
let output: Vec<_> = file_path_completion(span, &partial, &cwd, options.match_algorithm)
|
||||
.into_iter()
|
||||
.filter_map(move |x| {
|
||||
if x.1.ends_with(SEP) {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::completions::{file_path_completion, partial_from, Completer, SortBy};
|
||||
use crate::completions::{
|
||||
file_path_completion, partial_from, Completer, CompletionOptions, SortBy,
|
||||
};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
Span,
|
||||
|
@ -26,6 +28,7 @@ impl Completer for DotNuCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
||||
let mut search_dirs: Vec<String> = vec![];
|
||||
|
@ -88,7 +91,7 @@ impl Completer for DotNuCompletion {
|
|||
let output: Vec<Suggestion> = search_dirs
|
||||
.into_iter()
|
||||
.flat_map(|it| {
|
||||
file_path_completion(span, &partial, &it)
|
||||
file_path_completion(span, &partial, &it, options.match_algorithm)
|
||||
.into_iter()
|
||||
.filter(|it| {
|
||||
// Different base dir, so we list the .nu files or folders
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::completions::Completer;
|
||||
use crate::completions::{Completer, CompletionOptions, MatchAlgorithm};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
levenshtein_distance, Span,
|
||||
|
@ -28,6 +28,7 @@ impl Completer for FileCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let cwd = if let Some(d) = self.engine_state.env_vars.get("PWD") {
|
||||
match d.as_string() {
|
||||
|
@ -38,7 +39,7 @@ impl Completer for FileCompletion {
|
|||
"".to_string()
|
||||
};
|
||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd)
|
||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options.match_algorithm)
|
||||
.into_iter()
|
||||
.map(move |x| Suggestion {
|
||||
value: x.1,
|
||||
|
@ -110,6 +111,7 @@ pub fn file_path_completion(
|
|||
span: nu_protocol::Span,
|
||||
partial: &str,
|
||||
cwd: &str,
|
||||
match_algorithm: MatchAlgorithm,
|
||||
) -> Vec<(nu_protocol::Span, String)> {
|
||||
let (base_dir_name, partial) = partial_from(partial);
|
||||
|
||||
|
@ -125,7 +127,7 @@ pub fn file_path_completion(
|
|||
.filter_map(|entry| {
|
||||
entry.ok().and_then(|entry| {
|
||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||
if matches(&partial, &file_name) {
|
||||
if matches(&partial, &file_name, match_algorithm) {
|
||||
let mut path = format!("{}{}", base_dir_name, file_name);
|
||||
if entry.path().is_dir() {
|
||||
path.push(SEP);
|
||||
|
@ -153,7 +155,6 @@ pub fn file_path_completion(
|
|||
Vec::new()
|
||||
}
|
||||
|
||||
pub fn matches(partial: &str, from: &str) -> bool {
|
||||
from.to_ascii_lowercase()
|
||||
.starts_with(&partial.to_ascii_lowercase())
|
||||
pub fn matches(partial: &str, from: &str, match_algorithm: MatchAlgorithm) -> bool {
|
||||
match_algorithm.matches_str(&from.to_ascii_lowercase(), &partial.to_ascii_lowercase())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::completions::Completer;
|
||||
use crate::completions::{Completer, CompletionOptions};
|
||||
use nu_protocol::{
|
||||
ast::{Expr, Expression},
|
||||
engine::StateWorkingSet,
|
||||
|
@ -26,6 +26,7 @@ impl Completer for FlagCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
// Check if it's a flag
|
||||
if let Expr::Call(call) = &self.expression.expr {
|
||||
|
@ -40,7 +41,8 @@ impl Completer for FlagCompletion {
|
|||
let mut named = vec![0; short.len_utf8()];
|
||||
short.encode_utf8(&mut named);
|
||||
named.insert(0, b'-');
|
||||
if named.starts_with(&prefix) {
|
||||
|
||||
if options.match_algorithm.matches_u8(&named, &prefix) {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(&named).to_string(),
|
||||
description: Some(flag_desc.to_string()),
|
||||
|
@ -60,7 +62,8 @@ impl Completer for FlagCompletion {
|
|||
let mut named = named.long.as_bytes().to_vec();
|
||||
named.insert(0, b'-');
|
||||
named.insert(0, b'-');
|
||||
if named.starts_with(&prefix) {
|
||||
|
||||
if options.match_algorithm.matches_u8(&named, &prefix) {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(&named).to_string(),
|
||||
description: Some(flag_desc.to_string()),
|
||||
|
|
|
@ -12,7 +12,7 @@ mod variable_completions;
|
|||
pub use base::Completer;
|
||||
pub use command_completions::CommandCompletion;
|
||||
pub use completer::NuCompleter;
|
||||
pub use completion_options::{CompletionOptions, SortBy};
|
||||
pub use completion_options::{CompletionOptions, MatchAlgorithm, SortBy};
|
||||
pub use custom_completions::CustomCompletion;
|
||||
pub use directory_completions::DirectoryCompletion;
|
||||
pub use dotnu_completions::DotNuCompletion;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::completions::Completer;
|
||||
use crate::completions::{Completer, CompletionOptions};
|
||||
use nu_engine::eval_variable;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
|
@ -37,6 +37,7 @@ impl Completer for VariableCompletion {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let mut output = vec![];
|
||||
let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
|
||||
|
@ -54,7 +55,10 @@ impl Completer for VariableCompletion {
|
|||
// Completion for $env.<tab>
|
||||
if var_str.as_str() == "$env" {
|
||||
for env_var in self.stack.get_env_vars(&self.engine_state) {
|
||||
if env_var.0.as_bytes().starts_with(&prefix) {
|
||||
if options
|
||||
.match_algorithm
|
||||
.matches_u8(env_var.0.as_bytes(), &prefix)
|
||||
{
|
||||
output.push(Suggestion {
|
||||
value: env_var.0,
|
||||
description: None,
|
||||
|
@ -155,7 +159,10 @@ impl Completer for VariableCompletion {
|
|||
|
||||
// Variable completion (e.g: $en<tab> to complete $env)
|
||||
for builtin in builtins {
|
||||
if builtin.as_bytes().starts_with(&prefix) {
|
||||
if options
|
||||
.match_algorithm
|
||||
.matches_u8(builtin.as_bytes(), &prefix)
|
||||
{
|
||||
output.push(Suggestion {
|
||||
value: builtin.to_string(),
|
||||
description: None,
|
||||
|
@ -168,7 +175,7 @@ impl Completer for VariableCompletion {
|
|||
// Working set scope vars
|
||||
for scope in &working_set.delta.scope {
|
||||
for v in &scope.vars {
|
||||
if v.0.starts_with(&prefix) {
|
||||
if options.match_algorithm.matches_u8(v.0, &prefix) {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
description: None,
|
||||
|
@ -182,7 +189,7 @@ impl Completer for VariableCompletion {
|
|||
// Permanent state vars
|
||||
for scope in &self.engine_state.scope {
|
||||
for v in &scope.vars {
|
||||
if v.0.starts_with(&prefix) {
|
||||
if options.match_algorithm.matches_u8(v.0, &prefix) {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
description: None,
|
||||
|
|
|
@ -444,12 +444,15 @@ impl EngineState {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn find_commands_by_prefix(&self, name: &[u8]) -> Vec<(Vec<u8>, Option<String>)> {
|
||||
pub fn find_commands_by_predicate(
|
||||
&self,
|
||||
predicate: impl Fn(&[u8]) -> bool,
|
||||
) -> Vec<(Vec<u8>, Option<String>)> {
|
||||
let mut output = vec![];
|
||||
|
||||
for scope in self.scope.iter().rev() {
|
||||
for decl in &scope.decls {
|
||||
if decl.0.starts_with(name) {
|
||||
if predicate(decl.0) {
|
||||
let command = self.get_decl(*decl.1);
|
||||
output.push((decl.0.clone(), Some(command.usage().to_string())));
|
||||
}
|
||||
|
@ -459,12 +462,12 @@ impl EngineState {
|
|||
output
|
||||
}
|
||||
|
||||
pub fn find_aliases_by_prefix(&self, name: &[u8]) -> Vec<Vec<u8>> {
|
||||
pub fn find_aliases_by_predicate(&self, predicate: impl Fn(&[u8]) -> bool) -> Vec<Vec<u8>> {
|
||||
self.scope
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|scope| &scope.aliases)
|
||||
.filter(|decl| decl.0.starts_with(name))
|
||||
.filter(|decl| predicate(decl.0))
|
||||
.map(|decl| decl.0.clone())
|
||||
.collect()
|
||||
}
|
||||
|
@ -1315,34 +1318,40 @@ impl<'a> StateWorkingSet<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_commands_by_prefix(&self, name: &[u8]) -> Vec<(Vec<u8>, Option<String>)> {
|
||||
pub fn find_commands_by_predicate(
|
||||
&self,
|
||||
predicate: impl Fn(&[u8]) -> bool,
|
||||
) -> Vec<(Vec<u8>, Option<String>)> {
|
||||
let mut output = vec![];
|
||||
|
||||
for scope in self.delta.scope.iter().rev() {
|
||||
for decl in &scope.decls {
|
||||
if decl.0.starts_with(name) {
|
||||
if predicate(decl.0) {
|
||||
let command = self.get_decl(*decl.1);
|
||||
output.push((decl.0.clone(), Some(command.usage().to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut permanent = self.permanent_state.find_commands_by_prefix(name);
|
||||
let mut permanent = self.permanent_state.find_commands_by_predicate(predicate);
|
||||
|
||||
output.append(&mut permanent);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
pub fn find_aliases_by_prefix(&self, name: &[u8]) -> Vec<Vec<u8>> {
|
||||
pub fn find_aliases_by_predicate(
|
||||
&self,
|
||||
predicate: impl Fn(&[u8]) -> bool + Copy,
|
||||
) -> Vec<Vec<u8>> {
|
||||
self.delta
|
||||
.scope
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|scope| &scope.aliases)
|
||||
.filter(|decl| decl.0.starts_with(name))
|
||||
.filter(|decl| predicate(decl.0))
|
||||
.map(|decl| decl.0.clone())
|
||||
.chain(self.permanent_state.find_aliases_by_prefix(name))
|
||||
.chain(self.permanent_state.find_aliases_by_predicate(predicate))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue