@retorquere/bibtex-parser 6.4.19 → 7.0.2
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 +4 -0
- package/index.js +12 -2
- package/package.json +2 -2
- package/sentence-case.d.ts +1 -7
- package/sentence-case.js +33 -34
package/index.d.ts
CHANGED
|
@@ -205,6 +205,10 @@ export interface ParserOptions {
|
|
|
205
205
|
* If you pass an empty array, or `false`, no sentence casing will be applied (even when there's no language field).
|
|
206
206
|
*/
|
|
207
207
|
sentenceCase?: string[] | boolean;
|
|
208
|
+
/**
|
|
209
|
+
* If you have sentence-casing on, you can independently choose whether quoted titles within a title are preserved as-is (true) or also sentence-cased(false)
|
|
210
|
+
*/
|
|
211
|
+
sentenceCasePreserveQuoted?: boolean;
|
|
208
212
|
/**
|
|
209
213
|
* Some bibtex has titles in sentence case, or all-uppercase. If this is on, and there is a field that would normally have sentence-casing applied in which more words are all-`X`case
|
|
210
214
|
* (where `X` is either lower or upper) than mixed-case, it is assumed that you want them this way, and no sentence-casing will be applied to that field
|
package/index.js
CHANGED
|
@@ -1228,6 +1228,12 @@ class Parser {
|
|
|
1228
1228
|
}
|
|
1229
1229
|
return parsed;
|
|
1230
1230
|
}
|
|
1231
|
+
restore(text, orig, preserve) {
|
|
1232
|
+
for (const { start, end } of preserve) {
|
|
1233
|
+
text = text.substring(0, start) + orig.substring(start, end) + text.substring(end);
|
|
1234
|
+
}
|
|
1235
|
+
return text;
|
|
1236
|
+
}
|
|
1231
1237
|
convert_entry(node) {
|
|
1232
1238
|
this.entry = {
|
|
1233
1239
|
key: node.id,
|
|
@@ -1324,10 +1330,14 @@ class Parser {
|
|
|
1324
1330
|
}
|
|
1325
1331
|
else {
|
|
1326
1332
|
if (this.field.preserveRanges) {
|
|
1327
|
-
|
|
1333
|
+
const allCaps = this.field.text.match(/\s/) && this.field.words.upper && !this.field.words.lower && !this.field.words.other;
|
|
1334
|
+
const seemsSentenceCased = Math.max(this.field.words.upper, this.field.words.lower) > (this.field.words.other + Math.min(this.field.words.upper, this.field.words.lower));
|
|
1335
|
+
if (!allCaps && seemsSentenceCased && this.options.guessAlreadySentenceCased) {
|
|
1328
1336
|
this.preserve(null, null); // , 'mostly sentence cased already')
|
|
1329
1337
|
}
|
|
1330
1338
|
else {
|
|
1339
|
+
if (allCaps)
|
|
1340
|
+
this.field.preserveRanges = [];
|
|
1331
1341
|
const txt = this.field.text.replace(preserveCase.markup, markup => marker.markup.repeat(markup.length));
|
|
1332
1342
|
let match;
|
|
1333
1343
|
preserveCase.sentenceStart.lastIndex = 0;
|
|
@@ -1357,7 +1367,7 @@ class Parser {
|
|
|
1357
1367
|
convertToSentenceCase(text) {
|
|
1358
1368
|
if (!this.field.preserveRanges)
|
|
1359
1369
|
return text;
|
|
1360
|
-
const sentenceCased =
|
|
1370
|
+
const sentenceCased = this.restore((0, sentence_case_2.toSentenceCase)(text, this.options.sentenceCasePreserveQuoted || false), text, this.field.preserveRanges);
|
|
1361
1371
|
if (text !== sentenceCased)
|
|
1362
1372
|
this.entry.sentenceCased = true;
|
|
1363
1373
|
return sentenceCased;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retorquere/bibtex-parser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.2",
|
|
4
4
|
"description": "BibTeX parser",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "./index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"prestart": "npm run lint && peggy grammar.peggy",
|
|
10
10
|
"start": "tsc -d",
|
|
11
11
|
"tsc": "tsc -d",
|
|
12
|
-
"poststart": "
|
|
12
|
+
"poststart": "npm run docs",
|
|
13
13
|
"pretest": "npm start",
|
|
14
14
|
"test": "tap test/tap --no-timeout --no-coverage",
|
|
15
15
|
"snap": "npm test --snapshot",
|
package/sentence-case.d.ts
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
start: number;
|
|
3
|
-
end: number;
|
|
4
|
-
description?: string;
|
|
5
|
-
};
|
|
6
|
-
export declare function restore(text: string, orig: string, preserve: TextRange[]): string;
|
|
7
|
-
export declare function toSentenceCase(text: string): string;
|
|
1
|
+
export declare function toSentenceCase(sentence: string, preserveQuoted?: boolean): string;
|
|
8
2
|
//# sourceMappingURL=sentence-case.d.ts.map
|
package/sentence-case.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toSentenceCase =
|
|
3
|
+
exports.toSentenceCase = void 0;
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
5
5
|
const categories = require("xregexp/tools/output/categories");
|
|
6
6
|
const cat = name => categories.find(c => c.name === name).bmp;
|
|
@@ -10,13 +10,6 @@ const Ll = cat('Ll');
|
|
|
10
10
|
const N = cat('N');
|
|
11
11
|
const No = cat('No');
|
|
12
12
|
const Pc = cat('Pc');
|
|
13
|
-
function restore(text, orig, preserve) {
|
|
14
|
-
for (const { start, end } of preserve) {
|
|
15
|
-
text = text.substring(0, start) + orig.substring(start, end) + text.substring(end);
|
|
16
|
-
}
|
|
17
|
-
return text;
|
|
18
|
-
}
|
|
19
|
-
exports.restore = restore;
|
|
20
13
|
const re = {
|
|
21
14
|
acronym: new RegExp(`([${Lu}][.])+$`),
|
|
22
15
|
innerCaps: new RegExp(`.[${Lu}]`),
|
|
@@ -30,9 +23,11 @@ const re = {
|
|
|
30
23
|
// private word = XRegExp('^\\p{L}+([-.]\\p{L}+)*') // also match gallium-nitride as one word
|
|
31
24
|
// private and = XRegExp('^\\p{Lu}&\\p{Lu}(?=$|[\\P{L}])') // Q&A
|
|
32
25
|
};
|
|
33
|
-
function lowercase(word) {
|
|
26
|
+
function lowercase(word, allcaps) {
|
|
34
27
|
if (!word)
|
|
35
28
|
return word;
|
|
29
|
+
if (allcaps)
|
|
30
|
+
return word.toLowerCase();
|
|
36
31
|
const unmasked = word.replace(/\uFFFD/g, '');
|
|
37
32
|
if (unmasked.match(re.skipWords))
|
|
38
33
|
return word.toLowerCase();
|
|
@@ -47,43 +42,47 @@ function lowercase(word) {
|
|
|
47
42
|
return word;
|
|
48
43
|
return word.toLowerCase();
|
|
49
44
|
}
|
|
50
|
-
function toSentenceCase(
|
|
45
|
+
function toSentenceCase(sentence, preserveQuoted = false) {
|
|
46
|
+
const allcaps = sentence === sentence.toUpperCase();
|
|
51
47
|
const preserve = [];
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
});
|
|
56
|
-
text.replace(/‘.*?’/g, (match, i) => {
|
|
57
|
-
preserve.push({ start: i, end: i + match.length, description: 'quoted' });
|
|
58
|
-
return '';
|
|
59
|
-
});
|
|
60
|
-
text.replace(/(["]).*?\1/g, (match, _quote, i) => {
|
|
61
|
-
preserve.push({ start: i, end: i + match.length, description: 'quoted' });
|
|
62
|
-
return '';
|
|
63
|
-
});
|
|
64
|
-
text.replace(/([.?!][\s]+)(<[^>]+>)?([A-Z])/g, (match, end, markup, char, i) => {
|
|
65
|
-
if (!text.substring(0, i + 1).match(re.acronym)) {
|
|
66
|
-
preserve.push({ start: i + end.length + ((markup === null || markup === void 0 ? void 0 : markup.length) || 0), end: i + end.length + ((markup === null || markup === void 0 ? void 0 : markup.length) || 0) + char.length, description: 'sub-sentence-start' });
|
|
48
|
+
sentence.replace(/([.?!][\s]+)(<[^>]+>)?([A-Z])/g, (match, end, markup, char, i) => {
|
|
49
|
+
if (!sentence.substring(0, i + 1).match(re.acronym)) {
|
|
50
|
+
preserve.push({ pos: i + end.length + ((markup === null || markup === void 0 ? void 0 : markup.length) || 0), text: char, description: 'sub-sentence-start' });
|
|
67
51
|
}
|
|
68
52
|
return '';
|
|
69
53
|
});
|
|
70
|
-
|
|
71
|
-
preserve.push({
|
|
54
|
+
sentence.replace(/^(<[^>]+>)?([A-Z])/, (match, markup, char) => {
|
|
55
|
+
preserve.push({ pos: ((markup === null || markup === void 0 ? void 0 : markup.length) || 0), text: char, description: 'sentence-start' });
|
|
72
56
|
return '';
|
|
73
57
|
});
|
|
74
|
-
|
|
75
|
-
preserve.push({
|
|
58
|
+
sentence.replace(/<span class="nocase">.*?<\/span>|<nc>.*?<\/nc>/gi, (text, pos) => {
|
|
59
|
+
preserve.push({ pos, text, description: 'nocase' });
|
|
76
60
|
return '';
|
|
77
61
|
});
|
|
78
|
-
let masked =
|
|
79
|
-
preserve.push({
|
|
80
|
-
return '\uFFFD'.repeat(
|
|
62
|
+
let masked = sentence.replace(/<[^>]+>/g, (text, pos) => {
|
|
63
|
+
preserve.push({ pos, text, description: 'markup' });
|
|
64
|
+
return '\uFFFD'.repeat(text.length);
|
|
81
65
|
});
|
|
66
|
+
// last because we're potentially modifying the original
|
|
67
|
+
for (const q of [/(“)(.*?)”/g, /(‘)(.*?)’/g, /(["])(.*?)\1/g]) {
|
|
68
|
+
sentence.replace(q, (text, quote, quoted, pos) => {
|
|
69
|
+
preserve.push({
|
|
70
|
+
pos: pos + (preserveQuoted ? 0 : quote.length),
|
|
71
|
+
text: preserveQuoted ? text : toSentenceCase(quoted, preserveQuoted),
|
|
72
|
+
description: `quoted with ${q}`,
|
|
73
|
+
});
|
|
74
|
+
return '';
|
|
75
|
+
});
|
|
76
|
+
}
|
|
82
77
|
masked = masked
|
|
83
78
|
.replace(/[;:]\uFFFD*\s+\uFFFD*A\s/g, match => match.toLowerCase())
|
|
84
79
|
.replace(/[–—]\uFFFD*\s*\uFFFD*A\s/g, match => match.toLowerCase())
|
|
85
|
-
.replace(re.words, word => lowercase(word));
|
|
86
|
-
|
|
80
|
+
.replace(re.words, word => lowercase(word, allcaps));
|
|
81
|
+
sentence = masked;
|
|
82
|
+
for (const { pos, text } of preserve) {
|
|
83
|
+
sentence = sentence.substring(0, pos) + text + sentence.substring(pos + text.length);
|
|
84
|
+
}
|
|
85
|
+
return sentence;
|
|
87
86
|
}
|
|
88
87
|
exports.toSentenceCase = toSentenceCase;
|
|
89
88
|
//# sourceMappingURL=sentence-case.js.map
|