Skip to content

Commit

Permalink
Merge pull request #42 from krassowski/latex-and-links
Browse files Browse the repository at this point in the history
Support LaTeX output and linking to bibliography
  • Loading branch information
krassowski authored Nov 28, 2021
2 parents 89a08c4 + 25ac296 commit 354a51a
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 34 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ from reference providers (like Zotero), in which case the item from the provider

![animation of migrating from cite2c and from DOIs][migrate-gif]

### Exporting to LaTeX

Citation Manager supports LaTeX as one of the output formats. For now the citations are embedded as plain-text,
while the bibliography uses semantic `thebibliography` LaTeX environment.
To configure notebook to export citations to LaTeX:

1. Go to `Advanced Settings Editor` -> `Citation Manager` and change the default `outputFormat` to `latex`.
2. Insert bibliography in a cell, but instead of using `Markdown` cell, use `Raw` cell
3. Open `Property Inspector` sidebar and change `Raw NBConvert Format` to `LaTeX` (you need to have the `Raw` cell selected for the dropdown to appear)
4. From `File` menu select `Save and Export Notebook As…` and choose `LaTeX`
5. Compile the resulting `.tex` to desired output such as PDF with preferred tool (e.g. `pdflatex` on Linux)

![LaTeX setup overview][latex-setup]

[bookshelf]: https://raw.githubusercontent.com/krassowski/jupyterlab-citation-manager/main/style/icons/bookshelf.svg?sanitize=true
[book-open-variant]: https://raw.githubusercontent.com/krassowski/jupyterlab-citation-manager/main/style/icons/book-open-variant.svg?sanitize=true
[book-plus]: https://raw.githubusercontent.com/krassowski/jupyterlab-citation-manager/main/style/icons/book-plus.svg?sanitize=true
Expand All @@ -90,10 +104,11 @@ from reference providers (like Zotero), in which case the item from the provider
[add-bibliography]: https://raw.githubusercontent.com/krassowski/jupyterlab-citation-manager/main/docs/images/add-bibliography.gif
[change-style]: https://raw.githubusercontent.com/krassowski/jupyterlab-citation-manager/main/docs/images/change-style.gif
[migrate-gif]: https://raw.githubusercontent.com/krassowski/jupyterlab-citation-manager/main/docs/images/migrate-cite2c-and-doi.gif
[latex-setup]: https://raw.githubusercontent.com/krassowski/jupyterlab-citation-manager/main/docs/images/latex.png

## Requirements

* JupyterLab >= 3.1
* JupyterLab >= 3.2
* Modern browser (with ES 2019 support, e.g. Firefox 64+, or Chrome 73+)
* [Zotero account](https://www.zotero.org/user/register)

Expand Down
Binary file added docs/images/latex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@
"default": "apa.csl",
"description": "The default citation style, one of the styles in the Citation Style Language (CSL) repository."
},
"linkToBibliography": {
"title": "Link citations to bibliography",
"type": "boolean",
"default": true,
"description": "Should citations link to the bibliography? Only available for HTML output format."
},
"outputFormat": {
"title": "Format of citations and bibliography",
"title": "The default format of citations and bibliography",
"type": "string",
"default": "html",
"description": "One of: html, text, rtf, asciidoc, fo, latex",
"description": "One of: html, text, rtf, asciidoc, fo, latex. Only `html` and `latex` are supported.",
"enum": ["html", "text", "rtf", "asciidoc", "fo", "latex"]
}
},
Expand Down
96 changes: 81 additions & 15 deletions src/adapters/notebook.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
CitationInsertData,
CitationQuerySubset,
CiteProc,
CommandIDs,
ICitableItemRecords,
ICitableItemRecordsBySource,
ICitation,
ICitationFormattingOptions,
ICitationManager,
ICitationMap,
IDocumentAdapter
Expand All @@ -18,6 +20,8 @@ import { JupyterFrontEnd } from '@jupyterlab/application';
import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
import ICellMetadata = NotebookAdapter.ICellMetadata;
import type { Cell } from '@jupyterlab/cells';
import OutputMode = CiteProc.OutputMode;
import { itemIdToPrimitive } from '../index';

export namespace NotebookAdapter {
export interface INotebookMetadata extends ReadonlyPartialJSONObject {
Expand All @@ -29,6 +33,10 @@ export namespace NotebookAdapter {
* Mapping of citable items used in this document, grouped by the source.
*/
items: ICitableItemRecordsBySource;
/**
* The output format (default `html`).
*/
format?: OutputMode;
}

export interface ICellMetadata extends ReadonlyPartialJSONObject {
Expand All @@ -45,7 +53,10 @@ export const cellMetadataKey = 'citation-manager';
export class NotebookAdapter implements IDocumentAdapter<NotebookPanel> {
citations: ICitation[];

constructor(public document: NotebookPanel) {
constructor(
public document: NotebookPanel,
public options: ICitationFormattingOptions
) {
this.citations = [];
}

Expand Down Expand Up @@ -126,6 +137,14 @@ export class NotebookAdapter implements IDocumentAdapter<NotebookPanel> {
return metadata.style;
}

get outputFormat(): OutputMode {
const metadata = this.notebookMetadata;
if (!metadata) {
return this.options.defaultFormat;
}
return metadata.format || this.options.defaultFormat;
}

setCitationStyle(newStyle: string): void {
if (!this.document.model) {
console.warn('Cannot set style on', this.document, ' - no model');
Expand All @@ -141,11 +160,29 @@ export class NotebookAdapter implements IDocumentAdapter<NotebookPanel> {
}

formatBibliography(bibliography: string): string {
console.log('format', this.outputFormat);
if (this.outputFormat === 'latex') {
return bibliography;
}
return `<!-- BIBLIOGRAPHY START -->${bibliography}<!-- BIBLIOGRAPHY END -->`;
}

formatCitation(citation: CitationInsertData): string {
return `<cite id="${citation.citationId}">${citation.text}</cite>`;
// note: not using `wrapCitationEntry` as that was causing more problems
// (itemID undefined).
let text = citation.text;
if (this.outputFormat === 'html' && this.options.linkToBibliography) {
// link to the first mentioned element
const first = citation.items[0];
const firstID = itemIdToPrimitive(first);
text = `<a href="#${firstID}">${text}</a>`;
} else if (this.outputFormat === 'latex') {
// this does not work well with MathJax - we need to figure out something else!
// but it might still be useful (without $) for text editor adapter
// const citationIDs = citation.items.map(itemIdToPrimitive).join(',');
// text = `$$\\cite{${citationIDs}}$$`;
}
return `<cite id="${citation.citationId}">${text}</cite>`;
}

insertCitation(citation: CitationInsertData): void {
Expand All @@ -169,7 +206,7 @@ export class NotebookAdapter implements IDocumentAdapter<NotebookPanel> {
`<cite id=["']${citation.citationId}["'][^>]*?>([\\s\\S]*?)<\\/cite>`
);
let matches = 0;
this.markdownCells.forEach(cell => {
this.nonCodeCells.forEach(cell => {
const oldText = cell.model.value.text;
const matchIndex = oldText.search(pattern);
if (matchIndex !== -1) {
Expand All @@ -196,13 +233,38 @@ export class NotebookAdapter implements IDocumentAdapter<NotebookPanel> {
}

updateBibliography(bibliography: string): void {
const pattern =
const htmlPattern =
/(?<=<!-- BIBLIOGRAPHY START -->)([\s\S]*?)(?=<!-- BIBLIOGRAPHY END -->)/;
this.markdownCells.forEach(cell => {
const htmlFullyCapturingPattern =
/(<!-- BIBLIOGRAPHY START -->[\s\S]*?<!-- BIBLIOGRAPHY END -->)/;
const latexPattern =
/(\\begin{thebibliography}[\s\S]*?\\end{thebibliography})/;

this.nonCodeCells.forEach(cell => {
const oldText = cell.model.value.text;
if (oldText.match(/<!-- BIBLIOGRAPHY START -->/)) {
cell.model.value.text = oldText.replace(pattern, bibliography);
if (oldText.search(pattern) === -1) {
cell.model.value.text = oldText.replace(
this.outputFormat === 'latex'
? htmlFullyCapturingPattern
: htmlPattern,
this.outputFormat === 'latex' ? bibliography.trim() : bibliography
);
if (oldText.search(htmlPattern) === -1) {
console.warn(
'Failed to update bibliography',
bibliography,
'in',
oldText
);
}
} else if (oldText.match(/\\begin{thebibliography}/)) {
cell.model.value.text = oldText.replace(
latexPattern,
this.outputFormat !== 'latex'
? this.formatBibliography(bibliography)
: bibliography.trim()
);
if (oldText.search(latexPattern) === -1) {
console.warn(
'Failed to update bibliography',
bibliography,
Expand All @@ -217,15 +279,15 @@ export class NotebookAdapter implements IDocumentAdapter<NotebookPanel> {
private chooseCells(subset: CitationQuerySubset) {
switch (subset) {
case 'all':
return this.markdownCells;
return this.nonCodeCells;
case 'after-cursor':
// TODO check for off by one
return this.selectMarkdownCells(
return this.selectNonCodeCells(
this.document.content.activeCellIndex,
Infinity
);
case 'before-cursor':
return this.selectMarkdownCells(
return this.selectNonCodeCells(
0,
this.document.content.activeCellIndex
);
Expand Down Expand Up @@ -275,19 +337,23 @@ export class NotebookAdapter implements IDocumentAdapter<NotebookPanel> {
}
}

private get markdownCells() {
/**
* We want to insert citation/bibliography in Markdown and Raw cells
* (raw cells so that LaTeX can be exported as-is).
*/
private get nonCodeCells() {
return this.document.content.widgets.filter(
cell => cell.model.type === 'markdown'
cell => cell.model.type !== 'code'
);
}

private selectMarkdownCells(min: number, max: number) {
private selectNonCodeCells(min: number, max: number) {
return this.document.content.widgets
.slice(min, max)
.filter(cell => cell.model.type === 'markdown');
.filter(cell => cell.model.type !== 'code');
}

addCitationMetadata(cell: Cell, citationsInCell: ICitation[]) {
addCitationMetadata(cell: Cell, citationsInCell: ICitation[]): void {
let metadata: ICellMetadata = cell.model.metadata.get(
cellMetadataKey
) as ICellMetadata;
Expand Down
Loading

0 comments on commit 354a51a

Please sign in to comment.