mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 08:23:24 +01:00
did_you_mean returns just the word matches (#2595)
This commit is contained in:
parent
75f8247af1
commit
bd9e598bf0
@ -132,7 +132,7 @@ pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellErr
|
|||||||
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried) {
|
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried) {
|
||||||
ShellError::labeled_error(
|
ShellError::labeled_error(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
format!("did you mean '{}'?", suggestions[0].1),
|
format!("did you mean '{}'?", suggestions[0]),
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -156,7 +156,7 @@ pub fn get_column_path_from_table_error(
|
|||||||
let suggestions: IndexSet<_> = rows
|
let suggestions: IndexSet<_> = rows
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|r| did_you_mean(&r, &column_path_tried))
|
.filter_map(|r| did_you_mean(&r, &column_path_tried))
|
||||||
.map(|s| s[0].1.to_owned())
|
.map(|s| s[0].to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
||||||
let mut names: Vec<String> = vec![];
|
let mut names: Vec<String> = vec![];
|
||||||
@ -239,7 +239,7 @@ pub fn get_column_from_row_error(
|
|||||||
column_path_tried.span,
|
column_path_tried.span,
|
||||||
format!(
|
format!(
|
||||||
"Perhaps you meant '{}'? Columns available: {}",
|
"Perhaps you meant '{}'? Columns available: {}",
|
||||||
suggestions[0].1,
|
suggestions[0],
|
||||||
&obj_source.data_descriptors().join(", ")
|
&obj_source.data_descriptors().join(", ")
|
||||||
),
|
),
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
|
@ -113,8 +113,8 @@ impl PathMember {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares a list of "sounds like" matches for the string you're trying to find
|
/// Prepares a list of "sounds like" matches (using edit distance) for the string you're trying to find
|
||||||
pub fn did_you_mean(obj_source: &Value, field_tried: &PathMember) -> Option<Vec<(usize, String)>> {
|
pub fn did_you_mean(obj_source: &Value, field_tried: &PathMember) -> Option<Vec<String>> {
|
||||||
let field_tried = match &field_tried.unspanned {
|
let field_tried = match &field_tried.unspanned {
|
||||||
UnspannedPathMember::String(string) => string.clone(),
|
UnspannedPathMember::String(string) => string.clone(),
|
||||||
UnspannedPathMember::Int(int) => format!("{}", int),
|
UnspannedPathMember::Int(int) => format!("{}", int),
|
||||||
@ -124,18 +124,68 @@ pub fn did_you_mean(obj_source: &Value, field_tried: &PathMember) -> Option<Vec<
|
|||||||
|
|
||||||
let mut possible_matches: Vec<_> = possibilities
|
let mut possible_matches: Vec<_> = possibilities
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| {
|
.map(|word| {
|
||||||
let word = x;
|
let edit_distance = natural::distance::levenshtein_distance(&word, &field_tried);
|
||||||
let distance = natural::distance::levenshtein_distance(&word, &field_tried);
|
(edit_distance, word)
|
||||||
|
|
||||||
(distance, word)
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !possible_matches.is_empty() {
|
if !possible_matches.is_empty() {
|
||||||
possible_matches.sort();
|
possible_matches.sort();
|
||||||
Some(possible_matches)
|
let words_matched: Vec<String> = possible_matches.into_iter().map(|m| m.1).collect();
|
||||||
|
Some(words_matched)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::UntaggedValue;
|
||||||
|
use indexmap::indexmap;
|
||||||
|
use nu_source::Tag;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn did_you_mean_returns_possible_column_matches() {
|
||||||
|
let value = UntaggedValue::row(indexmap! {
|
||||||
|
"dog".to_string() => UntaggedValue::int(1).into(),
|
||||||
|
"cat".to_string() => UntaggedValue::int(1).into(),
|
||||||
|
"alt".to_string() => UntaggedValue::int(1).into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let source = Value {
|
||||||
|
tag: Tag::unknown(),
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
|
||||||
|
let guess = PathMember {
|
||||||
|
unspanned: UnspannedPathMember::String("hat".to_string()),
|
||||||
|
span: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Some(vec![
|
||||||
|
"cat".to_string(),
|
||||||
|
"alt".to_string(),
|
||||||
|
"dog".to_string()
|
||||||
|
]),
|
||||||
|
did_you_mean(&source, &guess)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn did_you_mean_returns_no_matches_when_empty() {
|
||||||
|
let empty_source = Value {
|
||||||
|
tag: Tag::unknown(),
|
||||||
|
value: UntaggedValue::row(indexmap! {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let guess = PathMember {
|
||||||
|
unspanned: UnspannedPathMember::String("hat".to_string()),
|
||||||
|
span: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(None, did_you_mean(&empty_source, &guess))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -239,7 +239,7 @@ where
|
|||||||
let suggestions: IndexSet<_> = rows
|
let suggestions: IndexSet<_> = rows
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|r| nu_protocol::did_you_mean(&r, &column_path_tried))
|
.filter_map(|r| nu_protocol::did_you_mean(&r, &column_path_tried))
|
||||||
.map(|s| s[0].1.to_owned())
|
.map(|s| s[0].to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
||||||
let mut names: Vec<String> = vec![];
|
let mut names: Vec<String> = vec![];
|
||||||
@ -316,7 +316,7 @@ where
|
|||||||
column_path_tried.span,
|
column_path_tried.span,
|
||||||
format!(
|
format!(
|
||||||
"Perhaps you meant '{}'? Columns available: {}",
|
"Perhaps you meant '{}'? Columns available: {}",
|
||||||
suggestions[0].1,
|
suggestions[0],
|
||||||
&obj_source.data_descriptors().join(",")
|
&obj_source.data_descriptors().join(",")
|
||||||
),
|
),
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
@ -345,7 +345,7 @@ where
|
|||||||
if let Some(suggestions) = nu_protocol::did_you_mean(&obj_source, &column_path_tried) {
|
if let Some(suggestions) = nu_protocol::did_you_mean(&obj_source, &column_path_tried) {
|
||||||
return ShellError::labeled_error(
|
return ShellError::labeled_error(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
format!("did you mean '{}'?", suggestions[0].1),
|
format!("did you mean '{}'?", suggestions[0]),
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ impl Inc {
|
|||||||
) {
|
) {
|
||||||
Some(suggestions) => ShellError::labeled_error(
|
Some(suggestions) => ShellError::labeled_error(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
format!("did you mean '{}'?", suggestions[0].1),
|
format!("did you mean '{}'?", suggestions[0]),
|
||||||
span_for_spanned_list(fields.iter().map(|p| p.span)),
|
span_for_spanned_list(fields.iter().map(|p| p.span)),
|
||||||
),
|
),
|
||||||
None => ShellError::labeled_error(
|
None => ShellError::labeled_error(
|
||||||
|
Loading…
Reference in New Issue
Block a user