Skip to content
This repository was archived by the owner on Nov 13, 2022. It is now read-only.

Commit 77e2dfe

Browse files
committed
Fix #4, fix #5
1 parent 0bdea1e commit 77e2dfe

File tree

5 files changed

+103
-19
lines changed

5 files changed

+103
-19
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 1.2.1
2+
3+
- Fixed a logical error where activating strict mode would actually use the loose mode while parsing a single citation.
4+
- Fixed a problem where citation parts with no commas after the citekey would be reported as false and hence not parsed correctly.
5+
16
# 1.2.0
27

38
- Refactored the code base to be more maintainable and streamlined.

dist/citr.js

+23-6
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,44 @@ function parseSingle(citation, strict = false) {
2323
throw new Error(`Invalid Citation - Invalid citation passed: ${citation}.`);
2424
let returnCitations = [];
2525
let _citation = citation.substr(1, citation.length - 2).split(';');
26+
let invalidPrefixes = [];
2627
for (let c of _citation) {
2728
if (c === '')
2829
continue;
29-
if (!validator_1.validateCitationPart(c))
30-
throw new Error(`No key or multiple keys Found - Invalid citation passed: ${c}.`);
31-
let prefix = c.split('@')[0].trim();
30+
if (!validator_1.validateCitationPart(c)) {
31+
invalidPrefixes.push(c);
32+
continue;
33+
}
34+
let prefix = '';
35+
if (invalidPrefixes.length === 1) {
36+
prefix = invalidPrefixes + ';';
37+
}
38+
else if (invalidPrefixes.length > 1) {
39+
prefix = invalidPrefixes.join(';');
40+
}
41+
prefix += c.split('@')[0];
42+
prefix = prefix.trim();
43+
invalidPrefixes = [];
3244
let suppressAuthor = c.indexOf('@') > 0 && c[c.indexOf('@') - 1] === '-';
3345
if (suppressAuthor)
3446
prefix = prefix.substr(0, prefix.length - 1).trim();
3547
let commaIndex = c.split('@')[1].indexOf(',') + 1;
48+
if (commaIndex === 0)
49+
commaIndex = c.split('@')[1].indexOf(' ') + 1;
3650
if (commaIndex <= 0)
3751
commaIndex = undefined;
3852
let citationKeyPart = c.substr(c.indexOf('@'), commaIndex);
3953
let extractedKey = null;
4054
if (strict) {
41-
extractedKey = regex_1.looseCitekeyValidatorRE.exec(citationKeyPart);
55+
extractedKey = regex_1.strictCitekeyValidatorRE.exec(citationKeyPart);
4256
}
4357
else {
44-
extractedKey = regex_1.strictCitekeyValidatorRE.exec(citationKeyPart);
58+
extractedKey = regex_1.looseCitekeyValidatorRE.exec(citationKeyPart);
4559
}
4660
if (extractedKey === null)
4761
throw new Error(`Invalid Key - Invalid citation passed: ${c}`);
4862
let citeKey = extractedKey[1];
49-
let afterKey = c.split('@')[1].substr(extractedKey[0].length + 1).trim();
63+
let afterKey = c.split('@')[1].substr(extractedKey[1].length).trim();
5064
let { suffix, locator, label } = retrieve_locator_1.extractLocator(afterKey);
5165
returnCitations.push({
5266
prefix: prefix,
@@ -57,6 +71,9 @@ function parseSingle(citation, strict = false) {
5771
'suppress-author': suppressAuthor
5872
});
5973
}
74+
if (returnCitations.length === 0 && _citation.length > 0) {
75+
throw new Error(`Invalid citation passed: ${citation}`);
76+
}
6077
return returnCitations;
6178
}
6279
exports.parseSingle = parseSingle;

lib/citr.ts

+44-11
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,42 @@ export function parseSingle(citation: string, strict: boolean = false): Citation
8181
// they are simply square brackets. Additionally, split the citation along delimiters.
8282
let _citation = citation.substr(1, citation.length - 2).split(';')
8383

84+
let invalidPrefixes: string[] = []
85+
8486
// Now iterate over all citations the user passed in to return them as an array.
8587
for (let c of _citation) {
8688
// It could be that the user just ended his/her citation with a ;
8789
if (c === '') continue
8890

8991
// Make sure there is exactly one @ available.
90-
if (!validateCitationPart(c)) throw new Error(`No key or multiple keys Found - Invalid citation passed: ${c}.`)
92+
if (!validateCitationPart(c)) {
93+
// If the validator fails, this means that there's no @ or multiple @,
94+
// and hence no valid citation key in there. This means that the user has
95+
// written something along the lines of [as we can see here; further
96+
// @citekey1234; @citekey4321] or [see the corresponding mails
97+
// hello@example.com and [email protected]; further @citekey1234].
98+
// --> treat it as a part of the prefix for the next citation part.
99+
invalidPrefixes.push(c)
100+
continue
101+
}
91102

92-
// The Prefix is defined as everything before the citation key, so the first index of
93-
// the split array will contain the Prefix (If @ is the first character, the string will
94-
// be empty).
95-
let prefix = c.split('@')[0].trim()
103+
// The Prefix is defined as everything before the citation key, so the
104+
// first index of the split array will contain the Prefix (If @ is the
105+
// first character, the string will be empty). Make sure to add possible
106+
// invalid prefixes from before
107+
let prefix = ''
108+
if (invalidPrefixes.length === 1) {
109+
prefix = invalidPrefixes + ';'
110+
}
111+
else if (invalidPrefixes.length > 1) {
112+
prefix = invalidPrefixes.join(';')
113+
}
114+
115+
prefix += c.split('@')[0] // Add the actual prefix
116+
prefix = prefix.trim() // Trim whitespaces
117+
118+
// Reset the additional prefixes here.
119+
invalidPrefixes = []
96120

97121
// Next, the user can decide to omit the author from the citation by prepending the
98122
// @-character with a minus (-). We cannot look for the end of the prefix because
@@ -106,18 +130,21 @@ export function parseSingle(citation: string, strict: boolean = false): Citation
106130

107131
// Now we need to extract the citation key. We'll be reusing the citation
108132
// validator regular expression. But as the secondHalf also contains the
109-
// suffix, locator, etc., we have to first cut it down. Therefore, we'll
110-
// assume a comma to separate the citekey from the rest of the suffix (or
111-
// extract everything, if there is no comma in there.)
133+
// suffix, locator, etc., we have to first cut it down. The citation key
134+
// can either be terminated with a comma or with a space.
112135
let commaIndex: number | undefined = c.split('@')[1].indexOf(',') + 1
136+
// If the commaIndex is 0, this means there was no comma - check for space
137+
if (commaIndex === 0) commaIndex = c.split('@')[1].indexOf(' ') + 1
113138
// Pass undefined to extract everything
114139
if (commaIndex <= 0) commaIndex = undefined
140+
141+
// Now extract the key
115142
let citationKeyPart = c.substr(c.indexOf('@'), commaIndex)
116143
let extractedKey: RegExpExecArray | null = null
117144
if (strict) {
118-
extractedKey = looseCitekeyValidatorRE.exec(citationKeyPart)
119-
} else {
120145
extractedKey = strictCitekeyValidatorRE.exec(citationKeyPart)
146+
} else {
147+
extractedKey = looseCitekeyValidatorRE.exec(citationKeyPart)
121148
}
122149

123150
// If the match has not been found, abort
@@ -128,7 +155,7 @@ export function parseSingle(citation: string, strict: boolean = false): Citation
128155

129156
// The final two things that could possibly still be in the citation are a
130157
// locator and a suffix. Let us first extract everything after the key.
131-
let afterKey = c.split('@')[1].substr(extractedKey[0].length + 1).trim()
158+
let afterKey = c.split('@')[1].substr(extractedKey[1].length).trim()
132159

133160
// The logic to get the locator is extremely difficult, as the locator
134161
// mainly is written in natural language. We'll offload the work to
@@ -146,6 +173,12 @@ export function parseSingle(citation: string, strict: boolean = false): Citation
146173
})
147174
}
148175

176+
// Indicate that no citation has been found, which is a good indicator
177+
// that there is no valid citation (even excluding the invalid prefixes)
178+
if (returnCitations.length === 0 && _citation.length > 0) {
179+
throw new Error(`Invalid citation passed: ${citation}`)
180+
}
181+
149182
// After everything has run, return all citations found.
150183
return returnCitations
151184
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zettlr/citr",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "A small library to parse Markdown Citeproc notation as CSL JSON",
55
"author": "Hendrik Erz <[email protected]>",
66
"license": "GPL-3.0",

test/parse-single.js

+30-1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,35 @@ let singleCitations = [
294294
}
295295
]
296296
},
297+
// These tests test whether Citr works even if there is no comma immediately
298+
// after the citation key
299+
{
300+
'input': '[vgl. @Koselleck2006 für einen Überblick]',
301+
'expected': [
302+
{
303+
prefix: 'vgl.',
304+
suffix: 'für einen Überblick',
305+
id: 'Koselleck2006',
306+
locator: '',
307+
label: 'page',
308+
'suppress-author': false
309+
}
310+
]
311+
},
312+
// Tests to check if invalid citation parts end up being prefixes of the following ones
313+
{
314+
'input': '[ein invalider prefix; außerdem @Volk2017, Abschn. 2-3]',
315+
'expected': [
316+
{
317+
prefix: 'ein invalider prefix; außerdem',
318+
suffix: '',
319+
id: 'Volk2017',
320+
locator: '2-3',
321+
label: 'section',
322+
'suppress-author': false
323+
}
324+
]
325+
},
297326
// expected = undefined indicates that the function should throw
298327
{
299328
'input': 'Should not @work out',
@@ -310,7 +339,7 @@ let singleCitations = [
310339
{
311340
'input': '[Malformed ID inside @.this key]',
312341
'expected': undefined
313-
},
342+
}
314343
]
315344

316345
describe('Citr#parseSingle()', function () {

0 commit comments

Comments
 (0)