mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-04 10:18:16 +00:00
feat: complete array/tuple literals (#201)
* dev: introduce type checking on arrays/tuples * dev: complete array literals * dev: complete columns/ros/gutter/column-gutter/row-gutter/size/dash array types * chore: reduce two todos
This commit is contained in:
parent
f6f2454d37
commit
fa0899b7cf
7 changed files with 332 additions and 175 deletions
|
@ -295,7 +295,17 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
fn check_array(&mut self, root: LinkedNode<'_>) -> Option<FlowType> {
|
||||
let _arr: ast::Array = root.cast()?;
|
||||
|
||||
Some(FlowType::Array)
|
||||
let mut elements = EcoVec::new();
|
||||
|
||||
for elem in root.children() {
|
||||
let ty = self.check(elem);
|
||||
if matches!(ty, FlowType::Clause) {
|
||||
continue;
|
||||
}
|
||||
elements.push(ty);
|
||||
}
|
||||
|
||||
Some(FlowType::Tuple(elements))
|
||||
}
|
||||
|
||||
fn check_dict(&mut self, root: LinkedNode<'_>) -> Option<FlowType> {
|
||||
|
@ -700,6 +710,8 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
candidates.push(f.ret.clone());
|
||||
}
|
||||
FlowType::Dict(_v) => {}
|
||||
FlowType::Tuple(_v) => {}
|
||||
FlowType::Array(_v) => {}
|
||||
// todo: with
|
||||
FlowType::With(_e) => {}
|
||||
FlowType::Args(_e) => {}
|
||||
|
@ -716,7 +728,6 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
}
|
||||
}
|
||||
|
||||
FlowType::Array => {}
|
||||
FlowType::Clause => {}
|
||||
FlowType::Undef => {}
|
||||
FlowType::Content => {}
|
||||
|
@ -902,7 +913,8 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
FlowType::Value(..) => e,
|
||||
FlowType::ValueDoc(..) => e,
|
||||
|
||||
FlowType::Array => e,
|
||||
FlowType::Tuple(..) => e,
|
||||
FlowType::Array(..) => e,
|
||||
FlowType::Clause => e,
|
||||
FlowType::Undef => e,
|
||||
FlowType::Content => e,
|
||||
|
@ -957,7 +969,8 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
}
|
||||
_ => {}
|
||||
},
|
||||
FlowType::Array => {}
|
||||
FlowType::Array(..) => {}
|
||||
FlowType::Dict(..) => {}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -1104,6 +1117,14 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
self.analyze(p, pol);
|
||||
}
|
||||
}
|
||||
FlowType::Tuple(e) => {
|
||||
for ty in e.iter() {
|
||||
self.analyze(ty, pol);
|
||||
}
|
||||
}
|
||||
FlowType::Array(e) => {
|
||||
self.analyze(e, pol);
|
||||
}
|
||||
FlowType::With(w) => {
|
||||
self.analyze(&w.0, pol);
|
||||
for m in &w.1 {
|
||||
|
@ -1155,8 +1176,6 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
FlowType::FlowNone => {}
|
||||
FlowType::Auto => {}
|
||||
FlowType::Builtin(_) => {}
|
||||
// todo
|
||||
FlowType::Array => {}
|
||||
FlowType::Element(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -1230,6 +1249,16 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
|
||||
FlowType::Dict(FlowRecord { fields })
|
||||
}
|
||||
FlowType::Tuple(e) => {
|
||||
let e2 = e.iter().map(|ty| self.transform(ty, pol)).collect();
|
||||
|
||||
FlowType::Tuple(e2)
|
||||
}
|
||||
FlowType::Array(e) => {
|
||||
let e2 = self.transform(e, pol);
|
||||
|
||||
FlowType::Array(Box::new(e2))
|
||||
}
|
||||
FlowType::With(w) => {
|
||||
let primary = self.transform(&w.0, pol);
|
||||
FlowType::With(Box::new((primary, w.1.clone())))
|
||||
|
@ -1274,7 +1303,6 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
}
|
||||
// todo
|
||||
FlowType::Let(_) => FlowType::Any,
|
||||
FlowType::Array => FlowType::Array,
|
||||
FlowType::Value(v) => FlowType::Value(v.clone()),
|
||||
FlowType::ValueDoc(v) => FlowType::ValueDoc(v.clone()),
|
||||
FlowType::Element(v) => FlowType::Element(*v),
|
||||
|
@ -1337,8 +1365,10 @@ impl Joiner {
|
|||
(FlowType::Content, _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Var(v), _) => self.possibles.push(FlowType::Var(v)),
|
||||
// todo: check possibles
|
||||
(FlowType::Array, FlowType::None) => self.definite = FlowType::Array,
|
||||
(FlowType::Array, _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Array(e), FlowType::None) => self.definite = FlowType::Array(e),
|
||||
(FlowType::Array(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Tuple(e), FlowType::None) => self.definite = FlowType::Tuple(e),
|
||||
(FlowType::Tuple(..), _) => self.definite = FlowType::Undef,
|
||||
// todo: possible some style
|
||||
(FlowType::Auto, FlowType::None) => self.definite = FlowType::Auto,
|
||||
(FlowType::Auto, _) => self.definite = FlowType::Undef,
|
||||
|
|
|
@ -2,7 +2,7 @@ use ecow::EcoVec;
|
|||
use once_cell::sync::Lazy;
|
||||
use regex::RegexSet;
|
||||
use typst::{
|
||||
foundations::{Func, ParamInfo, Value},
|
||||
foundations::{Func, ParamInfo, Type, Value},
|
||||
syntax::Span,
|
||||
};
|
||||
|
||||
|
@ -81,75 +81,6 @@ impl PathPreference {
|
|||
}
|
||||
}
|
||||
|
||||
pub(in crate::analysis::ty) fn param_mapping(f: &Func, p: &ParamInfo) -> Option<FlowType> {
|
||||
match (f.name().unwrap(), p.name) {
|
||||
("cbor", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::None,
|
||||
))),
|
||||
("csv", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::Csv,
|
||||
))),
|
||||
("image", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::Image,
|
||||
))),
|
||||
("read", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::None,
|
||||
))),
|
||||
("json", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::Json,
|
||||
))),
|
||||
("yaml", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::Yaml,
|
||||
))),
|
||||
("xml", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::Xml,
|
||||
))),
|
||||
("toml", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::Toml,
|
||||
))),
|
||||
("raw", "theme") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::RawTheme,
|
||||
))),
|
||||
("raw", "syntaxes") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::RawSyntax,
|
||||
))),
|
||||
("bibliography", "path") => Some(FlowType::Builtin(FlowBuiltinType::Path(
|
||||
PathPreference::Bibliography,
|
||||
))),
|
||||
("text", "size") => Some(FlowType::Builtin(FlowBuiltinType::TextSize)),
|
||||
("text", "font") => Some(FlowType::Builtin(FlowBuiltinType::TextFont)),
|
||||
("text", "lang") => Some(FlowType::Builtin(FlowBuiltinType::TextLang)),
|
||||
("text", "region") => Some(FlowType::Builtin(FlowBuiltinType::TextRegion)),
|
||||
("text" | "stack", "dir") => Some(FlowType::Builtin(FlowBuiltinType::Dir)),
|
||||
(
|
||||
// todo: polygon.regular
|
||||
"page" | "highlight" | "text" | "path" | "rect" | "ellipse" | "circle" | "polygon"
|
||||
| "box" | "block" | "table" | "regular",
|
||||
"fill",
|
||||
) => Some(FlowType::Builtin(FlowBuiltinType::Color)),
|
||||
(
|
||||
// todo: table.cell
|
||||
"table" | "cell" | "block" | "box" | "circle" | "ellipse" | "rect" | "square",
|
||||
"inset",
|
||||
) => Some(FlowType::Builtin(FlowBuiltinType::Inset)),
|
||||
("block" | "box" | "circle" | "ellipse" | "rect" | "square", "outset") => {
|
||||
Some(FlowType::Builtin(FlowBuiltinType::Outset))
|
||||
}
|
||||
("block" | "box" | "rect" | "square", "radius") => {
|
||||
Some(FlowType::Builtin(FlowBuiltinType::Radius))
|
||||
}
|
||||
(
|
||||
//todo: table.cell, table.hline, table.vline, math.cancel, grid.cell, polygon.regular
|
||||
"cancel" | "highlight" | "overline" | "strike" | "underline" | "text" | "path" | "rect"
|
||||
| "ellipse" | "circle" | "polygon" | "box" | "block" | "table" | "line" | "cell"
|
||||
| "hline" | "vline" | "regular",
|
||||
"stroke",
|
||||
) => Some(FlowType::Builtin(FlowBuiltinType::Stroke)),
|
||||
("page", "margin") => Some(FlowType::Builtin(FlowBuiltinType::Margin)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub(crate) enum FlowBuiltinType {
|
||||
Args,
|
||||
|
@ -172,26 +103,34 @@ pub(crate) enum FlowBuiltinType {
|
|||
Path(PathPreference),
|
||||
}
|
||||
|
||||
use FlowBuiltinType::*;
|
||||
|
||||
fn literally(s: impl FlowBuiltinLiterally) -> FlowType {
|
||||
s.literally()
|
||||
}
|
||||
|
||||
trait FlowBuiltinLiterally {
|
||||
fn literally(&self) -> FlowType;
|
||||
fn literally(self) -> FlowType;
|
||||
}
|
||||
|
||||
impl FlowBuiltinLiterally for &str {
|
||||
fn literally(&self) -> FlowType {
|
||||
fn literally(self) -> FlowType {
|
||||
FlowType::Value(Box::new((Value::Str((*self).into()), Span::detached())))
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowBuiltinLiterally for FlowBuiltinType {
|
||||
fn literally(&self) -> FlowType {
|
||||
fn literally(self) -> FlowType {
|
||||
FlowType::Builtin(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowBuiltinLiterally for FlowType {
|
||||
fn literally(self) -> FlowType {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// separate by middle
|
||||
macro_rules! flow_builtin_union_inner {
|
||||
($literal_kind:expr) => {
|
||||
|
@ -228,7 +167,99 @@ macro_rules! flow_record {
|
|||
};
|
||||
}
|
||||
|
||||
use FlowBuiltinType::*;
|
||||
pub(in crate::analysis::ty) fn param_mapping(f: &Func, p: &ParamInfo) -> Option<FlowType> {
|
||||
match (f.name().unwrap(), p.name) {
|
||||
("cbor", "path") => Some(literally(Path(PathPreference::None))),
|
||||
("csv", "path") => Some(literally(Path(PathPreference::Csv))),
|
||||
("image", "path") => Some(literally(Path(PathPreference::Image))),
|
||||
("read", "path") => Some(literally(Path(PathPreference::None))),
|
||||
("json", "path") => Some(literally(Path(PathPreference::Json))),
|
||||
("yaml", "path") => Some(literally(Path(PathPreference::Yaml))),
|
||||
("xml", "path") => Some(literally(Path(PathPreference::Xml))),
|
||||
("toml", "path") => Some(literally(Path(PathPreference::Toml))),
|
||||
("raw", "theme") => Some(literally(Path(PathPreference::RawTheme))),
|
||||
("raw", "syntaxes") => Some(literally(Path(PathPreference::RawSyntax))),
|
||||
("bibliography", "path") => Some(literally(Path(PathPreference::Bibliography))),
|
||||
("text", "size") => Some(literally(TextSize)),
|
||||
("text", "font") => {
|
||||
static FONT_TYPE: Lazy<FlowType> = Lazy::new(|| {
|
||||
FlowType::Union(Box::new(vec![
|
||||
literally(TextFont),
|
||||
FlowType::Array(Box::new(literally(TextFont))),
|
||||
]))
|
||||
});
|
||||
Some(FONT_TYPE.clone())
|
||||
}
|
||||
("text", "lang") => Some(literally(TextLang)),
|
||||
("text", "region") => Some(literally(TextRegion)),
|
||||
("text" | "stack", "dir") => Some(literally(Dir)),
|
||||
(
|
||||
// todo: polygon.regular
|
||||
"page" | "highlight" | "text" | "path" | "rect" | "ellipse" | "circle" | "polygon"
|
||||
| "box" | "block" | "table" | "regular",
|
||||
"fill",
|
||||
) => Some(literally(Color)),
|
||||
(
|
||||
// todo: table.cell
|
||||
"table" | "cell" | "block" | "box" | "circle" | "ellipse" | "rect" | "square",
|
||||
"inset",
|
||||
) => Some(literally(Inset)),
|
||||
("block" | "box" | "circle" | "ellipse" | "rect" | "square", "outset") => {
|
||||
Some(literally(Outset))
|
||||
}
|
||||
("block" | "box" | "rect" | "square", "radius") => Some(literally(Radius)),
|
||||
("grid" | "table", "columns" | "rows" | "gutter" | "column-gutter" | "row-gutter") => {
|
||||
static COLUMN_TYPE: Lazy<FlowType> = Lazy::new(|| {
|
||||
flow_union!(
|
||||
FlowType::Value(Box::new((Value::Auto, Span::detached()))),
|
||||
FlowType::Value(Box::new((Value::Type(Type::of::<i64>()), Span::detached()))),
|
||||
literally(Length),
|
||||
FlowType::Array(Box::new(literally(Length))),
|
||||
)
|
||||
});
|
||||
Some(COLUMN_TYPE.clone())
|
||||
}
|
||||
("pattern", "size") => {
|
||||
static PATTERN_SIZE_TYPE: Lazy<FlowType> = Lazy::new(|| {
|
||||
flow_union!(
|
||||
FlowType::Value(Box::new((Value::Auto, Span::detached()))),
|
||||
FlowType::Array(Box::new(FlowType::Builtin(Length))),
|
||||
)
|
||||
});
|
||||
Some(PATTERN_SIZE_TYPE.clone())
|
||||
}
|
||||
("stroke", "dash") => Some(FLOW_STROKE_DASH_TYPE.clone()),
|
||||
(
|
||||
//todo: table.cell, table.hline, table.vline, math.cancel, grid.cell, polygon.regular
|
||||
"cancel" | "highlight" | "overline" | "strike" | "underline" | "text" | "path" | "rect"
|
||||
| "ellipse" | "circle" | "polygon" | "box" | "block" | "table" | "line" | "cell"
|
||||
| "hline" | "vline" | "regular",
|
||||
"stroke",
|
||||
) => Some(FlowType::Builtin(Stroke)),
|
||||
("page", "margin") => Some(FlowType::Builtin(Margin)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
static FLOW_STROKE_DASH_TYPE: Lazy<FlowType> = Lazy::new(|| {
|
||||
flow_union!(
|
||||
"solid",
|
||||
"dotted",
|
||||
"densely-dotted",
|
||||
"loosely-dotted",
|
||||
"dashed",
|
||||
"densely-dashed",
|
||||
"loosely-dashed",
|
||||
"dash-dotted",
|
||||
"densely-dash-dotted",
|
||||
"loosely-dash-dotted",
|
||||
FlowType::Array(Box::new(flow_union!("dot", literally(Float)))),
|
||||
FlowType::Dict(flow_record!(
|
||||
"array" => FlowType::Array(Box::new(flow_union!("dot", literally(Float)))),
|
||||
"phase" => literally(Length),
|
||||
))
|
||||
)
|
||||
});
|
||||
|
||||
pub static FLOW_STROKE_DICT: Lazy<FlowRecord> = Lazy::new(|| {
|
||||
flow_record!(
|
||||
|
@ -236,18 +267,7 @@ pub static FLOW_STROKE_DICT: Lazy<FlowRecord> = Lazy::new(|| {
|
|||
"thickness" => literally(Length),
|
||||
"cap" => flow_union!("butt", "round", "square"),
|
||||
"join" => flow_union!("miter", "round", "bevel"),
|
||||
"dash" => flow_union!(
|
||||
"solid",
|
||||
"dotted",
|
||||
"densely-dotted",
|
||||
"loosely-dotted",
|
||||
"dashed",
|
||||
"densely-dashed",
|
||||
"loosely-dashed",
|
||||
"dash-dotted",
|
||||
"densely-dash-dotted",
|
||||
"loosely-dash-dotted",
|
||||
),
|
||||
"dash" => FLOW_STROKE_DASH_TYPE.clone(),
|
||||
"miter-limit" => literally(Float),
|
||||
)
|
||||
});
|
||||
|
@ -318,10 +338,6 @@ pub static FLOW_RADIUS_DICT: Lazy<FlowRecord> = Lazy::new(|| {
|
|||
// todo: math.cancel.angle can be a function
|
||||
// todo: text.features array/dictionary
|
||||
// todo: math.mat.augment
|
||||
// todo: text.lang
|
||||
// todo: text.region
|
||||
// todo: text.font array
|
||||
// todo: stroke.dash can be an array
|
||||
// todo: csv.row-type can be an array or a dictionary
|
||||
|
||||
// ISO 639
|
||||
|
|
|
@ -31,7 +31,6 @@ pub(crate) enum FlowType {
|
|||
Undef,
|
||||
Content,
|
||||
Any,
|
||||
Array,
|
||||
None,
|
||||
Infer,
|
||||
FlowNone,
|
||||
|
@ -44,6 +43,9 @@ pub(crate) enum FlowType {
|
|||
Var(Box<(DefId, EcoString)>),
|
||||
Func(Box<FlowSignature>),
|
||||
Dict(FlowRecord),
|
||||
Array(Box<FlowType>),
|
||||
// Note: may contains spread types
|
||||
Tuple(EcoVec<FlowType>),
|
||||
With(Box<(FlowType, Vec<FlowArgs>)>),
|
||||
Args(Box<FlowArgs>),
|
||||
At(FlowAt),
|
||||
|
@ -61,7 +63,6 @@ impl fmt::Debug for FlowType {
|
|||
FlowType::Undef => f.write_str("Undef"),
|
||||
FlowType::Content => f.write_str("Content"),
|
||||
FlowType::Any => f.write_str("Any"),
|
||||
FlowType::Array => f.write_str("Array"),
|
||||
FlowType::None => f.write_str("None"),
|
||||
FlowType::Infer => f.write_str("Infer"),
|
||||
FlowType::FlowNone => f.write_str("FlowNone"),
|
||||
|
@ -70,6 +71,14 @@ impl fmt::Debug for FlowType {
|
|||
FlowType::Args(a) => write!(f, "&({a:?})"),
|
||||
FlowType::Func(s) => write!(f, "{s:?}"),
|
||||
FlowType::Dict(r) => write!(f, "{r:?}"),
|
||||
FlowType::Array(a) => write!(f, "Array<{a:?}>"),
|
||||
FlowType::Tuple(t) => {
|
||||
f.write_str("(")?;
|
||||
for t in t {
|
||||
write!(f, "{t:?}, ")?;
|
||||
}
|
||||
f.write_str(")")
|
||||
}
|
||||
FlowType::With(w) => write!(f, "({:?}).with(..{:?})", w.0, w.1),
|
||||
FlowType::At(a) => write!(f, "{a:?}"),
|
||||
FlowType::Union(u) => {
|
||||
|
|
|
@ -7,8 +7,8 @@ input_file: crates/tinymist-query/src/fixtures/type_check/infer2.typ
|
|||
1..52 -> Element(text)
|
||||
6..15 -> TextSize
|
||||
12..15 -> TextSize
|
||||
17..25 -> TextFont
|
||||
23..25 -> TextFont
|
||||
17..25 -> (TextFont | Array<TextFont>)
|
||||
23..25 -> (TextFont | Array<TextFont>)
|
||||
27..38 -> Stroke
|
||||
35..38 -> Stroke
|
||||
40..49 -> Color
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/text_font.typ
|
||||
---
|
||||
"x" = "Test"
|
||||
"y" = ("Test", )
|
||||
---
|
||||
1..21 -> Element(text)
|
||||
6..18 -> (TextFont | Array<TextFont>)
|
||||
12..18 -> (TextFont | Array<TextFont>)
|
||||
19..21 -> Type(content)
|
||||
23..39 -> Element(text)
|
||||
28..36 -> (TextFont | Array<TextFont>)
|
||||
34..36 -> (TextFont | Array<TextFont>)
|
||||
37..39 -> Type(content)
|
||||
41..64 -> Element(text)
|
||||
46..61 -> (TextFont | Array<TextFont>)
|
||||
52..61 -> (TextFont | Array<TextFont>)
|
||||
62..64 -> Type(content)
|
||||
70..71 -> @x
|
||||
82..97 -> Element(text)
|
||||
87..94 -> (TextFont | Array<TextFont>)
|
||||
93..94 -> (TextFont | Array<TextFont>)
|
||||
95..97 -> Type(content)
|
||||
103..104 -> @y
|
||||
118..133 -> Element(text)
|
||||
123..130 -> (TextFont | Array<TextFont>)
|
||||
129..130 -> (TextFont | Array<TextFont>)
|
||||
131..133 -> Type(content)
|
|
@ -0,0 +1,7 @@
|
|||
#text(font: "Test")[]
|
||||
#text(font: ())[]
|
||||
#text(font: ("Test",))[]
|
||||
#let x = "Test"
|
||||
#text(font: x)[]
|
||||
#let y = ("Test",)
|
||||
#text(font: y)[]
|
|
@ -2,16 +2,17 @@ use std::collections::{BTreeMap, HashSet};
|
|||
|
||||
use ecow::{eco_format, EcoString};
|
||||
use lsp_types::{CompletionItem, CompletionTextEdit, InsertTextFormat, TextEdit};
|
||||
use once_cell::sync::OnceCell;
|
||||
use reflexo::path::{unix_slash, PathClean};
|
||||
use typst::foundations::{AutoValue, Func, Label, NoneValue, Type, Value};
|
||||
use typst::layout::Length;
|
||||
use typst::layout::{Dir, Length};
|
||||
use typst::syntax::ast::AstNode;
|
||||
use typst::syntax::{ast, Span, SyntaxKind};
|
||||
use typst::visualize::Color;
|
||||
|
||||
use super::{Completion, CompletionContext, CompletionKind};
|
||||
use crate::analysis::{
|
||||
analyze_dyn_signature, analyze_import, resolve_callee, FlowBuiltinType, FlowType,
|
||||
analyze_dyn_signature, analyze_import, resolve_callee, FlowBuiltinType, FlowRecord, FlowType,
|
||||
PathPreference, FLOW_INSET_DICT, FLOW_MARGIN_DICT, FLOW_OUTSET_DICT, FLOW_RADIUS_DICT,
|
||||
FLOW_STROKE_DICT,
|
||||
};
|
||||
|
@ -326,7 +327,7 @@ fn type_completion(
|
|||
FlowType::Undef => return None,
|
||||
FlowType::Content => return None,
|
||||
FlowType::Any => return None,
|
||||
FlowType::Array => {
|
||||
FlowType::Tuple(..) | FlowType::Array(..) => {
|
||||
ctx.snippet_completion("()", "(${})", "An array.");
|
||||
}
|
||||
FlowType::Dict(..) => {
|
||||
|
@ -356,8 +357,8 @@ fn type_completion(
|
|||
FlowBuiltinType::Stroke => {
|
||||
ctx.snippet_completion("stroke()", "stroke(${})", "Stroke type.");
|
||||
ctx.snippet_completion("()", "(${})", "Stroke dictionary.");
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Color)), None);
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), None);
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Color)), docs);
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), docs);
|
||||
}
|
||||
FlowBuiltinType::Color => {
|
||||
ctx.snippet_completion("luma()", "luma(${v})", "A custom grayscale color.");
|
||||
|
@ -426,23 +427,28 @@ fn type_completion(
|
|||
});
|
||||
}
|
||||
}
|
||||
FlowBuiltinType::TextFont => return None,
|
||||
FlowBuiltinType::Dir => return None,
|
||||
FlowBuiltinType::Dir => {
|
||||
let ty = Type::of::<Dir>();
|
||||
ctx.strict_scope_completions(false, |value| value.ty() == ty);
|
||||
}
|
||||
FlowBuiltinType::TextFont => {
|
||||
ctx.font_completions();
|
||||
}
|
||||
FlowBuiltinType::Margin => {
|
||||
ctx.snippet_completion("()", "(${})", "Margin dictionary.");
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), None);
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), docs);
|
||||
}
|
||||
FlowBuiltinType::Inset => {
|
||||
ctx.snippet_completion("()", "(${})", "Inset dictionary.");
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), None);
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), docs);
|
||||
}
|
||||
FlowBuiltinType::Outset => {
|
||||
ctx.snippet_completion("()", "(${})", "Outset dictionary.");
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), None);
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), docs);
|
||||
}
|
||||
FlowBuiltinType::Radius => {
|
||||
ctx.snippet_completion("()", "(${})", "Radius dictionary.");
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), None);
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Length)), docs);
|
||||
}
|
||||
FlowBuiltinType::Length => {
|
||||
ctx.snippet_completion("pt", "${1}pt", "Point length unit.");
|
||||
|
@ -452,6 +458,7 @@ fn type_completion(
|
|||
ctx.snippet_completion("em", "${1}em", "Em length unit.");
|
||||
let length_ty = Type::of::<Length>();
|
||||
ctx.strict_scope_completions(false, |value| value.ty() == length_ty);
|
||||
type_completion(ctx, Some(&FlowType::Auto), docs);
|
||||
}
|
||||
FlowBuiltinType::Float => {
|
||||
ctx.snippet_completion("exponential notation", "${1}e${0}", "Exponential notation");
|
||||
|
@ -474,9 +481,9 @@ fn type_completion(
|
|||
FlowType::Value(v) => {
|
||||
if let Value::Type(ty) = &v.0 {
|
||||
if *ty == Type::of::<NoneValue>() {
|
||||
ctx.snippet_completion("none", "none", "Nothing.")
|
||||
type_completion(ctx, Some(&FlowType::None), docs);
|
||||
} else if *ty == Type::of::<AutoValue>() {
|
||||
ctx.snippet_completion("auto", "auto", "A smart default.");
|
||||
type_completion(ctx, Some(&FlowType::Auto), docs);
|
||||
} else if *ty == Type::of::<bool>() {
|
||||
ctx.snippet_completion("false", "false", "No / Disabled.");
|
||||
ctx.snippet_completion("true", "true", "Yes / Enabled.");
|
||||
|
@ -501,13 +508,24 @@ fn type_completion(
|
|||
});
|
||||
ctx.strict_scope_completions(false, |value| value.ty() == *ty);
|
||||
}
|
||||
} else if v.0.ty() == Type::of::<NoneValue>() {
|
||||
type_completion(ctx, Some(&FlowType::None), docs);
|
||||
} else if v.0.ty() == Type::of::<AutoValue>() {
|
||||
type_completion(ctx, Some(&FlowType::Auto), docs);
|
||||
} else {
|
||||
ctx.value_completion(None, &v.0, true, docs);
|
||||
}
|
||||
}
|
||||
FlowType::ValueDoc(v) => {
|
||||
let (value, docs) = v.as_ref();
|
||||
ctx.value_completion(None, value, true, Some(docs));
|
||||
type_completion(
|
||||
ctx,
|
||||
Some(&FlowType::Value(Box::new((
|
||||
value.clone(),
|
||||
Span::detached(),
|
||||
)))),
|
||||
Some(*docs),
|
||||
);
|
||||
}
|
||||
FlowType::Element(e) => {
|
||||
ctx.value_completion(Some(e.name().into()), &Value::Func((*e).into()), true, docs);
|
||||
|
@ -581,9 +599,6 @@ pub fn named_param_value_completions<'a>(
|
|||
{
|
||||
ctx.cast_completions(¶m.input);
|
||||
}
|
||||
if name == "font" {
|
||||
ctx.font_completions();
|
||||
}
|
||||
|
||||
if ctx.before.ends_with(':') {
|
||||
ctx.enrich(" ", "");
|
||||
|
@ -612,21 +627,18 @@ pub fn complete_literal(ctx: &mut CompletionContext) -> Option<()> {
|
|||
log::debug!("check complete_literal 3: {:?}", ctx.leaf);
|
||||
|
||||
// or empty array
|
||||
let dict_span;
|
||||
let dict_lit = match parent.kind() {
|
||||
let lit_span;
|
||||
let (dict_lit, _tuple_lit) = match parent.kind() {
|
||||
SyntaxKind::Dict => {
|
||||
let dict_lit = parent.get().cast::<ast::Dict>()?;
|
||||
|
||||
dict_span = dict_lit.span();
|
||||
dict_lit
|
||||
lit_span = dict_lit.span();
|
||||
(dict_lit, None)
|
||||
}
|
||||
SyntaxKind::Array => {
|
||||
let w = parent.get().cast::<ast::Array>()?;
|
||||
if w.items().next().is_some() {
|
||||
return None;
|
||||
}
|
||||
dict_span = w.span();
|
||||
ast::Dict::default()
|
||||
lit_span = w.span();
|
||||
(ast::Dict::default(), Some(w))
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
@ -634,61 +646,114 @@ pub fn complete_literal(ctx: &mut CompletionContext) -> Option<()> {
|
|||
// query type of the dict
|
||||
let named_span = named.map(|n| n.span()).unwrap_or_else(Span::detached);
|
||||
let named_ty = ctx.ctx.type_of_span(named_span);
|
||||
let dict_ty = ctx.ctx.type_of_span(dict_span);
|
||||
log::info!("complete_literal: {:?} {:?}", dict_ty, named_ty);
|
||||
let lit_ty = ctx.ctx.type_of_span(lit_span);
|
||||
log::info!("complete_literal: {lit_ty:?} {named_ty:?}");
|
||||
|
||||
// todo: check if the dict is named
|
||||
if named_ty.is_some() {
|
||||
let res = type_completion(ctx, named_ty.as_ref(), None);
|
||||
if res.is_some() {
|
||||
ctx.incomplete = false;
|
||||
}
|
||||
return res;
|
||||
enum LitComplAction<'a> {
|
||||
Dict(&'a FlowRecord),
|
||||
Positional(&'a FlowType),
|
||||
}
|
||||
let existing = OnceCell::new();
|
||||
|
||||
struct LitComplWorker<'a, 'b, 'w> {
|
||||
ctx: &'a mut CompletionContext<'b, 'w>,
|
||||
dict_lit: ast::Dict<'a>,
|
||||
existing: &'a OnceCell<HashSet<EcoString>>,
|
||||
}
|
||||
|
||||
let existing = dict_lit
|
||||
.items()
|
||||
.filter_map(|field| match field {
|
||||
ast::DictItem::Named(n) => Some(n.name().get().clone()),
|
||||
ast::DictItem::Keyed(k) => {
|
||||
let key = ctx.ctx.const_eval(k.key());
|
||||
if let Some(Value::Str(key)) = key {
|
||||
return Some(key.into());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
// todo: var dict union
|
||||
ast::DictItem::Spread(_s) => None,
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let dict_ty = dict_ty?;
|
||||
let dict_interface = match dict_ty {
|
||||
FlowType::Builtin(FlowBuiltinType::Stroke) => &FLOW_STROKE_DICT,
|
||||
FlowType::Builtin(FlowBuiltinType::Margin) => &FLOW_MARGIN_DICT,
|
||||
FlowType::Builtin(FlowBuiltinType::Inset) => &FLOW_INSET_DICT,
|
||||
FlowType::Builtin(FlowBuiltinType::Outset) => &FLOW_OUTSET_DICT,
|
||||
FlowType::Builtin(FlowBuiltinType::Radius) => &FLOW_RADIUS_DICT,
|
||||
_ => return None,
|
||||
let mut ctx = LitComplWorker {
|
||||
ctx,
|
||||
dict_lit,
|
||||
existing: &existing,
|
||||
};
|
||||
|
||||
for (key, _, _) in dict_interface.fields.iter() {
|
||||
if existing.contains(key) {
|
||||
continue;
|
||||
impl<'a, 'b, 'w> LitComplWorker<'a, 'b, 'w> {
|
||||
fn on_iface(&mut self, lit_interface: LitComplAction<'_>) {
|
||||
match lit_interface {
|
||||
LitComplAction::Positional(a) => {
|
||||
type_completion(self.ctx, Some(a), None);
|
||||
}
|
||||
LitComplAction::Dict(dict_iface) => {
|
||||
let existing = self.existing.get_or_init(|| {
|
||||
self.dict_lit
|
||||
.items()
|
||||
.filter_map(|field| match field {
|
||||
ast::DictItem::Named(n) => Some(n.name().get().clone()),
|
||||
ast::DictItem::Keyed(k) => {
|
||||
let key = self.ctx.ctx.const_eval(k.key());
|
||||
if let Some(Value::Str(key)) = key {
|
||||
return Some(key.into());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
// todo: var dict union
|
||||
ast::DictItem::Spread(_s) => None,
|
||||
})
|
||||
.collect::<HashSet<_>>()
|
||||
});
|
||||
|
||||
for (key, _, _) in dict_iface.fields.iter() {
|
||||
if existing.contains(key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.ctx.completions.push(Completion {
|
||||
kind: CompletionKind::Field,
|
||||
label: key.clone(),
|
||||
apply: Some(eco_format!("{}: ${{}}", key)),
|
||||
detail: None,
|
||||
label_detail: None,
|
||||
// todo: only vscode and neovim (0.9.1) support this
|
||||
command: Some("editor.action.triggerSuggest"),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.completions.push(Completion {
|
||||
kind: CompletionKind::Field,
|
||||
label: key.clone(),
|
||||
apply: Some(eco_format!("{}: ${{}}", key)),
|
||||
detail: None,
|
||||
label_detail: None,
|
||||
// todo: only vscode and neovim (0.9.1) support this
|
||||
command: Some("editor.action.triggerSuggest"),
|
||||
});
|
||||
fn on_lit_ty(&mut self, ty: &FlowType) {
|
||||
match ty {
|
||||
FlowType::Builtin(FlowBuiltinType::Stroke) => {
|
||||
self.on_iface(LitComplAction::Dict(&FLOW_STROKE_DICT))
|
||||
}
|
||||
FlowType::Builtin(FlowBuiltinType::Margin) => {
|
||||
self.on_iface(LitComplAction::Dict(&FLOW_MARGIN_DICT))
|
||||
}
|
||||
FlowType::Builtin(FlowBuiltinType::Inset) => {
|
||||
self.on_iface(LitComplAction::Dict(&FLOW_INSET_DICT))
|
||||
}
|
||||
FlowType::Builtin(FlowBuiltinType::Outset) => {
|
||||
self.on_iface(LitComplAction::Dict(&FLOW_OUTSET_DICT))
|
||||
}
|
||||
FlowType::Builtin(FlowBuiltinType::Radius) => {
|
||||
self.on_iface(LitComplAction::Dict(&FLOW_RADIUS_DICT))
|
||||
}
|
||||
FlowType::Dict(d) => self.on_iface(LitComplAction::Dict(d)),
|
||||
FlowType::Array(a) => self.on_iface(LitComplAction::Positional(a)),
|
||||
FlowType::Union(u) => {
|
||||
for info in u.as_ref() {
|
||||
self.on_lit_ty(info);
|
||||
}
|
||||
}
|
||||
// todo: var, let, etc.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn work(&mut self, named_ty: Option<FlowType>, lit_ty: Option<FlowType>) {
|
||||
if let Some(named_ty) = &named_ty {
|
||||
type_completion(self.ctx, Some(named_ty), None);
|
||||
} else if let Some(lit_ty) = &lit_ty {
|
||||
self.on_lit_ty(lit_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.work(named_ty, lit_ty);
|
||||
|
||||
let ctx = ctx.ctx;
|
||||
|
||||
if ctx.before.ends_with(',') {
|
||||
ctx.enrich(" ", "");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue