feat: lsp label descriptions for labels (#228)

This commit is contained in:
Myriad-Dreamin 2024-05-04 13:59:07 +08:00 committed by GitHub
parent 0a76b4b18a
commit 97043b9789
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 51 additions and 17 deletions

View file

@ -78,36 +78,59 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
.map(Value::Module)
}
/// A label with a description and details.
pub struct DynLabel {
/// The label itself.
pub label: Label,
/// A description of the label.
pub label_desc: Option<EcoString>,
/// Additional details about the label.
pub detail: Option<EcoString>,
}
/// Find all labels and details for them.
///
/// Returns:
/// - All labels and descriptions for them, if available
/// - A split offset: All labels before this offset belong to nodes, all after
/// belong to a bibliography.
pub fn analyze_labels(document: &Document) -> (Vec<(Label, Option<EcoString>)>, usize) {
pub fn analyze_labels(document: &Document) -> (Vec<DynLabel>, usize) {
let mut output = vec![];
// Labels in the document.
for elem in document.introspector.all() {
let Some(label) = elem.label() else { continue };
let details = elem
.get_by_name("caption")
.or_else(|| elem.get_by_name("body"))
.and_then(|field| match field {
Value::Content(content) => Some(content),
_ => None,
})
.as_ref()
.unwrap_or(elem)
.plain_text();
output.push((label, Some(details)));
let (is_derived, details) = {
let derived = elem
.get_by_name("caption")
.or_else(|| elem.get_by_name("body"));
match derived {
Some(Value::Content(content)) => (true, content.plain_text()),
Some(Value::Str(s)) => (true, s.into()),
_ => (false, elem.plain_text()),
}
};
output.push(DynLabel {
label,
label_desc: Some(if is_derived {
details.clone()
} else {
eco_format!("{}(..)", elem.func().name())
}),
detail: Some(details),
});
}
let split = output.len();
// Bibliography keys.
for (key, detail) in BibliographyElem::keys(document.introspector.track()) {
output.push((Label::new(&key), detail));
output.push(DynLabel {
label: Label::new(&key),
label_desc: detail.clone(),
detail,
});
}
(output, split)

View file

@ -17,7 +17,7 @@ use typst::visualize::Color;
use unscanny::Scanner;
use super::{plain_docs_sentence, summarize_font_family};
use crate::analysis::{analyze_expr, analyze_import, analyze_labels};
use crate::analysis::{analyze_expr, analyze_import, analyze_labels, DynLabel};
use crate::AnalysisContext;
mod ext;
@ -1144,7 +1144,12 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
(0, split)
};
for (label, detail) in labels.into_iter().skip(skip).take(take) {
for DynLabel {
label,
label_desc,
detail,
} in labels.into_iter().skip(skip).take(take)
{
self.completions.push(Completion {
kind: CompletionKind::Constant,
apply: (open || close).then(|| {
@ -1155,6 +1160,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
if close { ">" } else { "" }
)
}),
label_detail: label_desc,
label: label.as_str().into(),
detail,
..Completion::default()

View file

@ -11,7 +11,7 @@ use typst::util::{round_2, Numeric};
use typst::World;
use super::summarize_font_family;
use crate::analysis::{analyze_expr, analyze_labels};
use crate::analysis::{analyze_expr, analyze_labels, DynLabel};
/// Describe the item under the cursor.
///
@ -161,7 +161,12 @@ fn label_tooltip(document: &Document, leaf: &LinkedNode) -> Option<Tooltip> {
_ => return None,
};
for (label, detail) in analyze_labels(document).0 {
for DynLabel {
label,
label_desc: _,
detail,
} in analyze_labels(document).0
{
if label.as_str() == target {
return Some(Tooltip::Text(detail?));
}