mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 09:52:27 +00:00
refactor: merge some calculation stuff in completion worker (#1077)
This commit is contained in:
parent
fb528ec70a
commit
282b1d7b4d
3 changed files with 87 additions and 109 deletions
|
@ -33,6 +33,7 @@ use crate::adt::interner::Interned;
|
||||||
use crate::analysis::{
|
use crate::analysis::{
|
||||||
analyze_labels, func_signature, BuiltinTy, DynLabel, LocalContext, PathPreference, Ty,
|
analyze_labels, func_signature, BuiltinTy, DynLabel, LocalContext, PathPreference, Ty,
|
||||||
};
|
};
|
||||||
|
use crate::prelude::*;
|
||||||
use crate::snippet::{
|
use crate::snippet::{
|
||||||
CompletionCommand, CompletionContextKey, ParsedSnippet, PostfixSnippet, PostfixSnippetScope,
|
CompletionCommand, CompletionContextKey, ParsedSnippet, PostfixSnippet, PostfixSnippetScope,
|
||||||
PrefixSnippet, DEFAULT_POSTFIX_SNIPPET, DEFAULT_PREFIX_SNIPPET,
|
PrefixSnippet, DEFAULT_POSTFIX_SNIPPET, DEFAULT_PREFIX_SNIPPET,
|
||||||
|
@ -46,7 +47,9 @@ use crate::ty::{
|
||||||
DynTypeBounds, Iface, IfaceChecker, InsTy, SigTy, TyCtx, TypeInfo, TypeInterface, TypeVar,
|
DynTypeBounds, Iface, IfaceChecker, InsTy, SigTy, TyCtx, TypeInfo, TypeInterface, TypeVar,
|
||||||
};
|
};
|
||||||
use crate::upstream::{plain_docs_sentence, summarize_font_family};
|
use crate::upstream::{plain_docs_sentence, summarize_font_family};
|
||||||
use crate::{prelude::*, LspCompletion, LspCompletionKind};
|
|
||||||
|
type LspCompletion = lsp_types::CompletionItem;
|
||||||
|
type LspCompletionKind = lsp_types::CompletionItemKind;
|
||||||
|
|
||||||
/// Tinymist's completion features.
|
/// Tinymist's completion features.
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -205,10 +208,12 @@ pub struct CompletionWorker<'a> {
|
||||||
pub before: &'a str,
|
pub before: &'a str,
|
||||||
/// The text after the cursor.
|
/// The text after the cursor.
|
||||||
pub after: &'a str,
|
pub after: &'a str,
|
||||||
/// The root node of the source.
|
|
||||||
pub root: LinkedNode<'a>,
|
|
||||||
/// The leaf node at the cursor.
|
/// The leaf node at the cursor.
|
||||||
pub leaf: LinkedNode<'a>,
|
pub leaf: LinkedNode<'a>,
|
||||||
|
/// The syntax class at the cursor.
|
||||||
|
pub syntax: Option<SyntaxClass<'a>>,
|
||||||
|
/// The syntax context at the cursor.
|
||||||
|
pub syntax_context: Option<SyntaxContext<'a>>,
|
||||||
/// The cursor position.
|
/// The cursor position.
|
||||||
pub cursor: usize,
|
pub cursor: usize,
|
||||||
/// Whether the completion was explicitly requested.
|
/// Whether the completion was explicitly requested.
|
||||||
|
@ -217,8 +222,6 @@ pub struct CompletionWorker<'a> {
|
||||||
pub trigger_character: Option<char>,
|
pub trigger_character: Option<char>,
|
||||||
/// The position from which the completions apply.
|
/// The position from which the completions apply.
|
||||||
pub from: usize,
|
pub from: usize,
|
||||||
/// The type of the expression before the cursor.
|
|
||||||
pub from_ty: Option<Ty>,
|
|
||||||
/// The completions.
|
/// The completions.
|
||||||
pub raw_completions: Vec<Completion>,
|
pub raw_completions: Vec<Completion>,
|
||||||
/// The (lsp_types) completions.
|
/// The (lsp_types) completions.
|
||||||
|
@ -247,6 +250,10 @@ impl<'a> CompletionWorker<'a> {
|
||||||
let text = source.text();
|
let text = source.text();
|
||||||
let root = LinkedNode::new(source.root());
|
let root = LinkedNode::new(source.root());
|
||||||
let leaf = root.leaf_at_compat(cursor)?;
|
let leaf = root.leaf_at_compat(cursor)?;
|
||||||
|
let syntax = classify_syntax(leaf.clone(), cursor);
|
||||||
|
let syntax_context = classify_context(leaf.clone(), Some(cursor));
|
||||||
|
|
||||||
|
crate::log_debug_ct!("CompletionWorker: context {leaf:?} -> {syntax_context:#?}");
|
||||||
Some(Self {
|
Some(Self {
|
||||||
ctx,
|
ctx,
|
||||||
document,
|
document,
|
||||||
|
@ -254,13 +261,13 @@ impl<'a> CompletionWorker<'a> {
|
||||||
text,
|
text,
|
||||||
before: &text[..cursor],
|
before: &text[..cursor],
|
||||||
after: &text[cursor..],
|
after: &text[cursor..],
|
||||||
root,
|
|
||||||
leaf,
|
leaf,
|
||||||
|
syntax,
|
||||||
|
syntax_context,
|
||||||
cursor,
|
cursor,
|
||||||
trigger_character,
|
trigger_character,
|
||||||
explicit,
|
explicit,
|
||||||
from: cursor,
|
from: cursor,
|
||||||
from_ty: None,
|
|
||||||
incomplete: true,
|
incomplete: true,
|
||||||
raw_completions: vec![],
|
raw_completions: vec![],
|
||||||
completions: vec![],
|
completions: vec![],
|
||||||
|
@ -305,6 +312,45 @@ impl<'a> CompletionWorker<'a> {
|
||||||
|
|
||||||
/// Starts the completion process.
|
/// Starts the completion process.
|
||||||
pub(crate) fn work(mut self) -> Option<(bool, Vec<LspCompletion>)> {
|
pub(crate) fn work(mut self) -> Option<(bool, Vec<LspCompletion>)> {
|
||||||
|
// Skip if is the let binding item *directly*
|
||||||
|
if let Some(SyntaxClass::VarAccess(var)) = &self.syntax {
|
||||||
|
let node = var.node();
|
||||||
|
match node.parent_kind() {
|
||||||
|
// complete the init part of the let binding
|
||||||
|
Some(SyntaxKind::LetBinding) => {
|
||||||
|
let parent = node.parent()?;
|
||||||
|
let parent_init = parent.cast::<ast::LetBinding>()?.init()?;
|
||||||
|
let parent_init = parent.find(parent_init.span())?;
|
||||||
|
parent_init.find(node.span())?;
|
||||||
|
}
|
||||||
|
Some(SyntaxKind::Closure) => {
|
||||||
|
let parent = node.parent()?;
|
||||||
|
let parent_body = parent.cast::<ast::Closure>()?.body();
|
||||||
|
let parent_body = parent.find(parent_body.span())?;
|
||||||
|
parent_body.find(node.span())?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if an error node starts with number (e.g. `1pt`)
|
||||||
|
if matches!(
|
||||||
|
self.syntax,
|
||||||
|
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..) | SyntaxClass::Normal(..))
|
||||||
|
) && self.leaf.erroneous()
|
||||||
|
{
|
||||||
|
let mut chars = self.leaf.text().chars();
|
||||||
|
match chars.next() {
|
||||||
|
Some(ch) if ch.is_numeric() => return None,
|
||||||
|
Some('.') => {
|
||||||
|
if matches!(chars.next(), Some(ch) if ch.is_numeric()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Exclude it self from auto completion
|
// Exclude it self from auto completion
|
||||||
// e.g. `#let x = (1.);`
|
// e.g. `#let x = (1.);`
|
||||||
let self_ty = self.leaf.cast::<ast::Expr>().and_then(|leaf| {
|
let self_ty = self.leaf.cast::<ast::Expr>().and_then(|leaf| {
|
||||||
|
@ -317,32 +363,25 @@ impl<'a> CompletionWorker<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = self.complete_root();
|
let _ = self.complete_root();
|
||||||
let ctx = self.ctx;
|
|
||||||
let offset = self.from;
|
|
||||||
let worker_incomplete = self.incomplete;
|
let worker_incomplete = self.incomplete;
|
||||||
let mut raw_completions = self.raw_completions;
|
let mut raw_completions = self.raw_completions;
|
||||||
let mut completions_rest = self.completions;
|
let mut completions_rest = self.completions;
|
||||||
|
|
||||||
// Todo: remove these repeatedly identified information
|
|
||||||
let source = self.source.clone();
|
|
||||||
let syntax = classify_syntax(self.leaf.clone(), self.cursor);
|
|
||||||
let cursor = self.cursor;
|
|
||||||
|
|
||||||
// Filter and determine range to replace
|
// Filter and determine range to replace
|
||||||
let mut from_ident = None;
|
let mut from_ident = None;
|
||||||
let is_callee = matches!(syntax, Some(SyntaxClass::Callee(..)));
|
let is_callee = matches!(self.syntax, Some(SyntaxClass::Callee(..)));
|
||||||
if matches!(
|
if matches!(
|
||||||
syntax,
|
self.syntax,
|
||||||
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..))
|
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..))
|
||||||
) {
|
) {
|
||||||
let node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
let node = LinkedNode::new(self.source.root()).leaf_at_compat(self.cursor)?;
|
||||||
if is_ident_like(&node) && node.offset() == offset {
|
if is_ident_like(&node) && node.offset() == self.from {
|
||||||
from_ident = Some(node);
|
from_ident = Some(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let replace_range = if let Some(from_ident) = from_ident {
|
let replace_range = if let Some(from_ident) = from_ident {
|
||||||
let mut rng = from_ident.range();
|
let mut rng = from_ident.range();
|
||||||
let ident_prefix = source.text()[rng.start..cursor].to_string();
|
let ident_prefix = self.source.text()[rng.start..self.cursor].to_string();
|
||||||
|
|
||||||
raw_completions.retain(|item| {
|
raw_completions.retain(|item| {
|
||||||
let mut prefix_matcher = item.label.chars();
|
let mut prefix_matcher = item.label.chars();
|
||||||
|
@ -360,7 +399,7 @@ impl<'a> CompletionWorker<'a> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// if modifying some arguments, we need to truncate and add a comma
|
// if modifying some arguments, we need to truncate and add a comma
|
||||||
if !is_callee && cursor != rng.end && is_arg_like_context(&from_ident) {
|
if !is_callee && self.cursor != rng.end && is_arg_like_context(&from_ident) {
|
||||||
// extend comma
|
// extend comma
|
||||||
for item in raw_completions.iter_mut() {
|
for item in raw_completions.iter_mut() {
|
||||||
let apply = match &mut item.apply {
|
let apply = match &mut item.apply {
|
||||||
|
@ -377,12 +416,12 @@ impl<'a> CompletionWorker<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate
|
// Truncate
|
||||||
rng.end = cursor;
|
rng.end = self.cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.to_lsp_range(rng, &source)
|
self.ctx.to_lsp_range(rng, &self.source)
|
||||||
} else {
|
} else {
|
||||||
ctx.to_lsp_range(offset..cursor, &source)
|
self.ctx.to_lsp_range(self.from..self.cursor, &self.source)
|
||||||
};
|
};
|
||||||
|
|
||||||
let completions = raw_completions.iter().map(|typst_completion| {
|
let completions = raw_completions.iter().map(|typst_completion| {
|
||||||
|
@ -441,12 +480,9 @@ impl<'a> CompletionWorker<'a> {
|
||||||
return self.complete_imports().then_some(());
|
return self.complete_imports().then_some(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let syntax_context = classify_context(self.leaf.clone(), Some(self.cursor));
|
|
||||||
let syntax = classify_syntax(self.leaf.clone(), self.cursor);
|
|
||||||
crate::log_debug_ct!("complete_type: pos {:?} -> {syntax_context:#?}", self.leaf);
|
|
||||||
let mut args_node = None;
|
let mut args_node = None;
|
||||||
|
|
||||||
match syntax_context {
|
match self.syntax_context.clone() {
|
||||||
Some(SyntaxContext::Element { container, .. }) => {
|
Some(SyntaxContext::Element { container, .. }) => {
|
||||||
if let Some(container) = container.cast::<ast::Dict>() {
|
if let Some(container) = container.cast::<ast::Dict>() {
|
||||||
for named in container.items() {
|
for named in container.items() {
|
||||||
|
@ -525,8 +561,6 @@ impl<'a> CompletionWorker<'a> {
|
||||||
| None => {}
|
| None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::log_debug_ct!("ctx.leaf {:?}", self.leaf);
|
|
||||||
|
|
||||||
let ty = self
|
let ty = self
|
||||||
.ctx
|
.ctx
|
||||||
.post_type_of_node(self.leaf.clone())
|
.post_type_of_node(self.leaf.clone())
|
||||||
|
@ -534,16 +568,12 @@ impl<'a> CompletionWorker<'a> {
|
||||||
|
|
||||||
crate::log_debug_ct!("complete_type: {:?} -> ({scope:?}, {ty:#?})", self.leaf);
|
crate::log_debug_ct!("complete_type: {:?} -> ({scope:?}, {ty:#?})", self.leaf);
|
||||||
|
|
||||||
// if matches!((scope, &ty), (Regular | StringContent, None)) {
|
|
||||||
// return None;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// adjust the completion position
|
// adjust the completion position
|
||||||
// todo: syntax class seems not being considering `is_ident_like`
|
// todo: syntax class seems not being considering `is_ident_like`
|
||||||
// todo: merge ident_content_offset and label_content_offset
|
// todo: merge ident_content_offset and label_content_offset
|
||||||
if is_ident_like(&self.leaf) {
|
if is_ident_like(&self.leaf) {
|
||||||
self.from = self.leaf.offset();
|
self.from = self.leaf.offset();
|
||||||
} else if let Some(offset) = syntax.as_ref().and_then(SyntaxClass::complete_offset) {
|
} else if let Some(offset) = self.syntax.as_ref().and_then(SyntaxClass::complete_offset) {
|
||||||
self.from = offset;
|
self.from = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,8 +597,6 @@ impl<'a> CompletionWorker<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut completions = std::mem::take(&mut self.raw_completions);
|
let mut completions = std::mem::take(&mut self.raw_completions);
|
||||||
let ty = Some(Ty::from_types(self.seen_types.iter().cloned()));
|
|
||||||
let from_ty = std::mem::replace(&mut self.from_ty, ty);
|
|
||||||
match mode {
|
match mode {
|
||||||
InterpretMode::Code => {
|
InterpretMode::Code => {
|
||||||
self.complete_code();
|
self.complete_code();
|
||||||
|
@ -590,7 +618,6 @@ impl<'a> CompletionWorker<'a> {
|
||||||
},
|
},
|
||||||
InterpretMode::Comment | InterpretMode::String => {}
|
InterpretMode::Comment | InterpretMode::String => {}
|
||||||
};
|
};
|
||||||
self.from_ty = from_ty;
|
|
||||||
|
|
||||||
match scope {
|
match scope {
|
||||||
Regular | StringContent | ImportList | SetRule => {}
|
Regular | StringContent | ImportList | SetRule => {}
|
||||||
|
|
|
@ -489,27 +489,38 @@ impl SharedContext {
|
||||||
self.analysis.position_encoding
|
self.analysis.position_encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a LSP position to a Typst position.
|
/// Convert an LSP position to a Typst position.
|
||||||
pub fn to_typst_pos(&self, position: LspPosition, src: &Source) -> Option<usize> {
|
pub fn to_typst_pos(&self, position: LspPosition, src: &Source) -> Option<usize> {
|
||||||
crate::to_typst_position(position, self.analysis.position_encoding, src)
|
crate::to_typst_position(position, self.analysis.position_encoding, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a Typst offset to a LSP position.
|
/// Converts an LSP position with some offset.
|
||||||
|
pub fn to_typst_pos_offset(
|
||||||
|
&self,
|
||||||
|
source: &Source,
|
||||||
|
position: LspPosition,
|
||||||
|
shift: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
let offset = self.to_typst_pos(position, source)?;
|
||||||
|
Some(ceil_char_boundary(source.text(), offset + shift))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a Typst offset to an LSP position.
|
||||||
pub fn to_lsp_pos(&self, typst_offset: usize, src: &Source) -> LspPosition {
|
pub fn to_lsp_pos(&self, typst_offset: usize, src: &Source) -> LspPosition {
|
||||||
crate::to_lsp_position(typst_offset, self.analysis.position_encoding, src)
|
crate::to_lsp_position(typst_offset, self.analysis.position_encoding, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a LSP range to a Typst range.
|
/// Convert an LSP range to a Typst range.
|
||||||
pub fn to_typst_range(&self, position: LspRange, src: &Source) -> Option<Range<usize>> {
|
pub fn to_typst_range(&self, position: LspRange, src: &Source) -> Option<Range<usize>> {
|
||||||
crate::to_typst_range(position, self.analysis.position_encoding, src)
|
crate::to_typst_range(position, self.analysis.position_encoding, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a Typst range to a LSP range.
|
/// Convert a Typst range to an LSP range.
|
||||||
pub fn to_lsp_range(&self, position: Range<usize>, src: &Source) -> LspRange {
|
pub fn to_lsp_range(&self, position: Range<usize>, src: &Source) -> LspRange {
|
||||||
crate::to_lsp_range(position, src, self.analysis.position_encoding)
|
crate::to_lsp_range(position, src, self.analysis.position_encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a Typst range to a LSP range.
|
/// Convert a Typst range to an LSP range.
|
||||||
pub fn to_lsp_range_(&self, position: Range<usize>, fid: TypstFileId) -> Option<LspRange> {
|
pub fn to_lsp_range_(&self, position: Range<usize>, fid: TypstFileId) -> Option<LspRange> {
|
||||||
let ext = fid
|
let ext = fid
|
||||||
.vpath()
|
.vpath()
|
||||||
|
@ -578,23 +589,9 @@ impl SharedContext {
|
||||||
position: LspPosition,
|
position: LspPosition,
|
||||||
shift: usize,
|
shift: usize,
|
||||||
) -> Option<SyntaxClass<'s>> {
|
) -> Option<SyntaxClass<'s>> {
|
||||||
let (_, syntax) = self.classify_pos_(source, position, shift)?;
|
let cursor = self.to_typst_pos_offset(source, position, shift)?;
|
||||||
syntax
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Classifies the syntax under position that can be operated on by IDE
|
|
||||||
/// functionality.
|
|
||||||
pub fn classify_pos_<'s>(
|
|
||||||
&self,
|
|
||||||
source: &'s Source,
|
|
||||||
position: LspPosition,
|
|
||||||
shift: usize,
|
|
||||||
) -> Option<(usize, Option<SyntaxClass<'s>>)> {
|
|
||||||
let offset = self.to_typst_pos(position, source)?;
|
|
||||||
let cursor = ceil_char_boundary(source.text(), offset + shift);
|
|
||||||
|
|
||||||
let node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
let node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
||||||
Some((cursor, classify_syntax(node, cursor)))
|
classify_syntax(node, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the real definition of a compilation.
|
/// Get the real definition of a compilation.
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
use lsp_types::CompletionList;
|
use lsp_types::CompletionList;
|
||||||
use typst_shim::syntax::LinkedNodeExt;
|
|
||||||
|
|
||||||
use crate::{analysis::CompletionWorker, prelude::*, syntax::SyntaxClass, StatefulRequest};
|
use crate::{analysis::CompletionWorker, prelude::*, StatefulRequest};
|
||||||
|
|
||||||
pub(crate) type LspCompletion = lsp_types::CompletionItem;
|
|
||||||
pub(crate) type LspCompletionKind = lsp_types::CompletionItemKind;
|
|
||||||
|
|
||||||
pub(crate) mod snippet;
|
pub(crate) mod snippet;
|
||||||
|
|
||||||
|
@ -57,52 +53,6 @@ impl StatefulRequest for CompletionRequest {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let doc = doc.as_ref().map(|doc| doc.document.as_ref());
|
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
|
||||||
let (cursor, syntax) = ctx.classify_pos_(&source, self.position, 0)?;
|
|
||||||
|
|
||||||
// Skip if is the let binding item *directly*
|
|
||||||
if let Some(SyntaxClass::VarAccess(var)) = &syntax {
|
|
||||||
let node = var.node();
|
|
||||||
match node.parent_kind() {
|
|
||||||
// complete the init part of the let binding
|
|
||||||
Some(SyntaxKind::LetBinding) => {
|
|
||||||
let parent = node.parent()?;
|
|
||||||
let parent_init = parent.cast::<ast::LetBinding>()?.init()?;
|
|
||||||
let parent_init = parent.find(parent_init.span())?;
|
|
||||||
parent_init.find(node.span())?;
|
|
||||||
}
|
|
||||||
Some(SyntaxKind::Closure) => {
|
|
||||||
let parent = node.parent()?;
|
|
||||||
let parent_body = parent.cast::<ast::Closure>()?.body();
|
|
||||||
let parent_body = parent.find(parent_body.span())?;
|
|
||||||
parent_body.find(node.span())?;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if an error node starts with number (e.g. `1pt`)
|
|
||||||
if matches!(
|
|
||||||
syntax,
|
|
||||||
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..) | SyntaxClass::Normal(..))
|
|
||||||
) {
|
|
||||||
let node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
|
||||||
if node.erroneous() {
|
|
||||||
let mut chars = node.text().chars();
|
|
||||||
|
|
||||||
match chars.next() {
|
|
||||||
Some(ch) if ch.is_numeric() => return None,
|
|
||||||
Some('.') => {
|
|
||||||
if matches!(chars.next(), Some(ch) if ch.is_numeric()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Please see <https://github.com/nvarner/typst-lsp/commit/2d66f26fb96ceb8e485f492e5b81e9db25c3e8ec>
|
// Please see <https://github.com/nvarner/typst-lsp/commit/2d66f26fb96ceb8e485f492e5b81e9db25c3e8ec>
|
||||||
//
|
//
|
||||||
// FIXME: correctly identify a completion which is triggered
|
// FIXME: correctly identify a completion which is triggered
|
||||||
|
@ -118,6 +68,10 @@ impl StatefulRequest for CompletionRequest {
|
||||||
// assume that the completion is not explicit.
|
// assume that the completion is not explicit.
|
||||||
let explicit = false;
|
let explicit = false;
|
||||||
|
|
||||||
|
let doc = doc.as_ref().map(|doc| doc.document.as_ref());
|
||||||
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
|
let cursor = ctx.to_typst_pos_offset(&source, self.position, 0)?;
|
||||||
|
|
||||||
let worker =
|
let worker =
|
||||||
CompletionWorker::new(ctx, doc, &source, cursor, explicit, self.trigger_character)?;
|
CompletionWorker::new(ctx, doc, &source, cursor, explicit, self.trigger_character)?;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue