use tower_lsp::lsp_types::*;
use typst_syntax::{Source, SyntaxKind, SyntaxNode};
use crate::backend::Backend;
pub trait TypstCompletion {
fn get_completion_items_from_typst(
&self,
doc_pos: TextDocumentPositionParams,
) -> Vec<CompletionItem>;
fn find_node_at_position(&self, source: &Source, offset: usize) -> Option<SyntaxNode>;
}
impl TypstCompletion for Backend {
fn get_completion_items_from_typst(
&self,
doc_pos: TextDocumentPositionParams,
) -> Vec<CompletionItem> {
let uri = doc_pos.text_document.uri.to_string();
let position = doc_pos.position;
let mut items = Vec::new();
if let Some(source) = self.sources.get(&uri) {
if let Some(offset) = self.position_to_offset(source.text(), position) {
if let Some(node) = self.find_node_at_position(&source, offset) {
match node.kind() {
SyntaxKind::Markup => {}
SyntaxKind::Ident => {
items.push(CompletionItem {
label: "identifier".to_owned(),
kind: Some(CompletionItemKind::VARIABLE),
detail: Some("Typst identifier".to_owned()),
insert_text: Some("identifier".to_owned()),
..Default::default()
});
}
SyntaxKind::FuncCall => {
items.push(CompletionItem {
label: "function".to_owned(),
kind: Some(CompletionItemKind::FUNCTION),
detail: Some("Typst function call".to_owned()),
insert_text: Some("function()".to_owned()),
..Default::default()
});
}
_ => {
let keywords = vec![
"import", "include", "set", "show", "if", "else", "for",
"while",
];
for keyword in keywords {
items.push(CompletionItem {
label: keyword.to_owned(),
kind: Some(CompletionItemKind::KEYWORD),
detail: Some("Typst keyword".to_owned()),
insert_text: Some(keyword.to_owned()),
..Default::default()
});
}
}
}
}
}
}
items
}
fn find_node_at_position(&self, source: &Source, offset: usize) -> Option<SyntaxNode> {
fn traverse(node: &SyntaxNode, offset: usize, source: &Source) -> Option<SyntaxNode> {
if let Some(range) = source.range(node.span()) {
if range.start <= offset && offset < range.end {
for child in node.children() {
if let Some(found) = traverse(child, offset, source) {
return Some(found);
}
}
return Some(node.clone());
}
}
None
}
traverse(source.root(), offset, source)
}
}