Skip to content

Commit

Permalink
Add context completion
Browse files Browse the repository at this point in the history
  • Loading branch information
jneem committed Sep 15, 2023
1 parent b2459d3 commit 2608a34
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 144 deletions.
106 changes: 77 additions & 29 deletions core/src/term/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,12 @@ impl Traverse<RichTerm> for RuntimeContract {
Ok(RuntimeContract { contract, ..self })
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&RichTerm) -> TraverseControl<U>) -> Option<U> {
self.contract.traverse_ref(f)
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
self.contract.traverse_ref(f, state)
}
}

Expand Down Expand Up @@ -455,8 +459,12 @@ impl Traverse<RichTerm> for LabeledType {
.map(|typ| LabeledType { typ, label })
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&RichTerm) -> TraverseControl<U>) -> Option<U> {
self.typ.traverse_ref(f)
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
self.typ.traverse_ref(f, state)
}
}

Expand Down Expand Up @@ -568,11 +576,15 @@ impl Traverse<RichTerm> for TypeAnnotation {
Ok(TypeAnnotation { typ, contracts })
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&RichTerm) -> TraverseControl<U>) -> Option<U> {
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
self.contracts
.iter()
.find_map(|c| c.traverse_ref(f))
.or_else(|| self.typ.as_ref().and_then(|t| t.traverse_ref(f)))
.find_map(|c| c.traverse_ref(f, state))
.or_else(|| self.typ.as_ref().and_then(|t| t.traverse_ref(f, state)))
}
}

Expand Down Expand Up @@ -1539,16 +1551,22 @@ impl RichTerm {
}

/// Flow control for tree traverals.
pub enum TraverseControl<U> {
pub enum TraverseControl<S, U> {
/// Normal control flow: continue recursing into the children.
///
/// Pass the state &S to all children.
ContinueWithScope(S),
/// Normal control flow: continue recursing into the children.
///
/// The state that was passed to the parent will be re-used for the children.
Continue,
/// Skip this branch of the tree.
SkipBranch,
/// Finish traversing immediately (and return a value).
Return(U),
}

impl<U> From<Option<U>> for TraverseControl<U> {
impl<S, U> From<Option<U>> for TraverseControl<S, U> {
fn from(value: Option<U>) -> Self {
match value {
Some(u) => TraverseControl::Return(u),
Expand All @@ -1571,7 +1589,19 @@ pub trait Traverse<T>: Sized {
///
/// Through its return value, `f` can short-circuit one branch of the traversal or
/// the entire traversal.
fn traverse_ref<U>(&self, f: &mut dyn FnMut(&T) -> TraverseControl<U>) -> Option<U>;
///
/// This traversal can make use of "scoped" state. The `scope` argument is passed to
/// each callback, and the callback can optionally override that scope just for its
/// own subtree in the traversal. For example, when traversing a tree of terms you can
/// maintain an environment. Most of the time the environment should get passed around
/// unchanged, but a `Term::Let` should override the environment of its subtree. It
/// does this by returning a `TraverseControl::ContinueWithScope` that contains the
/// new environment.
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&T, &S) -> TraverseControl<S, U>,
scope: &S,
) -> Option<U>;
}

impl Traverse<RichTerm> for RichTerm {
Expand Down Expand Up @@ -1758,16 +1788,22 @@ impl Traverse<RichTerm> for RichTerm {
}
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&RichTerm) -> TraverseControl<U>) -> Option<U> {
match f(self) {
TraverseControl::Continue => {}
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
let child_state = match f(self, state) {
TraverseControl::Continue => None,
TraverseControl::ContinueWithScope(s) => Some(s),
TraverseControl::SkipBranch => {
return None;
}
TraverseControl::Return(ret) => {
return Some(ret);
}
};
let state = child_state.as_ref().unwrap_or(state);

match &*self.term {
Term::Null
Expand All @@ -1784,37 +1820,45 @@ impl Traverse<RichTerm> for RichTerm {
| Term::RuntimeError(_) => None,
Term::StrChunks(chunks) => chunks.iter().find_map(|ch| {
if let StrChunk::Expr(term, _) = ch {
term.traverse_ref(f)
term.traverse_ref(f, state)
} else {
None
}
}),
Term::Fun(_, t)
| Term::FunPattern(_, _, t)
| Term::Op1(_, t)
| Term::Sealed(_, t, _) => t.traverse_ref(f),
| Term::Sealed(_, t, _) => t.traverse_ref(f, state),
Term::Let(_, t1, t2, _)
| Term::LetPattern(_, _, t1, t2)
| Term::App(t1, t2)
| Term::Op2(_, t1, t2) => t1.traverse_ref(f).or_else(|| t2.traverse_ref(f)),
Term::Record(data) => data.fields.values().find_map(|field| field.traverse_ref(f)),
| Term::Op2(_, t1, t2) => t1
.traverse_ref(f, state)
.or_else(|| t2.traverse_ref(f, state)),
Term::Record(data) => data
.fields
.values()
.find_map(|field| field.traverse_ref(f, state)),
Term::RecRecord(data, dyn_data, _) => data
.fields
.values()
.find_map(|field| field.traverse_ref(f))
.find_map(|field| field.traverse_ref(f, state))
.or_else(|| {
dyn_data.iter().find_map(|(id, field)| {
id.traverse_ref(f).or_else(|| field.traverse_ref(f))
id.traverse_ref(f, state)
.or_else(|| field.traverse_ref(f, state))
})
}),
Term::Match { cases, default } => cases
.iter()
.find_map(|(_id, t)| t.traverse_ref(f))
.or_else(|| default.as_ref().and_then(|t| t.traverse_ref(f))),
Term::Array(ts, _) => ts.iter().find_map(|t| t.traverse_ref(f)),
Term::OpN(_, ts) => ts.iter().find_map(|t| t.traverse_ref(f)),
Term::Annotated(annot, t) => t.traverse_ref(f).or_else(|| annot.traverse_ref(f)),
Term::Type(ty) => ty.traverse_ref(f),
.find_map(|(_id, t)| t.traverse_ref(f, state))
.or_else(|| default.as_ref().and_then(|t| t.traverse_ref(f, state))),
Term::Array(ts, _) => ts.iter().find_map(|t| t.traverse_ref(f, state)),
Term::OpN(_, ts) => ts.iter().find_map(|t| t.traverse_ref(f, state)),
Term::Annotated(annot, t) => t
.traverse_ref(f, state)
.or_else(|| annot.traverse_ref(f, state)),
Term::Type(ty) => ty.traverse_ref(f, state),
}
}
}
Expand All @@ -1833,12 +1877,16 @@ impl Traverse<Type> for RichTerm {
self.traverse(&f_on_term, state, order)
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&Type) -> TraverseControl<U>) -> Option<U> {
let mut f_on_term = |rt: &RichTerm| match &*rt.term {
Term::Type(ty) => ty.traverse_ref(f).into(),
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&Type, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
let mut f_on_term = |rt: &RichTerm, state: &S| match &*rt.term {
Term::Type(ty) => ty.traverse_ref(f, state).into(),
_ => TraverseControl::Continue,
};
self.traverse_ref(&mut f_on_term)
self.traverse_ref(&mut f_on_term, state)
}
}

Expand Down
12 changes: 8 additions & 4 deletions core/src/term/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,19 @@ impl Traverse<RichTerm> for Field {
})
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&RichTerm) -> TraverseControl<U>) -> Option<U> {
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
self.metadata
.annotation
.traverse_ref(f)
.or_else(|| self.value.as_ref().and_then(|v| v.traverse_ref(f)))
.traverse_ref(f, state)
.or_else(|| self.value.as_ref().and_then(|v| v.traverse_ref(f, state)))
.or_else(|| {
self.pending_contracts
.iter()
.find_map(|c| c.traverse_ref(f))
.find_map(|c| c.traverse_ref(f, state))
})
}
}
Expand Down
58 changes: 39 additions & 19 deletions core/src/typ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,11 +625,16 @@ impl Traverse<Type> for RecordRows {
Ok(RecordRows(inner))
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&Type) -> TraverseControl<U>) -> Option<U> {
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&Type, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
match &self.0 {
RecordRowsF::Extend { row, tail } => {
row.typ.traverse_ref(f).or_else(|| tail.traverse_ref(f))
}
RecordRowsF::Extend { row, tail } => row
.typ
.traverse_ref(f, state)
.or_else(|| tail.traverse_ref(f, state)),
_ => None,
}
}
Expand Down Expand Up @@ -1056,10 +1061,13 @@ impl Type {

/// Searches for a `TypeF::Flat`. If one is found, returns the term it contains.
pub fn find_flat(&self) -> Option<RichTerm> {
self.traverse_ref(&mut |ty: &Type| match &ty.typ {
TypeF::Flat(f) => TraverseControl::Return(f.clone()),
_ => TraverseControl::Continue,
})
self.traverse_ref(
&mut |ty: &Type, _: &()| match &ty.typ {
TypeF::Flat(f) => TraverseControl::Return(f.clone()),
_ => TraverseControl::Continue,
},
&(),
)
}
}

Expand Down Expand Up @@ -1099,16 +1107,22 @@ impl Traverse<Type> for Type {
}
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&Type) -> TraverseControl<U>) -> Option<U> {
match f(self) {
TraverseControl::Continue => {}
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&Type, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
let child_state = match f(self, state) {
TraverseControl::Continue => None,
TraverseControl::ContinueWithScope(s) => Some(s),
TraverseControl::SkipBranch => {
return None;
}
TraverseControl::Return(ret) => {
return Some(ret);
}
};
let state = child_state.as_ref().unwrap_or(state);

match &self.typ {
TypeF::Dyn
Expand All @@ -1119,12 +1133,14 @@ impl Traverse<Type> for Type {
| TypeF::Var(_)
| TypeF::Enum(_)
| TypeF::Wildcard(_) => None,
TypeF::Flat(rt) => rt.traverse_ref(f),
TypeF::Arrow(t1, t2) => t1.traverse_ref(f).or_else(|| t2.traverse_ref(f)),
TypeF::Flat(rt) => rt.traverse_ref(f, state),
TypeF::Arrow(t1, t2) => t1
.traverse_ref(f, state)
.or_else(|| t2.traverse_ref(f, state)),
TypeF::Forall { body: t, .. }
| TypeF::Dict { type_fields: t, .. }
| TypeF::Array(t) => t.traverse_ref(f),
TypeF::Record(rrows) => rrows.traverse_ref(f),
| TypeF::Array(t) => t.traverse_ref(f, state),
TypeF::Record(rrows) => rrows.traverse_ref(f, state),
}
}
}
Expand All @@ -1144,18 +1160,22 @@ impl Traverse<RichTerm> for Type {
self.traverse(&f_on_type, state, order)
}

fn traverse_ref<U>(&self, f: &mut dyn FnMut(&RichTerm) -> TraverseControl<U>) -> Option<U> {
let mut f_on_type = |ty: &Type| match &ty.typ {
fn traverse_ref<S, U>(
&self,
f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
state: &S,
) -> Option<U> {
let mut f_on_type = |ty: &Type, s: &S| match &ty.typ {
TypeF::Flat(t) => {
if let Some(ret) = t.traverse_ref(f) {
if let Some(ret) = t.traverse_ref(f, s) {
TraverseControl::Return(ret)
} else {
TraverseControl::SkipBranch
}
}
_ => TraverseControl::Continue,
};
self.traverse_ref(&mut f_on_type)
self.traverse_ref(&mut f_on_type, state)
}
}

Expand Down
15 changes: 14 additions & 1 deletion lsp/nls/src/field_walker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ pub struct DefWithPath {
pub metadata: Option<FieldMetadata>,
}

impl DefWithPath {
pub fn completion_item(&self) -> CompletionItem {
CompletionItem {
label: ident_quoted(&self.ident.into()),
detail: self.metadata.as_ref().and_then(metadata_detail),
kind: Some(CompletionItemKind::Property),
documentation: self.metadata.as_ref().and_then(metadata_doc),
..Default::default()
}
}
}

#[cfg(test)]
impl DefWithPath {
pub fn path(&self) -> &[Ident] {
Expand Down Expand Up @@ -197,7 +209,7 @@ impl<'a> FieldResolver<'a> {
/// This a best-effort thing; it doesn't do full evaluation but it has some reasonable
/// heuristics. For example, it knows that the fields defined on a merge of two records
/// are the fields defined on either record.
fn resolve_term(&self, rt: &RichTerm) -> Vec<FieldHaver> {
pub fn resolve_term(&self, rt: &RichTerm) -> Vec<FieldHaver> {
let term_fields = match rt.term.as_ref() {
Term::Record(data) | Term::RecRecord(data, ..) => {
vec![FieldHaver::RecordTerm(data.clone())]
Expand Down Expand Up @@ -229,6 +241,7 @@ impl<'a> FieldResolver<'a> {
let defs = self.resolve_annot(annot);
defs.chain(self.resolve_term(term)).collect()
}
Term::Type(typ) => self.resolve_type(typ),
_ => Default::default(),
};

Expand Down
Loading

0 comments on commit 2608a34

Please sign in to comment.