reslop 0.2.5 → 0.2.7

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 (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/LICENSE +1 -1
  3. package/README.md +11 -11
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +72 -19
  6. package/lib/api.js +1 -1
  7. package/lib/capabilities.js +8 -8
  8. package/lib/cli.js +61 -75
  9. package/lib/clipboard.js +39 -47
  10. package/lib/dashboard/git.js +128 -0
  11. package/lib/dashboard/model.js +357 -0
  12. package/lib/dashboard/npm.js +290 -0
  13. package/lib/dashboard/tree.js +278 -0
  14. package/lib/deps-diff.js +197 -402
  15. package/lib/deps.js +113 -240
  16. package/lib/detect.js +8 -18
  17. package/lib/diff/align.js +41 -113
  18. package/lib/diff/diff.js +12 -43
  19. package/lib/diff/display.js +25 -36
  20. package/lib/diff/edit.js +6 -20
  21. package/lib/diff/inline.js +76 -144
  22. package/lib/diff/items.js +33 -47
  23. package/lib/diff/parse.js +28 -56
  24. package/lib/diff/patch.js +12 -33
  25. package/lib/diff/unit.js +79 -178
  26. package/lib/editor.js +52 -69
  27. package/lib/files.js +79 -91
  28. package/lib/find.js +61 -0
  29. package/lib/git-branches.js +254 -416
  30. package/lib/git-deps.js +148 -272
  31. package/lib/git-reword-todo.js +5 -13
  32. package/lib/git-worktree.js +161 -214
  33. package/lib/git.js +21 -49
  34. package/lib/github.js +67 -181
  35. package/lib/gitlab.js +40 -90
  36. package/lib/highlight/bash.js +55 -177
  37. package/lib/highlight/core.js +155 -141
  38. package/lib/highlight/css.js +25 -58
  39. package/lib/highlight/csv.js +19 -37
  40. package/lib/highlight/dart.js +72 -213
  41. package/lib/highlight/dot.js +14 -14
  42. package/lib/highlight/html.js +42 -63
  43. package/lib/highlight/js.js +177 -506
  44. package/lib/highlight/json.js +17 -37
  45. package/lib/highlight/log.js +19 -25
  46. package/lib/highlight/txt.js +7 -10
  47. package/lib/highlight.js +17 -46
  48. package/lib/keys.js +75 -83
  49. package/lib/manifest.js +67 -126
  50. package/lib/npm-commands.js +120 -169
  51. package/lib/remote.js +140 -190
  52. package/lib/render/branches.js +63 -61
  53. package/lib/render/chrome.js +412 -347
  54. package/lib/render/commits.js +160 -361
  55. package/lib/render/dash-activity.js +212 -0
  56. package/lib/render/dash-blocks.js +496 -0
  57. package/lib/render/dash-table.js +295 -0
  58. package/lib/render/dashboard.js +62 -0
  59. package/lib/render/diff.js +123 -258
  60. package/lib/render/files.js +60 -86
  61. package/lib/render/frame.js +82 -117
  62. package/lib/render/notes.js +150 -210
  63. package/lib/render/npm.js +213 -177
  64. package/lib/render/packages.js +93 -0
  65. package/lib/render/present.js +4 -11
  66. package/lib/render/primitives.js +77 -172
  67. package/lib/render/render.js +5 -14
  68. package/lib/render/report.js +43 -0
  69. package/lib/render/repos.js +226 -0
  70. package/lib/render/search.js +25 -0
  71. package/lib/render/tiles.js +355 -0
  72. package/lib/render/unit.js +15 -31
  73. package/lib/report-model.js +110 -33
  74. package/lib/report-parse.js +56 -37
  75. package/lib/report-render.js +102 -59
  76. package/lib/report-run.js +87 -77
  77. package/lib/report-test.js +221 -99
  78. package/lib/report-tools.js +96 -154
  79. package/lib/review-merge.js +33 -41
  80. package/lib/review.js +69 -129
  81. package/lib/runs.js +225 -0
  82. package/lib/select.js +53 -91
  83. package/lib/session/accessors.js +14 -21
  84. package/lib/session/actions.js +189 -65
  85. package/lib/session/branches.js +164 -222
  86. package/lib/session/change-actions.js +49 -119
  87. package/lib/session/commit.js +298 -352
  88. package/lib/session/compose.js +572 -715
  89. package/lib/session/dashboard.js +530 -0
  90. package/lib/session/dispatch.js +197 -171
  91. package/lib/session/files-pane.js +92 -45
  92. package/lib/session/find.js +94 -0
  93. package/lib/session/items.js +6 -17
  94. package/lib/session/lifecycle.js +420 -466
  95. package/lib/session/load.js +1 -14
  96. package/lib/session/navigation.js +4 -2
  97. package/lib/session/npm.js +403 -420
  98. package/lib/session/ops.js +5 -13
  99. package/lib/session/packages.js +143 -0
  100. package/lib/session/pointer.js +30 -14
  101. package/lib/session/progress.js +39 -30
  102. package/lib/session/review.js +1 -3
  103. package/lib/session/tasks.js +277 -236
  104. package/lib/session/templates.js +51 -59
  105. package/lib/session/terminal.js +85 -132
  106. package/lib/session/view.js +59 -80
  107. package/lib/session/watch.js +201 -212
  108. package/lib/session/workspace.js +455 -0
  109. package/lib/session.js +95 -161
  110. package/lib/update.js +44 -111
  111. package/lib/utilities.js +46 -88
  112. package/lib/workspace.js +166 -0
  113. package/lib/wrap.js +29 -55
  114. package/package.json +1 -1
@@ -1,71 +1,38 @@
1
1
  'use strict';
2
2
 
3
3
  const core = require('./core.js');
4
- const { QUOTES, emit, readBlockComment, readString } = core;
4
+ const { QUOTES, emit, scanWhile, blockCommentEnd, stringEnd } = core;
5
5
 
6
- const CSS_NEXT_STYLE = {
7
- ':': 'property',
8
- '{': 'tag',
9
- };
6
+ const CSS_NEXT_STYLE = { ':': 'property', '{': 'tag' };
7
+ const CSS_NAME = /[A-Za-z0-9_-]/;
10
8
 
11
9
  const highlight = (source) => {
12
10
  const out = [];
13
11
  let i = 0;
14
- const n = source.length;
15
- while (i < n) {
12
+ const take = (style, end) => {
13
+ emit(out, style, source.slice(i, end));
14
+ i = end;
15
+ };
16
+ while (i < source.length) {
16
17
  const ch = source[i];
17
- const next = source[i + 1];
18
- if (ch === '/' && next === '*') {
19
- const { text, end } = readBlockComment(source, i);
20
- emit(out, 'comment', text);
21
- i = end;
22
- continue;
23
- }
24
- if (QUOTES.includes(ch)) {
25
- const { text, end } = readString(source, i);
26
- emit(out, 'string', text);
27
- i = end;
28
- continue;
29
- }
30
- if (ch === '@') {
31
- let j = i + 1;
32
- while (j < n && /[A-Za-z0-9-]/.test(source[j])) j += 1;
33
- emit(out, 'keyword', source.slice(i, j));
34
- i = j;
35
- continue;
36
- }
37
- if ('#.'.includes(ch)) {
38
- let j = i + 1;
39
- while (j < n && /[A-Za-z0-9_-]/.test(source[j])) j += 1;
40
- const kind = ch === '#' ? 'constant' : 'className';
41
- emit(out, kind, source.slice(i, j));
42
- i = j;
43
- continue;
44
- }
45
- if (/[0-9]/.test(ch)) {
46
- let j = i;
47
- while (j < n && /[0-9.%]/.test(source[j])) j += 1;
48
- emit(out, 'number', source.slice(i, j));
49
- i = j;
50
- continue;
51
- }
52
- if (/[A-Za-z_-]/.test(ch)) {
53
- let j = i + 1;
54
- while (j < n && /[A-Za-z0-9_-]/.test(source[j])) j += 1;
55
- const word = source.slice(i, j);
56
- let k = j;
57
- while (k < n && /\s/.test(source[k])) k += 1;
58
- emit(out, CSS_NEXT_STYLE[source[k]] ?? 'plain', word);
59
- i = j;
60
- continue;
61
- }
62
- if (/[{};:,]/.test(ch)) {
63
- emit(out, 'punct', ch);
64
- i += 1;
65
- continue;
18
+ if (ch === '/' && source[i + 1] === '*') {
19
+ take('comment', blockCommentEnd(source, i));
20
+ } else if (QUOTES.includes(ch)) {
21
+ take('string', stringEnd(source, i));
22
+ } else if (ch === '@') {
23
+ take('keyword', scanWhile(source, i + 1, /[A-Za-z0-9-]/));
24
+ } else if (ch === '#' || ch === '.') {
25
+ const style = ch === '#' ? 'constant' : 'className';
26
+ take(style, scanWhile(source, i + 1, CSS_NAME));
27
+ } else if (/[0-9]/.test(ch)) {
28
+ take('number', scanWhile(source, i, /[0-9.%]/));
29
+ } else if (/[A-Za-z_-]/.test(ch)) {
30
+ const end = scanWhile(source, i + 1, CSS_NAME);
31
+ const after = source[scanWhile(source, end, /\s/)];
32
+ take(CSS_NEXT_STYLE[after] ?? 'plain', end);
33
+ } else {
34
+ take(/[{};:,]/.test(ch) ? 'punct' : 'plain', i + 1);
66
35
  }
67
- emit(out, 'plain', ch);
68
- i += 1;
69
36
  }
70
37
  return out;
71
38
  };
@@ -1,65 +1,47 @@
1
1
  'use strict';
2
2
 
3
3
  const core = require('./core.js');
4
- const { emit } = core;
4
+ const { emit, highlightLines } = core;
5
5
 
6
6
  const splitCsv = (line) => {
7
7
  const cells = [];
8
8
  let cur = '';
9
- let inQ = false;
9
+ let inQuotes = false;
10
10
  for (let i = 0; i < line.length; i += 1) {
11
11
  const ch = line[i];
12
- if (ch === '"') {
13
- if (inQ && line[i + 1] === '"') {
14
- cur += '"';
15
- i += 1;
16
- } else {
17
- inQ = !inQ;
18
- cur += ch;
19
- }
20
- continue;
21
- }
22
- if (ch === ',' && !inQ) {
12
+ if (ch === '"' && inQuotes && line[i + 1] === '"') {
13
+ cur += '"';
14
+ i += 1;
15
+ } else if (ch === '"') {
16
+ inQuotes = !inQuotes;
17
+ cur += ch;
18
+ } else if (ch === ',' && !inQuotes) {
23
19
  cells.push(cur);
24
20
  cur = '';
25
- continue;
21
+ } else {
22
+ cur += ch;
26
23
  }
27
- cur += ch;
28
24
  }
29
25
  cells.push(cur);
30
26
  return cells;
31
27
  };
32
28
 
33
- const highlightCsvCell = (cell, row, col) => {
34
- if (row === 0) return { text: cell, style: 'function' };
35
- const num = /^-?\d+(\.\d+)?$/.test(cell.trim());
36
- if (num) return { text: cell, style: 'number' };
37
- if (col % 2 === 0) return { text: cell, style: 'string' };
38
- return { text: cell, style: 'variable' };
29
+ const cellStyle = (cell, col) => {
30
+ if (/^-?\d+(\.\d+)?$/.test(cell.trim())) return 'number';
31
+ return col % 2 === 0 ? 'string' : 'variable';
39
32
  };
40
33
 
41
- const highlightCsvLine = (line, row) => {
42
- if (!line) return [];
34
+ const highlightCsvLine = (line) => {
43
35
  const cells = splitCsv(line);
44
36
  const out = [];
45
37
  for (let col = 0; col < cells.length; col += 1) {
46
38
  if (col) emit(out, 'punct', ',');
47
- const cell = highlightCsvCell(cells[col], row, col);
48
- emit(out, cell.style, cell.text);
39
+ emit(out, cellStyle(cells[col], col), cells[col]);
49
40
  }
50
41
  return out;
51
42
  };
52
43
 
53
- const highlight = (source) => {
54
- const lines = source.split(/\r?\n/);
55
- const out = [];
56
- for (let row = 0; row < lines.length; row += 1) {
57
- if (row) emit(out, 'plain', '\n');
58
- for (const t of highlightCsvLine(lines[row], row === 0 ? 1 : row)) {
59
- out.push(t);
60
- }
61
- }
62
- return out;
44
+ module.exports = {
45
+ langs: ['csv'],
46
+ highlight: highlightLines(highlightCsvLine, /\r?\n/),
63
47
  };
64
-
65
- module.exports = { langs: ['csv'], highlight };
@@ -1,245 +1,104 @@
1
1
  'use strict';
2
2
 
3
- const hl = require('./core.js');
4
- const { SQUOTE, QUOTES, EXPECT_STYLE, emit, wordStyle } = hl;
5
- const { peekIdent, skipWs, readUntilNl, readBlockComment } = hl;
6
- const { readJsNumber, readJsOperator, readTemplateInterp } = hl;
7
- const { styleConstant, stylePascal, styleCall } = hl;
3
+ const core = require('./core.js');
4
+ const { QUOTES, IDENT_START, IDENT_CHAR, EXPECT_STYLE } = core;
5
+ const { words, emit, wordStyles, appendNode, scanWhile } = core;
6
+ const { lineEnd, blockCommentEnd, numberEnd, operatorEnd } = core;
7
+ const { highlightInterpolated, nameStyle } = core;
8
8
 
9
- const STORAGE = [
10
- 'abstract',
11
- 'as',
12
- 'base',
13
- 'class',
14
- 'const',
15
- 'covariant',
16
- 'deferred',
17
- 'enum',
18
- 'export',
19
- 'extends',
20
- 'extension',
21
- 'external',
22
- 'factory',
23
- 'final',
24
- 'get',
25
- 'hide',
26
- 'implements',
27
- 'import',
28
- 'interface',
29
- 'late',
30
- 'library',
31
- 'mixin',
32
- 'on',
33
- 'operator',
34
- 'part',
35
- 'required',
36
- 'sealed',
37
- 'set',
38
- 'show',
39
- 'static',
40
- 'typedef',
41
- 'var',
42
- 'with',
43
- ];
9
+ const STORAGE = words(`
10
+ abstract as base class const covariant deferred enum export extends
11
+ extension external factory final get hide implements import interface late
12
+ library mixin on operator part required sealed set show static typedef var
13
+ with
14
+ `);
44
15
 
45
- const CONTROL = [
46
- 'assert',
47
- 'async',
48
- 'await',
49
- 'break',
50
- 'case',
51
- 'catch',
52
- 'continue',
53
- 'default',
54
- 'do',
55
- 'else',
56
- 'for',
57
- 'if',
58
- 'in',
59
- 'is',
60
- 'new',
61
- 'rethrow',
62
- 'return',
63
- 'switch',
64
- 'sync',
65
- 'throw',
66
- 'try',
67
- 'when',
68
- 'while',
69
- 'yield',
70
- ];
16
+ const CONTROL = words(`
17
+ assert async await break case catch continue default do else for if in is
18
+ new rethrow return switch sync throw try when while yield
19
+ `);
71
20
 
72
- const LITERALS = ['true', 'false', 'null', 'this', 'super'];
21
+ const LITERALS = words('true false null this super');
73
22
 
74
- const TYPES = ['void', 'dynamic', 'Never', 'int', 'double', 'num', 'bool'];
23
+ const TYPES = words('void dynamic Never int double num bool');
75
24
 
76
- const NEXT_EXPECT = {
77
- class: 'class',
78
- mixin: 'class',
79
- enum: 'class',
80
- extension: 'class',
81
- typedef: 'type',
82
- };
25
+ const NEXT_EXPECT = wordStyles({
26
+ class: ['class', 'mixin', 'enum', 'extension'],
27
+ type: ['typedef'],
28
+ });
83
29
 
84
- const WORD_STYLE = {
85
- ...wordStyle(CONTROL, 'control'),
86
- ...wordStyle(LITERALS, 'literal'),
87
- ...wordStyle(STORAGE, 'storage'),
88
- ...wordStyle(TYPES, 'type'),
89
- };
30
+ const WORD_STYLE = wordStyles({
31
+ control: CONTROL,
32
+ literal: LITERALS,
33
+ storage: STORAGE,
34
+ type: TYPES,
35
+ });
90
36
 
91
- const dartStringOpen = (source, i) => {
92
- let at = i;
37
+ const stringOpen = (source, i) => {
93
38
  const raw = source[i] === 'r';
94
- if (raw) at = i + 1;
39
+ const at = raw ? i + 1 : i;
95
40
  let quote = '';
96
- const triple = SQUOTE.repeat(3);
97
- const dtriple = '"'.repeat(3);
98
- if (source.startsWith(triple, at) || source.startsWith(dtriple, at)) {
41
+ if (source.startsWith(`'''`, at) || source.startsWith('"""', at)) {
99
42
  quote = source.slice(at, at + 3);
100
43
  } else if (QUOTES.includes(source[at] || '')) {
101
44
  quote = source[at];
102
45
  }
103
46
  if (!quote) return null;
104
- return { raw, quote, body: at + quote.length };
105
- };
106
-
107
- const dartIdentStyle = (ctx) => {
108
- const known = WORD_STYLE[ctx.word];
109
- if (known) return known;
110
- if (EXPECT_STYLE[ctx.expectName]) return EXPECT_STYLE[ctx.expectName];
111
- if (styleConstant(ctx)) return 'constant';
112
- if (stylePascal(ctx)) return 'className';
113
- if (styleCall(ctx)) return 'function';
114
- return 'variable';
115
- };
116
-
117
- const highlightDartString = (source, i, inner) => {
118
- const open = dartStringOpen(source, i);
119
- const n = source.length;
120
- const parts = [];
121
- emit(parts, 'string', source.slice(i, open.body));
122
- let j = open.body;
123
- let buf = '';
124
- const flush = () => {
125
- if (!buf) return;
126
- emit(parts, 'string', buf);
127
- buf = '';
128
- };
129
- const quote = open.quote;
130
- while (j < n) {
131
- if (!open.raw && source[j] === '\\') {
132
- flush();
133
- emit(parts, 'escape', source.slice(j, j + 2));
134
- j += 2;
135
- continue;
136
- }
137
- if (source.startsWith(quote, j)) {
138
- flush();
139
- emit(parts, 'string', quote);
140
- j += quote.length;
141
- break;
142
- }
143
- if (!open.raw && source[j] === '$') {
144
- const next = source[j + 1];
145
- if (next === '{') {
146
- flush();
147
- emit(parts, 'interpolation', '${');
148
- j += 2;
149
- const interp = readTemplateInterp(source, j);
150
- for (const t of inner(interp.expr)) parts.push(t);
151
- j = interp.end;
152
- if (source[j] === '}') {
153
- emit(parts, 'interpolation', '}');
154
- j += 1;
155
- }
156
- continue;
157
- }
158
- if (/[A-Za-z_$]/.test(next || '')) {
159
- flush();
160
- const ident = peekIdent(source, j + 1);
161
- emit(parts, 'interpolation', `$${ident}`);
162
- j += 1 + ident.length;
163
- continue;
164
- }
165
- }
166
- if (quote.length === 1 && source[j] === '\n') break;
167
- buf += source[j];
168
- j += 1;
169
- }
170
- flush();
171
- return { tokens: parts, end: j };
47
+ const multiline = quote.length === 3;
48
+ const kind = { style: 'string', quote, raw, multiline, dollarIdent: true };
49
+ return { body: at + quote.length, kind };
172
50
  };
173
51
 
174
52
  const highlight = (source) => {
175
- const inner = (expr) => highlight(expr);
176
53
  const out = [];
177
54
  let i = 0;
178
- const n = source.length;
179
55
  let expectName = null;
180
- while (i < n) {
56
+ const take = (style, end) => {
57
+ emit(out, style, source.slice(i, end));
58
+ i = end;
59
+ };
60
+ while (i < source.length) {
181
61
  const ch = source[i];
182
62
  const next = source[i + 1];
63
+ const open = stringOpen(source, i);
183
64
  if (ch === '/' && next === '/') {
184
- const { text, end } = readUntilNl(source, i);
185
- emit(out, 'comment', text);
186
- i = end;
187
- continue;
188
- }
189
- if (ch === '/' && next === '*') {
190
- const { text, end } = readBlockComment(source, i);
191
- emit(out, 'comment', text);
192
- i = end;
193
- continue;
194
- }
195
- if (dartStringOpen(source, i)) {
196
- const node = highlightDartString(source, i, inner);
197
- for (const t of node.tokens) out.push(t);
198
- i = node.end;
65
+ take('comment', lineEnd(source, i));
66
+ } else if (ch === '/' && next === '*') {
67
+ take('comment', blockCommentEnd(source, i));
68
+ } else if (open) {
69
+ const node = highlightInterpolated(
70
+ source,
71
+ i,
72
+ open.body,
73
+ open.kind,
74
+ highlight,
75
+ );
76
+ i = appendNode(out, node);
199
77
  expectName = null;
200
- continue;
201
- }
202
- if (/[0-9]/.test(ch) || (ch === '.' && /[0-9]/.test(next || ''))) {
203
- const { text, end } = readJsNumber(source, i);
204
- emit(out, 'number', text);
205
- i = end;
206
- continue;
207
- }
208
- if (ch === '@' || ch === '#') {
209
- let j = i + 1;
210
- while (j < n && /[A-Za-z0-9_$]/.test(source[j])) j += 1;
211
- const kind = ch === '@' ? 'decorator' : 'constant';
212
- emit(out, kind, source.slice(i, j));
213
- i = j;
78
+ } else if (/[0-9]/.test(ch) || (ch === '.' && /[0-9]/.test(next || ''))) {
79
+ take('number', numberEnd(source, i));
80
+ } else if (ch === '@' || ch === '#') {
81
+ const style = ch === '@' ? 'decorator' : 'constant';
82
+ take(style, scanWhile(source, i + 1, IDENT_CHAR));
214
83
  expectName = null;
215
- continue;
216
- }
217
- if (/[A-Za-z_$]/.test(ch)) {
218
- const word = peekIdent(source, i);
219
- const end = i + word.length;
220
- const k = skipWs(source, end);
221
- const after = source[k];
222
- const ctx = { word, after, expectName };
223
- emit(out, dartIdentStyle(ctx), word);
224
- if (EXPECT_STYLE[expectName]) expectName = null;
225
- else expectName = NEXT_EXPECT[word] ?? null;
226
- i = end;
227
- continue;
228
- }
229
- if (/[=<>!+\-*/%&|^~?:]/.test(ch)) {
230
- const { text, end } = readJsOperator(source, i);
231
- emit(out, 'operator', text);
232
- i = end;
84
+ } else if (IDENT_START.test(ch)) {
85
+ const end = scanWhile(source, i + 1, IDENT_CHAR);
86
+ const word = source.slice(i, end);
87
+ const after = source[scanWhile(source, end, /\s/)];
88
+ const style =
89
+ WORD_STYLE[word] ||
90
+ EXPECT_STYLE[expectName] ||
91
+ nameStyle(word, after) ||
92
+ 'variable';
93
+ const nextExpect = NEXT_EXPECT[word] ?? null;
94
+ take(style, end);
95
+ expectName = EXPECT_STYLE[expectName] ? null : nextExpect;
96
+ } else if (/[=<>!+\-*/%&|^~?:]/.test(ch)) {
97
+ take('operator', operatorEnd(source, i));
233
98
  expectName = null;
234
- continue;
235
- }
236
- if (/[{}()[\];,.]/.test(ch)) {
237
- emit(out, 'punct', ch);
238
- i += 1;
239
- continue;
99
+ } else {
100
+ take(/[{}()[\];,.]/.test(ch) ? 'punct' : 'plain', i + 1);
240
101
  }
241
- emit(out, 'plain', ch);
242
- i += 1;
243
102
  }
244
103
  return out;
245
104
  };
@@ -3,30 +3,30 @@
3
3
  const core = require('./core.js');
4
4
  const { emit, highlightLines } = core;
5
5
 
6
+ const ASSIGN_RE = /^(\s*)([A-Za-z_][A-Za-z0-9_.-]*)(\s*=\s*)(.*)$/;
7
+
6
8
  const highlightDotLine = (line) => {
7
- if (!line) return [];
8
9
  const out = [];
9
- const t = line.trimStart();
10
- if (t.startsWith('#') || t.startsWith(';')) {
10
+ const trimmed = line.trimStart();
11
+ if (trimmed.startsWith('#') || trimmed.startsWith(';')) {
11
12
  emit(out, 'comment', line);
12
13
  return out;
13
14
  }
14
- if (t.startsWith('!')) {
15
- const lead = line.slice(0, line.length - t.length);
16
- emit(out, 'plain', lead);
15
+ if (trimmed.startsWith('!')) {
16
+ emit(out, 'plain', line.slice(0, line.length - trimmed.length));
17
17
  emit(out, 'operator', '!');
18
- emit(out, 'plain', t.slice(1));
18
+ emit(out, 'plain', trimmed.slice(1));
19
19
  return out;
20
20
  }
21
- const eq = line.match(/^(\s*)([A-Za-z_][A-Za-z0-9_.-]*)(\s*=\s*)(.*)$/);
22
- if (eq) {
23
- emit(out, 'plain', eq[1]);
24
- emit(out, 'variable', eq[2]);
25
- emit(out, 'operator', eq[3]);
26
- emit(out, 'string', eq[4]);
21
+ const assign = ASSIGN_RE.exec(line);
22
+ if (!assign) {
23
+ emit(out, 'plain', line);
27
24
  return out;
28
25
  }
29
- emit(out, 'plain', line);
26
+ emit(out, 'plain', assign[1]);
27
+ emit(out, 'variable', assign[2]);
28
+ emit(out, 'operator', assign[3]);
29
+ emit(out, 'string', assign[4]);
30
30
  return out;
31
31
  };
32
32
 
@@ -1,85 +1,64 @@
1
1
  'use strict';
2
2
 
3
3
  const core = require('./core.js');
4
- const { SQUOTE, emit, skipQuoted } = core;
4
+ const { QUOTES, emit, scanWhile, quotedEnd } = core;
5
5
 
6
6
  const colorHtmlTag = (tag) => {
7
7
  const out = [];
8
8
  let i = 1;
9
- const n = tag.length;
9
+ const take = (style, end) => {
10
+ emit(out, style, tag.slice(i, end));
11
+ i = end;
12
+ };
10
13
  emit(out, 'punct', '<');
11
- if (tag[i] === '/') {
12
- emit(out, 'punct', '/');
13
- i += 1;
14
- }
15
- let j = i;
16
- while (j < n && /[A-Za-z0-9:-]/.test(tag[j])) j += 1;
17
- emit(out, 'tag', tag.slice(i, j));
18
- i = j;
19
- while (i < n) {
20
- if (tag[i] === '>' || (tag[i] === '/' && tag[i + 1] === '>')) {
21
- emit(out, 'punct', tag.slice(i));
22
- break;
23
- }
24
- if (/\s/.test(tag[i])) {
25
- emit(out, 'plain', tag[i]);
26
- i += 1;
27
- continue;
28
- }
29
- if (tag[i] === '"' || tag[i] === SQUOTE) {
30
- const q = tag[i];
31
- let k = i + 1;
32
- while (k < n && tag[k] !== q) k += 1;
33
- k = Math.min(k + 1, n);
34
- emit(out, 'string', tag.slice(i, k));
35
- i = k;
36
- continue;
14
+ if (tag[i] === '/') take('punct', i + 1);
15
+ take('tag', scanWhile(tag, i, /[A-Za-z0-9:-]/));
16
+ while (i < tag.length) {
17
+ const ch = tag[i];
18
+ if (ch === '>' || (ch === '/' && tag[i + 1] === '>')) {
19
+ take('punct', tag.length);
20
+ } else if (/\s/.test(ch)) {
21
+ take('plain', i + 1);
22
+ } else if (QUOTES.includes(ch)) {
23
+ const close = tag.indexOf(ch, i + 1);
24
+ take('string', close < 0 ? tag.length : close + 1);
25
+ } else if (/[A-Za-z_:]/.test(ch)) {
26
+ take('attr', scanWhile(tag, i + 1, /[A-Za-z0-9_:.-]/));
27
+ } else {
28
+ take('punct', i + 1);
37
29
  }
38
- if (/[A-Za-z_:]/.test(tag[i])) {
39
- let k = i + 1;
40
- while (k < n && /[A-Za-z0-9_:.-]/.test(tag[k])) k += 1;
41
- emit(out, 'attr', tag.slice(i, k));
42
- i = k;
43
- continue;
44
- }
45
- emit(out, 'punct', tag[i]);
46
- i += 1;
47
30
  }
48
31
  return out;
49
32
  };
50
33
 
34
+ const tagEnd = (source, i) => {
35
+ const n = source.length;
36
+ let j = i + 1;
37
+ while (j < n && source[j] !== '>') {
38
+ j = QUOTES.includes(source[j]) ? quotedEnd(source, j) : j + 1;
39
+ }
40
+ return j < n ? j + 1 : j;
41
+ };
42
+
51
43
  const highlight = (source) => {
52
44
  const out = [];
53
- let i = 0;
54
45
  const n = source.length;
46
+ let i = 0;
55
47
  while (i < n) {
48
+ let end;
56
49
  if (source.startsWith('<!--', i)) {
57
- let j = source.indexOf('-->', i + 4);
58
- j = j === -1 ? n : j + 3;
59
- emit(out, 'comment', source.slice(i, j));
60
- i = j;
61
- continue;
62
- }
63
- if (source[i] === '<') {
64
- let j = i + 1;
65
- while (j < n && source[j] !== '>') {
66
- if (source[j] === '"' || source[j] === SQUOTE) {
67
- j = skipQuoted(source, j);
68
- continue;
69
- }
70
- j += 1;
71
- }
72
- if (j < n) j += 1;
73
- for (const t of colorHtmlTag(source.slice(i, j))) out.push(t);
74
- i = j;
75
- continue;
76
- }
77
- let j = i + 1;
78
- while (j < n && source[j] !== '<' && !source.startsWith('<!--', j)) {
79
- j += 1;
50
+ const close = source.indexOf('-->', i + 4);
51
+ end = close < 0 ? n : close + 3;
52
+ emit(out, 'comment', source.slice(i, end));
53
+ } else if (source[i] === '<') {
54
+ end = tagEnd(source, i);
55
+ for (const token of colorHtmlTag(source.slice(i, end))) out.push(token);
56
+ } else {
57
+ const next = source.indexOf('<', i + 1);
58
+ end = next < 0 ? n : next;
59
+ emit(out, 'plain', source.slice(i, end));
80
60
  }
81
- emit(out, 'plain', source.slice(i, j));
82
- i = j;
61
+ i = end;
83
62
  }
84
63
  return out;
85
64
  };