summaryrefslogtreecommitdiff
path: root/support/texlab/src/citeproc/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/citeproc/mod.rs')
-rw-r--r--support/texlab/src/citeproc/mod.rs192
1 files changed, 168 insertions, 24 deletions
diff --git a/support/texlab/src/citeproc/mod.rs b/support/texlab/src/citeproc/mod.rs
index 9827c190f3..6def2ed8a4 100644
--- a/support/texlab/src/citeproc/mod.rs
+++ b/support/texlab/src/citeproc/mod.rs
@@ -1,52 +1,88 @@
+mod bibutils;
mod name;
mod ris;
-use self::ris::*;
-use crate::formatting::bibtex::{format_entry, format_string, BibtexFormattingParams};
-use crate::syntax::*;
-use bibutils::{InputFormat, OutputFormat};
+use self::{
+ bibutils::{InputFormat, OutputFormat},
+ ris::{RisLibrary, RisReference},
+};
+use crate::{
+ protocol::{BibtexFormattingOptions, MarkupContent, MarkupKind},
+ syntax::bibtex,
+};
use citeproc::prelude::*;
use citeproc_db::PredefinedLocales;
-use lsp_types::{MarkupContent, MarkupKind};
+use once_cell::sync::Lazy;
+use regex::Regex;
use std::sync::Arc;
static APA_STYLE: &str = include_str!("apa.csl");
-pub fn render_citation(tree: &BibtexSyntaxTree, key: &str) -> Option<MarkupContent> {
- let reference: Reference = convert_to_ris(tree, key)?.into();
+static DOI_URL_PATTERN: &str = r#"https://doi.org/\[.*\]\(.*\)"#;
- let html = generate_bibliography(reference)?;
- let markdown = html2md::parse_html(&html).trim().to_owned();
+static DOI_URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(DOI_URL_PATTERN).unwrap());
+
+pub fn render_citation(tree: &bibtex::Tree, key: &str) -> Option<MarkupContent> {
+ let ris_reference = convert_to_ris(tree, key)?;
+ let doi_url = get_doi_url_markdown(&ris_reference);
+ let csl_reference: Reference = ris_reference.into();
+ let html = generate_bibliography(csl_reference)?;
+
+ let mut markdown = html2md::parse_html(&html).trim().to_owned();
if markdown == "" {
return None;
}
+ if let Some(doi_url) = doi_url {
+ markdown = DOI_URL_REGEX
+ .replace(&markdown, doi_url.as_str())
+ .into_owned();
+ }
+
+ markdown = markdown
+ .replace("..", ".")
+ .replace("\\\'", "'")
+ .replace("\\-", "-")
+ .replace("\\\\textsubscript", "")
+ .replace("\\\\textsuperscript", "");
let content = MarkupContent {
kind: MarkupKind::Markdown,
- value: markdown.replace("..", "."),
+ value: markdown,
};
Some(content)
}
-fn convert_to_ris(tree: &BibtexSyntaxTree, key: &str) -> Option<RisReference> {
- let bib_params = BibtexFormattingParams::default();
- let mut bib_code = String::new();
+fn convert_to_ris(tree: &bibtex::Tree, key: &str) -> Option<RisReference> {
+ let options = BibtexFormattingOptions {
+ line_length: None,
+ formatter: None,
+ };
+ let params = bibtex::FormattingParams {
+ insert_spaces: true,
+ tab_size: 4,
+ options: &options,
+ };
- for string in tree.strings() {
- bib_code.push_str(&format_string(string, &bib_params));
- bib_code.push('\n');
- }
+ let mut bib_code = String::new();
+ tree.children(tree.root)
+ .filter(|node| tree.as_string(*node).is_some())
+ .map(|node| bibtex::format(tree, node, params))
+ .for_each(|string| {
+ bib_code.push_str(&string);
+ bib_code.push('\n');
+ });
- let entry = tree.find_entry(key)?;
- if let Some(crossref) = tree.resolve_crossref(entry) {
- bib_code.push_str(&format_entry(crossref, &bib_params));
+ let entry = tree.entry_by_key(key)?;
+ if let Some(crossref) = tree.crossref(entry) {
+ bib_code.push_str(&bibtex::format(tree, crossref, params));
bib_code.push('\n');
}
- bib_code.push_str(&format_entry(entry, &bib_params));
+ bib_code.push_str(&bibtex::format(tree, entry, params));
bib_code.push('\n');
+ bib_code = bib_code.replace("\\hypen", "-");
- let ris_code = bibutils::convert(bib_code, InputFormat::Biblatex, OutputFormat::Ris)?;
+ let ris_code = bibutils::convert(&bib_code, InputFormat::Biblatex, OutputFormat::Ris)?;
let ris_lib = RisLibrary::parse(ris_code.lines());
ris_lib
.references
@@ -54,16 +90,124 @@ fn convert_to_ris(tree: &BibtexSyntaxTree, key: &str) -> Option<RisReference> {
.find(|reference| reference.id.as_ref().map(AsRef::as_ref) == Some(key))
}
+fn get_doi_url_markdown(ris_reference: &RisReference) -> Option<String> {
+ ris_reference
+ .doi
+ .as_ref()
+ .map(|doi| format!("[doi:{}](https://doi.org/{})", doi, doi))
+}
+
fn generate_bibliography(reference: Reference) -> Option<String> {
let locales = Arc::new(PredefinedLocales::bundled_en_us());
let mut processor = Processor::new(APA_STYLE, locales, false, SupportedFormat::Html).unwrap();
let cite = Cite::basic(&reference.id);
- let cluster = Cluster2::Note {
+ let cluster = Cluster {
id: 1,
- note: IntraNote::Single(1),
cites: vec![cite],
};
processor.insert_reference(reference);
processor.init_clusters(vec![cluster]);
+ processor
+ .set_cluster_order(&[ClusterPosition {
+ id: 1,
+ note: Some(1),
+ }])
+ .unwrap();
processor.get_bibliography().pop()
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use indoc::indoc;
+
+ #[test]
+ fn simple() {
+ let tree = bibtex::open(indoc!(
+ r#"
+ @article{foo,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2020}
+ }
+ "#
+ ));
+
+ let actual_md = render_citation(&tree, "foo").unwrap();
+
+ let expected_md = MarkupContent {
+ kind: MarkupKind::Markdown,
+ value: "Bar, F. (2020). *Baz Qux*.".into(),
+ };
+
+ assert_eq!(actual_md, expected_md);
+ }
+
+ #[test]
+ fn crossref() {
+ let tree = bibtex::open(indoc!(
+ r#"
+ https://tex.stackexchange.com/questions/401138/what-is-the-bibtex-crossref-field-used-for
+
+ @inproceedings{duck2015,
+ author = {Duck, D.},
+ title = {Duck tales},
+ crossref = {ICRC2015},
+ }
+
+ @inproceedings{mouse2015,
+ author = {Mouse, M.},
+ title = {Mouse stories},
+ crossref = {ICRC2015},
+ }
+
+ @proceedings{ICRC2015,
+ title = "{Proceedings of the 34\textsuperscript{th} International Cosmic Ray Conference}",
+ year = "2015",
+ month = aug,
+ }
+ "#
+ ));
+
+ let actual_md = render_citation(&tree, "mouse2015").unwrap();
+
+ let expected_md = MarkupContent {
+ kind: MarkupKind::Markdown,
+ value: "Mouse, M. (2015). Mouse stories. In *Proceedings of the 34th International Cosmic Ray Conference*.".into(),
+ };
+
+ assert_eq!(actual_md, expected_md);
+ }
+
+ #[test]
+ fn string() {
+ let tree = bibtex::open(indoc!(
+ r#"
+ @string{author = "Foo Bar"}
+ @article{foo,
+ author = author,
+ title = {Baz Qux},
+ year = {2020}
+ }
+ "#
+ ));
+
+ let actual_md = render_citation(&tree, "foo").unwrap();
+
+ let expected_md = MarkupContent {
+ kind: MarkupKind::Markdown,
+ value: "Bar, F. (2020). *Baz Qux*.".into(),
+ };
+
+ assert_eq!(actual_md, expected_md);
+ }
+
+ #[test]
+ fn unknown_key() {
+ let tree = bibtex::open("");
+
+ let actual_md = render_citation(&tree, "foo");
+
+ assert_eq!(actual_md, None);
+ }
+}