mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Distinguish between more completion kinds
This commit is contained in:
parent
c0dfe4aab7
commit
65bb1fbe6a
@ -6,8 +6,8 @@ use ecow::{eco_format, EcoString};
|
|||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typst::foundations::{
|
use typst::foundations::{
|
||||||
fields_on, format_str, repr, AutoValue, CastInfo, Func, Label, NoneValue, ParamInfo,
|
fields_on, repr, AutoValue, CastInfo, Func, Label, NoneValue, ParamInfo, Repr, Scope,
|
||||||
Repr, Scope, StyleChain, Styles, Type, Value,
|
StyleChain, Styles, Type, Value,
|
||||||
};
|
};
|
||||||
use typst::model::Document;
|
use typst::model::Document;
|
||||||
use typst::syntax::ast::AstNode;
|
use typst::syntax::ast::AstNode;
|
||||||
@ -89,6 +89,14 @@ pub enum CompletionKind {
|
|||||||
Param,
|
Param,
|
||||||
/// A constant.
|
/// A constant.
|
||||||
Constant,
|
Constant,
|
||||||
|
/// A file path.
|
||||||
|
Path,
|
||||||
|
/// A package.
|
||||||
|
Package,
|
||||||
|
/// A label.
|
||||||
|
Label,
|
||||||
|
/// A font family.
|
||||||
|
Font,
|
||||||
/// A symbol.
|
/// A symbol.
|
||||||
Symbol(char),
|
Symbol(char),
|
||||||
}
|
}
|
||||||
@ -391,12 +399,12 @@ fn field_access_completions(
|
|||||||
styles: &Option<Styles>,
|
styles: &Option<Styles>,
|
||||||
) {
|
) {
|
||||||
for (name, value, _) in value.ty().scope().iter() {
|
for (name, value, _) in value.ty().scope().iter() {
|
||||||
ctx.value_completion(Some(name.clone()), value, true, None);
|
ctx.call_completion(name.clone(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scope) = value.scope() {
|
if let Some(scope) = value.scope() {
|
||||||
for (name, value, _) in scope.iter() {
|
for (name, value, _) in scope.iter() {
|
||||||
ctx.value_completion(Some(name.clone()), value, true, None);
|
ctx.call_completion(name.clone(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,12 +414,7 @@ fn field_access_completions(
|
|||||||
// with method syntax;
|
// with method syntax;
|
||||||
// 2. We can unwrap the field's value since it's a field belonging to
|
// 2. We can unwrap the field's value since it's a field belonging to
|
||||||
// this value's type, so accessing it should not fail.
|
// this value's type, so accessing it should not fail.
|
||||||
ctx.value_completion(
|
ctx.value_completion(field, &value.field(field).unwrap());
|
||||||
Some(field.into()),
|
|
||||||
&value.field(field).unwrap(),
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
@ -429,12 +432,12 @@ fn field_access_completions(
|
|||||||
}
|
}
|
||||||
Value::Content(content) => {
|
Value::Content(content) => {
|
||||||
for (name, value) in content.fields() {
|
for (name, value) in content.fields() {
|
||||||
ctx.value_completion(Some(name.into()), &value, false, None);
|
ctx.value_completion(name, &value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Dict(dict) => {
|
Value::Dict(dict) => {
|
||||||
for (name, value) in dict.iter() {
|
for (name, value) in dict.iter() {
|
||||||
ctx.value_completion(Some(name.clone().into()), value, false, None);
|
ctx.value_completion(name.clone(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Func(func) => {
|
Value::Func(func) => {
|
||||||
@ -444,12 +447,7 @@ fn field_access_completions(
|
|||||||
if let Some(value) = elem.field_id(param.name).and_then(|id| {
|
if let Some(value) = elem.field_id(param.name).and_then(|id| {
|
||||||
elem.field_from_styles(id, StyleChain::new(styles)).ok()
|
elem.field_from_styles(id, StyleChain::new(styles)).ok()
|
||||||
}) {
|
}) {
|
||||||
ctx.value_completion(
|
ctx.value_completion(param.name, &value);
|
||||||
Some(param.name.into()),
|
|
||||||
&value,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -553,7 +551,7 @@ fn import_item_completions<'a>(
|
|||||||
|
|
||||||
for (name, value, _) in scope.iter() {
|
for (name, value, _) in scope.iter() {
|
||||||
if existing.iter().all(|item| item.original_name().as_str() != name) {
|
if existing.iter().all(|item| item.original_name().as_str() != name) {
|
||||||
ctx.value_completion(Some(name.clone()), value, false, None);
|
ctx.value_completion(name.clone(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1122,10 +1120,9 @@ impl<'a> CompletionContext<'a> {
|
|||||||
for (family, iter) in self.world.book().families() {
|
for (family, iter) in self.world.book().families() {
|
||||||
let detail = summarize_font_family(iter);
|
let detail = summarize_font_family(iter);
|
||||||
if !equation || family.contains("Math") {
|
if !equation || family.contains("Math") {
|
||||||
self.value_completion(
|
self.str_completion(
|
||||||
None,
|
family,
|
||||||
&Value::Str(family.into()),
|
Some(CompletionKind::Font),
|
||||||
false,
|
|
||||||
Some(detail.as_str()),
|
Some(detail.as_str()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1142,10 +1139,9 @@ impl<'a> CompletionContext<'a> {
|
|||||||
packages.dedup_by_key(|(spec, _)| (&spec.namespace, &spec.name));
|
packages.dedup_by_key(|(spec, _)| (&spec.namespace, &spec.name));
|
||||||
}
|
}
|
||||||
for (package, description) in packages {
|
for (package, description) in packages {
|
||||||
self.value_completion(
|
self.str_completion(
|
||||||
None,
|
eco_format!("{package}"),
|
||||||
&Value::Str(format_str!("{package}")),
|
Some(CompletionKind::Package),
|
||||||
false,
|
|
||||||
description.as_deref(),
|
description.as_deref(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1171,7 +1167,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
paths.sort();
|
paths.sort();
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
self.value_completion(None, &Value::Str(path.into()), false, None);
|
self.str_completion(path, Some(CompletionKind::Path), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1238,7 +1234,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
|
|
||||||
for (label, detail) in labels.into_iter().skip(skip).take(take) {
|
for (label, detail) in labels.into_iter().skip(skip).take(take) {
|
||||||
self.completions.push(Completion {
|
self.completions.push(Completion {
|
||||||
kind: CompletionKind::Constant,
|
kind: CompletionKind::Label,
|
||||||
apply: (open || close).then(|| {
|
apply: (open || close).then(|| {
|
||||||
eco_format!(
|
eco_format!(
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
@ -1253,18 +1249,40 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a completion for an arbitrary value.
|
||||||
|
fn value_completion(&mut self, label: impl Into<EcoString>, value: &Value) {
|
||||||
|
self.value_completion_full(Some(label.into()), value, false, None, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a completion for an arbitrary value, adding parentheses if it's a function.
|
||||||
|
fn call_completion(&mut self, label: impl Into<EcoString>, value: &Value) {
|
||||||
|
self.value_completion_full(Some(label.into()), value, true, None, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a completion for a specific string literal.
|
||||||
|
fn str_completion(
|
||||||
|
&mut self,
|
||||||
|
string: impl Into<EcoString>,
|
||||||
|
kind: Option<CompletionKind>,
|
||||||
|
detail: Option<&str>,
|
||||||
|
) {
|
||||||
|
let string = string.into();
|
||||||
|
self.value_completion_full(None, &Value::Str(string.into()), false, kind, detail);
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a completion for a specific value.
|
/// Add a completion for a specific value.
|
||||||
fn value_completion(
|
fn value_completion_full(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: Option<EcoString>,
|
label: Option<EcoString>,
|
||||||
value: &Value,
|
value: &Value,
|
||||||
parens: bool,
|
parens: bool,
|
||||||
docs: Option<&str>,
|
kind: Option<CompletionKind>,
|
||||||
|
detail: Option<&str>,
|
||||||
) {
|
) {
|
||||||
let at = label.as_deref().is_some_and(|field| !is_ident(field));
|
let at = label.as_deref().is_some_and(|field| !is_ident(field));
|
||||||
let label = label.unwrap_or_else(|| value.repr());
|
let label = label.unwrap_or_else(|| value.repr());
|
||||||
|
|
||||||
let detail = docs.map(Into::into).or_else(|| match value {
|
let detail = detail.map(Into::into).or_else(|| match value {
|
||||||
Value::Symbol(_) => None,
|
Value::Symbol(_) => None,
|
||||||
Value::Func(func) => func.docs().map(plain_docs_sentence),
|
Value::Func(func) => func.docs().map(plain_docs_sentence),
|
||||||
Value::Type(ty) => Some(plain_docs_sentence(ty.docs())),
|
Value::Type(ty) => Some(plain_docs_sentence(ty.docs())),
|
||||||
@ -1296,12 +1314,12 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.completions.push(Completion {
|
self.completions.push(Completion {
|
||||||
kind: match value {
|
kind: kind.unwrap_or_else(|| match value {
|
||||||
Value::Func(_) => CompletionKind::Func,
|
Value::Func(_) => CompletionKind::Func,
|
||||||
Value::Type(_) => CompletionKind::Type,
|
Value::Type(_) => CompletionKind::Type,
|
||||||
Value::Symbol(s) => CompletionKind::Symbol(s.get()),
|
Value::Symbol(s) => CompletionKind::Symbol(s.get()),
|
||||||
_ => CompletionKind::Constant,
|
_ => CompletionKind::Constant,
|
||||||
},
|
}),
|
||||||
label,
|
label,
|
||||||
apply,
|
apply,
|
||||||
detail,
|
detail,
|
||||||
@ -1318,7 +1336,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
match cast {
|
match cast {
|
||||||
CastInfo::Any => {}
|
CastInfo::Any => {}
|
||||||
CastInfo::Value(value, docs) => {
|
CastInfo::Value(value, docs) => {
|
||||||
self.value_completion(None, value, true, Some(docs));
|
self.value_completion_full(None, value, false, None, Some(docs));
|
||||||
}
|
}
|
||||||
CastInfo::Type(ty) => {
|
CastInfo::Type(ty) => {
|
||||||
if *ty == Type::of::<NoneValue>() {
|
if *ty == Type::of::<NoneValue>() {
|
||||||
@ -1419,7 +1437,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
let scope = if in_math { self.math } else { self.global };
|
let scope = if in_math { self.math } else { self.global };
|
||||||
for (name, value, _) in scope.iter() {
|
for (name, value, _) in scope.iter() {
|
||||||
if filter(value) && !defined.contains(name) {
|
if filter(value) && !defined.contains(name) {
|
||||||
self.value_completion(Some(name.clone()), value, parens, None);
|
self.value_completion_full(Some(name.clone()), value, parens, None, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user