uniorg-parse 0.4.3 → 0.5.0
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/lib/entities.js +1 -5
- package/lib/entities.js.map +1 -1
- package/lib/index.d.ts +1 -2
- package/lib/index.js +1 -6
- package/lib/index.js.map +1 -1
- package/lib/parse-options.js +1 -4
- package/lib/parse-options.js.map +1 -1
- package/lib/parser.d.ts +1 -1
- package/lib/parser.js +126 -121
- package/lib/parser.js.map +1 -1
- package/lib/reader.js +50 -59
- package/lib/reader.js.map +1 -1
- package/lib/unified-org-parse.js +4 -6
- package/lib/unified-org-parse.js.map +1 -1
- package/lib/utils.d.ts +1 -1
- package/lib/utils.js +6 -13
- package/lib/utils.js.map +1 -1
- package/package.json +14 -11
- package/lib/parser.spec.d.ts +0 -1
- package/lib/parser.spec.js +0 -466
- package/lib/parser.spec.js.map +0 -1
package/lib/parser.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const vfile_1 = __importDefault(require("vfile"));
|
|
8
|
-
const unist_builder_1 = __importDefault(require("unist-builder"));
|
|
9
|
-
const entities_1 = require("./entities");
|
|
10
|
-
const utils_1 = require("./utils");
|
|
11
|
-
const parse_options_1 = require("./parse-options");
|
|
12
|
-
const reader_1 = require("./reader");
|
|
1
|
+
import { VFile } from 'vfile';
|
|
2
|
+
import { u } from 'unist-builder';
|
|
3
|
+
import { getOrgEntity } from './entities.js';
|
|
4
|
+
import { restrictionFor, greaterElements, unescapeCodeInString, escapeRegExp, OrgRegexUtils, } from './utils.js';
|
|
5
|
+
import { defaultOptions } from './parse-options.js';
|
|
6
|
+
import { Reader } from './reader.js';
|
|
13
7
|
/*
|
|
14
8
|
(defun rasen/org-debug ()
|
|
15
9
|
"Show org AST for the current buffer."
|
|
@@ -50,20 +44,19 @@ var ParseMode;
|
|
|
50
44
|
/** Default parsing mode. */
|
|
51
45
|
ParseMode[ParseMode["Default"] = 7] = "Default";
|
|
52
46
|
})(ParseMode || (ParseMode = {}));
|
|
53
|
-
function parse(file, options) {
|
|
54
|
-
return new Parser(
|
|
47
|
+
export function parse(file, options) {
|
|
48
|
+
return new Parser(new VFile(file), options).parse();
|
|
55
49
|
}
|
|
56
|
-
exports.parse = parse;
|
|
57
50
|
class Parser {
|
|
58
51
|
constructor(file, options = {}) {
|
|
59
|
-
this.r = new
|
|
60
|
-
this.options =
|
|
61
|
-
this.re = new
|
|
52
|
+
this.r = new Reader(file);
|
|
53
|
+
this.options = { ...defaultOptions, ...options };
|
|
54
|
+
this.re = new OrgRegexUtils(this.options);
|
|
62
55
|
}
|
|
63
56
|
parse() {
|
|
64
57
|
this.parseEmptyLines();
|
|
65
58
|
const children = this.parseElements(ParseMode.TopComment);
|
|
66
|
-
return
|
|
59
|
+
return u('org-data', { contentsBegin: 0, contentsEnd: this.r.endOffset() }, children);
|
|
67
60
|
}
|
|
68
61
|
// General parsing structure
|
|
69
62
|
parseElements(mode, structure) {
|
|
@@ -78,25 +71,31 @@ class Parser {
|
|
|
78
71
|
prevOffset = offset;
|
|
79
72
|
const element = this.parseElement(mode, structure);
|
|
80
73
|
const type = element.type;
|
|
74
|
+
// @ts-expect-error contentsBegin is not defined for "literals"
|
|
81
75
|
const cbeg = element.contentsBegin;
|
|
76
|
+
// @ts-expect-error contentsBegin is not defined for "literals"
|
|
82
77
|
const cend = element.contentsEnd;
|
|
83
78
|
if (cbeg === undefined || cend === undefined) {
|
|
84
79
|
// do nothing
|
|
85
80
|
}
|
|
86
|
-
else if (
|
|
87
|
-
const elementStructure = element.structure;
|
|
81
|
+
else if (greaterElements.has(type)) {
|
|
88
82
|
this.r.narrow(cbeg, cend);
|
|
89
|
-
appendChildren(element, this.parseElements(Parser.nextMode(mode, type, true),
|
|
83
|
+
appendChildren(element, this.parseElements(Parser.nextMode(mode, type, true), element.type === 'plain-list' || element.type === 'list-item'
|
|
84
|
+
? // @ts-expect-error Property 'structure' does not exist on type 'OrgData'
|
|
85
|
+
element.structure
|
|
86
|
+
: undefined));
|
|
90
87
|
this.r.widen();
|
|
91
88
|
// Delete structure from lists. It’s only here to facilitate
|
|
92
89
|
// parsing and should not be exposed to the user.
|
|
93
|
-
|
|
90
|
+
// @ts-expect-error Property 'structure' does not exist on type 'OrgData'
|
|
91
|
+
if (element.structure) {
|
|
92
|
+
// @ts-expect-error Property 'structure' does not exist on type 'OrgData'
|
|
94
93
|
delete element.structure;
|
|
95
94
|
}
|
|
96
95
|
}
|
|
97
96
|
else {
|
|
98
97
|
this.r.narrow(cbeg, cend);
|
|
99
|
-
appendChildren(element, this.parseObjects(
|
|
98
|
+
appendChildren(element, this.parseObjects(restrictionFor(element.type)));
|
|
100
99
|
this.r.widen();
|
|
101
100
|
}
|
|
102
101
|
elements.push(element);
|
|
@@ -134,7 +133,6 @@ class Parser {
|
|
|
134
133
|
return ParseMode.Default;
|
|
135
134
|
}
|
|
136
135
|
parseElement(mode, structure) {
|
|
137
|
-
var _a, _b;
|
|
138
136
|
// List Item.
|
|
139
137
|
if (mode === ParseMode.ListItem)
|
|
140
138
|
return this.parseListItem(structure);
|
|
@@ -166,7 +164,7 @@ class Parser {
|
|
|
166
164
|
// && TODO: check previous line is headline
|
|
167
165
|
((mode === ParseMode.PropertyDrawer || mode === ParseMode.TopComment) &&
|
|
168
166
|
!this.r.lookingAt(/\s*$/m))) &&
|
|
169
|
-
this.r.lookingAt(/^[ \t]*:PROPERTIES:[ \t]*\n(?:[ \t]*:\S+:(?: .*)?[ \t]*\n)*?[ \t]*:END:[ \t]*$/
|
|
167
|
+
this.r.lookingAt(/^[ \t]*:PROPERTIES:[ \t]*\n(?:[ \t]*:\S+:(?: .*)?[ \t]*\n)*?[ \t]*:END:[ \t]*$/im)) {
|
|
170
168
|
return this.parsePropertyDrawer();
|
|
171
169
|
}
|
|
172
170
|
// When not at beginning of line, point is at the beginning of an
|
|
@@ -255,7 +253,7 @@ class Parser {
|
|
|
255
253
|
const offset = this.r.offset();
|
|
256
254
|
this.r.advance(this.r.line());
|
|
257
255
|
const nextLineOffset = this.r.offset();
|
|
258
|
-
const firstNonTable =
|
|
256
|
+
const firstNonTable = this.r.match(/^[ \t]*($|[^|])/m)?.index ?? null;
|
|
259
257
|
this.r.advance(firstNonTable);
|
|
260
258
|
const isTable = this.r.offset() > nextLineOffset && this.r.lookingAt(ruleRe);
|
|
261
259
|
this.r.resetOffset(offset);
|
|
@@ -295,13 +293,15 @@ class Parser {
|
|
|
295
293
|
if (objectBegin !== prevEnd) {
|
|
296
294
|
// parse text before object
|
|
297
295
|
const value = this.r.substring(prevEnd, objectBegin);
|
|
298
|
-
objects.push(
|
|
296
|
+
objects.push(u('text', { value }));
|
|
299
297
|
}
|
|
298
|
+
// @ts-expect-error contentsBegin is not defined for "literals"
|
|
300
299
|
const cbeg = o.contentsBegin;
|
|
300
|
+
// @ts-expect-error contentsBegin is not defined for "literals"
|
|
301
301
|
const cend = o.contentsEnd;
|
|
302
302
|
if (cbeg !== undefined && cend !== undefined) {
|
|
303
303
|
this.r.narrow(cbeg, cend);
|
|
304
|
-
appendChildren(o, this.parseObjects(
|
|
304
|
+
appendChildren(o, this.parseObjects(restrictionFor(o.type)));
|
|
305
305
|
this.r.widen();
|
|
306
306
|
}
|
|
307
307
|
objects.push(o);
|
|
@@ -312,7 +312,7 @@ class Parser {
|
|
|
312
312
|
const text = this.r.rest();
|
|
313
313
|
this.r.advance(text.length);
|
|
314
314
|
if (text.trim().length) {
|
|
315
|
-
objects.push(
|
|
315
|
+
objects.push(u('text', { value: text }));
|
|
316
316
|
}
|
|
317
317
|
return objects;
|
|
318
318
|
}
|
|
@@ -467,26 +467,25 @@ class Parser {
|
|
|
467
467
|
? this.r.offset() + endOfSubtree.index
|
|
468
468
|
: this.r.endOffset();
|
|
469
469
|
this.r.resetOffset(contentsEnd);
|
|
470
|
-
return
|
|
470
|
+
return u('section', { contentsBegin, contentsEnd }, []);
|
|
471
471
|
}
|
|
472
472
|
parseHeadline() {
|
|
473
|
-
var _a, _b, _c;
|
|
474
473
|
const begin = this.r.offset();
|
|
475
474
|
this.r.advance(this.r.line());
|
|
476
475
|
this.r.narrow(begin, this.r.offset());
|
|
477
476
|
const stars = this.r.advance(this.r.forceLookingAt(/^(\*+)[ \t]+/));
|
|
478
477
|
const level = stars[1].length;
|
|
479
478
|
const todoM = this.r.advance(this.r.lookingAt(new RegExp('^' + this.options.todoKeywords.join('|'))));
|
|
480
|
-
const todoKeyword =
|
|
479
|
+
const todoKeyword = todoM?.[0] ?? null;
|
|
481
480
|
this.r.advance(this.r.lookingAt(/^[ \t]*/));
|
|
482
481
|
const priorityM = this.r.advance(this.r.lookingAt(/^\[#.\]/));
|
|
483
|
-
const priority =
|
|
482
|
+
const priority = priorityM?.[0][2] ?? null;
|
|
484
483
|
this.r.advance(this.r.lookingAt(/^[ \t]*/));
|
|
485
484
|
const commented = !!this.r.advance(this.r.lookingAt(/^COMMENT/));
|
|
486
485
|
this.r.advance(this.r.lookingAt(/^[ \t]*/));
|
|
487
486
|
const titleStart = this.r.offset();
|
|
488
487
|
const tagsM = this.r.lookingAt(/^(.*?)[ \t]+:([\w@#%:]+):[ \t]*$/m);
|
|
489
|
-
const tags =
|
|
488
|
+
const tags = tagsM?.[2].split(':') ?? [];
|
|
490
489
|
const titleEnd = tagsM
|
|
491
490
|
? titleStart + tagsM.index + tagsM[1].length
|
|
492
491
|
: titleStart + this.r.forceLookingAt(/.*/)[0].length;
|
|
@@ -496,7 +495,7 @@ class Parser {
|
|
|
496
495
|
// Reset line restriction.
|
|
497
496
|
this.r.widen();
|
|
498
497
|
this.parseEmptyLines();
|
|
499
|
-
return
|
|
498
|
+
return u('headline', {
|
|
500
499
|
level,
|
|
501
500
|
todoKeyword,
|
|
502
501
|
priority,
|
|
@@ -530,17 +529,17 @@ class Parser {
|
|
|
530
529
|
this.r.widen();
|
|
531
530
|
this.r.advance(this.r.line());
|
|
532
531
|
this.parseEmptyLines();
|
|
533
|
-
return
|
|
532
|
+
return u('planning', { scheduled, deadline, closed });
|
|
534
533
|
}
|
|
535
534
|
parsePropertyDrawer() {
|
|
536
535
|
this.r.advance(this.r.line());
|
|
537
536
|
const contentsBegin = this.r.offset();
|
|
538
|
-
const endM = this.r.forceMatch(/^[ \t]*:END:[ \t]*$/
|
|
537
|
+
const endM = this.r.forceMatch(/^[ \t]*:END:[ \t]*$/im);
|
|
539
538
|
this.r.advance(endM.index);
|
|
540
539
|
const contentsEnd = this.r.offset();
|
|
541
540
|
this.r.advance(this.r.line());
|
|
542
541
|
this.parseEmptyLines();
|
|
543
|
-
return
|
|
542
|
+
return u('property-drawer', { contentsBegin, contentsEnd }, []);
|
|
544
543
|
}
|
|
545
544
|
parseBlock(type, pattern, affiliated) {
|
|
546
545
|
const endM = this.r.match(new RegExp(`^[ \\t]*#\\+end_${pattern}[ \\t]*$`, 'im'));
|
|
@@ -555,7 +554,7 @@ class Parser {
|
|
|
555
554
|
this.r.advance(this.r.line());
|
|
556
555
|
this.parseEmptyLines();
|
|
557
556
|
const _end = this.r.offset();
|
|
558
|
-
return
|
|
557
|
+
return u(type, { affiliated, contentsBegin, contentsEnd }, []);
|
|
559
558
|
}
|
|
560
559
|
parseComment() {
|
|
561
560
|
let valueLines = [];
|
|
@@ -571,7 +570,7 @@ class Parser {
|
|
|
571
570
|
if (value[value.length - 1] === '\n') {
|
|
572
571
|
value = value.substring(0, value.length - 1);
|
|
573
572
|
}
|
|
574
|
-
return
|
|
573
|
+
return u('comment', { value: value });
|
|
575
574
|
}
|
|
576
575
|
parseFixedWidth(affiliated) {
|
|
577
576
|
let valueLines = [];
|
|
@@ -583,7 +582,7 @@ class Parser {
|
|
|
583
582
|
valueLines.push(m[1]);
|
|
584
583
|
}
|
|
585
584
|
const value = valueLines.join('\n');
|
|
586
|
-
return
|
|
585
|
+
return u('fixed-width', { affiliated, value });
|
|
587
586
|
}
|
|
588
587
|
parseCommentBlock(affiliated) {
|
|
589
588
|
const comment = this.parseBlock('comment-block', 'comment', affiliated);
|
|
@@ -592,7 +591,7 @@ class Parser {
|
|
|
592
591
|
return comment;
|
|
593
592
|
}
|
|
594
593
|
const value = this.r.substring(comment.contentsBegin, comment.contentsEnd);
|
|
595
|
-
return
|
|
594
|
+
return u('comment-block', { affiliated, value });
|
|
596
595
|
}
|
|
597
596
|
parseSrcBlock(affiliated) {
|
|
598
597
|
const endM = this.r.match(/^[ \t]*#\+end_src[ \t]*$/im);
|
|
@@ -605,12 +604,12 @@ class Parser {
|
|
|
605
604
|
const begin = this.r.offset();
|
|
606
605
|
const contentsBegin = begin + this.r.line().length;
|
|
607
606
|
const contentsEnd = begin + endM.index;
|
|
608
|
-
const value =
|
|
607
|
+
const value = unescapeCodeInString(this.r.substring(contentsBegin, contentsEnd));
|
|
609
608
|
this.r.resetOffset(contentsEnd);
|
|
610
609
|
this.r.advance(this.r.line());
|
|
611
610
|
this.parseEmptyLines();
|
|
612
611
|
const _end = this.r.offset();
|
|
613
|
-
return
|
|
612
|
+
return u('src-block', { affiliated, language, value });
|
|
614
613
|
}
|
|
615
614
|
parseExampleBlock(affiliated) {
|
|
616
615
|
// TODO: parse switches
|
|
@@ -620,30 +619,29 @@ class Parser {
|
|
|
620
619
|
return block;
|
|
621
620
|
}
|
|
622
621
|
const value = this.r.substring(block.contentsBegin, block.contentsEnd);
|
|
623
|
-
return
|
|
622
|
+
return u('example-block', { affiliated, value });
|
|
624
623
|
}
|
|
625
624
|
parseExportBlock(affiliated) {
|
|
626
|
-
var _a;
|
|
627
625
|
const endM = this.r.match(/^[ \t]*#\+end_export[ \t]*$/im);
|
|
628
626
|
if (!endM) {
|
|
629
627
|
// Incomplete block: parse it as a paragraph.
|
|
630
628
|
return this.parseParagraph(affiliated);
|
|
631
629
|
}
|
|
632
630
|
const headerM = this.r.forceMatch(/^[ \t]*#\+begin_export(?:[ \t]+(\S+))?[ \t]*$/im);
|
|
633
|
-
const backend =
|
|
631
|
+
const backend = headerM[1] ?? null;
|
|
634
632
|
const begin = this.r.offset();
|
|
635
633
|
const contentsBegin = begin + this.r.line().length;
|
|
636
634
|
const contentsEnd = begin + endM.index;
|
|
637
|
-
const value =
|
|
635
|
+
const value = unescapeCodeInString(this.r.substring(contentsBegin, contentsEnd));
|
|
638
636
|
this.r.resetOffset(contentsEnd);
|
|
639
637
|
this.r.advance(this.r.line());
|
|
640
638
|
this.parseEmptyLines();
|
|
641
639
|
const _end = this.r.offset();
|
|
642
|
-
return
|
|
640
|
+
return u('export-block', { affiliated, backend, value });
|
|
643
641
|
}
|
|
644
642
|
parseSpecialBlock(affiliated) {
|
|
645
643
|
const blockType = this.r.forceLookingAt(/[ \t]*#\+begin_(\S+)/i)[1];
|
|
646
|
-
const endM = this.r.match(new RegExp(`^[ \\t]*#\\+end_${
|
|
644
|
+
const endM = this.r.match(new RegExp(`^[ \\t]*#\\+end_${escapeRegExp(blockType)}[ \\t]*$`, 'im'));
|
|
647
645
|
if (!endM) {
|
|
648
646
|
this.r.message('incomplete block', this.r.offset(), 'uniorg');
|
|
649
647
|
// Incomplete block: parse it as a paragraph.
|
|
@@ -656,29 +654,30 @@ class Parser {
|
|
|
656
654
|
this.r.advance(this.r.line());
|
|
657
655
|
this.parseEmptyLines();
|
|
658
656
|
const _end = this.r.offset();
|
|
659
|
-
return
|
|
657
|
+
return u('special-block', { affiliated, blockType, contentsBegin, contentsEnd }, []);
|
|
660
658
|
}
|
|
661
659
|
parseAffiliatedKeywords() {
|
|
662
|
-
var _a, _b, _c, _d;
|
|
663
660
|
const offset = this.r.offset();
|
|
664
661
|
const result = {};
|
|
665
662
|
while (!this.r.eof()) {
|
|
666
663
|
const keywordM = this.r.lookingAt(affiliatedRe);
|
|
667
664
|
if (!keywordM)
|
|
668
665
|
break;
|
|
669
|
-
const rawKeyword = (
|
|
670
|
-
|
|
666
|
+
const rawKeyword = (keywordM.groups.dualKeyword ??
|
|
667
|
+
keywordM.groups.regularKeyword ??
|
|
668
|
+
keywordM.groups.attributeKeyword).toUpperCase();
|
|
669
|
+
const keyword = keywordTranslationTable[rawKeyword] ?? rawKeyword;
|
|
671
670
|
// true if keyword should have its value parsed
|
|
672
671
|
const isParsed = parsedKeywords.has(keyword);
|
|
673
672
|
this.r.advance(keywordM);
|
|
674
673
|
this.r.narrow(this.r.offset(), this.r.offset() + this.r.line().length);
|
|
675
674
|
const mainValue = isParsed
|
|
676
|
-
? this.parseObjects(
|
|
675
|
+
? this.parseObjects(restrictionFor('keyword'))
|
|
677
676
|
: this.r.rest().trim();
|
|
678
677
|
this.r.widen();
|
|
679
678
|
this.r.advance(this.r.line());
|
|
680
679
|
const isDual = dualKeywords.has(keyword);
|
|
681
|
-
const dualValue = isDual ?
|
|
680
|
+
const dualValue = isDual ? keywordM.groups.dualValue ?? null : null;
|
|
682
681
|
const value = dualValue === null ? mainValue : [mainValue, dualValue];
|
|
683
682
|
if (multipleKeywords.has(keyword) ||
|
|
684
683
|
// Attributes can always appear on multiple lines.
|
|
@@ -704,7 +703,7 @@ class Parser {
|
|
|
704
703
|
const value = m[2].trim();
|
|
705
704
|
this.r.advance(this.r.line());
|
|
706
705
|
this.parseEmptyLines();
|
|
707
|
-
return
|
|
706
|
+
return u('keyword', { affiliated, key, value });
|
|
708
707
|
}
|
|
709
708
|
parseLatexEnvironment(affiliated) {
|
|
710
709
|
const beginOffset = this.r.offset();
|
|
@@ -720,10 +719,10 @@ class Parser {
|
|
|
720
719
|
const endOffset = this.r.offset();
|
|
721
720
|
this.parseEmptyLines();
|
|
722
721
|
const value = this.r.substring(beginOffset, endOffset);
|
|
723
|
-
return
|
|
722
|
+
return u('latex-environment', { affiliated, value });
|
|
724
723
|
}
|
|
725
724
|
parseDrawer(affiliated) {
|
|
726
|
-
const endM = this.r.match(/^[ \t]*:END:[ \t]*$/
|
|
725
|
+
const endM = this.r.match(/^[ \t]*:END:[ \t]*$/im);
|
|
727
726
|
if (!endM) {
|
|
728
727
|
this.r.message('incomplete drawer', this.r.offset(), 'uniorg');
|
|
729
728
|
// Incomplete drawer: parse it as a paragraph.
|
|
@@ -736,7 +735,7 @@ class Parser {
|
|
|
736
735
|
this.r.resetOffset(contentsEnd);
|
|
737
736
|
this.r.advance(this.r.line());
|
|
738
737
|
this.parseEmptyLines();
|
|
739
|
-
return
|
|
738
|
+
return u('drawer', { affiliated, name, contentsBegin, contentsEnd }, []);
|
|
740
739
|
}
|
|
741
740
|
parseClock() {
|
|
742
741
|
this.r.advance(this.r.forceMatch(/^[ \t]*CLOCK:[ \t]*/));
|
|
@@ -746,16 +745,15 @@ class Parser {
|
|
|
746
745
|
const duration = durationM ? durationM[1] : null;
|
|
747
746
|
const status = duration ? 'closed' : 'running';
|
|
748
747
|
this.parseEmptyLines();
|
|
749
|
-
return
|
|
748
|
+
return u('clock', { value, duration, status });
|
|
750
749
|
}
|
|
751
750
|
parseNodeProperty() {
|
|
752
|
-
var _a;
|
|
753
751
|
const propertyRe = /^[ \t]*:(?<key>\S+):(?:(?<value1>$)|[ \t]+(?<value2>.*?))[ \t]*$/m;
|
|
754
752
|
const m = this.r.forceLookingAt(propertyRe);
|
|
755
753
|
const key = m.groups['key'];
|
|
756
|
-
const value =
|
|
754
|
+
const value = m.groups['value1'] ?? m.groups['value2'];
|
|
757
755
|
this.r.advance(this.r.line());
|
|
758
|
-
return
|
|
756
|
+
return u('node-property', { key, value });
|
|
759
757
|
}
|
|
760
758
|
parseParagraph(affiliated) {
|
|
761
759
|
const contentsBegin = this.r.offset();
|
|
@@ -778,7 +776,7 @@ class Parser {
|
|
|
778
776
|
}
|
|
779
777
|
const drawerM = this.r.lookingAt(drawerRe);
|
|
780
778
|
if (drawerM) {
|
|
781
|
-
const endM = this.r.match(/^[ \t]*:END:[ \t]*$/
|
|
779
|
+
const endM = this.r.match(/^[ \t]*:END:[ \t]*$/im);
|
|
782
780
|
if (!endM) {
|
|
783
781
|
this.r.advance(this.r.line());
|
|
784
782
|
continue;
|
|
@@ -809,16 +807,15 @@ class Parser {
|
|
|
809
807
|
const contentsEnd = next ? this.r.offset() : this.r.endOffset();
|
|
810
808
|
this.r.resetOffset(contentsEnd);
|
|
811
809
|
this.parseEmptyLines();
|
|
812
|
-
return
|
|
810
|
+
return u('paragraph', { affiliated, contentsBegin, contentsEnd }, []);
|
|
813
811
|
}
|
|
814
812
|
parseFootnoteDefinition(affiliated) {
|
|
815
|
-
var _a;
|
|
816
813
|
const m = this.r.forceLookingAt(footnoteDefinitionRe);
|
|
817
814
|
const label = m[1];
|
|
818
815
|
const begin = this.r.offset();
|
|
819
816
|
this.r.advance(this.r.line());
|
|
820
817
|
const endM = this.r.match(footnoteDefinitionSeparatorRe);
|
|
821
|
-
this.r.advance(endM
|
|
818
|
+
this.r.advance(endM?.index);
|
|
822
819
|
let contentsEnd = endM ? this.r.offset() : this.r.endOffset();
|
|
823
820
|
if (endM && endM[0][0] === '[') {
|
|
824
821
|
// At a new footnote definition, make sure we end before any
|
|
@@ -829,7 +826,7 @@ class Parser {
|
|
|
829
826
|
lines = lines.slice(1, lines.length - 1);
|
|
830
827
|
while (lines.length) {
|
|
831
828
|
const line = lines.pop();
|
|
832
|
-
if (
|
|
829
|
+
if (line.match(affiliatedRe)?.index === 0) {
|
|
833
830
|
// -1 to compensate for \n
|
|
834
831
|
this.r.advance(-line.length - 1);
|
|
835
832
|
}
|
|
@@ -845,18 +842,18 @@ class Parser {
|
|
|
845
842
|
this.r.widen();
|
|
846
843
|
this.r.resetOffset(contentsEnd);
|
|
847
844
|
this.parseEmptyLines();
|
|
848
|
-
return
|
|
845
|
+
return u('footnote-definition', { affiliated, label, contentsBegin, contentsEnd }, []);
|
|
849
846
|
}
|
|
850
847
|
parseHorizontalRule(affiliated) {
|
|
851
848
|
this.r.advance(this.r.line());
|
|
852
849
|
this.parseEmptyLines();
|
|
853
|
-
return
|
|
850
|
+
return u('horizontal-rule', { affiliated });
|
|
854
851
|
}
|
|
855
852
|
parseDiarySexp(affiliated) {
|
|
856
853
|
const value = this.r.forceLookingAt(/^(%%\(.*)[ \t]*$/m)[1];
|
|
857
854
|
this.r.advance(this.r.line());
|
|
858
855
|
this.parseEmptyLines();
|
|
859
|
-
return
|
|
856
|
+
return u('diary-sexp', { affiliated, value });
|
|
860
857
|
}
|
|
861
858
|
parseTable(affiliated) {
|
|
862
859
|
const contentsBegin = this.r.offset();
|
|
@@ -877,10 +874,10 @@ class Parser {
|
|
|
877
874
|
}
|
|
878
875
|
this.parseEmptyLines();
|
|
879
876
|
if (tableType === 'org') {
|
|
880
|
-
return
|
|
877
|
+
return u('table', { tableType, tblfm, contentsBegin, contentsEnd }, []);
|
|
881
878
|
}
|
|
882
879
|
else {
|
|
883
|
-
return
|
|
880
|
+
return u('table', {
|
|
884
881
|
affiliated,
|
|
885
882
|
tableType,
|
|
886
883
|
tblfm,
|
|
@@ -899,14 +896,14 @@ class Parser {
|
|
|
899
896
|
// contentsBegin matches contentsEnd.
|
|
900
897
|
const contentsEnd = rowType === 'rule' ? contentsBegin : this.r.offset();
|
|
901
898
|
this.r.advance(this.r.line());
|
|
902
|
-
return
|
|
899
|
+
return u('table-row', { rowType, contentsBegin, contentsEnd }, []);
|
|
903
900
|
}
|
|
904
901
|
parseTableCell() {
|
|
905
902
|
this.r.advance(this.r.forceLookingAt(/^[ \t]*/));
|
|
906
903
|
const contentsBegin = this.r.offset();
|
|
907
904
|
const m = this.r.advance(this.r.forceLookingAt(/(.*?)[ \t]*(?:\||$)/m));
|
|
908
905
|
const contentsEnd = contentsBegin + m[1].length;
|
|
909
|
-
return
|
|
906
|
+
return u('table-cell', { contentsBegin, contentsEnd }, []);
|
|
910
907
|
}
|
|
911
908
|
parseList(structure, affiliated) {
|
|
912
909
|
const contentsBegin = this.r.offset();
|
|
@@ -929,7 +926,7 @@ class Parser {
|
|
|
929
926
|
}
|
|
930
927
|
const contentsEnd = pos;
|
|
931
928
|
this.r.resetOffset(contentsEnd);
|
|
932
|
-
return
|
|
929
|
+
return u('plain-list', {
|
|
933
930
|
affiliated,
|
|
934
931
|
indent,
|
|
935
932
|
listType,
|
|
@@ -941,14 +938,13 @@ class Parser {
|
|
|
941
938
|
}, []);
|
|
942
939
|
}
|
|
943
940
|
parseListItem(structure) {
|
|
944
|
-
var _a, _b;
|
|
945
941
|
const offset = this.r.offset();
|
|
946
942
|
const m = this.r.advance(this.r.forceMatch(this.re.fullListItemRe()));
|
|
947
943
|
const bullet = m.groups.bullet;
|
|
948
|
-
const counter =
|
|
944
|
+
const counter = m.groups.counter ?? null;
|
|
949
945
|
const checkbox = m.groups.checkbox === '[ ]'
|
|
950
946
|
? 'off'
|
|
951
|
-
:
|
|
947
|
+
: m.groups.checkbox?.toLowerCase() === '[x]'
|
|
952
948
|
? 'on'
|
|
953
949
|
: m.groups.checkbox === '[-]'
|
|
954
950
|
? 'trans'
|
|
@@ -957,21 +953,21 @@ class Parser {
|
|
|
957
953
|
const contentsBegin = this.r.offset();
|
|
958
954
|
const contentsEnd = item.end;
|
|
959
955
|
this.r.resetOffset(contentsEnd);
|
|
960
|
-
return
|
|
956
|
+
return u('list-item', {
|
|
961
957
|
indent: item.indent,
|
|
962
958
|
bullet,
|
|
963
959
|
counter,
|
|
964
960
|
checkbox,
|
|
965
961
|
contentsBegin,
|
|
966
962
|
contentsEnd,
|
|
963
|
+
structure,
|
|
967
964
|
}, item.tag ? [item.tag] : []);
|
|
968
965
|
}
|
|
969
966
|
parseListStructure() {
|
|
970
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
971
967
|
const items = [];
|
|
972
968
|
const struct = [];
|
|
973
969
|
while (true) {
|
|
974
|
-
if (this.r.eof() ||
|
|
970
|
+
if (this.r.eof() || this.r.match(this.re.listEndRe())?.index === 0) {
|
|
975
971
|
break;
|
|
976
972
|
}
|
|
977
973
|
const m = this.r.match(this.re.listItemRe());
|
|
@@ -994,21 +990,21 @@ class Parser {
|
|
|
994
990
|
let tag = null;
|
|
995
991
|
if (fullM.groups.tag !== undefined) {
|
|
996
992
|
const tagStartOffset = this.r.offset() +
|
|
997
|
-
(
|
|
998
|
-
(
|
|
999
|
-
(
|
|
1000
|
-
(
|
|
993
|
+
(fullM.groups.indent?.length ?? 0) +
|
|
994
|
+
(fullM.groups.bullet?.length ?? 0) +
|
|
995
|
+
(fullM.groups.counter_group?.length ?? 0) +
|
|
996
|
+
(fullM.groups.checkbox_group?.length ?? 0);
|
|
1001
997
|
const tagStopOffset = tagStartOffset + fullM.groups.tag.length;
|
|
1002
998
|
this.r.narrow(tagStartOffset, tagStopOffset);
|
|
1003
|
-
tag =
|
|
999
|
+
tag = u('list-item-tag', {}, this.parseObjects(restrictionFor('list-item')));
|
|
1004
1000
|
this.r.widen();
|
|
1005
1001
|
}
|
|
1006
1002
|
const item = {
|
|
1007
1003
|
begin: this.r.offset(),
|
|
1008
1004
|
indent,
|
|
1009
1005
|
bullet,
|
|
1010
|
-
counter: counter
|
|
1011
|
-
checkbox: checkbox
|
|
1006
|
+
counter: counter ?? null,
|
|
1007
|
+
checkbox: checkbox ?? null,
|
|
1012
1008
|
tag,
|
|
1013
1009
|
// will be overwritten later
|
|
1014
1010
|
end: this.r.offset(),
|
|
@@ -1032,7 +1028,14 @@ class Parser {
|
|
|
1032
1028
|
// closed full list
|
|
1033
1029
|
break;
|
|
1034
1030
|
}
|
|
1035
|
-
//
|
|
1031
|
+
// skip blocks (any type) and drawers contents.
|
|
1032
|
+
const mBlock = this.r.lookingAt(/[ \t]*#\+begin(:|_\S+)/i);
|
|
1033
|
+
if (mBlock) {
|
|
1034
|
+
this.r.advance(this.r.match(new RegExp(`^[ \\t]*#\\+end${mBlock[1]}[ \\t]*`, 'im')));
|
|
1035
|
+
}
|
|
1036
|
+
else if (this.r.lookingAt(drawerRe)) {
|
|
1037
|
+
this.r.advance(this.r.match(/^[ \t]*:END:[ \t]*$/im));
|
|
1038
|
+
}
|
|
1036
1039
|
this.r.advance(this.r.line());
|
|
1037
1040
|
}
|
|
1038
1041
|
}
|
|
@@ -1057,7 +1060,7 @@ class Parser {
|
|
|
1057
1060
|
const contentsBegin = begin + m[2].length + (inside ? 1 : 0);
|
|
1058
1061
|
const contentsEnd = begin + m[2].length + m[3].length - (inside ? 1 : 0);
|
|
1059
1062
|
const _end = this.r.offset();
|
|
1060
|
-
return
|
|
1063
|
+
return u('superscript', { contentsBegin, contentsEnd, children: [] });
|
|
1061
1064
|
}
|
|
1062
1065
|
parseSubscript() {
|
|
1063
1066
|
this.r.backoff(1); // backoff by one, to match previous char (should be non-space)
|
|
@@ -1070,7 +1073,7 @@ class Parser {
|
|
|
1070
1073
|
const contentsBegin = begin + m[2].length + (inside ? 1 : 0);
|
|
1071
1074
|
const contentsEnd = begin + m[2].length + m[3].length - (inside ? 1 : 0);
|
|
1072
1075
|
const _end = this.r.offset();
|
|
1073
|
-
return
|
|
1076
|
+
return u('subscript', { contentsBegin, contentsEnd, children: [] });
|
|
1074
1077
|
}
|
|
1075
1078
|
parseUnderline() {
|
|
1076
1079
|
// backoff one char to check border
|
|
@@ -1081,7 +1084,7 @@ class Parser {
|
|
|
1081
1084
|
const contentsBegin = this.r.offset() + m.index + m[1].length + m[3].length;
|
|
1082
1085
|
const contentsEnd = contentsBegin + m[4].length;
|
|
1083
1086
|
this.r.resetOffset(contentsEnd + 1);
|
|
1084
|
-
return
|
|
1087
|
+
return u('underline', { contentsBegin, contentsEnd }, []);
|
|
1085
1088
|
}
|
|
1086
1089
|
parseBold() {
|
|
1087
1090
|
// backoff one char to check border
|
|
@@ -1092,7 +1095,7 @@ class Parser {
|
|
|
1092
1095
|
const contentsBegin = this.r.offset() + m.index + m[1].length + m[3].length;
|
|
1093
1096
|
const contentsEnd = contentsBegin + m[4].length;
|
|
1094
1097
|
this.r.resetOffset(contentsEnd + 1);
|
|
1095
|
-
return
|
|
1098
|
+
return u('bold', { contentsBegin, contentsEnd }, []);
|
|
1096
1099
|
}
|
|
1097
1100
|
parseItalic() {
|
|
1098
1101
|
// backoff one char to check border
|
|
@@ -1103,7 +1106,7 @@ class Parser {
|
|
|
1103
1106
|
const contentsBegin = this.r.offset() + m.index + m[1].length + m[3].length;
|
|
1104
1107
|
const contentsEnd = contentsBegin + m[4].length;
|
|
1105
1108
|
this.r.resetOffset(contentsEnd + 1);
|
|
1106
|
-
return
|
|
1109
|
+
return u('italic', { contentsBegin, contentsEnd }, []);
|
|
1107
1110
|
}
|
|
1108
1111
|
parseCode() {
|
|
1109
1112
|
// backoff one char to check border
|
|
@@ -1115,7 +1118,7 @@ class Parser {
|
|
|
1115
1118
|
const contentsBegin = this.r.offset() + m.index + m[1].length + m[3].length;
|
|
1116
1119
|
const contentsEnd = contentsBegin + m[4].length;
|
|
1117
1120
|
this.r.resetOffset(contentsEnd + 1);
|
|
1118
|
-
return
|
|
1121
|
+
return u('code', { value }, []);
|
|
1119
1122
|
}
|
|
1120
1123
|
parseVerbatim() {
|
|
1121
1124
|
this.r.backoff(1);
|
|
@@ -1126,7 +1129,7 @@ class Parser {
|
|
|
1126
1129
|
const contentsBegin = this.r.offset() + m.index + m[1].length + m[3].length;
|
|
1127
1130
|
const contentsEnd = contentsBegin + m[4].length;
|
|
1128
1131
|
this.r.resetOffset(contentsEnd + 1);
|
|
1129
|
-
return
|
|
1132
|
+
return u('verbatim', { value }, []);
|
|
1130
1133
|
}
|
|
1131
1134
|
parseStrikeThrough() {
|
|
1132
1135
|
// backoff one char to check border
|
|
@@ -1137,10 +1140,9 @@ class Parser {
|
|
|
1137
1140
|
const contentsBegin = this.r.offset() + m.index + m[1].length + m[3].length;
|
|
1138
1141
|
const contentsEnd = contentsBegin + m[4].length;
|
|
1139
1142
|
this.r.resetOffset(contentsEnd + 1);
|
|
1140
|
-
return
|
|
1143
|
+
return u('strike-through', { contentsBegin, contentsEnd }, []);
|
|
1141
1144
|
}
|
|
1142
1145
|
parseEntity() {
|
|
1143
|
-
var _a;
|
|
1144
1146
|
const m = this.r.advance(this.r.lookingAt(/^\\(?:(?<value1>_ +)|(?<value2>there4|sup[123]|frac[13][24]|[a-zA-Z]+)(?<brackets>$|\{\}|\P{Letter}))/mu));
|
|
1145
1147
|
if (!m)
|
|
1146
1148
|
return null;
|
|
@@ -1151,13 +1153,12 @@ class Parser {
|
|
|
1151
1153
|
// as text later.
|
|
1152
1154
|
this.r.backoff(m.groups.brackets.length);
|
|
1153
1155
|
}
|
|
1154
|
-
const value =
|
|
1156
|
+
const value = getOrgEntity(m.groups.value1 ?? m.groups.value2);
|
|
1155
1157
|
if (!value)
|
|
1156
1158
|
return null;
|
|
1157
|
-
return
|
|
1159
|
+
return u('entity', { useBrackets: hasBrackets, ...value });
|
|
1158
1160
|
}
|
|
1159
1161
|
parseLatexFragment() {
|
|
1160
|
-
var _a;
|
|
1161
1162
|
const begin = this.r.offset();
|
|
1162
1163
|
const prefix = this.r.peek(2);
|
|
1163
1164
|
let contents = null;
|
|
@@ -1174,20 +1175,20 @@ class Parser {
|
|
|
1174
1175
|
default: {
|
|
1175
1176
|
// Macro.
|
|
1176
1177
|
const m = this.r.advance(this.r.lookingAt(/^\\[a-zA-Z]+\*?((\[[^\]\[\n{}]*\])|(\{[^{}\n]*\}))*/));
|
|
1177
|
-
contents = m
|
|
1178
|
+
contents = m?.[0];
|
|
1178
1179
|
}
|
|
1179
1180
|
}
|
|
1180
1181
|
}
|
|
1181
1182
|
else if (prefix[1] === '$') {
|
|
1182
1183
|
const m = this.r.advance(this.r.match(/\$\$((?:.|\n)*?)\$\$/m));
|
|
1183
|
-
contents = m
|
|
1184
|
+
contents = m?.[1];
|
|
1184
1185
|
}
|
|
1185
1186
|
else {
|
|
1186
1187
|
// TODO: limit search to 2 lines
|
|
1187
1188
|
const charBefore = this.r.substring(this.r.offset() - 1, this.r.offset());
|
|
1188
1189
|
if (charBefore !== '$' &&
|
|
1189
1190
|
!' \t\n,.;'.includes(prefix[1]) &&
|
|
1190
|
-
(contents =
|
|
1191
|
+
(contents = this.r.advance(this.r.match(/\$((?:.|\n)*?)\$/m))?.[1]) &&
|
|
1191
1192
|
!' \t\n,.'.includes(this.r.substring(this.r.offset() - 1, this.r.offset())) &&
|
|
1192
1193
|
this.r.lookingAt(/^(\p{Punctuation}|\p{White_Space}|\p{Open_Punctuation}|\p{Close_Punctuation}|\\"|'|$)/mu)) {
|
|
1193
1194
|
// we've found the end
|
|
@@ -1201,10 +1202,9 @@ class Parser {
|
|
|
1201
1202
|
if (begin === end)
|
|
1202
1203
|
return null;
|
|
1203
1204
|
const value = this.r.substring(begin, end);
|
|
1204
|
-
return
|
|
1205
|
+
return u('latex-fragment', { value, contents: contents ?? value });
|
|
1205
1206
|
}
|
|
1206
1207
|
parseFootnoteReference() {
|
|
1207
|
-
var _a;
|
|
1208
1208
|
const begin = this.r.offset();
|
|
1209
1209
|
const m = this.r.match(footnoteRe);
|
|
1210
1210
|
if (!m)
|
|
@@ -1233,9 +1233,10 @@ class Parser {
|
|
|
1233
1233
|
? 'inline'
|
|
1234
1234
|
: 'standard';
|
|
1235
1235
|
const label = footnoteType === 'inline'
|
|
1236
|
-
?
|
|
1236
|
+
? m.groups.label_inline ?? null
|
|
1237
|
+
: m.groups.label;
|
|
1237
1238
|
if (footnoteType === 'inline') {
|
|
1238
|
-
return
|
|
1239
|
+
return u('footnote-reference', {
|
|
1239
1240
|
label,
|
|
1240
1241
|
footnoteType,
|
|
1241
1242
|
contentsBegin,
|
|
@@ -1243,7 +1244,7 @@ class Parser {
|
|
|
1243
1244
|
}, []);
|
|
1244
1245
|
}
|
|
1245
1246
|
else {
|
|
1246
|
-
return
|
|
1247
|
+
return u('footnote-reference', { label, footnoteType }, []);
|
|
1247
1248
|
}
|
|
1248
1249
|
}
|
|
1249
1250
|
parseLink() {
|
|
@@ -1277,9 +1278,13 @@ class Parser {
|
|
|
1277
1278
|
.replace(/(\\+)([\[\]])/g, (p1, p2) => '\\'.repeat(p1.length / 2) + p2);
|
|
1278
1279
|
// TODO: org-link-expand-abbrev
|
|
1279
1280
|
const { linkType, path } = this.linkType(rawLink);
|
|
1280
|
-
return
|
|
1281
|
+
return u('link', {
|
|
1282
|
+
format: 'bracket',
|
|
1283
|
+
linkType,
|
|
1281
1284
|
rawLink,
|
|
1282
|
-
path
|
|
1285
|
+
path,
|
|
1286
|
+
...contents,
|
|
1287
|
+
}, []);
|
|
1283
1288
|
}
|
|
1284
1289
|
// TODO: this is different from OrgRegexUtils.linkPlainRe
|
|
1285
1290
|
// Type 3: Plain link, e.g., https://orgmode.org
|
|
@@ -1287,7 +1292,7 @@ class Parser {
|
|
|
1287
1292
|
const plainM = this.r.advance(this.r.lookingAt(linkPlainRe));
|
|
1288
1293
|
if (plainM) {
|
|
1289
1294
|
const m = plainM;
|
|
1290
|
-
return
|
|
1295
|
+
return u('link', {
|
|
1291
1296
|
format: 'plain',
|
|
1292
1297
|
linkType: m[1],
|
|
1293
1298
|
rawLink: m[0],
|
|
@@ -1304,7 +1309,7 @@ class Parser {
|
|
|
1304
1309
|
const linkType = m[1];
|
|
1305
1310
|
const rawLink = m[0].substring(1, m[0].length - 1); // strip < >
|
|
1306
1311
|
const path = m[2].replace(/[ \t]*\n[ \t]*/g, '');
|
|
1307
|
-
return
|
|
1312
|
+
return u('link', { format: 'angle', linkType, rawLink, path }, []);
|
|
1308
1313
|
}
|
|
1309
1314
|
return null;
|
|
1310
1315
|
}
|
|
@@ -1375,8 +1380,9 @@ class Parser {
|
|
|
1375
1380
|
: dateEnd
|
|
1376
1381
|
? Parser.parseDate(dateEnd)
|
|
1377
1382
|
: timeRange
|
|
1378
|
-
?
|
|
1379
|
-
|
|
1383
|
+
? { ...start, ...timeRange }
|
|
1384
|
+
: null;
|
|
1385
|
+
return u('timestamp', {
|
|
1380
1386
|
timestampType,
|
|
1381
1387
|
rawValue,
|
|
1382
1388
|
start,
|
|
@@ -1419,7 +1425,7 @@ class Parser {
|
|
|
1419
1425
|
}
|
|
1420
1426
|
const drawerRe = /^[ \t]*:((?:\w|[-_])+):[ \t]*$/m;
|
|
1421
1427
|
const latexBeginEnvironmentRe = /^[ \t]*\\begin\{([A-Za-z0-9*]+)\}/i;
|
|
1422
|
-
const latexEndEnvironmentRe = (name) => new RegExp(`\\\\end\\{${
|
|
1428
|
+
const latexEndEnvironmentRe = (name) => new RegExp(`\\\\end\\{${escapeRegExp(name)}\\}[ \\t]*$`, 'mi');
|
|
1423
1429
|
const affiliatedKeywords = [
|
|
1424
1430
|
'CAPTION',
|
|
1425
1431
|
'DATA',
|
|
@@ -1466,9 +1472,8 @@ const footnoteRe = /\[fn:(?:(?<label_inline>[-_\w]+)?(?<inline>:)|(?<label>[-_\w
|
|
|
1466
1472
|
const footnoteDefinitionRe = /^\[fn:([-_\w]+)\]/;
|
|
1467
1473
|
const footnoteDefinitionSeparatorRe = /^\*|^\[fn:([-_\w]+)\]|^([ \t]*\n){2,}/m;
|
|
1468
1474
|
function appendChildren(node, children) {
|
|
1469
|
-
var _a;
|
|
1470
1475
|
if ('children' in node) {
|
|
1471
|
-
const newChildren = [...(
|
|
1476
|
+
const newChildren = [...(node.children ?? []), ...children];
|
|
1472
1477
|
node.children = newChildren;
|
|
1473
1478
|
}
|
|
1474
1479
|
}
|