From 02d941fa9c9895bb08a84ac9afe3559abd1ba8ad Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 26 May 2022 03:01:06 +0000 Subject: CTAN sync 202205260301 --- support/texlab/src/citation.rs | 196 +++++++---------------------------------- 1 file changed, 32 insertions(+), 164 deletions(-) (limited to 'support/texlab/src/citation.rs') diff --git a/support/texlab/src/citation.rs b/support/texlab/src/citation.rs index 5be86561c9..e1d6f49268 100644 --- a/support/texlab/src/citation.rs +++ b/support/texlab/src/citation.rs @@ -1,168 +1,36 @@ -mod bibutils; -mod ris; - -use std::sync::Arc; - -use citeproc::{prelude::SupportedFormat, ClusterPosition, InitOptions, Processor}; -use citeproc_db::PredefinedLocales; -use citeproc_io::{Cite, Reference}; -use lsp_types::{MarkupContent, MarkupKind}; -use once_cell::sync::Lazy; -use regex::Regex; - -use crate::syntax::{bibtex, CstNode}; - -use self::{ - bibutils::*, - ris::{RisLibrary, RisReference}, -}; - -static APA_STYLE: &str = include_str!("citation/apa.csl"); - -static DOI_URL_PATTERN: &str = r#"https://doi.org/\[.*\]\(.*\)"#; - -static DOI_URL_REGEX: Lazy = Lazy::new(|| Regex::new(DOI_URL_PATTERN).unwrap()); - -pub fn render_citation(root: &bibtex::SyntaxNode, key: &str) -> Option { - let ris_reference = convert_to_ris(root, 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, - }; - Some(content) -} - -fn convert_to_ris(root: &bibtex::SyntaxNode, key: &str) -> Option { - let mut bib_code = String::new(); - for string in root - .children() - .filter_map(bibtex::String::cast) - .filter(|string| string.name().is_some()) - { - bib_code.push_str(&string.syntax().to_string()); +mod driver; +mod entry; +mod field; +mod output; + +use unicode_normalization::UnicodeNormalization; + +use crate::syntax::bibtex; + +use self::{driver::Driver, output::Inline}; + +pub fn render(entry: &bibtex::Entry) -> Option { + let mut output = String::new(); + let mut driver = Driver::default(); + driver.process(entry); + driver.finish().for_each(|(inline, punct)| { + let text = match inline { + Inline::Regular(text) => text, + Inline::Italic(text) => format!("*{text}*"), + Inline::Quoted(text) => format!("\"{text}\""), + Inline::Link { url, alt } => format!("[{alt}]({url})"), + }; + output.push_str(&text); + output.push_str(punct.as_str()); + }); + + if output.is_empty() { + None + } else { + output.push('.'); + Some(output.nfc().collect()) } - - let entry = root - .children() - .filter_map(bibtex::Entry::cast) - .find(|entry| entry.key().map(|key| key.to_string()).as_deref() == Some(key)) - .filter(|entry| entry.fields().next().is_some())?; - - bib_code.push_str(&entry.syntax().to_string()); - - bib_code = bib_code.replace("\\hypen", "-"); - - let ris_code = bibutils::convert(&bib_code, InputFormat::Biblatex, OutputFormat::Ris)?; - let ris_lib = RisLibrary::parse(ris_code.lines()); - ris_lib - .references - .into_iter() - .find(|reference| reference.id.as_ref().map(AsRef::as_ref) == Some(key)) -} - -fn get_doi_url_markdown(ris_reference: &RisReference) -> Option { - ris_reference - .doi - .as_ref() - .map(|doi| format!("[doi:{}](https://doi.org/{})", doi, doi)) -} - -fn generate_bibliography(reference: Reference) -> Option { - let mut processor = Processor::new(InitOptions { - style: APA_STYLE, - format: SupportedFormat::Html, - fetcher: Some(Arc::new(PredefinedLocales::bundled_en_us())), - ..InitOptions::default() - }) - .ok()?; - let cite = Cite::basic(&reference.id); - let cluster_id = processor.cluster_id("texlab"); - processor.insert_reference(reference); - processor.insert_cites(cluster_id, &[cite]); - processor - .set_cluster_order(&[ClusterPosition { - id: Some(cluster_id), - note: Some(1), - }]) - .unwrap(); - Some(processor.get_bibliography().pop()?.value.to_string()) } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_simple() { - let document = bibtex::parse( - r#" - @article{foo, - author = {Foo Bar}, - title = {Baz Qux}, - year = {2020} - } - "#, - ); - - let actual_md = render_citation(&document.root, "foo").unwrap(); - - let expected_md = MarkupContent { - kind: MarkupKind::Markdown, - value: "Bar, Foo. (2020). *Baz Qux*.".into(), - }; - - assert_eq!(actual_md, expected_md); - } - - #[test] - fn test_string() { - let document = bibtex::parse( - r#" - @string{author = "Foo Bar"} - @article{foo, - author = author, - title = {Baz Qux}, - year = {2020} - } - "#, - ); - let actual_md = render_citation(&document.root, "foo").unwrap(); - - let expected_md = MarkupContent { - kind: MarkupKind::Markdown, - value: "Bar, Foo. (2020). *Baz Qux*.".into(), - }; - - assert_eq!(actual_md, expected_md); - } - - #[test] - fn test_unknown_key() { - let document = bibtex::parse(""); - - let actual_md = render_citation(&document.root, "foo"); - - assert_eq!(actual_md, None); - } -} +mod tests; -- cgit v1.2.3