use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy,
};
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
levenshtein_distance, Span,
};
use nu_utils::IgnoreCaseExt;
use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP};
use std::sync::Arc;
#[derive(Clone)]
pub struct FileCompletion {
engine_state: Arc<EngineState>,
}
impl FileCompletion {
pub fn new(engine_state: Arc<EngineState>) -> Self {
Self { engine_state }
}
}
impl Completer for FileCompletion {
fn fetch(
&mut self,
working_set: &StateWorkingSet,
prefix: Vec<u8>,
span: Span,
offset: usize,
_: usize,
options: &CompletionOptions,
) -> Vec<Suggestion> {
let AdjustView {
prefix,
span,
readjusted,
} = adjust_if_intermediate(&prefix, working_set, span);
let output: Vec<_> = complete_item(
readjusted,
span,
&prefix,
&self.engine_state.current_work_dir(),
options,
)
.into_iter()
.map(move |x| Suggestion {
value: x.1,
description: None,
extra: None,
span: reedline::Span {
start: x.0.start - offset,
end: x.0.end - offset,
},
append_whitespace: false,
})
.collect();
output
}
fn sort(&self, items: Vec<Suggestion>, prefix: Vec<u8>) -> Vec<Suggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| {
a.value
.trim_end_matches(SEP)
.cmp(b.value.trim_end_matches(SEP))
});
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.value);
let b_distance = levenshtein_distance(&prefix_str, &b.value);
a_distance.cmp(&b_distance)
});
}
_ => (),
}
let mut hidden: Vec<Suggestion> = vec![];
let mut non_hidden: Vec<Suggestion> = vec![];
for item in sorted_items.into_iter() {
let item_path = Path::new(&item.value);
if let Some(value) = item_path.file_name() {
if let Some(value) = value.to_str() {
if value.starts_with('.') {
hidden.push(item);
} else {
non_hidden.push(item);
}
}
}
}
non_hidden.append(&mut hidden);
non_hidden
}
}
pub fn file_path_completion(
span: nu_protocol::Span,
partial: &str,
cwd: &str,
options: &CompletionOptions,
) -> Vec<(nu_protocol::Span, String)> {
complete_item(false, span, partial, cwd, options)
}
pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
if !options.case_sensitive {
return options
.match_algorithm
.matches_str(&from.to_folded_case(), &partial.to_folded_case());
}
options.match_algorithm.matches_str(from, partial)
}