Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setOutputFormat API #140

Merged
merged 7 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions crates/citeproc/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl HasFetcher for Processor {

impl ImplementationDetails for Processor {
fn get_formatter(&self) -> Markup {
self.formatter.clone()
self.formatter()
}
fn lookup_cluster_id(&self, symbol: ClusterId) -> Option<SmartString> {
let reader = self.interner.read().unwrap();
Expand Down Expand Up @@ -196,8 +196,6 @@ impl Processor {
let fetcher =
fetcher.unwrap_or_else(|| Arc::new(citeproc_db::PredefinedLocales::bundled_en_us()));
let mut db = Processor::safe_default(fetcher);
db.format_options = format_options;
db.formatter = format.make_markup(db.format_options);
let style = Style::parse_with_opts(
&style,
csl::ParseOptions {
Expand All @@ -207,11 +205,25 @@ impl Processor {
},
)?;
db.set_style_with_durability(Arc::new(style), Durability::HIGH);
db.set_output_format(format, format_options);
db.set_default_lang_override_with_durability(locale_override, Durability::HIGH);
db.set_bibliography_no_sort_with_durability(bibliography_no_sort, Durability::HIGH);
Ok(db)
}

/// Sets the output format. Will require nearly everything to be recomputed, so call sparingly.
pub fn set_output_format(&mut self, format: SupportedFormat, options: FormatOptions) {
self.format_options = options;
let formatter = format.make_markup(options);
if self.formatter == formatter {
// Avoid recomputing everything if possible
return;
}
self.formatter = formatter.clone();
self.set_formatter_with_durability(formatter, Durability::HIGH);
}

/// Sets the CSL style to be used. Will require nearly everything to be recomputed, so call sparingly.
pub fn set_style_text(&mut self, style_text: &str) -> Result<(), StyleError> {
let style = Style::parse(style_text)?;
self.set_style_with_durability(Arc::new(style), Durability::HIGH);
Expand Down Expand Up @@ -589,7 +601,7 @@ impl Processor {
csl::style::SecondFieldAlign::Flush => SecondFieldAlign::Flush,
csl::style::SecondFieldAlign::Margin => SecondFieldAlign::Margin,
}),
format_meta: self.formatter.meta(),
format_meta: self.get_formatter().meta(),
}
})
}
Expand Down Expand Up @@ -813,7 +825,7 @@ impl Processor {

let formatter = format
.map(|fmt| fmt.make_markup(self.format_options))
.unwrap_or_else(|| self.formatter.clone());
.unwrap_or_else(|| self.get_formatter());
let markup = citeproc_proc::db::built_cluster_preview(self, id, &formatter);
let cluster_cites_sorted = self.cluster_cites_sorted(id);
let nn = self.cluster_note_number(id);
Expand All @@ -835,7 +847,7 @@ impl Processor {
self.set_reference_input(preview_ref_id.clone(), arc.clone());
let formatter = format
.map(|fmt| fmt.make_markup(self.format_options))
.unwrap_or_else(|| self.formatter.clone());
.unwrap_or_else(|| self.get_formatter().clone());
citeproc_proc::bib_item_preview(self, preview_ref_id.clone(), arc.as_ref(), &formatter)
}

Expand Down
2 changes: 2 additions & 0 deletions crates/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod cluster;
mod xml;

pub use cite::*;
use citeproc_io::output::markup::Markup;
pub use cluster::*;
pub use xml::*;

Expand All @@ -14,6 +15,7 @@ use salsa::Durability;
pub fn safe_default(db: &mut (impl cite::CiteDatabase + xml::LocaleDatabase + xml::StyleDatabase)) {
use std::sync::Arc;
db.set_style_with_durability(Default::default(), Durability::HIGH);
db.set_formatter_with_durability(Markup::html(), Durability::HIGH);
db.set_all_keys_with_durability(Default::default(), Durability::MEDIUM);
db.set_all_uncited(Default::default());
db.set_all_cluster_ids(Arc::new(Default::default()));
Expand Down
4 changes: 4 additions & 0 deletions crates/db/src/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::io;
use std::sync::Arc;

use citeproc_io::output::markup::Markup;
use csl::{
locale::{Lang, Locale, LocaleSource, EN_US},
style::{Name, Style, TextElement, TextSource},
Expand All @@ -24,6 +25,9 @@ pub trait StyleDatabase {
#[salsa::input]
fn style(&self) -> Arc<Style>;

#[salsa::input]
fn formatter(&self) -> Markup;

/// Grabs the Name options from `<style>` + `<citation>` elements
/// First one is the inherited names-delimiter
fn name_info_citation(&self) -> (Option<SmartString>, Arc<Name>);
Expand Down
20 changes: 20 additions & 0 deletions crates/wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -638,3 +638,23 @@ if (parentMeta.independentMeta.hasBibliography) {
// ...
driver.free();
```

### `setOutputFormat` and `setStyle`

If you wish to change the output format of the entire driver, you can use
`setOutputFormat(format, formatOptions)`. The format is a string, one of `"html" |
"rtf" | "plain"` just like the `Driver.new` method. The options is an optional
argument with the same value as `formatOptions` in `Driver.new`.

`setStyle(xmlString)` will change the CSL style used by the driver.

Both of these methods will require throwing out almost all cached computation,
so use sparingly.

If you need to render a preview in a different format, there is an argument on
`previewCitationCluster` for doing just that. It does not throw out all the
computation. `citeproc-rs`' disambiguation procedures do take formatting into
account, so `<i>Title</i>` can be distinct from `<b>Title</b>` in HTML and RTF,
but not if the whole driver's output format is `"plain"`, since they both look
identical in plain text. `previewCitationCluster` will simply translate the
formatting into another format, without re-computing all the disambiguation.
81 changes: 81 additions & 0 deletions crates/wasm/js-tests/node/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { withDriver, oneOneOne, mkNoteStyle, mkInTextStyle, checkUpdatesLen } from './utils';
import { UpdateSummary, Driver } from '@citeproc-rs/wasm';

let italicStyle = mkNoteStyle(
`
<text variable="title" font-style="italic" />
<text variable="URL" prefix=" " />
`,
);
let boldStyle = mkNoteStyle(
`
<text variable="title" font-weight="bold" />
<text variable="URL" prefix=" " />
`,
);

describe("Driver", () => {

test('boots', () => {
Expand All @@ -20,6 +33,28 @@ describe("Driver", () => {
expect(res).toBe("TEST_TITLE");
});
});


test("can setOutputFormat", () => {
withDriver({ style: italicStyle, format: "html" }, driver => {
const one = "one";
oneOneOne(driver, { title: "Italicised", URL: "https://google.com" }, one);
expect(driver.builtCluster(one).unwrap())
.toBe("<i>Italicised</i> <a href=\"https://google.com/\">https://google.com</a>");
driver.setOutputFormat("html", {}).unwrap();
expect(driver.builtCluster(one).unwrap())
.toBe("<i>Italicised</i> <a href=\"https://google.com/\">https://google.com</a>");
driver.setOutputFormat("html", { linkAnchors: false }).unwrap();
expect(driver.builtCluster(one).unwrap())
.toBe("<i>Italicised</i> https://google.com");
driver.setOutputFormat("rtf", { linkAnchors: false }).unwrap();
expect(driver.builtCluster(one).unwrap())
.toBe("{\\i Italicised} https://google.com");
driver.setOutputFormat("plain").unwrap();
expect(driver.builtCluster(one).unwrap())
.toBe("Italicised https://google.com");
})
});
});

describe("batchedUpdates", () => {
Expand Down Expand Up @@ -100,6 +135,51 @@ describe("batchedUpdates", () => {
})
});

test("produces updates when style or output format change", () => {
withDriver({ style: italicStyle, format: "html" }, driver => {
const one = "one";
let once: UpdateSummary, twice: UpdateSummary;
oneOneOne(driver, { title: "Italicised", URL: "https://a.com" }, one);

expect(driver.builtCluster(one).unwrap()).toBe('<i>Italicised</i> <a href="https://a.com/">https://a.com</a>');
driver.batchedUpdates().unwrap();

// no change
driver.setOutputFormat("html", {}).unwrap();
once = driver.batchedUpdates().unwrap(); twice = driver.batchedUpdates().unwrap();
expect(once).toEqual(twice);
checkUpdatesLen(once, 0, 0); checkUpdatesLen(twice, 0, 0);

// change part of FormatOptions
driver.setOutputFormat("html", { linkAnchors: false }).unwrap();
once = driver.batchedUpdates().unwrap(); twice = driver.batchedUpdates().unwrap();
expect(once).not.toEqual(twice);
expect(once.clusters).toContainEqual([one, '<i>Italicised</i> https://a.com']);
checkUpdatesLen(twice, 0, 0);

// change to rtf
driver.setOutputFormat("rtf", { linkAnchors: false }).unwrap();
once = driver.batchedUpdates().unwrap(); twice = driver.batchedUpdates().unwrap();
expect(once).not.toEqual(twice);
expect(once.clusters).toContainEqual([one, '{\\i Italicised} https://a.com']);
checkUpdatesLen(twice, 0, 0);

// change style to bold instead of italic
driver.setStyle(boldStyle);
once = driver.batchedUpdates().unwrap(); twice = driver.batchedUpdates().unwrap();
expect(once).not.toEqual(twice);
expect(once.clusters).toContainEqual([one, '{\\b Italicised} https://a.com']);
checkUpdatesLen(twice, 0, 0);

// back to html
driver.setOutputFormat("html", { linkAnchors: false }).unwrap();
once = driver.batchedUpdates().unwrap(); twice = driver.batchedUpdates().unwrap();
expect(once).not.toEqual(twice);
expect(once.clusters).toContainEqual([one, "<b>Italicised</b> https://a.com"]);
checkUpdatesLen(twice, 0, 0);
})
});

});

describe("previewCitationCluster", () => {
Expand Down Expand Up @@ -295,3 +375,4 @@ describe("initialiser", () => {
});
});
});

Loading