mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Fix autocomplete
This commit is contained in:
parent
fdf5f864fa
commit
046cd4f5c7
@ -3,7 +3,7 @@ use crate::prelude::*;
|
|||||||
use crate::text::TextNode;
|
use crate::text::TextNode;
|
||||||
|
|
||||||
/// # Bullet List
|
/// # Bullet List
|
||||||
/// An bullet list.
|
/// A bullet list.
|
||||||
///
|
///
|
||||||
/// Displays a sequence of items vertically, with each item introduced by a
|
/// Displays a sequence of items vertically, with each item introduced by a
|
||||||
/// marker.
|
/// marker.
|
||||||
|
@ -307,8 +307,7 @@ fn complete_math(ctx: &mut CompletionContext) -> bool {
|
|||||||
// Behind existing atom or identifier: "$a|$" or "$abc|$".
|
// Behind existing atom or identifier: "$a|$" or "$abc|$".
|
||||||
if matches!(ctx.leaf.kind(), SyntaxKind::Atom(_) | SyntaxKind::Ident(_)) {
|
if matches!(ctx.leaf.kind(), SyntaxKind::Atom(_) | SyntaxKind::Ident(_)) {
|
||||||
ctx.from = ctx.leaf.offset();
|
ctx.from = ctx.leaf.offset();
|
||||||
ctx.symbol_completions(false);
|
ctx.math_completions();
|
||||||
ctx.scope_completions();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +333,7 @@ fn complete_code(ctx: &mut CompletionContext) -> bool {
|
|||||||
// An existing identifier: "{ pa| }".
|
// An existing identifier: "{ pa| }".
|
||||||
if matches!(ctx.leaf.kind(), SyntaxKind::Ident(_)) {
|
if matches!(ctx.leaf.kind(), SyntaxKind::Ident(_)) {
|
||||||
ctx.from = ctx.leaf.offset();
|
ctx.from = ctx.leaf.offset();
|
||||||
ctx.expr_completions(true);
|
ctx.expr_completions(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,13 +412,8 @@ impl<'a> CompletionContext<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add completions for the global scope.
|
|
||||||
fn scope_completions(&mut self) {
|
|
||||||
self.scope_completions_where(|_| true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add completions for a subset of the global scope.
|
/// Add completions for a subset of the global scope.
|
||||||
fn scope_completions_where(&mut self, filter: impl Fn(&Value) -> bool) {
|
fn scope_completions(&mut self, filter: impl Fn(&Value) -> bool) {
|
||||||
for (name, value) in self.scope.iter() {
|
for (name, value) in self.scope.iter() {
|
||||||
if filter(value) {
|
if filter(value) {
|
||||||
self.value_completion(Some(name.clone()), value, None);
|
self.value_completion(Some(name.clone()), value, None);
|
||||||
@ -459,7 +453,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
kind: CompletionKind::Param,
|
kind: CompletionKind::Param,
|
||||||
label: param.name.into(),
|
label: param.name.into(),
|
||||||
apply: Some(format_eco!("{}: ${{}}", param.name)),
|
apply: Some(format_eco!("{}: ${{}}", param.name)),
|
||||||
detail: Some(param.docs.into()),
|
detail: Some(plain_docs_sentence(param.docs)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,7 +523,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
"cmyk(${c}, ${m}, ${y}, ${k})",
|
"cmyk(${c}, ${m}, ${y}, ${k})",
|
||||||
"A custom CMYK color.",
|
"A custom CMYK color.",
|
||||||
);
|
);
|
||||||
self.scope_completions_where(|value| value.type_name() == "color");
|
self.scope_completions(|value| value.type_name() == "color");
|
||||||
}
|
}
|
||||||
CastInfo::Type("function") => {
|
CastInfo::Type("function") => {
|
||||||
self.snippet_completion(
|
self.snippet_completion(
|
||||||
@ -545,7 +539,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
apply: Some(format_eco!("${{{ty}}}")),
|
apply: Some(format_eco!("${{{ty}}}")),
|
||||||
detail: Some(format_eco!("A value of type {ty}.")),
|
detail: Some(format_eco!("A value of type {ty}.")),
|
||||||
});
|
});
|
||||||
self.scope_completions_where(|value| value.type_name() == *ty);
|
self.scope_completions(|value| value.type_name() == *ty);
|
||||||
}
|
}
|
||||||
CastInfo::Union(union) => {
|
CastInfo::Union(union) => {
|
||||||
for info in union {
|
for info in union {
|
||||||
@ -565,19 +559,14 @@ impl<'a> CompletionContext<'a> {
|
|||||||
let mut label = label.unwrap_or_else(|| value.repr().into());
|
let mut label = label.unwrap_or_else(|| value.repr().into());
|
||||||
let mut apply = None;
|
let mut apply = None;
|
||||||
|
|
||||||
if matches!(value, Value::Func(_)) {
|
if label.starts_with('"') {
|
||||||
apply = Some(format_eco!("{label}(${{}})"));
|
let trimmed = label.trim_matches('"').into();
|
||||||
label.push_str("()");
|
apply = Some(label);
|
||||||
} else {
|
label = trimmed;
|
||||||
if label.starts_with('"') {
|
|
||||||
let trimmed = label.trim_matches('"').into();
|
|
||||||
apply = Some(label);
|
|
||||||
label = trimmed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let detail = docs.map(Into::into).or_else(|| match value {
|
let detail = docs.map(Into::into).or_else(|| match value {
|
||||||
Value::Func(func) => func.info().map(|info| info.docs.into()),
|
Value::Func(func) => func.info().map(|info| plain_docs_sentence(info.docs)),
|
||||||
Value::Color(color) => Some(format_eco!("The color {color:?}.")),
|
Value::Color(color) => Some(format_eco!("The color {color:?}.")),
|
||||||
Value::Auto => Some("A smart default.".into()),
|
Value::Auto => Some("A smart default.".into()),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -770,7 +759,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
_ => true,
|
_ => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.scope_completions_where(|value| {
|
self.scope_completions(|value| {
|
||||||
matches!(
|
matches!(
|
||||||
value,
|
value,
|
||||||
Value::Func(func) if func.info().map_or(false, |info| {
|
Value::Func(func) if func.info().map_or(false, |info| {
|
||||||
@ -801,7 +790,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
/// Add completions for expression snippets.
|
/// Add completions for expression snippets.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn expr_completions(&mut self, short_form: bool) {
|
fn expr_completions(&mut self, short_form: bool) {
|
||||||
self.scope_completions_where(|value| {
|
self.scope_completions(|value| {
|
||||||
!short_form || matches!(
|
!short_form || matches!(
|
||||||
value,
|
value,
|
||||||
Value::Func(func) if func.info().map_or(true, |info| {
|
Value::Func(func) if func.info().map_or(true, |info| {
|
||||||
@ -943,7 +932,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
|
|
||||||
/// Add completions for all functions from the global scope.
|
/// Add completions for all functions from the global scope.
|
||||||
fn set_rule_completions(&mut self) {
|
fn set_rule_completions(&mut self) {
|
||||||
self.scope_completions_where(|value| {
|
self.scope_completions(|value| {
|
||||||
matches!(
|
matches!(
|
||||||
value,
|
value,
|
||||||
Value::Func(func) if func.info().map_or(false, |info| {
|
Value::Func(func) if func.info().map_or(false, |info| {
|
||||||
@ -955,7 +944,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
|
|
||||||
/// Add completions for selectors.
|
/// Add completions for selectors.
|
||||||
fn show_rule_selector_completions(&mut self) {
|
fn show_rule_selector_completions(&mut self) {
|
||||||
self.scope_completions_where(
|
self.scope_completions(
|
||||||
|value| matches!(value, Value::Func(func) if func.select(None).is_ok()),
|
|value| matches!(value, Value::Func(func) if func.select(None).is_ok()),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -974,7 +963,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add completions for selectors.
|
/// Add completions for recipes.
|
||||||
fn show_rule_recipe_completions(&mut self) {
|
fn show_rule_recipe_completions(&mut self) {
|
||||||
self.snippet_completion(
|
self.snippet_completion(
|
||||||
"replacement",
|
"replacement",
|
||||||
@ -994,6 +983,51 @@ impl<'a> CompletionContext<'a> {
|
|||||||
"Transform the element with a function.",
|
"Transform the element with a function.",
|
||||||
);
|
);
|
||||||
|
|
||||||
self.scope_completions_where(|value| matches!(value, Value::Func(_)));
|
self.scope_completions(|value| matches!(value, Value::Func(_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract the first sentence of plain text of a piece of documentation.
|
||||||
|
///
|
||||||
|
/// Removes Markdown formatting.
|
||||||
|
fn plain_docs_sentence(docs: &str) -> EcoString {
|
||||||
|
let mut s = unscanny::Scanner::new(docs);
|
||||||
|
let mut output = String::new();
|
||||||
|
let mut link = false;
|
||||||
|
while let Some(c) = s.eat() {
|
||||||
|
match c {
|
||||||
|
'`' => {
|
||||||
|
let mut raw = s.eat_until('`');
|
||||||
|
if (raw.starts_with('{') && raw.ends_with('}'))
|
||||||
|
|| (raw.starts_with('[') && raw.ends_with(']'))
|
||||||
|
{
|
||||||
|
raw = &raw[1..raw.len() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
s.eat();
|
||||||
|
output.push('`');
|
||||||
|
output.push_str(raw);
|
||||||
|
output.push('`');
|
||||||
|
}
|
||||||
|
'[' => link = true,
|
||||||
|
']' if link => {
|
||||||
|
if s.eat_if('(') {
|
||||||
|
s.eat_until(')');
|
||||||
|
s.eat();
|
||||||
|
} else if s.eat_if('[') {
|
||||||
|
s.eat_until(']');
|
||||||
|
s.eat();
|
||||||
|
}
|
||||||
|
link = false
|
||||||
|
}
|
||||||
|
'*' | '_' => {}
|
||||||
|
'.' => {
|
||||||
|
output.push('.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => output.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user