-
Notifications
You must be signed in to change notification settings - Fork 10
/
outline-lexer.js
91 lines (81 loc) · 2.87 KB
/
outline-lexer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// @ts-check
'use strict';
// Transforms a stream of lines with known indentation levels and leaders like
// bullets, and transforms these into a stream of lines with start and stop
// tokens around changes in indentation depth.
//
// The outline lexer receives lines from a scanner and sends start, stop, and
// text lines to an inline lexer.
// TODO remove the break emission feature
module.exports = class OutlineLexer {
debug = typeof process === 'object' && process.env.DEBUG_OUTLINE_LEXER
top = 0
broken = false
/** @typedef {import('./scanner')} Scanner */
/** Outline lexer state object, which receives typed text tokens, along
* with the current scanner. It is expected to return a subsequent state
* object, which will be retained by the lexer, and receive the subsequent
* token.
*
* @typedef {object} State
* @prop {(type: string, text: string, sc: Scanner) => State} next
*/
/**
* @param {State} generator
*/
constructor(generator) {
this.generator = generator;
this.stack = [this.top];
}
/**
* @param {string} line
* @param {Scanner} scanner
* @returns {OutlineLexer}
*/
next(line, scanner) {
if (this.debug) {
console.error(
'OLL', scanner.position(),
JSON.stringify(line),
'indent', scanner.indent,
'leader', JSON.stringify(scanner.leader),
'stack', this.stack,
'top', this.top
);
}
while (scanner.indent < this.top) {
this.generator = this.generator.next('stop', '', scanner);
this.stack.pop();
this.top = this.stack[this.stack.length - 1];
}
if (scanner.leader.length !== 0 && scanner.indent > this.top) {
this.generator = this.generator.next('start', scanner.leader, scanner);
this.stack.push(scanner.indent);
this.top = scanner.indent;
this.broken = false;
} else if (scanner.leader.length !== 0 && scanner.indent === this.top) {
this.generator = this.generator.next('stop', '', scanner);
this.generator = this.generator.next('start', scanner.leader, scanner);
this.top = scanner.indent;
this.broken = false;
}
if (line.length) {
this.generator = this.generator.next('text', line, scanner);
} else if (!this.broken) {
this.broken = true;
this.generator = this.generator.next('break', '', scanner);
}
return this;
}
/**
* @param {Scanner} scanner
* @returns {OutlineLexer}
*/
return(scanner) {
for (var i = 0; i < this.stack.length; i++) {
this.generator = this.generator.next('stop', '', scanner);
}
this.stack.length = 0;
return this;
}
}