summaryrefslogtreecommitdiff
path: root/support/texlab/src/citation.rs
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-05-26 03:01:06 +0000
committerNorbert Preining <norbert@preining.info>2022-05-26 03:01:06 +0000
commit02d941fa9c9895bb08a84ac9afe3559abd1ba8ad (patch)
tree481d1d368c6d295a779b590d23bcd5397bcba628 /support/texlab/src/citation.rs
parentf01a37f8311f33e32441d25bdadcda9dcdbd165d (diff)
CTAN sync 202205260301
Diffstat (limited to 'support/texlab/src/citation.rs')
-rw-r--r--support/texlab/src/citation.rs196
1 files changed, 32 insertions, 164 deletions
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<Regex> = Lazy::new(|| Regex::new(DOI_URL_PATTERN).unwrap());
-
-pub fn render_citation(root: &bibtex::SyntaxNode, key: &str) -> Option<MarkupContent> {
- 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<RisReference> {
- 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<String> {
+ 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<String> {
- ris_reference
- .doi
- .as_ref()
- .map(|doi| format!("[doi:{}](https://doi.org/{})", doi, doi))
-}
-
-fn generate_bibliography(reference: Reference) -> Option<String> {
- 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;