Autocomplete methods

This commit is contained in:
Laurenz 2023-01-27 16:09:35 +01:00
parent 2e039cb052
commit a96141a3ea
7 changed files with 61 additions and 5 deletions

View File

@ -140,8 +140,11 @@ impl<T> Trace<T> for SourceResult<T> {
F: Fn() -> Tracepoint, F: Fn() -> Tracepoint,
{ {
self.map_err(|mut errors| { self.map_err(|mut errors| {
if span.is_detached() {
return errors;
}
let range = world.source(span.source()).range(span); let range = world.source(span.source()).range(span);
for error in errors.iter_mut() { for error in errors.iter_mut().filter(|e| !e.span.is_detached()) {
// Skip traces that surround the error. // Skip traces that surround the error.
let error_range = world.source(error.span.source()).range(error.span); let error_range = world.source(error.span.source()).range(error.span);
if range.start <= error_range.start && range.end >= error_range.end { if range.start <= error_range.start && range.end >= error_range.end {

View File

@ -22,6 +22,8 @@ pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
return tracer.finish(); return tracer.finish();
} }
Some(ast::Expr::Str(s)) => return vec![Value::Str(s.get().into())],
Some(ast::Expr::FieldAccess(access)) => { Some(ast::Expr::FieldAccess(access)) => {
if let Some(child) = node.children().next() { if let Some(child) = node.children().next() {
return analyze(world, &child) return analyze(world, &child)

View File

@ -3,7 +3,7 @@ use std::collections::{BTreeSet, HashSet};
use if_chain::if_chain; use if_chain::if_chain;
use super::{analyze, plain_docs_sentence, summarize_font_family}; use super::{analyze, plain_docs_sentence, summarize_font_family};
use crate::model::{CastInfo, Scope, Value}; use crate::model::{methods_on, CastInfo, Scope, Value};
use crate::syntax::{ast, LinkedNode, Source, SyntaxKind, SyntaxNode}; use crate::syntax::{ast, LinkedNode, Source, SyntaxKind, SyntaxNode};
use crate::util::{format_eco, EcoString}; use crate::util::{format_eco, EcoString};
use crate::World; use crate::World;
@ -271,7 +271,7 @@ fn math_completions(ctx: &mut CompletionContext) {
/// Complete field accesses. /// Complete field accesses.
fn complete_field_accesses(ctx: &mut CompletionContext) -> bool { fn complete_field_accesses(ctx: &mut CompletionContext) -> bool {
// Behind an identifier plus dot: "emoji.|". // Behind an expression plus dot: "emoji.|".
if_chain! { if_chain! {
if ctx.leaf.kind() == SyntaxKind::Dot if ctx.leaf.kind() == SyntaxKind::Dot
|| (matches!(ctx.leaf.kind(), SyntaxKind::Text | SyntaxKind::MathAtom) || (matches!(ctx.leaf.kind(), SyntaxKind::Text | SyntaxKind::MathAtom)
@ -325,7 +325,16 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value) {
ctx.value_completion(Some(name.clone()), value, None); ctx.value_completion(Some(name.clone()), value, None);
} }
} }
_ => {} _ => {
for &method in methods_on(value.type_name()) {
ctx.completions.push(Completion {
kind: CompletionKind::Func,
label: method.into(),
apply: Some(format_eco!("{method}(${{}})")),
detail: None,
})
}
}
} }
} }

View File

@ -46,7 +46,12 @@ pub fn eval(
let route = unsafe { Route::insert(route, id) }; let route = unsafe { Route::insert(route, id) };
let scopes = Scopes::new(Some(library)); let scopes = Scopes::new(Some(library));
let mut vm = Vm::new(world, route.track(), tracer, id, scopes, 0); let mut vm = Vm::new(world, route.track(), tracer, id, scopes, 0);
let result = source.ast()?.eval(&mut vm); let root = match source.root().cast::<ast::Markup>() {
Some(markup) if vm.traced.is_some() => markup,
_ => source.ast()?,
};
let result = root.eval(&mut vm);
// Handle control flow. // Handle control flow.
if let Some(flow) = vm.flow { if let Some(flow) = vm.flow {

View File

@ -210,3 +210,36 @@ pub fn is_accessor(method: &str) -> bool {
fn missing_method(type_name: &str, method: &str) -> String { fn missing_method(type_name: &str, method: &str) -> String {
format!("type {type_name} has no method `{method}`") format!("type {type_name} has no method `{method}`")
} }
/// List the available methods for a type.
pub fn methods_on(type_name: &str) -> &[&'static str] {
match type_name {
"color" => &["lighten", "darken", "negate"],
"string" => &[
"len",
"at",
"contains",
"ends-with",
"find",
"first",
"last",
"match",
"matches",
"position",
"replace",
"slice",
"split",
"starts-with",
"trim",
],
"array" => &[
"all", "any", "at", "contains", "filter", "find", "first", "flatten", "fold",
"insert", "join", "last", "len", "map", "pop", "position", "push", "remove",
"rev", "slice", "sorted",
],
"dictionary" => &["at", "insert", "keys", "len", "pairs", "remove", "values"],
"function" => &["where", "with"],
"arguments" => &["named", "pos"],
_ => &[],
}
}

View File

@ -38,6 +38,7 @@ pub use self::dict::*;
pub use self::eval::*; pub use self::eval::*;
pub use self::func::*; pub use self::func::*;
pub use self::library::*; pub use self::library::*;
pub use self::methods::*;
pub use self::module::*; pub use self::module::*;
pub use self::realize::*; pub use self::realize::*;
pub use self::scope::*; pub use self::scope::*;

View File

@ -150,6 +150,7 @@ impl SyntaxNode {
} }
/// Convert the child to another kind. /// Convert the child to another kind.
#[track_caller]
pub(super) fn convert_to_kind(&mut self, kind: SyntaxKind) { pub(super) fn convert_to_kind(&mut self, kind: SyntaxKind) {
debug_assert!(!kind.is_error()); debug_assert!(!kind.is_error());
match &mut self.0 { match &mut self.0 {
@ -295,6 +296,7 @@ struct LeafNode {
impl LeafNode { impl LeafNode {
/// Create a new leaf node. /// Create a new leaf node.
#[track_caller]
fn new(kind: SyntaxKind, text: impl Into<EcoString>) -> Self { fn new(kind: SyntaxKind, text: impl Into<EcoString>) -> Self {
debug_assert!(!kind.is_error()); debug_assert!(!kind.is_error());
Self { kind, text: text.into(), span: Span::detached() } Self { kind, text: text.into(), span: Span::detached() }
@ -340,6 +342,7 @@ struct InnerNode {
impl InnerNode { impl InnerNode {
/// Create a new inner node with the given kind and children. /// Create a new inner node with the given kind and children.
#[track_caller]
fn new(kind: SyntaxKind, children: Vec<SyntaxNode>) -> Self { fn new(kind: SyntaxKind, children: Vec<SyntaxNode>) -> Self {
debug_assert!(!kind.is_error()); debug_assert!(!kind.is_error());