@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.
Files changed (3) hide show
  1. package/index.d.ts +1 -1
  2. package/index.js +73 -39
  3. 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 = /^[A-Z][.]([A-Z][.])+(?=$|[^\w])/;
14
- this.quoted = /^"[^"]+"(?=$|[^\w])/;
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 = this.input.match(this.quoted)) { // "Hello There"
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 = this.input.match(this.acronym)) { // U.S.
26
- this.add(m[0], true);
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.match(/^[a-z]+n't\b/i)) { // isn't
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.match(/^[\w-]+/)) { // gallium-nitride
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 = false) {
41
- const parts = word.split('-');
42
- const result = [];
43
- if (this.sentenceStart || keep)
44
- result.push(parts.shift());
45
- for (word of parts) {
46
- if (!word.match(/[a-z][A-Z]/))
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
- // always keep the leading char, but skip markup
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retorquere/bibtex-parser",
3
- "version": "6.3.0",
3
+ "version": "6.3.4",
4
4
  "description": "BibTeX parser",
5
5
  "main": "index.js",
6
6
  "scripts": {