mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-23 12:45:04 +00:00
feat: correctly parse and show hover doc (#105)
* feat: seperate content on hover tips * dev: half hover * fix: ensure extracting docs correctly
This commit is contained in:
parent
de2504b15f
commit
3344eebe3f
41 changed files with 355 additions and 131 deletions
|
@ -10,7 +10,6 @@ pub use global::*;
|
|||
|
||||
#[cfg(test)]
|
||||
mod module_tests {
|
||||
use ecow::EcoVec;
|
||||
use reflexo::path::unix_slash;
|
||||
use serde_json::json;
|
||||
|
||||
|
@ -63,6 +62,56 @@ mod module_tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod matcher_tests {
|
||||
|
||||
use typst::syntax::LinkedNode;
|
||||
|
||||
use crate::{syntax::get_def_target, tests::*};
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
snapshot_testing("match_def", &|ctx, path| {
|
||||
let source = ctx.source_by_path(&path).unwrap();
|
||||
|
||||
let pos = ctx
|
||||
.to_typst_pos(find_test_position(&source), &source)
|
||||
.unwrap();
|
||||
|
||||
let root = LinkedNode::new(source.root());
|
||||
let node = root.leaf_at(pos).unwrap();
|
||||
|
||||
let result = get_def_target(node).map(|e| format!("{:?}", e.node().range()));
|
||||
let result = result.as_deref().unwrap_or("<nil>");
|
||||
|
||||
assert_snapshot!(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod document_tests {
|
||||
|
||||
use crate::syntax::find_document_before;
|
||||
use crate::tests::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
snapshot_testing("docs", &|ctx, path| {
|
||||
let source = ctx.source_by_path(&path).unwrap();
|
||||
|
||||
let pos = ctx
|
||||
.to_typst_pos(find_test_position(&source), &source)
|
||||
.unwrap();
|
||||
|
||||
let result = find_document_before(&source, pos);
|
||||
let result = result.as_deref().unwrap_or("<nil>");
|
||||
|
||||
assert_snapshot!(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod lexical_hierarchy_tests {
|
||||
use def_use::DefUseSnapshot;
|
||||
|
|
|
@ -4,16 +4,16 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
|||
input_file: crates/tinymist-query/src/fixtures/def_use/base.typ
|
||||
---
|
||||
{
|
||||
"x@5..6@s0.typ": {
|
||||
"x@33..34@s0.typ": {
|
||||
"def": {
|
||||
"kind": {
|
||||
"Var": "Variable"
|
||||
},
|
||||
"name": "x",
|
||||
"range": "5:6"
|
||||
"range": "33:34"
|
||||
},
|
||||
"refs": [
|
||||
"x@13..14"
|
||||
"x@41..42"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
2
crates/tinymist-query/src/fixtures/docs/base.typ
Normal file
2
crates/tinymist-query/src/fixtures/docs/base.typ
Normal file
|
@ -0,0 +1,2 @@
|
|||
// This is X.
|
||||
#let x /* ident */ = 1;
|
2
crates/tinymist-query/src/fixtures/docs/blocky.typ
Normal file
2
crates/tinymist-query/src/fixtures/docs/blocky.typ
Normal file
|
@ -0,0 +1,2 @@
|
|||
/* This is X */
|
||||
#let x /* ident */ = 1;
|
3
crates/tinymist-query/src/fixtures/docs/blocky2.typ
Normal file
3
crates/tinymist-query/src/fixtures/docs/blocky2.typ
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* This is X
|
||||
Note: This is not Y */
|
||||
#let x /* ident */ = 1;
|
|
@ -0,0 +1,3 @@
|
|||
// This is X.
|
||||
// Note: this is not Y.
|
||||
#let x /* ident */ = 1;
|
2
crates/tinymist-query/src/fixtures/docs/no_comment.typ
Normal file
2
crates/tinymist-query/src/fixtures/docs/no_comment.typ
Normal file
|
@ -0,0 +1,2 @@
|
|||
#let x /* some comment */ = 1;
|
||||
#let x /* ident */ = 1;
|
4
crates/tinymist-query/src/fixtures/docs/not_attach.typ
Normal file
4
crates/tinymist-query/src/fixtures/docs/not_attach.typ
Normal file
|
@ -0,0 +1,4 @@
|
|||
/* This is X
|
||||
Note: This is not Y */
|
||||
|
||||
#let x /* ident */ = 1;
|
3
crates/tinymist-query/src/fixtures/docs/not_attach2.typ
Normal file
3
crates/tinymist-query/src/fixtures/docs/not_attach2.typ
Normal file
|
@ -0,0 +1,3 @@
|
|||
// This is X
|
||||
|
||||
#let x /* ident */ = 1;
|
4
crates/tinymist-query/src/fixtures/docs/param.typ
Normal file
4
crates/tinymist-query/src/fixtures/docs/param.typ
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Docs for f.
|
||||
#let f(/* ident after */ a) = {
|
||||
show it: it => it;
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
// Docs for f.
|
||||
#let f(a) = {
|
||||
show it: it => /* ident after */ it;
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/base.typ
|
||||
---
|
||||
This is X.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/blocky.typ
|
||||
---
|
||||
This is X
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/blocky2.typ
|
||||
---
|
||||
This is X
|
||||
Note: This is not Y
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/multiple_line.typ
|
||||
---
|
||||
This is X.
|
||||
Note: this is not Y.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: "result.as_deref().unwrap_or(\"<nil>\")"
|
||||
input_file: crates/tinymist-query/src/fixtures/document/no_comment.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/not_attach.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/not_attach2.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/param.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/docs/param_in_init.typ
|
||||
---
|
||||
<nil>
|
|
@ -4,16 +4,16 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
|||
input_file: crates/tinymist-query/src/fixtures/lexical_hierarchy/base.typ
|
||||
---
|
||||
{
|
||||
"x@5..6@s0.typ": {
|
||||
"x@33..34@s0.typ": {
|
||||
"def": {
|
||||
"kind": {
|
||||
"Var": "Variable"
|
||||
},
|
||||
"name": "x",
|
||||
"range": "5:6"
|
||||
"range": "33:34"
|
||||
},
|
||||
"refs": [
|
||||
"x@13..14"
|
||||
"x@41..42"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ input_file: crates/tinymist-query/src/fixtures/lexical_hierarchy/base.typ
|
|||
"Var": "Variable"
|
||||
},
|
||||
"name": "x",
|
||||
"range": "5:6"
|
||||
"range": "33:34"
|
||||
},
|
||||
{
|
||||
"kind": {
|
||||
"Var": "ValRef"
|
||||
},
|
||||
"name": "x",
|
||||
"range": "13:14"
|
||||
"range": "41:42"
|
||||
}
|
||||
]
|
||||
|
|
1
crates/tinymist-query/src/fixtures/match_def/base.typ
Normal file
1
crates/tinymist-query/src/fixtures/match_def/base.typ
Normal file
|
@ -0,0 +1 @@
|
|||
#let /* ident after */ f(a) = a;
|
|
@ -0,0 +1 @@
|
|||
#let f(a ) = /* ident after */a;
|
|
@ -0,0 +1,3 @@
|
|||
#let f(a) = {
|
||||
/* ident after */ a
|
||||
};
|
1
crates/tinymist-query/src/fixtures/match_def/param.typ
Normal file
1
crates/tinymist-query/src/fixtures/match_def/param.typ
Normal file
|
@ -0,0 +1 @@
|
|||
#let f(a /* ident */) = a;
|
|
@ -0,0 +1,3 @@
|
|||
#let f(a) = {
|
||||
show it: /* ident after */ it => it;
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
#let f(a) = {
|
||||
set text(/* ident after */ fill: a);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/match_def/base.typ
|
||||
---
|
||||
1..31
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/match_def/ident_in_init.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/match_def/ident_in_init2.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/match_def/param.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/match_def/param_in_init.typ
|
||||
---
|
||||
<nil>
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/match_def/param_in_init2.typ
|
||||
---
|
||||
<nil>
|
|
@ -3,7 +3,7 @@ use core::fmt;
|
|||
use crate::{
|
||||
analyze_signature, find_definition,
|
||||
prelude::*,
|
||||
syntax::{get_def_target, get_deref_target, LexicalVarKind},
|
||||
syntax::{find_document_before, get_deref_target, LexicalKind, LexicalVarKind},
|
||||
upstream::{expr_tooltip, tooltip, Tooltip},
|
||||
DefinitionLink, LspHoverContents, StatefulRequest,
|
||||
};
|
||||
|
@ -68,42 +68,58 @@ fn def_tooltip(
|
|||
|
||||
let lnk = find_definition(ctx, source.clone(), deref_target.clone())?;
|
||||
|
||||
let mut results = vec![];
|
||||
|
||||
match lnk.kind {
|
||||
crate::syntax::LexicalKind::Mod(_)
|
||||
| crate::syntax::LexicalKind::Var(LexicalVarKind::Label)
|
||||
| crate::syntax::LexicalKind::Var(LexicalVarKind::LabelRef)
|
||||
| crate::syntax::LexicalKind::Var(LexicalVarKind::ValRef)
|
||||
| crate::syntax::LexicalKind::Block
|
||||
| crate::syntax::LexicalKind::Heading(..) => None,
|
||||
crate::syntax::LexicalKind::Var(LexicalVarKind::Function) => Some(
|
||||
LspHoverContents::Scalar(lsp_types::MarkedString::String(format!(
|
||||
r#"```typc
|
||||
let {name}({params});
|
||||
```{doc}"#,
|
||||
name = lnk.name,
|
||||
params = ParamTooltip(&lnk),
|
||||
doc = DocTooltip::get(ctx, &lnk).unwrap_or_default(),
|
||||
))),
|
||||
),
|
||||
crate::syntax::LexicalKind::Var(LexicalVarKind::Variable) => {
|
||||
LexicalKind::Mod(_)
|
||||
| LexicalKind::Var(LexicalVarKind::Label)
|
||||
| LexicalKind::Var(LexicalVarKind::LabelRef)
|
||||
| LexicalKind::Var(LexicalVarKind::ValRef)
|
||||
| LexicalKind::Block
|
||||
| LexicalKind::Heading(..) => None,
|
||||
LexicalKind::Var(LexicalVarKind::Function) => {
|
||||
results.push(MarkedString::LanguageString(LanguageString {
|
||||
language: "typc".to_owned(),
|
||||
value: format!(
|
||||
"let {name}({params});",
|
||||
name = lnk.name,
|
||||
params = ParamTooltip(&lnk)
|
||||
),
|
||||
}));
|
||||
|
||||
if let Some(doc) = DocTooltip::get(ctx, &lnk) {
|
||||
results.push(MarkedString::String(doc));
|
||||
}
|
||||
|
||||
Some(LspHoverContents::Array(results))
|
||||
}
|
||||
LexicalKind::Var(LexicalVarKind::Variable) => {
|
||||
let deref_node = deref_target.node();
|
||||
// todo: check sensible length, value highlighting
|
||||
let values = expr_tooltip(ctx.world(), deref_node)
|
||||
.map(|t| match t {
|
||||
Tooltip::Text(s) => format!("// Values: {s}"),
|
||||
Tooltip::Code(s) => format!("// Values: {s}"),
|
||||
})
|
||||
.unwrap_or_default();
|
||||
Some(LspHoverContents::Scalar(lsp_types::MarkedString::String(
|
||||
format!(
|
||||
r#"```typc
|
||||
{values}
|
||||
let {name};
|
||||
```{doc}"#,
|
||||
name = lnk.name,
|
||||
doc = DocTooltip::get(ctx, &lnk).unwrap_or_default(),
|
||||
),
|
||||
)))
|
||||
if let Some(values) = expr_tooltip(ctx.world(), deref_node) {
|
||||
match values {
|
||||
Tooltip::Text(values) => {
|
||||
results.push(MarkedString::String(format!("Values: {values}")));
|
||||
}
|
||||
Tooltip::Code(values) => {
|
||||
results.push(MarkedString::LanguageString(LanguageString {
|
||||
language: "typc".to_owned(),
|
||||
value: format!("// Values\n{values}"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.push(MarkedString::LanguageString(LanguageString {
|
||||
language: "typc".to_owned(),
|
||||
value: format!("let {name};", name = lnk.name),
|
||||
}));
|
||||
|
||||
if let Some(doc) = DocTooltip::get(ctx, &lnk) {
|
||||
results.push(MarkedString::String(doc));
|
||||
}
|
||||
|
||||
Some(LspHoverContents::Array(results))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,81 +186,18 @@ impl DocTooltip {
|
|||
}
|
||||
|
||||
fn get_inner(ctx: &mut AnalysisContext, lnk: &DefinitionLink) -> Option<String> {
|
||||
// let doc = find_document_before(ctx, &lnk);
|
||||
|
||||
if matches!(lnk.value, Some(Value::Func(..))) {
|
||||
if let Some(builtin) = Self::builtin_func_tooltip(lnk) {
|
||||
return Some(builtin.to_owned());
|
||||
}
|
||||
};
|
||||
|
||||
Self::find_document_before(ctx, lnk)
|
||||
let src = ctx.source_by_id(lnk.fid).ok()?;
|
||||
find_document_before(&src, lnk.def_range.start)
|
||||
}
|
||||
}
|
||||
|
||||
impl DocTooltip {
|
||||
fn find_document_before(ctx: &mut AnalysisContext, lnk: &DefinitionLink) -> Option<String> {
|
||||
log::debug!("finding comment at: {:?}, {}", lnk.fid, lnk.def_range.start);
|
||||
let src = ctx.source_by_id(lnk.fid).ok()?;
|
||||
let root = LinkedNode::new(src.root());
|
||||
let leaf = root.leaf_at(lnk.def_range.start + 1)?;
|
||||
let def_target = get_def_target(leaf.clone())?;
|
||||
log::info!("found comment target: {:?}", def_target.node().kind());
|
||||
// collect all comments before the definition
|
||||
let mut comments = vec![];
|
||||
// todo: import
|
||||
let target = def_target.node().clone();
|
||||
let mut node = def_target.node().clone();
|
||||
while let Some(prev) = node.prev_sibling() {
|
||||
node = prev;
|
||||
if node.kind() == SyntaxKind::Hash {
|
||||
continue;
|
||||
}
|
||||
|
||||
let start = node.range().end;
|
||||
let end = target.range().start;
|
||||
|
||||
if end <= start {
|
||||
break;
|
||||
}
|
||||
|
||||
let nodes = node.parent()?.children();
|
||||
for n in nodes {
|
||||
let offset = n.offset();
|
||||
if offset > end || offset < start {
|
||||
continue;
|
||||
}
|
||||
|
||||
if n.kind() == SyntaxKind::Hash {
|
||||
continue;
|
||||
}
|
||||
if n.kind() == SyntaxKind::LineComment {
|
||||
// comments.push(n.text().strip_prefix("//")?.trim().to_owned());
|
||||
// strip all slash prefix
|
||||
let text = n.text().trim_start_matches('/');
|
||||
comments.push(text.trim().to_owned());
|
||||
continue;
|
||||
}
|
||||
if n.kind() == SyntaxKind::BlockComment {
|
||||
let text = n.text();
|
||||
let mut text = text.strip_prefix("/*")?.strip_suffix("*/")?.trim();
|
||||
// trip start star
|
||||
if text.starts_with('*') {
|
||||
text = text.strip_prefix('*')?.trim();
|
||||
}
|
||||
comments.push(text.to_owned());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if comments.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(comments.join("\n"))
|
||||
}
|
||||
|
||||
fn builtin_func_tooltip(lnk: &DefinitionLink) -> Option<&'_ str> {
|
||||
let Some(Value::Func(func)) = &lnk.value else {
|
||||
return None;
|
||||
|
|
|
@ -5,15 +5,16 @@ pub use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
pub use ecow::EcoVec;
|
||||
pub use itertools::{Format, Itertools};
|
||||
pub use log::{error, trace};
|
||||
pub use lsp_types::{
|
||||
request::GotoDeclarationResponse, CodeLens, CompletionResponse, DiagnosticRelatedInformation,
|
||||
DocumentSymbol, DocumentSymbolResponse, Documentation, FoldingRange, GotoDefinitionResponse,
|
||||
Hover, InlayHint, Location as LspLocation, LocationLink, MarkupContent, MarkupKind,
|
||||
Position as LspPosition, PrepareRenameResponse, SelectionRange, SemanticTokens,
|
||||
SemanticTokensDelta, SemanticTokensFullDeltaResult, SemanticTokensResult, SignatureHelp,
|
||||
SignatureInformation, SymbolInformation, Url, WorkspaceEdit,
|
||||
Hover, InlayHint, LanguageString, Location as LspLocation, LocationLink, MarkedString,
|
||||
MarkupContent, MarkupKind, Position as LspPosition, PrepareRenameResponse, SelectionRange,
|
||||
SemanticTokens, SemanticTokensDelta, SemanticTokensFullDeltaResult, SemanticTokensResult,
|
||||
SignatureHelp, SignatureInformation, SymbolInformation, Url, WorkspaceEdit,
|
||||
};
|
||||
pub use reflexo::vector::ir::DefId;
|
||||
pub use serde_json::Value as JsonValue;
|
||||
|
@ -22,7 +23,8 @@ pub use typst::foundations::{Func, ParamInfo, Value};
|
|||
pub use typst::syntax::FileId as TypstFileId;
|
||||
pub use typst::syntax::{
|
||||
ast::{self, AstNode},
|
||||
LinkedNode, Source, Spanned, SyntaxKind,
|
||||
package::{PackageManifest, PackageSpec},
|
||||
LinkedNode, Source, Spanned, SyntaxKind, VirtualPath,
|
||||
};
|
||||
pub use typst::World;
|
||||
|
||||
|
|
98
crates/tinymist-query/src/syntax/comment.rs
Normal file
98
crates/tinymist-query/src/syntax/comment.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::syntax::get_def_target;
|
||||
|
||||
fn extract_document_between(node: &LinkedNode, rng: Range<usize>) -> Option<String> {
|
||||
// collect all comments before the definition
|
||||
let mut comments = vec![];
|
||||
|
||||
let mut newline_count = 0;
|
||||
let nodes = node.parent()?.children();
|
||||
for n in nodes {
|
||||
let offset = n.offset();
|
||||
if !rng.contains(&offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log::debug!("found comment for docs: {:?}: {:?}", n.kind(), n.text());
|
||||
|
||||
match n.kind() {
|
||||
SyntaxKind::Hash => {
|
||||
newline_count = 0;
|
||||
}
|
||||
SyntaxKind::Space => {
|
||||
if n.text().contains('\n') {
|
||||
newline_count += 1;
|
||||
}
|
||||
if newline_count > 1 {
|
||||
comments.clear();
|
||||
}
|
||||
}
|
||||
SyntaxKind::Parbreak => {
|
||||
newline_count = 2;
|
||||
comments.clear();
|
||||
}
|
||||
SyntaxKind::LineComment => {
|
||||
newline_count = 0;
|
||||
// comments.push(n.text().strip_prefix("//")?.trim().to_owned());
|
||||
// strip all slash prefix
|
||||
let text = n.text().trim_start_matches('/');
|
||||
comments.push(text.trim().to_owned());
|
||||
continue;
|
||||
}
|
||||
SyntaxKind::BlockComment => {
|
||||
newline_count = 0;
|
||||
let text = n.text();
|
||||
let mut text = text.strip_prefix("/*")?.strip_suffix("*/")?.trim();
|
||||
// trip start star
|
||||
if text.starts_with('*') {
|
||||
text = text.strip_prefix('*')?.trim();
|
||||
}
|
||||
comments.push(text.to_owned());
|
||||
}
|
||||
_ => {
|
||||
newline_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if comments.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(comments.join("\n"))
|
||||
}
|
||||
|
||||
pub fn find_document_before(src: &Source, cursor: usize) -> Option<String> {
|
||||
log::debug!("finding docs at: {id:?}, {cursor}", id = src.id());
|
||||
|
||||
let root = LinkedNode::new(src.root());
|
||||
let leaf = root.leaf_at(cursor)?;
|
||||
let def_target = get_def_target(leaf.clone())?;
|
||||
log::info!("found docs target: {:?}", def_target.node().kind());
|
||||
// todo: import
|
||||
let target = def_target.node().clone();
|
||||
let mut node = target.clone();
|
||||
while let Some(prev) = node.prev_sibling() {
|
||||
node = prev;
|
||||
if node.kind() == SyntaxKind::Hash {
|
||||
continue;
|
||||
}
|
||||
|
||||
let start = node.range().end;
|
||||
let end = target.range().start;
|
||||
|
||||
if end <= start {
|
||||
return None;
|
||||
}
|
||||
|
||||
return extract_document_between(&node, start..end);
|
||||
}
|
||||
|
||||
if node.parent()?.range() == root.range() && node.prev_sibling().is_none() {
|
||||
return extract_document_between(&node, root.offset()..node.range().start);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
|
@ -1,12 +1,3 @@
|
|||
use std::path::Path;
|
||||
|
||||
use ecow::EcoVec;
|
||||
use typst::syntax::{
|
||||
ast,
|
||||
package::{PackageManifest, PackageSpec},
|
||||
FileId as TypstFileId, LinkedNode, Source, SyntaxKind, VirtualPath,
|
||||
};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
fn resolve_id_by_path(
|
||||
|
|
|
@ -13,6 +13,8 @@ pub(crate) mod matcher;
|
|||
pub use matcher::*;
|
||||
pub(crate) mod module;
|
||||
pub use module::*;
|
||||
pub(crate) mod comment;
|
||||
pub use comment::*;
|
||||
|
||||
use core::fmt;
|
||||
use std::ops::Range;
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
use std::{collections::HashMap, path::Path, sync::Once};
|
||||
|
||||
use ecow::EcoVec;
|
||||
use typst::syntax::{FileId as TypstFileId, VirtualPath};
|
||||
|
||||
use crate::prelude::AnalysisContext;
|
||||
use std::{collections::HashMap, sync::Once};
|
||||
|
||||
use super::find_imports;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The dependency information of a module (file).
|
||||
pub struct ModuleDependency {
|
||||
|
|
|
@ -106,10 +106,12 @@ pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut TypstSystemWorld, P
|
|||
|
||||
if source.starts_with("//") {
|
||||
let first_line = source.lines().next().unwrap();
|
||||
source = source.strip_prefix(first_line).unwrap().trim();
|
||||
|
||||
let content = first_line.strip_prefix("//").unwrap().trim();
|
||||
path = content.strip_prefix("path:").map(|e| e.trim().to_owned())
|
||||
|
||||
if let Some(path_attr) = content.strip_prefix("path:") {
|
||||
source = source.strip_prefix(first_line).unwrap().trim();
|
||||
path = Some(path_attr.trim().to_owned())
|
||||
}
|
||||
};
|
||||
|
||||
let path = path.unwrap_or_else(|| format!("/s{i}.typ"));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue