From 905e1e3c21093dcfaa03eb986d225f4697796788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Mon, 30 Oct 2023 20:04:08 +0100 Subject: [PATCH 1/4] Move inlay hints to separate crate --- Cargo.lock | 11 ++++ crates/inlay-hints/Cargo.toml | 18 +++++ crates/inlay-hints/src/label.rs | 29 +++++++++ crates/inlay-hints/src/lib.rs | 35 ++++++++++ crates/texlab/Cargo.toml | 1 + crates/texlab/src/features/inlay_hint.rs | 83 +++++++++++++----------- 6 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 crates/inlay-hints/Cargo.toml create mode 100644 crates/inlay-hints/src/label.rs create mode 100644 crates/inlay-hints/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 83f8a78e..e434df32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -800,6 +800,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "inlay-hints" +version = "0.0.0" +dependencies = [ + "base-db", + "rowan", + "syntax", + "test-utils", +] + [[package]] name = "inotify" version = "0.9.6" @@ -1651,6 +1661,7 @@ dependencies = [ "folding", "fuzzy-matcher", "hover", + "inlay-hints", "insta", "itertools 0.11.0", "log", diff --git a/crates/inlay-hints/Cargo.toml b/crates/inlay-hints/Cargo.toml new file mode 100644 index 00000000..081f7731 --- /dev/null +++ b/crates/inlay-hints/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "inlay-hints" +version = "0.0.0" +license.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +base-db = { path = "../base-db" } +rowan = "0.15.11" +syntax = { path = "../syntax" } + +[dev-dependencies] +test-utils = { path = "../test-utils" } + +[lib] +doctest = false diff --git a/crates/inlay-hints/src/label.rs b/crates/inlay-hints/src/label.rs new file mode 100644 index 00000000..350771c7 --- /dev/null +++ b/crates/inlay-hints/src/label.rs @@ -0,0 +1,29 @@ +use base_db::{semantics::tex::LabelKind, util::render_label}; + +use crate::InlayHintData; + +use super::InlayHintBuilder; + +pub(super) fn find_hints(builder: &mut InlayHintBuilder) -> Option<()> { + let params = &builder.params.feature; + let data = params.document.data.as_tex()?; + let range = builder.params.range; + for label in data + .semantics + .labels + .iter() + .filter(|label| label.kind == LabelKind::Definition) + .filter(|label| label.name.range.intersect(range).is_some()) + { + let Some(rendered) = render_label(params.workspace, ¶ms.project, label) else { + continue; + }; + + builder.hints.push(crate::InlayHint { + offset: label.full_range.end(), + data: InlayHintData::LabelDefinition(rendered), + }); + } + + Some(()) +} diff --git a/crates/inlay-hints/src/lib.rs b/crates/inlay-hints/src/lib.rs new file mode 100644 index 00000000..fe86201a --- /dev/null +++ b/crates/inlay-hints/src/lib.rs @@ -0,0 +1,35 @@ +mod label; + +use base_db::{util::RenderedLabel, FeatureParams}; +use rowan::{TextRange, TextSize}; + +pub struct InlayHintParams<'a> { + pub range: TextRange, + pub feature: FeatureParams<'a>, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct InlayHint<'a> { + pub offset: TextSize, + pub data: InlayHintData<'a>, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum InlayHintData<'a> { + LabelDefinition(RenderedLabel<'a>), +} + +pub fn find_all<'a>(params: InlayHintParams<'a>) -> Option> { + let mut builder = InlayHintBuilder { + params, + hints: Vec::new(), + }; + + label::find_hints(&mut builder); + Some(builder.hints) +} + +struct InlayHintBuilder<'a> { + params: InlayHintParams<'a>, + hints: Vec>, +} diff --git a/crates/texlab/Cargo.toml b/crates/texlab/Cargo.toml index e2ca8b44..5d2e3429 100644 --- a/crates/texlab/Cargo.toml +++ b/crates/texlab/Cargo.toml @@ -39,6 +39,7 @@ fern = "0.6.2" folding = { path = "../folding" } fuzzy-matcher = { version = "0.3.7", features = ["compact"] } hover = { path = "../hover" } +inlay-hints = { path = "../inlay-hints" } itertools = "0.11.0" log = "0.4.19" lsp-server = "0.7.4" diff --git a/crates/texlab/src/features/inlay_hint.rs b/crates/texlab/src/features/inlay_hint.rs index 14a53ae2..7062b383 100644 --- a/crates/texlab/src/features/inlay_hint.rs +++ b/crates/texlab/src/features/inlay_hint.rs @@ -1,48 +1,53 @@ -mod label; - -use base_db::{Document, Project, Workspace}; -use lsp_types::{InlayHint, InlayHintLabel, Range, Url}; -use rowan::{TextRange, TextSize}; +use base_db::{util::RenderedObject, FeatureParams, Workspace}; +use inlay_hints::InlayHintParams; use crate::util::line_index_ext::LineIndexExt; -pub fn find_all(workspace: &Workspace, uri: &Url, range: Range) -> Option> { +pub fn find_all( + workspace: &Workspace, + uri: &lsp_types::Url, + range: lsp_types::Range, +) -> Option> { let document = workspace.lookup(uri)?; - let range = document.line_index.offset_lsp_range(range); - let project = workspace.project(document); + let line_index = &document.line_index; + let range = line_index.offset_lsp_range(range); - let mut builder = InlayHintBuilder { - workspace, - document, - project, - range, - hints: Vec::new(), - }; + let feature = FeatureParams::new(workspace, document); + let params = InlayHintParams { range, feature }; + let hints = inlay_hints::find_all(params)?; + let hints = hints.into_iter().filter_map(|hint| { + let position = line_index.line_col_lsp(hint.offset); + Some(match hint.data { + inlay_hints::InlayHintData::LabelDefinition(label) => { + let number = label.number?; - label::find_hints(&mut builder); - Some(builder.hints) -} + let text = match &label.object { + RenderedObject::Section { prefix, .. } => { + format!("{} {}", prefix, number) + } + RenderedObject::Float { kind, .. } => { + format!("{} {}", kind.as_str(), number) + } + RenderedObject::Theorem { kind, .. } => { + format!("{} {}", kind, number) + } + RenderedObject::Equation => format!("Equation ({})", number), + RenderedObject::EnumItem => format!("Item {}", number), + }; -struct InlayHintBuilder<'a> { - workspace: &'a Workspace, - document: &'a Document, - project: Project<'a>, - range: TextRange, - hints: Vec, -} + lsp_types::InlayHint { + position, + label: lsp_types::InlayHintLabel::String(format!(" {text} ")), + kind: None, + text_edits: None, + tooltip: None, + padding_left: Some(true), + padding_right: None, + data: None, + } + } + }) + }); -impl<'db> InlayHintBuilder<'db> { - pub fn push(&mut self, offset: TextSize, text: String) { - let position = self.document.line_index.line_col_lsp(offset); - self.hints.push(InlayHint { - position, - label: InlayHintLabel::String(format!(" {text} ")), - kind: None, - text_edits: None, - tooltip: None, - padding_left: Some(true), - padding_right: None, - data: None, - }); - } + Some(hints.collect()) } From f5ce598d69de35183bfdfdfd9d21020d3009fbf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Mon, 30 Oct 2023 21:07:57 +0100 Subject: [PATCH 2/4] Display inlay hints for label references --- CHANGELOG.md | 7 +++ Cargo.lock | 1 + crates/base-db/src/config.rs | 17 +++++++ crates/inlay-hints/Cargo.toml | 1 + crates/inlay-hints/src/label.rs | 56 ++++++++++++++++++------ crates/inlay-hints/src/lib.rs | 1 + crates/texlab/src/features/inlay_hint.rs | 18 +++++++- crates/texlab/src/server/options.rs | 12 +++++ 8 files changed, 98 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 752480b1..d859724e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Add `texlab.inlayHints.labelDefinitions` and `texlab.inlayHints.labelReferences` options ([#753](https://github.com/latex-lsp/texlab/issues/753)) +- Display inlay hints for label references by default ([#753](https://github.com/latex-lsp/texlab/issues/753)) + ## [5.10.1] - 2023-10-10 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index e434df32..b565eaf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -806,6 +806,7 @@ version = "0.0.0" dependencies = [ "base-db", "rowan", + "rustc-hash", "syntax", "test-utils", ] diff --git a/crates/base-db/src/config.rs b/crates/base-db/src/config.rs index 65728e08..7c4187f7 100644 --- a/crates/base-db/src/config.rs +++ b/crates/base-db/src/config.rs @@ -14,6 +14,7 @@ pub struct Config { pub symbols: SymbolConfig, pub syntax: SyntaxConfig, pub completion: CompletionConfig, + pub inlay_hints: InlayHintConfig, } #[derive(Debug)] @@ -77,6 +78,12 @@ pub struct SymbolConfig { pub ignored_patterns: Vec, } +#[derive(Debug)] +pub struct InlayHintConfig { + pub label_definitions: bool, + pub label_references: bool, +} + #[derive(Debug)] pub struct CompletionConfig { pub matcher: MatchingAlgo, @@ -101,6 +108,7 @@ impl Default for Config { symbols: SymbolConfig::default(), syntax: SyntaxConfig::default(), completion: CompletionConfig::default(), + inlay_hints: InlayHintConfig::default(), } } } @@ -174,6 +182,15 @@ impl Default for SymbolConfig { } } +impl Default for InlayHintConfig { + fn default() -> Self { + Self { + label_definitions: true, + label_references: true, + } + } +} + impl Default for CompletionConfig { fn default() -> Self { Self { diff --git a/crates/inlay-hints/Cargo.toml b/crates/inlay-hints/Cargo.toml index 081f7731..82b1c729 100644 --- a/crates/inlay-hints/Cargo.toml +++ b/crates/inlay-hints/Cargo.toml @@ -9,6 +9,7 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } rowan = "0.15.11" +rustc-hash = "1.1.0" syntax = { path = "../syntax" } [dev-dependencies] diff --git a/crates/inlay-hints/src/label.rs b/crates/inlay-hints/src/label.rs index 350771c7..8db85275 100644 --- a/crates/inlay-hints/src/label.rs +++ b/crates/inlay-hints/src/label.rs @@ -1,10 +1,19 @@ -use base_db::{semantics::tex::LabelKind, util::render_label}; +use base_db::{ + semantics::tex::{Label, LabelKind}, + util::{queries::Object, render_label}, + FeatureParams, +}; +use rustc_hash::FxHashMap; -use crate::InlayHintData; - -use super::InlayHintBuilder; +use crate::{InlayHint, InlayHintBuilder, InlayHintData}; pub(super) fn find_hints(builder: &mut InlayHintBuilder) -> Option<()> { + let definitions = base_db::semantics::tex::Label::find_all(&builder.params.feature.project) + .into_iter() + .filter(|(_, label)| label.kind == LabelKind::Definition) + .map(|(_, label)| (label.name_text(), label)) + .collect::>(); + let params = &builder.params.feature; let data = params.document.data.as_tex()?; let range = builder.params.range; @@ -12,18 +21,39 @@ pub(super) fn find_hints(builder: &mut InlayHintBuilder) -> Option<()> { .semantics .labels .iter() - .filter(|label| label.kind == LabelKind::Definition) .filter(|label| label.name.range.intersect(range).is_some()) { - let Some(rendered) = render_label(params.workspace, ¶ms.project, label) else { - continue; - }; - - builder.hints.push(crate::InlayHint { - offset: label.full_range.end(), - data: InlayHintData::LabelDefinition(rendered), - }); + if let Some(hint) = process_label(params, &definitions, label) { + builder.hints.push(hint); + } } Some(()) } + +fn process_label<'a>( + params: &FeatureParams<'a>, + definitions: &FxHashMap<&str, &'a Label>, + label: &'a Label, +) -> Option> { + let config = ¶ms.workspace.config().inlay_hints; + let offset = label.full_range.end(); + let data = if label.kind == LabelKind::Definition { + if !config.label_definitions { + return None; + } + + let label = render_label(params.workspace, ¶ms.project, label)?; + InlayHintData::LabelDefinition(label) + } else { + if !config.label_references { + return None; + } + + let label = definitions.get(label.name.text.as_str())?; + let label = render_label(params.workspace, ¶ms.project, label)?; + InlayHintData::LabelReference(label) + }; + + Some(InlayHint { offset, data }) +} diff --git a/crates/inlay-hints/src/lib.rs b/crates/inlay-hints/src/lib.rs index fe86201a..48373d30 100644 --- a/crates/inlay-hints/src/lib.rs +++ b/crates/inlay-hints/src/lib.rs @@ -17,6 +17,7 @@ pub struct InlayHint<'a> { #[derive(Debug, PartialEq, Eq)] pub enum InlayHintData<'a> { LabelDefinition(RenderedLabel<'a>), + LabelReference(RenderedLabel<'a>), } pub fn find_all<'a>(params: InlayHintParams<'a>) -> Option> { diff --git a/crates/texlab/src/features/inlay_hint.rs b/crates/texlab/src/features/inlay_hint.rs index 7062b383..cb4056f9 100644 --- a/crates/texlab/src/features/inlay_hint.rs +++ b/crates/texlab/src/features/inlay_hint.rs @@ -1,5 +1,5 @@ use base_db::{util::RenderedObject, FeatureParams, Workspace}; -use inlay_hints::InlayHintParams; +use inlay_hints::{InlayHintData, InlayHintParams}; use crate::util::line_index_ext::LineIndexExt; @@ -18,7 +18,7 @@ pub fn find_all( let hints = hints.into_iter().filter_map(|hint| { let position = line_index.line_col_lsp(hint.offset); Some(match hint.data { - inlay_hints::InlayHintData::LabelDefinition(label) => { + InlayHintData::LabelDefinition(label) => { let number = label.number?; let text = match &label.object { @@ -35,6 +35,20 @@ pub fn find_all( RenderedObject::EnumItem => format!("Item {}", number), }; + lsp_types::InlayHint { + position, + label: lsp_types::InlayHintLabel::String(format!(" {text} ")), + kind: None, + text_edits: None, + tooltip: None, + padding_left: Some(true), + padding_right: None, + data: None, + } + } + InlayHintData::LabelReference(label) => { + let text = label.reference(); + lsp_types::InlayHint { position, label: lsp_types::InlayHintLabel::String(format!(" {text} ")), diff --git a/crates/texlab/src/server/options.rs b/crates/texlab/src/server/options.rs index 3a2bf1bb..38287452 100644 --- a/crates/texlab/src/server/options.rs +++ b/crates/texlab/src/server/options.rs @@ -22,6 +22,7 @@ pub struct Options { pub latexindent: LatexindentOptions, pub forward_search: ForwardSearchOptions, pub completion: CompletionOptions, + pub inlay_hints: InlayHintOptions, pub experimental: ExperimentalOptions, } @@ -100,6 +101,14 @@ pub struct DiagnosticsOptions { pub ignored_patterns: Vec, } +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(default)] +pub struct InlayHintOptions { + pub label_definitions: Option, + pub label_references: Option, +} + #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(default)] @@ -243,6 +252,9 @@ impl From for Config { .map(|pattern| pattern.0) .collect(); + config.inlay_hints.label_definitions = value.inlay_hints.label_definitions.unwrap_or(true); + config.inlay_hints.label_references = value.inlay_hints.label_references.unwrap_or(true); + config.completion.matcher = match value.completion.matcher { CompletionMatcher::Fuzzy => base_db::MatchingAlgo::Skim, CompletionMatcher::FuzzyIgnoreCase => base_db::MatchingAlgo::SkimIgnoreCase, From 0944a698b4a8d28e447834fe71ea0fc92bfc11e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Mon, 30 Oct 2023 22:05:00 +0100 Subject: [PATCH 3/4] WIP --- Cargo.lock | 1 + crates/inlay-hints/Cargo.toml | 1 + crates/inlay-hints/src/citations.rs | 52 ++++++++++++++++++++++++ crates/inlay-hints/src/lib.rs | 3 ++ crates/texlab/src/features/inlay_hint.rs | 10 +++++ 5 files changed, 67 insertions(+) create mode 100644 crates/inlay-hints/src/citations.rs diff --git a/Cargo.lock b/Cargo.lock index b565eaf5..f427f9ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -805,6 +805,7 @@ name = "inlay-hints" version = "0.0.0" dependencies = [ "base-db", + "citeproc", "rowan", "rustc-hash", "syntax", diff --git a/crates/inlay-hints/Cargo.toml b/crates/inlay-hints/Cargo.toml index 82b1c729..34537bd2 100644 --- a/crates/inlay-hints/Cargo.toml +++ b/crates/inlay-hints/Cargo.toml @@ -8,6 +8,7 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } +citeproc = { path = "../citeproc" } rowan = "0.15.11" rustc-hash = "1.1.0" syntax = { path = "../syntax" } diff --git a/crates/inlay-hints/src/citations.rs b/crates/inlay-hints/src/citations.rs new file mode 100644 index 00000000..ddfa45d4 --- /dev/null +++ b/crates/inlay-hints/src/citations.rs @@ -0,0 +1,52 @@ +use base_db::{ + semantics::{bib::Entry, tex::Citation}, + util::queries::Object, + Document, +}; +use rowan::ast::AstNode; +use rustc_hash::FxHashMap; +use syntax::bibtex; + +use crate::{InlayHint, InlayHintBuilder, InlayHintData}; + +pub(super) fn find_hints(builder: &mut InlayHintBuilder) -> Option<()> { + let params = &builder.params.feature; + let data = params.document.data.as_tex()?; + let range = builder.params.range; + + let entries = Entry::find_all(¶ms.project) + .map(|(document, entry)| (entry.name_text(), (document, entry))) + .collect::>(); + + for citation in data + .semantics + .citations + .iter() + .filter(|citation| citation.name.range.intersect(range).is_some()) + { + if let Some(hint) = process_citation(&entries, citation) { + builder.hints.push(hint); + } + } + + Some(()) +} + +fn process_citation<'a>( + entries: &FxHashMap<&str, (&'a Document, &'a Entry)>, + citation: &'a Citation, +) -> Option> { + let offset = citation.name.range.end(); + let (document, entry) = entries.get(citation.name.text.as_str())?; + + let data = document.data.as_bib()?; + let root = &data.root_node(); + let name = root + .token_at_offset(entry.name.range.start()) + .right_biased()?; + + let entry = name.parent_ancestors().find_map(bibtex::Entry::cast)?; + let text = citeproc::render(&entry)?; + let data = InlayHintData::Citation(text); + Some(InlayHint { offset, data }) +} diff --git a/crates/inlay-hints/src/lib.rs b/crates/inlay-hints/src/lib.rs index 48373d30..4fc3a2ed 100644 --- a/crates/inlay-hints/src/lib.rs +++ b/crates/inlay-hints/src/lib.rs @@ -1,3 +1,4 @@ +mod citations; mod label; use base_db::{util::RenderedLabel, FeatureParams}; @@ -18,6 +19,7 @@ pub struct InlayHint<'a> { pub enum InlayHintData<'a> { LabelDefinition(RenderedLabel<'a>), LabelReference(RenderedLabel<'a>), + Citation(String), } pub fn find_all<'a>(params: InlayHintParams<'a>) -> Option> { @@ -27,6 +29,7 @@ pub fn find_all<'a>(params: InlayHintParams<'a>) -> Option> { }; label::find_hints(&mut builder); + citations::find_hints(&mut builder); Some(builder.hints) } diff --git a/crates/texlab/src/features/inlay_hint.rs b/crates/texlab/src/features/inlay_hint.rs index cb4056f9..6c89affd 100644 --- a/crates/texlab/src/features/inlay_hint.rs +++ b/crates/texlab/src/features/inlay_hint.rs @@ -60,6 +60,16 @@ pub fn find_all( data: None, } } + InlayHintData::Citation(text) => lsp_types::InlayHint { + position, + label: lsp_types::InlayHintLabel::String(format!(" {text} ")), + kind: None, + text_edits: None, + tooltip: None, + padding_left: Some(true), + padding_right: None, + data: None, + }, }) }); From 1ec872938d65eaa5e03aacfedca289a51c169543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Tue, 31 Oct 2023 20:05:56 +0100 Subject: [PATCH 4/4] Add inlay hints for citations --- CHANGELOG.md | 7 +++-- crates/base-db/src/config.rs | 2 ++ crates/citeproc/src/driver.rs | 43 +++++++++++++++++++++++++++-- crates/citeproc/src/lib.rs | 21 ++++++++++++-- crates/citeproc/src/tests.rs | 4 ++- crates/hover/src/citation.rs | 2 +- crates/inlay-hints/src/citations.rs | 6 +++- crates/texlab/src/server.rs | 4 ++- crates/texlab/src/server/options.rs | 2 ++ 9 files changed, 80 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d859724e..7e33aeea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `texlab.inlayHints.labelDefinitions` and `texlab.inlayHints.labelReferences` options ([#753](https://github.com/latex-lsp/texlab/issues/753)) -- Display inlay hints for label references by default ([#753](https://github.com/latex-lsp/texlab/issues/753)) +- Add inlay hints for label references and citations ([#753](https://github.com/latex-lsp/texlab/issues/753)) +- Add new options to configure inlay hints ([#753](https://github.com/latex-lsp/texlab/issues/753)): + - `texlab.inlayHints.labelDefinitions` + - `texlab.inlayHints.labelReferences` + - `texlab.inlayHints.citations` ## [5.10.1] - 2023-10-10 diff --git a/crates/base-db/src/config.rs b/crates/base-db/src/config.rs index 7c4187f7..0ee9b440 100644 --- a/crates/base-db/src/config.rs +++ b/crates/base-db/src/config.rs @@ -82,6 +82,7 @@ pub struct SymbolConfig { pub struct InlayHintConfig { pub label_definitions: bool, pub label_references: bool, + pub citations: bool, } #[derive(Debug)] @@ -187,6 +188,7 @@ impl Default for InlayHintConfig { Self { label_definitions: true, label_references: true, + citations: false, } } } diff --git a/crates/citeproc/src/driver.rs b/crates/citeproc/src/driver.rs index 778e2539..e6c5f9b0 100644 --- a/crates/citeproc/src/driver.rs +++ b/crates/citeproc/src/driver.rs @@ -10,17 +10,27 @@ use syntax::bibtex; use titlecase::titlecase; use url::Url; +use crate::{Mode, Options}; + use super::{ entry::{EntryData, EntryKind}, output::{Inline, InlineBuilder, Punct}, }; -#[derive(Debug, Default)] -pub struct Driver { +#[derive(Debug)] +pub struct Driver<'a> { builder: InlineBuilder, + options: &'a Options, } -impl Driver { +impl<'a> Driver<'a> { + pub fn new(options: &'a Options) -> Self { + Self { + builder: InlineBuilder::default(), + options, + } + } + pub fn process(&mut self, entry: &bibtex::Entry) { let entry = EntryData::from(entry); match entry.kind { @@ -531,6 +541,8 @@ impl Driver { } fn introduction(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let author = entry.author.remove(&AuthorField::Introduction)?; self.builder.push( Inline::Regular(format!("With an intro. by {}", author)), @@ -542,6 +554,8 @@ impl Driver { } fn foreword(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let author = entry.author.remove(&AuthorField::Commentator)?; self.builder.push( Inline::Regular(format!("With a forew. by {}", author)), @@ -553,6 +567,8 @@ impl Driver { } fn afterword(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let author = entry.author.remove(&AuthorField::Commentator)?; self.builder.push( Inline::Regular(format!("With an afterw. by {}", author)), @@ -564,6 +580,8 @@ impl Driver { } fn note(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let note = entry.text.remove(&TextField::Note)?; self.builder .push(Inline::Regular(note.text), Punct::Dot, Punct::Comma); @@ -614,6 +632,8 @@ impl Driver { } fn eid(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let eid = entry.text.remove(&TextField::Eid)?; self.builder .push(Inline::Regular(eid.text), Punct::Comma, Punct::Space); @@ -622,6 +642,8 @@ impl Driver { } fn isbn(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let isbn = entry.text.remove(&TextField::Isbn)?; self.builder.push( Inline::Regular("ISBN".to_string()), @@ -636,6 +658,8 @@ impl Driver { } fn issn(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let issn = entry.text.remove(&TextField::Issn)?; self.builder.push( Inline::Regular("ISSN".to_string()), @@ -650,6 +674,8 @@ impl Driver { } fn url(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let url = entry.text.remove(&TextField::Url)?; self.builder @@ -672,6 +698,8 @@ impl Driver { } fn doi(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let doi = entry.text.remove(&TextField::Doi)?; self.builder .push(Inline::Regular("DOI".to_string()), Punct::Dot, Punct::Colon); @@ -690,6 +718,8 @@ impl Driver { } fn eprint(&mut self, entry: &mut EntryData) -> Option<()> { + self.check_detailed_mode()?; + let eprint = entry.text.remove(&TextField::Eprint)?; let eprint_type = entry .text @@ -722,6 +752,13 @@ impl Driver { Some(()) } + fn check_detailed_mode(&self) -> Option<()> { + match self.options.mode { + Mode::Detailed => Some(()), + Mode::Overview => None, + } + } + pub fn finish(self) -> impl Iterator { self.builder.finish() } diff --git a/crates/citeproc/src/lib.rs b/crates/citeproc/src/lib.rs index de243065..7b48208c 100644 --- a/crates/citeproc/src/lib.rs +++ b/crates/citeproc/src/lib.rs @@ -7,10 +7,27 @@ use unicode_normalization::UnicodeNormalization; use self::{driver::Driver, output::Inline}; +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum Mode { + Detailed, + Overview, +} + +impl Default for Mode { + fn default() -> Self { + Self::Detailed + } +} + +#[derive(Debug, Default)] +pub struct Options { + pub mode: Mode, +} + #[must_use] -pub fn render(entry: &bibtex::Entry) -> Option { +pub fn render(entry: &bibtex::Entry, options: &Options) -> Option { let mut output = String::new(); - let mut driver = Driver::default(); + let mut driver = Driver::new(options); driver.process(entry); driver.finish().for_each(|(inline, punct)| { let text = match inline { diff --git a/crates/citeproc/src/tests.rs b/crates/citeproc/src/tests.rs index 1c91580f..3d4b0f4c 100644 --- a/crates/citeproc/src/tests.rs +++ b/crates/citeproc/src/tests.rs @@ -3,11 +3,13 @@ use parser::parse_bibtex; use rowan::ast::AstNode; use syntax::bibtex; +use crate::Options; + fn check(input: &str, expect: Expect) { let green = parse_bibtex(input); let root = bibtex::Root::cast(bibtex::SyntaxNode::new_root(green)).unwrap(); let entry = root.entries().next().unwrap(); - let output = super::render(&entry).unwrap(); + let output = super::render(&entry, &Options::default()).unwrap(); expect.assert_eq(&output); } diff --git a/crates/hover/src/citation.rs b/crates/hover/src/citation.rs index 49e5359b..4711b890 100644 --- a/crates/hover/src/citation.rs +++ b/crates/hover/src/citation.rs @@ -31,7 +31,7 @@ pub(super) fn find_hover<'db>(params: &HoverParams<'db>) -> Option> { let data = document.data.as_bib()?; let root = bibtex::Root::cast(data.root_node())?; let entry = root.find_entry(name)?; - citeproc::render(&entry) + citeproc::render(&entry, &citeproc::Options::default()) })?; let data = HoverData::Citation(text); diff --git a/crates/inlay-hints/src/citations.rs b/crates/inlay-hints/src/citations.rs index ddfa45d4..e143b2e6 100644 --- a/crates/inlay-hints/src/citations.rs +++ b/crates/inlay-hints/src/citations.rs @@ -46,7 +46,11 @@ fn process_citation<'a>( .right_biased()?; let entry = name.parent_ancestors().find_map(bibtex::Entry::cast)?; - let text = citeproc::render(&entry)?; + let options = citeproc::Options { + mode: citeproc::Mode::Overview, + }; + + let text = citeproc::render(&entry, &options)?; let data = InlayHintData::Citation(text); Some(InlayHint { offset, data }) } diff --git a/crates/texlab/src/server.rs b/crates/texlab/src/server.rs index 5a0c4896..dc2230d5 100644 --- a/crates/texlab/src/server.rs +++ b/crates/texlab/src/server.rs @@ -576,7 +576,9 @@ impl Server { { item.documentation = bibtex::Root::cast(data.root_node()) .and_then(|root| root.find_entry(&key)) - .and_then(|entry| citeproc::render(&entry)) + .and_then(|entry| { + citeproc::render(&entry, &citeproc::Options::default()) + }) .map(|value| { Documentation::MarkupContent(MarkupContent { kind: MarkupKind::Markdown, diff --git a/crates/texlab/src/server/options.rs b/crates/texlab/src/server/options.rs index 38287452..c50cea51 100644 --- a/crates/texlab/src/server/options.rs +++ b/crates/texlab/src/server/options.rs @@ -107,6 +107,7 @@ pub struct DiagnosticsOptions { pub struct InlayHintOptions { pub label_definitions: Option, pub label_references: Option, + pub citations: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -254,6 +255,7 @@ impl From for Config { config.inlay_hints.label_definitions = value.inlay_hints.label_definitions.unwrap_or(true); config.inlay_hints.label_references = value.inlay_hints.label_references.unwrap_or(true); + config.inlay_hints.citations = value.inlay_hints.citations.unwrap_or(false); config.completion.matcher = match value.completion.matcher { CompletionMatcher::Fuzzy => base_db::MatchingAlgo::Skim,