fix: refine label types to remove hacking citation filter (#603)

* test: check types

* test: confirm bug

* fix: refine label types

* dev: update snapshot

* dev: update snapshot
This commit is contained in:
Myriad-Dreamin 2024-09-20 12:30:36 +08:00 committed by GitHub
parent 58fea291ad
commit 6ce6bb8018
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 439 additions and 45 deletions

View file

@ -116,7 +116,7 @@ impl StatefulRequest for CompletionRequest {
let ty_chk = ctx.type_check(source.clone());
if let Some(ty_chk) = ty_chk {
let ty = ty_chk.type_of_span(cano_expr.span());
log::debug!("check string ty: {:?}", ty);
log::debug!("check string ty: {ty:?}");
if let Some(Ty::Builtin(BuiltinTy::Path(path_filter))) = ty {
completion_result =
complete_path(ctx, Some(cano_expr), &source, cursor, &path_filter);
@ -371,17 +371,12 @@ mod tests {
position: ctx.to_lsp_pos(s, &source),
explicit: false,
};
results.push(request.request(ctx, doc.clone()).map(|resp| {
// CompletionResponse::Array(items)
match resp {
CompletionResponse::List(l) => CompletionResponse::List(CompletionList {
is_incomplete: l.is_incomplete,
items: get_items(l.items),
}),
CompletionResponse::Array(items) => {
CompletionResponse::Array(get_items(items))
}
}
results.push(request.request(ctx, doc.clone()).map(|resp| match resp {
CompletionResponse::List(l) => CompletionResponse::List(CompletionList {
is_incomplete: l.is_incomplete,
items: get_items(l.items),
}),
CompletionResponse::Array(items) => CompletionResponse::Array(get_items(items)),
}));
}
with_settings!({

View file

@ -0,0 +1,29 @@
// path: references.bib
@article{Russell:1908,
Author = {Bertand Russell},
Journal = {American Journal of Mathematics},
Pages = {222--262},
Title = {Mathematical logic based on the theory of types},
Volume = 30,
Year = 1908}
@article{Rus,
Author = {Bertand Russell},
Journal = {American Journal of Mathematics},
Pages = {222--262},
Title = {Mathematical logic based on the theory of types},
Volume = 30,
Year = 1908}
-----
// contains:Russell:1908,Mathematical logic based on the theory of types
// compile:true
#set heading(numbering: "1.1")
#let cite_prose(labl) = cite(labl)
#let cite_prose_different_name(labl) = cite(labl)
#bibliography("references.bib")
#cite_prose(<Rus> /* range -2..-1 */)

View file

@ -0,0 +1,28 @@
// path: references.bib
@article{Russell:1908,
Author = {Bertand Russell},
Journal = {American Journal of Mathematics},
Pages = {222--262},
Title = {Mathematical logic based on the theory of types},
Volume = 30,
Year = 1908}
@article{Rus,
Author = {Bertand Russell},
Journal = {American Journal of Mathematics},
Pages = {222--262},
Title = {Mathematical logic based on the theory of types},
Volume = 30,
Year = 1908}
-----
// contains:Russell:1908,Mathematical logic based on the theory of types
// compile:true
#set heading(numbering: "1.1")
#let cite_prose(labl) = cite(labl)
#let cite_prose_different_name(labl) = cite(labl)
#bibliography("references.bib")
#cite_prose_different_name(<Rus> /* range -2..-1 */)

View file

@ -0,0 +1,17 @@
// path: references.bib
@article{t,}
-----
// contains:form,test
// compile:true
#set heading(numbering: "1.1")
#let cite_prose(labl) = cite(labl)
= H <test>
#cite_prose(<t> /* range -3..-2 */)
#bibliography("references.bib")

View file

@ -0,0 +1,12 @@
// contains:test
// compile:true
#set heading(numbering: "1.1")
= H <t>
== H2 <test>
aba aba
#<t> /* range -3..-2 */

View file

@ -0,0 +1,12 @@
// contains:test
// compile:true
#set heading(numbering: "1.1")
= H <t>
== H2 <test>
aba aba
@t /* range -2..-1 */

View file

@ -0,0 +1,76 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on > (262..263)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/bug_cite_function_infer.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 21,
"label": "Mathematical logic based on the theory of types",
"labelDetails": {
"description": "Russell:1908"
},
"sortText": "000",
"textEdit": {
"newText": "<Russell:1908",
"range": {
"end": {
"character": 16,
"line": 10
},
"start": {
"character": 16,
"line": 10
}
}
}
},
{
"kind": 21,
"label": "Mathematical logic based on the theory of types",
"labelDetails": {
"description": "Rus"
},
"sortText": "001",
"textEdit": {
"newText": "<Rus",
"range": {
"end": {
"character": 16,
"line": 10
},
"start": {
"character": 16,
"line": 10
}
}
}
},
{
"kind": 18,
"label": "Russell:1908",
"labelDetails": {
"description": "Mathematical logic based on the theory of types"
},
"sortText": "003",
"textEdit": {
"newText": "<Russell:1908",
"range": {
"end": {
"character": 16,
"line": 10
},
"start": {
"character": 16,
"line": 10
}
}
}
}
]
}
]

View file

@ -0,0 +1,76 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on > (272..273)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/bug_cite_function_infer2.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 21,
"label": "Mathematical logic based on the theory of types",
"labelDetails": {
"description": "Russell:1908"
},
"sortText": "000",
"textEdit": {
"newText": "<Russell:1908",
"range": {
"end": {
"character": 31,
"line": 10
},
"start": {
"character": 31,
"line": 10
}
}
}
},
{
"kind": 21,
"label": "Mathematical logic based on the theory of types",
"labelDetails": {
"description": "Rus"
},
"sortText": "001",
"textEdit": {
"newText": "<Rus",
"range": {
"end": {
"character": 31,
"line": 10
},
"start": {
"character": 31,
"line": 10
}
}
}
},
{
"kind": 18,
"label": "Russell:1908",
"labelDetails": {
"description": "Mathematical logic based on the theory of types"
},
"sortText": "003",
"textEdit": {
"newText": "<Russell:1908",
"range": {
"end": {
"character": 31,
"line": 10
},
"start": {
"character": 31,
"line": 10
}
}
}
}
]
}
]

View file

@ -0,0 +1,12 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on t (132..133)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/cite_heading.typ
---
[
{
"isIncomplete": false,
"items": []
}
]

View file

@ -0,0 +1,33 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on t (100..101)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/complete_purely_label.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 18,
"label": "test",
"labelDetails": {
"description": "H2"
},
"textEdit": {
"newText": "test>",
"range": {
"end": {
"character": 2,
"line": 11
},
"start": {
"character": 2,
"line": 11
}
}
}
}
]
}
]

View file

@ -0,0 +1,33 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on t (99..100)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/complete_purely_ref.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 18,
"label": "test",
"labelDetails": {
"description": "H2"
},
"textEdit": {
"newText": "test",
"range": {
"end": {
"character": 1,
"line": 11
},
"start": {
"character": 1,
"line": 11
}
}
}
}
]
}
]

View file

@ -14,15 +14,16 @@ input_file: crates/tinymist-query/src/fixtures/completion/completion_title2.typ
"labelDetails": {
"description": "Russell:1908"
},
"sortText": "000",
"textEdit": {
"newText": "Russell:1908>",
"newText": "<Russell:1908>",
"range": {
"end": {
"character": 16,
"line": 3
},
"start": {
"character": 7,
"character": 16,
"line": 3
}
}
@ -34,15 +35,16 @@ input_file: crates/tinymist-query/src/fixtures/completion/completion_title2.typ
"labelDetails": {
"description": "Mathematical logic based on the theory of types"
},
"sortText": "001",
"textEdit": {
"newText": "Russell:1908>",
"newText": "<Russell:1908>",
"range": {
"end": {
"character": 16,
"line": 3
},
"start": {
"character": 7,
"character": 16,
"line": 3
}
}

View file

@ -14,15 +14,16 @@ input_file: crates/tinymist-query/src/fixtures/completion/completion_title3.typ
"labelDetails": {
"description": "harry"
},
"sortText": "000",
"textEdit": {
"newText": "harry>",
"newText": "<harry>",
"range": {
"end": {
"character": 13,
"line": 3
},
"start": {
"character": 7,
"character": 13,
"line": 3
}
}
@ -34,15 +35,16 @@ input_file: crates/tinymist-query/src/fixtures/completion/completion_title3.typ
"labelDetails": {
"description": "electronic"
},
"sortText": "001",
"textEdit": {
"newText": "electronic>",
"newText": "<electronic>",
"range": {
"end": {
"character": 13,
"line": 3
},
"start": {
"character": 7,
"character": 13,
"line": 3
}
}
@ -54,15 +56,16 @@ input_file: crates/tinymist-query/src/fixtures/completion/completion_title3.typ
"labelDetails": {
"description": "Ishkur's Guide to Electronic Music"
},
"sortText": "002",
"textEdit": {
"newText": "electronic>",
"newText": "<electronic>",
"range": {
"end": {
"character": 13,
"line": 3
},
"start": {
"character": 7,
"character": 13,
"line": 3
}
}
@ -74,15 +77,16 @@ input_file: crates/tinymist-query/src/fixtures/completion/completion_title3.typ
"labelDetails": {
"description": "Harry Potter and the Order of the Phoenix"
},
"sortText": "004",
"textEdit": {
"newText": "harry>",
"newText": "<harry>",
"range": {
"end": {
"character": 13,
"line": 3
},
"start": {
"character": 7,
"character": 13,
"line": 3
}
}

View file

@ -0,0 +1,3 @@
#let cite_prose(labl) = ref(labl)
#let cite_prose_different_name(labl) = ref(labl)

View file

@ -0,0 +1,20 @@
---
source: crates/tinymist-query/src/analysis.rs
expression: result
input_file: crates/tinymist-query/src/fixtures/type_check/bug_cite_func_infer.typ
---
"cite_prose" = (( ⪯ (RefLabel))) => Element(ref)
"cite_prose_different_name" = (( ⪯ (RefLabel))) => Element(ref)
"labl" = Any
"labl" = Any
---
5..15 -> @cite_prose
16..20 -> @labl
24..27 -> Func(ref)
24..33 -> Element(ref)
28..32 -> @labl
39..64 -> @cite_prose_different_name
65..69 -> @labl
73..76 -> Func(ref)
73..82 -> Element(ref)
77..81 -> @labl

View file

@ -372,8 +372,10 @@ pub fn get_check_target_by_context<'a>(
match context_deref_target {
DerefTarget::Callee(callee)
if matches!(node_deref_target, DerefTarget::Normal(..))
&& !matches!(node_deref_target, DerefTarget::Callee(..)) =>
if matches!(
node_deref_target,
DerefTarget::Normal(..) | DerefTarget::Label(..) | DerefTarget::Ref(..)
) && !matches!(node_deref_target, DerefTarget::Callee(..)) =>
{
let parent = callee.parent()?;
let args = match parent.cast::<ast::Expr>() {

View file

@ -167,6 +167,8 @@ pub enum BuiltinTy {
TextLang,
TextRegion,
CiteLabel,
RefLabel,
Dir,
Length,
Float,
@ -202,6 +204,8 @@ impl fmt::Debug for BuiltinTy {
BuiltinTy::TextRegion => write!(f, "TextRegion"),
BuiltinTy::Dir => write!(f, "Dir"),
BuiltinTy::Length => write!(f, "Length"),
BuiltinTy::CiteLabel => write!(f, "CiteLabel"),
BuiltinTy::RefLabel => write!(f, "RefLabel"),
BuiltinTy::Float => write!(f, "Float"),
BuiltinTy::Stroke => write!(f, "Stroke"),
BuiltinTy::Margin => write!(f, "Margin"),
@ -270,6 +274,8 @@ impl BuiltinTy {
BuiltinTy::Dir => "dir",
BuiltinTy::Length => "length",
BuiltinTy::Float => "float",
BuiltinTy::CiteLabel => "cite-label",
BuiltinTy::RefLabel => "ref-label",
BuiltinTy::Stroke => "stroke",
BuiltinTy::Margin => "margin",
BuiltinTy::Inset => "inset",
@ -374,6 +380,12 @@ pub(super) fn param_mapping(f: &Func, p: &ParamInfo) -> Option<Ty> {
literally(Path(PathPreference::Csl)),
Ty::from_cast_info(&p.input),
])),
("cite", "key") => Some(Ty::iter_union([literally(CiteLabel)])),
("ref", "target") => Some(Ty::iter_union([literally(RefLabel)])),
("link", "dest") | ("footnote", "body") => Some(Ty::iter_union([
literally(RefLabel),
Ty::from_cast_info(&p.input),
])),
("bibliography", "path") => Some(literally(Path(PathPreference::Bibliography))),
("text", "size") => Some(literally(TextSize)),
("text", "font") => {

View file

@ -37,16 +37,16 @@ pub fn autocomplete(
mut ctx: CompletionContext,
) -> Option<(usize, bool, Vec<Completion>, Vec<lsp_types::CompletionItem>)> {
let _ = complete_comments(&mut ctx)
|| complete_labels(&mut ctx)
|| complete_type(&mut ctx).is_none() && {
log::info!("continue after completing type");
complete_field_accesses(&mut ctx)
complete_labels(&mut ctx)
|| complete_field_accesses(&mut ctx)
|| complete_imports(&mut ctx)
|| complete_rules(&mut ctx)
|| complete_params(&mut ctx)
|| complete_markup(&mut ctx)
|| complete_math(&mut ctx)
|| complete_code(&mut ctx)
|| complete_code(&mut ctx, false)
};
Some((ctx.from, ctx.incomplete, ctx.completions, ctx.completions2))
@ -142,7 +142,7 @@ fn complete_markup(ctx: &mut CompletionContext) -> bool {
// Start of a reference: "@|" or "@he|".
if ctx.leaf.kind() == SyntaxKind::RefMarker {
ctx.from = ctx.leaf.offset() + 1;
ctx.label_completions();
ctx.ref_completions();
return true;
}
@ -475,7 +475,7 @@ fn complete_labels(ctx: &mut CompletionContext) -> bool {
|| ctx.leaf.kind() == SyntaxKind::Label
{
ctx.from = ctx.leaf.offset() + 1;
ctx.label_completions();
ctx.label_completions(false);
return true;
}
@ -735,7 +735,7 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
}
/// Complete in code mode.
fn complete_code(ctx: &mut CompletionContext) -> bool {
fn complete_code(ctx: &mut CompletionContext, from_type: bool) -> bool {
if matches!(
ctx.leaf.parent_kind(),
None | Some(SyntaxKind::Markup)
@ -755,9 +755,9 @@ fn complete_code(ctx: &mut CompletionContext) -> bool {
}
// A potential label (only at the start of an argument list): "(<|".
if ctx.before.ends_with("(<") {
if !from_type && ctx.before.ends_with("(<") {
ctx.from = ctx.cursor;
ctx.label_completions();
ctx.label_completions(false);
return true;
}
@ -1101,7 +1101,17 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
}
/// Add completions for labels and references.
fn label_completions(&mut self) {
fn ref_completions(&mut self) {
self.label_completions_(false, true);
}
/// Add completions for labels and references.
fn label_completions(&mut self, only_citation: bool) {
self.label_completions_(only_citation, false);
}
/// Add completions for labels and references.
fn label_completions_(&mut self, only_citation: bool, ref_label: bool) {
let Some(document) = self.document else {
return;
};
@ -1111,9 +1121,9 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
let at = head.ends_with('@');
let open = !at && !head.ends_with('<');
let close = !at && !self.after.starts_with('>');
let citation = !at && self.before_window(15).contains("cite");
let citation = !at && only_citation;
let (skip, take) = if at {
let (skip, take) = if at || ref_label {
(0, usize::MAX)
} else if citation {
(split, usize::MAX)
@ -1128,6 +1138,9 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
bib_title,
} in labels.into_iter().skip(skip).take(take)
{
if !self.seen_casts.insert(typst_shim::utils::hash128(&label)) {
continue;
}
let label: EcoString = label.as_str().into();
let completion = Completion {
kind: CompletionKind::Reference,

View file

@ -493,7 +493,7 @@ fn sort_and_explicit_code_completion(ctx: &mut CompletionContext) {
let mut completions = std::mem::take(&mut ctx.completions);
let explict = ctx.explicit;
ctx.explicit = true;
complete_code(ctx);
complete_code(ctx, true);
ctx.explicit = explict;
log::debug!(
@ -694,7 +694,7 @@ fn type_completion(
return Some(());
}
log::debug!("type_completion: {:?}", infer_type);
log::debug!("type_completion: {infer_type:?}");
match infer_type {
Ty::Any => return None,
@ -714,6 +714,14 @@ fn type_completion(
return Some(());
}
let mut rev_stream = ctx.before.chars().rev();
let ch = rev_stream.find(|c| !typst::syntax::is_id_continue(*c));
// skip label/ref completion.
// todo: more elegant way
if matches!(ch, Some('<' | '@')) {
return Some(());
}
ctx.completions.push(Completion {
kind: CompletionKind::Field,
label: f.into(),
@ -854,6 +862,12 @@ fn type_completion(
BuiltinTy::Float => {
ctx.snippet_completion("exponential notation", "${1}e${0}", "Exponential notation");
}
BuiltinTy::CiteLabel => {
ctx.label_completions(true);
}
BuiltinTy::RefLabel => {
ctx.ref_completions();
}
BuiltinTy::Type(ty) => {
if *ty == Type::of::<NoneValue>() {
let docs = docs.or(Some("Nothing."));
@ -867,7 +881,7 @@ fn type_completion(
} else if *ty == Type::of::<Color>() {
type_completion(ctx, &Ty::Builtin(BuiltinTy::Color), docs);
} else if *ty == Type::of::<Label>() {
ctx.label_completions()
ctx.label_completions(false)
} else if *ty == Type::of::<Func>() {
ctx.snippet_completion(
"function",
@ -1018,7 +1032,7 @@ pub(crate) fn complete_type(ctx: &mut CompletionContext) -> Option<()> {
use crate::syntax::get_check_target;
let check_target = get_check_target(ctx.leaf.clone());
log::debug!("complete_type: pos {:?} -> {:#?}", ctx.leaf, check_target);
log::debug!("complete_type: pos {:?} -> {check_target:#?}", ctx.leaf);
match check_target {
Some(CheckTarget::Element { container, .. }) => {
@ -1038,6 +1052,8 @@ pub(crate) fn complete_type(ctx: &mut CompletionContext) -> Option<()> {
}
}
}
Some(CheckTarget::Normal(e)) if matches!(e.kind(), SyntaxKind::Label | SyntaxKind::Ref) => {
}
Some(CheckTarget::Paren { .. }) => {}
Some(CheckTarget::Normal(..)) => return None,
None => return None,
@ -1048,8 +1064,7 @@ pub(crate) fn complete_type(ctx: &mut CompletionContext) -> Option<()> {
.literal_type_of_node(ctx.leaf.clone())
.filter(|ty| !matches!(ty, Ty::Any))?;
log::debug!("complete_type: ty {:?} -> {:#?}", ctx.leaf, ty);
// log::debug!("complete_type: before {:?}", ctx.before.chars().last());
log::debug!("complete_type: ty {:?} -> {ty:#?}", ctx.leaf);
type_completion(ctx, &ty, None);
if ctx.before.ends_with(',') || ctx.before.ends_with(':') {

View file

@ -374,7 +374,7 @@ fn e2e() {
});
let hash = replay_log(&tinymist_binary, &root.join("neovim"));
insta::assert_snapshot!(hash, @"siphash128_13:66084c120dc211e83beb6a5898442ad4");
insta::assert_snapshot!(hash, @"siphash128_13:57d85be77d6449db5c0bd9c9b3a7a480");
}
{
@ -385,7 +385,7 @@ fn e2e() {
});
let hash = replay_log(&tinymist_binary, &root.join("vscode"));
insta::assert_snapshot!(hash, @"siphash128_13:8ee9ca54a6a2f444930c7fa932a3d013");
insta::assert_snapshot!(hash, @"siphash128_13:2a067ea884ed66dcf681c3fa7ec167a3");
}
}