mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Autocomplete different functions with different brackets
This commit is contained in:
parent
c526f031cc
commit
5d003fb1b2
@ -1191,16 +1191,17 @@ impl<'a> CompletionContext<'a> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut apply = None;
|
let mut apply = None;
|
||||||
if parens && matches!(value, Value::Func(_)) {
|
if parens
|
||||||
|
&& matches!(value, Value::Func(_))
|
||||||
|
&& !self.after.starts_with(['(', '['])
|
||||||
|
{
|
||||||
if let Value::Func(func) = value {
|
if let Value::Func(func) = value {
|
||||||
if func
|
apply = Some(match BracketMode::of(func) {
|
||||||
.params()
|
BracketMode::RoundAfter => eco_format!("{label}()${{}}"),
|
||||||
.is_some_and(|params| params.iter().all(|param| param.name == "self"))
|
BracketMode::RoundWithin => eco_format!("{label}(${{}})"),
|
||||||
{
|
BracketMode::RoundNewline => eco_format!("{label}(\n ${{}}\n)"),
|
||||||
apply = Some(eco_format!("{label}()${{}}"));
|
BracketMode::SquareWithin => eco_format!("{label}[${{}}]"),
|
||||||
} else {
|
});
|
||||||
apply = Some(eco_format!("{label}(${{}})"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if at {
|
} else if at {
|
||||||
apply = Some(eco_format!("at(\"{label}\")"));
|
apply = Some(eco_format!("at(\"{label}\")"));
|
||||||
@ -1351,6 +1352,39 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What kind of parentheses to autocomplete for a function.
|
||||||
|
enum BracketMode {
|
||||||
|
/// Round parenthesis, with the cursor within: `(|)`.
|
||||||
|
RoundWithin,
|
||||||
|
/// Round parenthesis, with the cursor after them: `()|`.
|
||||||
|
RoundAfter,
|
||||||
|
/// Round parenthesis, with newlines and indent.
|
||||||
|
RoundNewline,
|
||||||
|
/// Square brackets, with the cursor within: `[|]`.
|
||||||
|
SquareWithin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BracketMode {
|
||||||
|
fn of(func: &Func) -> Self {
|
||||||
|
if func
|
||||||
|
.params()
|
||||||
|
.is_some_and(|params| params.iter().all(|param| param.name == "self"))
|
||||||
|
{
|
||||||
|
return Self::RoundAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
match func.name() {
|
||||||
|
Some(
|
||||||
|
"emph" | "footnote" | "quote" | "strong" | "highlight" | "overline"
|
||||||
|
| "underline" | "smallcaps" | "strike" | "sub" | "super",
|
||||||
|
) => Self::SquareWithin,
|
||||||
|
Some("colbreak" | "parbreak" | "linebreak" | "pagebreak") => Self::RoundAfter,
|
||||||
|
Some("figure" | "table" | "grid" | "stack") => Self::RoundNewline,
|
||||||
|
_ => Self::RoundWithin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
@ -1363,21 +1397,26 @@ mod tests {
|
|||||||
type Response = Option<(usize, Vec<Completion>)>;
|
type Response = Option<(usize, Vec<Completion>)>;
|
||||||
|
|
||||||
trait ResponseExt {
|
trait ResponseExt {
|
||||||
|
fn completions(&self) -> &[Completion];
|
||||||
fn labels(&self) -> BTreeSet<&str>;
|
fn labels(&self) -> BTreeSet<&str>;
|
||||||
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self;
|
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self;
|
||||||
fn must_exclude<'a>(&self, excludes: impl IntoIterator<Item = &'a str>) -> &Self;
|
fn must_exclude<'a>(&self, excludes: impl IntoIterator<Item = &'a str>) -> &Self;
|
||||||
|
fn must_apply<'a>(&self, label: &str, apply: impl Into<Option<&'a str>>)
|
||||||
|
-> &Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseExt for Response {
|
impl ResponseExt for Response {
|
||||||
fn labels(&self) -> BTreeSet<&str> {
|
fn completions(&self) -> &[Completion] {
|
||||||
match self {
|
match self {
|
||||||
None => BTreeSet::new(),
|
Some((_, completions)) => completions.as_slice(),
|
||||||
Some((_, completions)) => {
|
None => &[],
|
||||||
completions.iter().map(|c| c.label.as_str()).collect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn labels(&self) -> BTreeSet<&str> {
|
||||||
|
self.completions().iter().map(|c| c.label.as_str()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self {
|
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self {
|
||||||
let labels = self.labels();
|
let labels = self.labels();
|
||||||
@ -1401,6 +1440,20 @@ mod tests {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn must_apply<'a>(
|
||||||
|
&self,
|
||||||
|
label: &str,
|
||||||
|
apply: impl Into<Option<&'a str>>,
|
||||||
|
) -> &Self {
|
||||||
|
let Some(completion) = self.completions().iter().find(|c| c.label == label)
|
||||||
|
else {
|
||||||
|
panic!("found no completion for {label:?}");
|
||||||
|
};
|
||||||
|
assert_eq!(completion.apply.as_deref(), apply.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@ -1474,4 +1527,18 @@ mod tests {
|
|||||||
.must_include(["netwok", "glacier-melt", "supplement"])
|
.must_include(["netwok", "glacier-melt", "supplement"])
|
||||||
.must_exclude(["bib"]);
|
.must_exclude(["bib"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test what kind of brackets we autocomplete for function calls depending
|
||||||
|
/// on the function and existing parens.
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_bracket_mode() {
|
||||||
|
test("#", 1).must_apply("list", "list(${})");
|
||||||
|
test("#", 1).must_apply("linebreak", "linebreak()${}");
|
||||||
|
test("#", 1).must_apply("strong", "strong[${}]");
|
||||||
|
test("#", 1).must_apply("footnote", "footnote[${}]");
|
||||||
|
test("#", 1).must_apply("figure", "figure(\n ${}\n)");
|
||||||
|
test("#", 1).must_apply("table", "table(\n ${}\n)");
|
||||||
|
test("#()", 1).must_apply("list", None);
|
||||||
|
test("#[]", 1).must_apply("strong", None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user