Skip to content

Commit

Permalink
feat(completion): Don't suggest fields already written in a pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Dec 3, 2017
1 parent a37222a commit bb37141
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 12 deletions.
60 changes: 48 additions & 12 deletions completion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ use either::Either;

use itertools::Itertools;

use base::ast::{walk_expr, walk_pattern, AstType, Expr, Pattern, SpannedExpr, SpannedIdent,
SpannedPattern, Typed, TypedIdent, Visitor};
use base::fnv::FnvMap;
use base::ast::{walk_expr, walk_pattern, AstType, Expr, Pattern, PatternField, SpannedExpr,
SpannedIdent, SpannedPattern, Typed, TypedIdent, Visitor};
use base::fnv::{FnvMap, FnvSet};
use base::kind::{ArcKind, Kind};
use base::metadata::Metadata;
use base::resolve;
Expand Down Expand Up @@ -901,17 +901,37 @@ pub fn suggest<T>(env: &T, expr: &SpannedExpr<Symbol>, pos: BytePos) -> Vec<Sugg
where
T: TypeEnv,
{
fn suggest_fields_of_type(result: &mut Vec<Suggestion>, prefix: &str, typ: &ArcType) {
fn suggest_fields_of_type(
result: &mut Vec<Suggestion>,
types: &[PatternField<Symbol, Symbol>],
fields: &[PatternField<Symbol, SpannedPattern<Symbol>>],
prefix: &str,
typ: &ArcType,
) {
let existing_fields: FnvSet<&str> = types
.iter()
.map(|field| field.name.value.as_ref())
.chain(fields.iter().map(|field| field.name.value.as_ref()))
.collect();

let should_suggest = |name: &str| {
// Filter out fields that has already been defined in the pattern
(!existing_fields.contains(name) && name.starts_with(prefix))
// But keep exact matches to keep that suggestion when the user has typed a whole
// field
|| name == prefix
};

let fields = typ.row_iter()
.filter(|field| field.name.declared_name().starts_with(prefix))
.filter(|field| should_suggest(field.name.declared_name()))
.map(|field| {
Suggestion {
name: field.name.declared_name().into(),
typ: Either::Right(field.typ.clone()),
}
});
let types = typ.type_field_iter()
.filter(|field| field.name.declared_name().starts_with(prefix))
.filter(|field| should_suggest(field.name.declared_name()))
.map(|field| {
Suggestion {
name: field.name.declared_name().into(),
Expand Down Expand Up @@ -942,9 +962,13 @@ where
Match::Pattern(pattern) => {
let prefix = match pattern.value {
Pattern::Constructor(ref id, _) | Pattern::Ident(ref id) => id.as_ref(),
Pattern::Record { .. } => {
Pattern::Record {
ref types,
ref fields,
..
} => {
let typ = pattern.env_type_of(env);
suggest_fields_of_type(&mut result, "", &typ);
suggest_fields_of_type(&mut result, types, fields, "", &typ);
""
}
_ => "",
Expand Down Expand Up @@ -972,8 +996,16 @@ where
}
}));
}
Match::Pattern { .. } => {
suggest_fields_of_type(&mut result, ident.as_ref(), typ);
Match::Pattern(&Spanned {
value:
Pattern::Record {
ref types,
ref fields,
..
},
..
}) => {
suggest_fields_of_type(&mut result, types, fields, ident.declared_name(), typ);
}
_ => (),
},
Expand Down Expand Up @@ -1024,9 +1056,13 @@ where
}

Match::Pattern(pattern) => match pattern.value {
Pattern::Record { .. } => {
Pattern::Record {
ref types,
ref fields,
..
} => {
let typ = pattern.env_type_of(env);
suggest_fields_of_type(&mut result, "", &typ);
suggest_fields_of_type(&mut result, types, fields, "", &typ);
}
_ => result.extend(suggest.patterns.iter().map(|(name, typ)| {
Suggestion {
Expand Down
29 changes: 29 additions & 0 deletions completion/tests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,35 @@ let { } = { abc = "" }
assert_eq!(result, expected);
}

#[test]
fn dont_suggest_field_already_in_pattern() {
let _ = env_logger::init();

let text = r#"
type Test = | Test Int
let { abc, a, Test } = { Test, x = 1, abc = "", abcd = 2 }
()
"#;
let result = suggest_loc(text, 2, 12);
let expected = Ok(vec!["abcd".into()]);

assert_eq!(result, expected);
}

#[test]
fn suggest_exact_field_match_in_pattern() {
let _ = env_logger::init();

let text = r#"
let { abc } = { abc = "" }
()
"#;
let result = suggest_loc(text, 1, 8);
let expected = Ok(vec!["abc".into()]);

assert_eq!(result, expected);
}

#[test]
fn suggest_type_field_in_record_pattern_at_ident() {
let _ = env_logger::init();
Expand Down

0 comments on commit bb37141

Please sign in to comment.