summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-01-27 16:09:35 +0100
committerLaurenz <laurmaedje@gmail.com>2023-01-27 16:09:35 +0100
commita96141a3ea9d1b11ef4cdc924216d8979689e6f0 (patch)
tree0192bdd4e63f3fb3c9172faae35bf8b08c8c957d /src
parent2e039cb052fcb768027053cbf02ce396f6d7a6be (diff)
Autocomplete methods
Diffstat (limited to 'src')
-rw-r--r--src/diag.rs5
-rw-r--r--src/ide/analyze.rs2
-rw-r--r--src/ide/complete.rs15
-rw-r--r--src/model/eval.rs7
-rw-r--r--src/model/methods.rs33
-rw-r--r--src/model/mod.rs1
-rw-r--r--src/syntax/node.rs3
7 files changed, 61 insertions, 5 deletions
diff --git a/src/diag.rs b/src/diag.rs
index 5c5d9de9..054a7b03 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -140,8 +140,11 @@ impl<T> Trace<T> for SourceResult<T> {
F: Fn() -> Tracepoint,
{
self.map_err(|mut errors| {
+ if span.is_detached() {
+ return errors;
+ }
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.
let error_range = world.source(error.span.source()).range(error.span);
if range.start <= error_range.start && range.end >= error_range.end {
diff --git a/src/ide/analyze.rs b/src/ide/analyze.rs
index d8925cfc..a1ac5778 100644
--- a/src/ide/analyze.rs
+++ b/src/ide/analyze.rs
@@ -22,6 +22,8 @@ pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
return tracer.finish();
}
+ Some(ast::Expr::Str(s)) => return vec![Value::Str(s.get().into())],
+
Some(ast::Expr::FieldAccess(access)) => {
if let Some(child) = node.children().next() {
return analyze(world, &child)
diff --git a/src/ide/complete.rs b/src/ide/complete.rs
index f4b9be5e..9302b552 100644
--- a/src/ide/complete.rs
+++ b/src/ide/complete.rs
@@ -3,7 +3,7 @@ use std::collections::{BTreeSet, HashSet};
use if_chain::if_chain;
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::util::{format_eco, EcoString};
use crate::World;
@@ -271,7 +271,7 @@ fn math_completions(ctx: &mut CompletionContext) {
/// Complete field accesses.
fn complete_field_accesses(ctx: &mut CompletionContext) -> bool {
- // Behind an identifier plus dot: "emoji.|".
+ // Behind an expression plus dot: "emoji.|".
if_chain! {
if ctx.leaf.kind() == SyntaxKind::Dot
|| (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);
}
}
- _ => {}
+ _ => {
+ 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,
+ })
+ }
+ }
}
}
diff --git a/src/model/eval.rs b/src/model/eval.rs
index d52b1272..3e1ccfa6 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -46,7 +46,12 @@ pub fn eval(
let route = unsafe { Route::insert(route, id) };
let scopes = Scopes::new(Some(library));
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.
if let Some(flow) = vm.flow {
diff --git a/src/model/methods.rs b/src/model/methods.rs
index 173b95fe..1671a5c4 100644
--- a/src/model/methods.rs
+++ b/src/model/methods.rs
@@ -210,3 +210,36 @@ pub fn is_accessor(method: &str) -> bool {
fn missing_method(type_name: &str, method: &str) -> String {
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"],
+ _ => &[],
+ }
+}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index d96a314c..32b0a003 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -38,6 +38,7 @@ pub use self::dict::*;
pub use self::eval::*;
pub use self::func::*;
pub use self::library::*;
+pub use self::methods::*;
pub use self::module::*;
pub use self::realize::*;
pub use self::scope::*;
diff --git a/src/syntax/node.rs b/src/syntax/node.rs
index d133fc5d..a0fa5e1e 100644
--- a/src/syntax/node.rs
+++ b/src/syntax/node.rs
@@ -150,6 +150,7 @@ impl SyntaxNode {
}
/// Convert the child to another kind.
+ #[track_caller]
pub(super) fn convert_to_kind(&mut self, kind: SyntaxKind) {
debug_assert!(!kind.is_error());
match &mut self.0 {
@@ -295,6 +296,7 @@ struct LeafNode {
impl LeafNode {
/// Create a new leaf node.
+ #[track_caller]
fn new(kind: SyntaxKind, text: impl Into<EcoString>) -> Self {
debug_assert!(!kind.is_error());
Self { kind, text: text.into(), span: Span::detached() }
@@ -340,6 +342,7 @@ struct InnerNode {
impl InnerNode {
/// Create a new inner node with the given kind and children.
+ #[track_caller]
fn new(kind: SyntaxKind, children: Vec<SyntaxNode>) -> Self {
debug_assert!(!kind.is_error());