@retorquere/bibtex-parser 6.3.0 → 6.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1 -1
- package/index.js +73 -39
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as bibtex from './grammar';
|
|
2
2
|
import { JabRefMetadata } from './jabref';
|
|
3
|
-
export declare function toSentenceCase(text: string): string;
|
|
3
|
+
export declare function toSentenceCase(text: string, ignoreHTML?: boolean): string;
|
|
4
4
|
declare type Node = bibtex.Bibliography | bibtex.Math | bibtex.Environment | bibtex.Block | bibtex.RegularCommand | bibtex.DiacriticCommand | bibtex.Entry | bibtex.Field | bibtex.NumberValue | bibtex.StringDeclaration | bibtex.StringReference | bibtex.SubscriptCommand | bibtex.SuperscriptCommand | bibtex.SymbolCommand | bibtex.TextValue | bibtex.PreambleExpression | bibtex.BracedComment | bibtex.LineComment | bibtex.Markup | bibtex.NonEntryText;
|
|
5
5
|
export interface Name {
|
|
6
6
|
/**
|
package/index.js
CHANGED
|
@@ -8,56 +8,101 @@ const chunker = tslib_1.__importStar(require("./chunker"));
|
|
|
8
8
|
const jabref_1 = require("./jabref");
|
|
9
9
|
const unicode2latex_1 = require("unicode2latex");
|
|
10
10
|
const crossref_json_1 = tslib_1.__importDefault(require("./crossref.json"));
|
|
11
|
+
const xregexp_1 = tslib_1.__importDefault(require("xregexp"));
|
|
12
|
+
function restore(text, orig, preserve) {
|
|
13
|
+
for (const { start, end } of preserve) {
|
|
14
|
+
text = text.substring(0, start) + orig.substring(start, end) + text.substring(end);
|
|
15
|
+
}
|
|
16
|
+
return text;
|
|
17
|
+
}
|
|
11
18
|
class SentenceCaser {
|
|
12
19
|
constructor() {
|
|
13
|
-
this.acronym =
|
|
14
|
-
this.quoted =
|
|
20
|
+
this.acronym = (0, xregexp_1.default)('^(\\p{Lu}[.])+(?=$|[\\P{L}])');
|
|
21
|
+
this.quoted = (0, xregexp_1.default)('^"[^"]+"(?=$|[\\P{L}])');
|
|
22
|
+
this.innerCaps = (0, xregexp_1.default)('\\p{Ll}\\p{Lu}');
|
|
23
|
+
this.allCaps = (0, xregexp_1.default)('^\\p{Lu}+$');
|
|
24
|
+
this.aint = (0, xregexp_1.default)("^\\p{L}n't(?=$|[\\P{L}])"); // isn't
|
|
25
|
+
this.word = (0, xregexp_1.default)('^\\p{L}+(-\\p{L}+)*'); // also match gallium-nitride as one word
|
|
26
|
+
this.and = (0, xregexp_1.default)('^\\p{Lu}&\\p{Lu}(?=$|[\\P{L}])'); // Q&A
|
|
15
27
|
}
|
|
16
|
-
convert(text) {
|
|
28
|
+
convert(text, ignoreHTML = false) {
|
|
17
29
|
this.input = text;
|
|
18
30
|
this.result = '';
|
|
19
31
|
this.sentenceStart = true;
|
|
32
|
+
const preserve = [];
|
|
33
|
+
if (ignoreHTML) {
|
|
34
|
+
let replace = true;
|
|
35
|
+
while (replace) {
|
|
36
|
+
replace = false;
|
|
37
|
+
// always keep the leading char, but skip markup
|
|
38
|
+
this.input = this.input.replace(/[^<>]<[^>]+>/, (match, i) => {
|
|
39
|
+
preserve.push({ start: i + 1, end: i + match.length });
|
|
40
|
+
replace = true;
|
|
41
|
+
return match[0].repeat(match.length);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
replace = true;
|
|
45
|
+
while (replace) {
|
|
46
|
+
replace = false;
|
|
47
|
+
// always keep the leading char, but skip markup
|
|
48
|
+
this.input = this.input.replace(/<[^>]+>[^<>]/, (match, i) => {
|
|
49
|
+
preserve.push({ start: i, end: i + match.length - 1 });
|
|
50
|
+
replace = true;
|
|
51
|
+
return match[match.length - 1].repeat(match.length);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.input = this.input.replace(/[;:]\s+A\s/g, match => match.toLowerCase());
|
|
56
|
+
this.input = this.input.replace(/[–—]\s*A\s/g, match => match.toLowerCase());
|
|
20
57
|
let m;
|
|
21
58
|
while (this.input) {
|
|
22
|
-
if (m =
|
|
23
|
-
this.add(m[0], true);
|
|
59
|
+
if (m = xregexp_1.default.exec(this.input, this.quoted)) { // "Hello There"
|
|
60
|
+
this.add(m[0], undefined, true);
|
|
61
|
+
}
|
|
62
|
+
else if (m = xregexp_1.default.exec(this.input, this.acronym)) { // U.S.
|
|
63
|
+
this.add(m[0], undefined, true);
|
|
24
64
|
}
|
|
25
|
-
else if (m =
|
|
26
|
-
this.add(m[0],
|
|
65
|
+
else if (m = xregexp_1.default.exec(this.input, this.aint)) { // isn't
|
|
66
|
+
this.add(m[0], undefined, false);
|
|
27
67
|
}
|
|
28
|
-
else if (m = this.input.
|
|
29
|
-
this.add(m[0]);
|
|
68
|
+
else if (m = xregexp_1.default.exec(this.input, this.word)) {
|
|
69
|
+
this.add(m[0], '-', false);
|
|
30
70
|
}
|
|
31
|
-
else if (m = this.input.
|
|
32
|
-
this.add(m[0]);
|
|
71
|
+
else if (m = xregexp_1.default.exec(this.input, this.and)) {
|
|
72
|
+
this.add(m[0], undefined, true);
|
|
33
73
|
}
|
|
34
74
|
else {
|
|
35
|
-
this.add(this.input[0]);
|
|
75
|
+
this.add(this.input[0], undefined, false);
|
|
36
76
|
}
|
|
37
77
|
}
|
|
38
|
-
return this.result;
|
|
78
|
+
return restore(this.result, text, preserve);
|
|
39
79
|
}
|
|
40
|
-
add(word, keep
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
80
|
+
add(word, splitter, keep) {
|
|
81
|
+
if (splitter) {
|
|
82
|
+
word = word.split(splitter).map((part, i) => {
|
|
83
|
+
if ((keep || this.sentenceStart) && i === 0)
|
|
84
|
+
return part;
|
|
85
|
+
if (xregexp_1.default.exec(part, this.innerCaps))
|
|
86
|
+
return part;
|
|
87
|
+
if (xregexp_1.default.exec(part, this.allCaps))
|
|
88
|
+
return part;
|
|
89
|
+
return part.toLowerCase();
|
|
90
|
+
}).join(splitter);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
if (!keep)
|
|
47
94
|
word = word.toLowerCase();
|
|
48
|
-
result.push(word);
|
|
49
95
|
}
|
|
50
|
-
word = result.join('-');
|
|
51
96
|
this.result += word;
|
|
52
97
|
this.input = this.input.substr(word.length);
|
|
53
98
|
if (!word.match(/^\s+$/)) {
|
|
54
|
-
this.sentenceStart = !!word.match(/^[.?!]$/);
|
|
99
|
+
this.sentenceStart = !!word.match(/^[.?!]$/) || (word.length === 2 && word[1] === '.'); // Vitamin A. Vitamin B.
|
|
55
100
|
}
|
|
56
101
|
}
|
|
57
102
|
}
|
|
58
103
|
const sentenceCaser = new SentenceCaser;
|
|
59
|
-
function toSentenceCase(text) {
|
|
60
|
-
return sentenceCaser.convert(text);
|
|
104
|
+
function toSentenceCase(text, ignoreHTML = false) {
|
|
105
|
+
return sentenceCaser.convert(text, ignoreHTML);
|
|
61
106
|
}
|
|
62
107
|
exports.toSentenceCase = toSentenceCase;
|
|
63
108
|
class ParserError extends Error {
|
|
@@ -245,7 +290,7 @@ class Parser {
|
|
|
245
290
|
options.sentenceCase = false;
|
|
246
291
|
options.caseProtection = false;
|
|
247
292
|
}
|
|
248
|
-
this.options = Object.assign({ caseProtection: 'as-needed', verbatimFields: [/^citeulike-linkout-[0-9]+$/, ...exports.fields.verbatim], verbatimCommands: ['url'], unnestFields: [...exports.fields.title, ...exports.fields.unnest, ...exports.fields.verbatim], unnestMode: 'unwrap', htmlFields: exports.fields.html, guessAlreadySentenceCased: true, applyCrossRef: options.applyCrossRef || typeof options.applyCrossRef === 'undefined', markup: {} }, options);
|
|
293
|
+
this.options = Object.assign({ caseProtection: 'as-needed', verbatimFields: [/^citeulike-linkout-[0-9]+$/, /^bdsk-url-[0-9]+$/, ...exports.fields.verbatim], verbatimCommands: ['url'], unnestFields: [...exports.fields.title, ...exports.fields.unnest, ...exports.fields.verbatim], unnestMode: 'unwrap', htmlFields: exports.fields.html, guessAlreadySentenceCased: true, applyCrossRef: options.applyCrossRef || typeof options.applyCrossRef === 'undefined', markup: {} }, options);
|
|
249
294
|
const markup_defaults = {
|
|
250
295
|
enquote: { open: '\u201c', close: '\u201d' },
|
|
251
296
|
sub: { open: '<sub>', close: '</sub>' },
|
|
@@ -405,7 +450,7 @@ class Parser {
|
|
|
405
450
|
if (start < last.start) throw new Error(JSON.stringify({...last, new: { start, end, reason }, text: this.field.text}))
|
|
406
451
|
}
|
|
407
452
|
*/
|
|
408
|
-
this.field.preserveRanges.push({ start, end, reason });
|
|
453
|
+
this.field.preserveRanges.push({ start, end, description: reason });
|
|
409
454
|
}
|
|
410
455
|
parseChunk(chunk) {
|
|
411
456
|
this.chunk = chunk.text;
|
|
@@ -1328,18 +1373,7 @@ class Parser {
|
|
|
1328
1373
|
convertToSentenceCase(text) {
|
|
1329
1374
|
if (!this.field.preserveRanges)
|
|
1330
1375
|
return text;
|
|
1331
|
-
|
|
1332
|
-
const lead = text.match(/^(<[^>]+>)*./);
|
|
1333
|
-
if (lead) {
|
|
1334
|
-
this.preserve(lead[0].length - 1, lead[0].length);
|
|
1335
|
-
}
|
|
1336
|
-
else {
|
|
1337
|
-
this.preserve(0, 1);
|
|
1338
|
-
}
|
|
1339
|
-
let sentenceCased = toSentenceCase(text);
|
|
1340
|
-
for (const { start, end } of this.field.preserveRanges) {
|
|
1341
|
-
sentenceCased = sentenceCased.substring(0, start) + text.substring(start, end) + sentenceCased.substring(end);
|
|
1342
|
-
}
|
|
1376
|
+
const sentenceCased = restore(sentenceCaser.convert(text, true), text, this.field.preserveRanges);
|
|
1343
1377
|
if (text !== sentenceCased)
|
|
1344
1378
|
this.entry.sentenceCased = true;
|
|
1345
1379
|
return sentenceCased;
|