@lexical/code 0.8.0 → 0.9.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/CodeHighlightNode.d.ts +4 -1
- package/CodeHighlighter.d.ts +2 -0
- package/CodeNode.d.ts +2 -0
- package/LexicalCode.dev.js +241 -60
- package/LexicalCode.prod.js +2 -2
- package/package.json +3 -3
package/CodeHighlightNode.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import type { EditorConfig, LexicalNode, NodeKey, SerializedTextNode, Spread } from 'lexical';
|
|
9
9
|
import 'prismjs/components/prism-clike';
|
|
10
10
|
import 'prismjs/components/prism-javascript';
|
|
11
11
|
import 'prismjs/components/prism-markup';
|
|
@@ -18,6 +18,9 @@ import 'prismjs/components/prism-python';
|
|
|
18
18
|
import 'prismjs/components/prism-rust';
|
|
19
19
|
import 'prismjs/components/prism-swift';
|
|
20
20
|
import 'prismjs/components/prism-typescript';
|
|
21
|
+
import 'prismjs/components/prism-java';
|
|
22
|
+
import 'prismjs/components/prism-cpp';
|
|
23
|
+
import { ElementNode, TextNode } from 'lexical';
|
|
21
24
|
export declare const DEFAULT_CODE_LANGUAGE = "javascript";
|
|
22
25
|
declare type SerializedCodeHighlightNode = Spread<{
|
|
23
26
|
highlightType: string | null | undefined;
|
package/CodeHighlighter.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ import 'prismjs/components/prism-python';
|
|
|
18
18
|
import 'prismjs/components/prism-rust';
|
|
19
19
|
import 'prismjs/components/prism-swift';
|
|
20
20
|
import 'prismjs/components/prism-typescript';
|
|
21
|
+
import 'prismjs/components/prism-java';
|
|
22
|
+
import 'prismjs/components/prism-cpp';
|
|
21
23
|
import { TextNode } from 'lexical';
|
|
22
24
|
declare type TokenContent = string | Token | (string | Token)[];
|
|
23
25
|
export interface Token {
|
package/CodeNode.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ import 'prismjs/components/prism-python';
|
|
|
19
19
|
import 'prismjs/components/prism-rust';
|
|
20
20
|
import 'prismjs/components/prism-swift';
|
|
21
21
|
import 'prismjs/components/prism-typescript';
|
|
22
|
+
import 'prismjs/components/prism-java';
|
|
23
|
+
import 'prismjs/components/prism-cpp';
|
|
22
24
|
import { ElementNode } from 'lexical';
|
|
23
25
|
export declare type SerializedCodeNode = Spread<{
|
|
24
26
|
language: string | null | undefined;
|
package/LexicalCode.dev.js
CHANGED
|
@@ -19,6 +19,8 @@ require('prismjs/components/prism-python');
|
|
|
19
19
|
require('prismjs/components/prism-rust');
|
|
20
20
|
require('prismjs/components/prism-swift');
|
|
21
21
|
require('prismjs/components/prism-typescript');
|
|
22
|
+
require('prismjs/components/prism-java');
|
|
23
|
+
require('prismjs/components/prism-cpp');
|
|
22
24
|
var utils = require('@lexical/utils');
|
|
23
25
|
var lexical = require('lexical');
|
|
24
26
|
|
|
@@ -29,50 +31,60 @@ var lexical = require('lexical');
|
|
|
29
31
|
* LICENSE file in the root directory of this source tree.
|
|
30
32
|
*
|
|
31
33
|
*/
|
|
34
|
+
|
|
32
35
|
const mapToPrismLanguage = language => {
|
|
33
36
|
// eslint-disable-next-line no-prototype-builtins
|
|
34
37
|
return language != null && Prism.languages.hasOwnProperty(language) ? language : undefined;
|
|
35
38
|
};
|
|
39
|
+
|
|
36
40
|
function hasChildDOMNodeTag(node, tagName) {
|
|
37
41
|
for (const child of node.childNodes) {
|
|
38
|
-
if (child
|
|
42
|
+
if (utils.isHTMLElement(child) && child.tagName === tagName) {
|
|
39
43
|
return true;
|
|
40
44
|
}
|
|
45
|
+
|
|
41
46
|
hasChildDOMNodeTag(child, tagName);
|
|
42
47
|
}
|
|
48
|
+
|
|
43
49
|
return false;
|
|
44
50
|
}
|
|
45
|
-
const LANGUAGE_DATA_ATTRIBUTE = 'data-highlight-language';
|
|
46
51
|
|
|
52
|
+
const LANGUAGE_DATA_ATTRIBUTE = 'data-highlight-language';
|
|
47
53
|
/** @noInheritDoc */
|
|
54
|
+
|
|
48
55
|
class CodeNode extends lexical.ElementNode {
|
|
49
56
|
/** @internal */
|
|
50
|
-
|
|
51
57
|
static getType() {
|
|
52
58
|
return 'code';
|
|
53
59
|
}
|
|
60
|
+
|
|
54
61
|
static clone(node) {
|
|
55
62
|
return new CodeNode(node.__language, node.__key);
|
|
56
63
|
}
|
|
64
|
+
|
|
57
65
|
constructor(language, key) {
|
|
58
66
|
super(key);
|
|
59
67
|
this.__language = mapToPrismLanguage(language);
|
|
60
|
-
}
|
|
68
|
+
} // View
|
|
69
|
+
|
|
61
70
|
|
|
62
|
-
// View
|
|
63
71
|
createDOM(config) {
|
|
64
72
|
const element = document.createElement('code');
|
|
65
73
|
utils.addClassNamesToElement(element, config.theme.code);
|
|
66
74
|
element.setAttribute('spellcheck', 'false');
|
|
67
75
|
const language = this.getLanguage();
|
|
76
|
+
|
|
68
77
|
if (language) {
|
|
69
78
|
element.setAttribute(LANGUAGE_DATA_ATTRIBUTE, language);
|
|
70
79
|
}
|
|
80
|
+
|
|
71
81
|
return element;
|
|
72
82
|
}
|
|
83
|
+
|
|
73
84
|
updateDOM(prevNode, dom, config) {
|
|
74
85
|
const language = this.__language;
|
|
75
86
|
const prevLanguage = prevNode.__language;
|
|
87
|
+
|
|
76
88
|
if (language) {
|
|
77
89
|
if (language !== prevLanguage) {
|
|
78
90
|
dom.setAttribute(LANGUAGE_DATA_ATTRIBUTE, language);
|
|
@@ -80,8 +92,10 @@ class CodeNode extends lexical.ElementNode {
|
|
|
80
92
|
} else if (prevLanguage) {
|
|
81
93
|
dom.removeAttribute(LANGUAGE_DATA_ATTRIBUTE);
|
|
82
94
|
}
|
|
95
|
+
|
|
83
96
|
return false;
|
|
84
97
|
}
|
|
98
|
+
|
|
85
99
|
static importDOM() {
|
|
86
100
|
return {
|
|
87
101
|
// Typically <pre> is used for code blocks, and <code> for inline code styles
|
|
@@ -103,26 +117,29 @@ class CodeNode extends lexical.ElementNode {
|
|
|
103
117
|
priority: 0
|
|
104
118
|
}),
|
|
105
119
|
table: node => {
|
|
106
|
-
const table = node;
|
|
107
|
-
|
|
120
|
+
const table = node; // domNode is a <table> since we matched it by nodeName
|
|
121
|
+
|
|
108
122
|
if (isGitHubCodeTable(table)) {
|
|
109
123
|
return {
|
|
110
124
|
conversion: convertTableElement,
|
|
111
125
|
priority: 3
|
|
112
126
|
};
|
|
113
127
|
}
|
|
128
|
+
|
|
114
129
|
return null;
|
|
115
130
|
},
|
|
116
131
|
td: node => {
|
|
117
132
|
// element is a <td> since we matched it by nodeName
|
|
118
133
|
const td = node;
|
|
119
134
|
const table = td.closest('table');
|
|
135
|
+
|
|
120
136
|
if (isGitHubCodeCell(td)) {
|
|
121
137
|
return {
|
|
122
138
|
conversion: convertTableCellElement,
|
|
123
139
|
priority: 3
|
|
124
140
|
};
|
|
125
141
|
}
|
|
142
|
+
|
|
126
143
|
if (table && isGitHubCodeTable(table)) {
|
|
127
144
|
// Return a no-op if it's a table cell in a code table, but not a code line.
|
|
128
145
|
// Otherwise it'll fall back to the T
|
|
@@ -131,22 +148,26 @@ class CodeNode extends lexical.ElementNode {
|
|
|
131
148
|
priority: 3
|
|
132
149
|
};
|
|
133
150
|
}
|
|
151
|
+
|
|
134
152
|
return null;
|
|
135
153
|
},
|
|
136
154
|
tr: node => {
|
|
137
155
|
// element is a <tr> since we matched it by nodeName
|
|
138
156
|
const tr = node;
|
|
139
157
|
const table = tr.closest('table');
|
|
158
|
+
|
|
140
159
|
if (table && isGitHubCodeTable(table)) {
|
|
141
160
|
return {
|
|
142
161
|
conversion: convertCodeNoop,
|
|
143
162
|
priority: 3
|
|
144
163
|
};
|
|
145
164
|
}
|
|
165
|
+
|
|
146
166
|
return null;
|
|
147
167
|
}
|
|
148
168
|
};
|
|
149
169
|
}
|
|
170
|
+
|
|
150
171
|
static importJSON(serializedNode) {
|
|
151
172
|
const node = $createCodeNode(serializedNode.language);
|
|
152
173
|
node.setFormat(serializedNode.format);
|
|
@@ -154,38 +175,42 @@ class CodeNode extends lexical.ElementNode {
|
|
|
154
175
|
node.setDirection(serializedNode.direction);
|
|
155
176
|
return node;
|
|
156
177
|
}
|
|
178
|
+
|
|
157
179
|
exportJSON() {
|
|
158
|
-
return {
|
|
159
|
-
...super.exportJSON(),
|
|
180
|
+
return { ...super.exportJSON(),
|
|
160
181
|
language: this.getLanguage(),
|
|
161
182
|
type: 'code',
|
|
162
183
|
version: 1
|
|
163
184
|
};
|
|
164
|
-
}
|
|
185
|
+
} // Mutation
|
|
186
|
+
|
|
165
187
|
|
|
166
|
-
// Mutation
|
|
167
188
|
insertNewAfter(selection, restoreSelection = true) {
|
|
168
189
|
const children = this.getChildren();
|
|
169
190
|
const childrenLength = children.length;
|
|
191
|
+
|
|
170
192
|
if (childrenLength >= 2 && children[childrenLength - 1].getTextContent() === '\n' && children[childrenLength - 2].getTextContent() === '\n' && selection.isCollapsed() && selection.anchor.key === this.__key && selection.anchor.offset === childrenLength) {
|
|
171
193
|
children[childrenLength - 1].remove();
|
|
172
194
|
children[childrenLength - 2].remove();
|
|
173
195
|
const newElement = lexical.$createParagraphNode();
|
|
174
196
|
this.insertAfter(newElement, restoreSelection);
|
|
175
197
|
return newElement;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// If the selection is within the codeblock, find all leading tabs and
|
|
198
|
+
} // If the selection is within the codeblock, find all leading tabs and
|
|
179
199
|
// spaces of the current line. Create a new line that has all those
|
|
180
200
|
// tabs and spaces, such that leading indentation is preserved.
|
|
201
|
+
|
|
202
|
+
|
|
181
203
|
const anchor = selection.anchor.getNode();
|
|
182
204
|
const firstNode = getFirstCodeHighlightNodeOfLine(anchor);
|
|
205
|
+
|
|
183
206
|
if (firstNode != null) {
|
|
184
207
|
let leadingWhitespace = 0;
|
|
185
208
|
const firstNodeText = firstNode.getTextContent();
|
|
209
|
+
|
|
186
210
|
while (leadingWhitespace < firstNodeText.length && /[\t ]/.test(firstNodeText[leadingWhitespace])) {
|
|
187
211
|
leadingWhitespace += 1;
|
|
188
212
|
}
|
|
213
|
+
|
|
189
214
|
if (leadingWhitespace > 0) {
|
|
190
215
|
const whitespace = firstNodeText.substring(0, leadingWhitespace);
|
|
191
216
|
const indentedChild = $createCodeHighlightNode(whitespace);
|
|
@@ -195,18 +220,24 @@ class CodeNode extends lexical.ElementNode {
|
|
|
195
220
|
return indentedChild;
|
|
196
221
|
}
|
|
197
222
|
}
|
|
223
|
+
|
|
198
224
|
return null;
|
|
199
225
|
}
|
|
226
|
+
|
|
200
227
|
canInsertTab() {
|
|
201
228
|
const selection = lexical.$getSelection();
|
|
229
|
+
|
|
202
230
|
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
203
231
|
return false;
|
|
204
232
|
}
|
|
233
|
+
|
|
205
234
|
return true;
|
|
206
235
|
}
|
|
236
|
+
|
|
207
237
|
canIndent() {
|
|
208
238
|
return false;
|
|
209
239
|
}
|
|
240
|
+
|
|
210
241
|
collapseAtStart() {
|
|
211
242
|
const paragraph = lexical.$createParagraphNode();
|
|
212
243
|
const children = this.getChildren();
|
|
@@ -214,13 +245,16 @@ class CodeNode extends lexical.ElementNode {
|
|
|
214
245
|
this.replace(paragraph);
|
|
215
246
|
return true;
|
|
216
247
|
}
|
|
248
|
+
|
|
217
249
|
setLanguage(language) {
|
|
218
250
|
const writable = this.getWritable();
|
|
219
251
|
writable.__language = mapToPrismLanguage(language);
|
|
220
252
|
}
|
|
253
|
+
|
|
221
254
|
getLanguage() {
|
|
222
255
|
return this.getLatest().__language;
|
|
223
256
|
}
|
|
257
|
+
|
|
224
258
|
}
|
|
225
259
|
function $createCodeNode(language) {
|
|
226
260
|
return lexical.$applyNodeReplacement(new CodeNode(language));
|
|
@@ -228,44 +262,53 @@ function $createCodeNode(language) {
|
|
|
228
262
|
function $isCodeNode(node) {
|
|
229
263
|
return node instanceof CodeNode;
|
|
230
264
|
}
|
|
265
|
+
|
|
231
266
|
function convertPreElement(domNode) {
|
|
232
267
|
return {
|
|
233
268
|
node: $createCodeNode(),
|
|
234
269
|
preformatted: true
|
|
235
270
|
};
|
|
236
271
|
}
|
|
272
|
+
|
|
237
273
|
function convertDivElement(domNode) {
|
|
238
274
|
// domNode is a <div> since we matched it by nodeName
|
|
239
275
|
const div = domNode;
|
|
240
276
|
const isCode = isCodeElement(div);
|
|
277
|
+
|
|
241
278
|
if (!isCode && !isCodeChildElement(div)) {
|
|
242
279
|
return {
|
|
243
280
|
node: null
|
|
244
281
|
};
|
|
245
282
|
}
|
|
283
|
+
|
|
246
284
|
return {
|
|
247
285
|
after: childLexicalNodes => {
|
|
248
286
|
const domParent = domNode.parentNode;
|
|
287
|
+
|
|
249
288
|
if (domParent != null && domNode !== domParent.lastChild) {
|
|
250
289
|
childLexicalNodes.push(lexical.$createLineBreakNode());
|
|
251
290
|
}
|
|
291
|
+
|
|
252
292
|
return childLexicalNodes;
|
|
253
293
|
},
|
|
254
294
|
node: isCode ? $createCodeNode() : null,
|
|
255
295
|
preformatted: isCode
|
|
256
296
|
};
|
|
257
297
|
}
|
|
298
|
+
|
|
258
299
|
function convertTableElement() {
|
|
259
300
|
return {
|
|
260
301
|
node: $createCodeNode(),
|
|
261
302
|
preformatted: true
|
|
262
303
|
};
|
|
263
304
|
}
|
|
305
|
+
|
|
264
306
|
function convertCodeNoop() {
|
|
265
307
|
return {
|
|
266
308
|
node: null
|
|
267
309
|
};
|
|
268
310
|
}
|
|
311
|
+
|
|
269
312
|
function convertTableCellElement(domNode) {
|
|
270
313
|
// domNode is a <td> since we matched it by nodeName
|
|
271
314
|
const cell = domNode;
|
|
@@ -275,27 +318,35 @@ function convertTableCellElement(domNode) {
|
|
|
275
318
|
// Append newline between code lines
|
|
276
319
|
childLexicalNodes.push(lexical.$createLineBreakNode());
|
|
277
320
|
}
|
|
321
|
+
|
|
278
322
|
return childLexicalNodes;
|
|
279
323
|
},
|
|
280
324
|
node: null
|
|
281
325
|
};
|
|
282
326
|
}
|
|
327
|
+
|
|
283
328
|
function isCodeElement(div) {
|
|
284
329
|
return div.style.fontFamily.match('monospace') !== null;
|
|
285
330
|
}
|
|
331
|
+
|
|
286
332
|
function isCodeChildElement(node) {
|
|
287
333
|
let parent = node.parentElement;
|
|
334
|
+
|
|
288
335
|
while (parent !== null) {
|
|
289
336
|
if (isCodeElement(parent)) {
|
|
290
337
|
return true;
|
|
291
338
|
}
|
|
339
|
+
|
|
292
340
|
parent = parent.parentElement;
|
|
293
341
|
}
|
|
342
|
+
|
|
294
343
|
return false;
|
|
295
344
|
}
|
|
345
|
+
|
|
296
346
|
function isGitHubCodeCell(cell) {
|
|
297
347
|
return cell.classList.contains('js-file-line');
|
|
298
348
|
}
|
|
349
|
+
|
|
299
350
|
function isGitHubCodeTable(table) {
|
|
300
351
|
return table.classList.contains('js-file-line-container');
|
|
301
352
|
}
|
|
@@ -311,8 +362,10 @@ const DEFAULT_CODE_LANGUAGE = 'javascript';
|
|
|
311
362
|
const CODE_LANGUAGE_FRIENDLY_NAME_MAP = {
|
|
312
363
|
c: 'C',
|
|
313
364
|
clike: 'C-like',
|
|
365
|
+
cpp: 'C++',
|
|
314
366
|
css: 'CSS',
|
|
315
367
|
html: 'HTML',
|
|
368
|
+
java: 'Java',
|
|
316
369
|
js: 'JavaScript',
|
|
317
370
|
markdown: 'Markdown',
|
|
318
371
|
objc: 'Objective-C',
|
|
@@ -325,6 +378,8 @@ const CODE_LANGUAGE_FRIENDLY_NAME_MAP = {
|
|
|
325
378
|
xml: 'XML'
|
|
326
379
|
};
|
|
327
380
|
const CODE_LANGUAGE_MAP = {
|
|
381
|
+
cpp: 'cpp',
|
|
382
|
+
java: 'java',
|
|
328
383
|
javascript: 'js',
|
|
329
384
|
md: 'markdown',
|
|
330
385
|
plaintext: 'plain',
|
|
@@ -337,52 +392,60 @@ function normalizeCodeLang(lang) {
|
|
|
337
392
|
}
|
|
338
393
|
function getLanguageFriendlyName(lang) {
|
|
339
394
|
const _lang = normalizeCodeLang(lang);
|
|
395
|
+
|
|
340
396
|
return CODE_LANGUAGE_FRIENDLY_NAME_MAP[_lang] || _lang;
|
|
341
397
|
}
|
|
342
398
|
const getDefaultCodeLanguage = () => DEFAULT_CODE_LANGUAGE;
|
|
343
|
-
const getCodeLanguages = () => Object.keys(Prism.languages).filter(
|
|
344
|
-
// Prism has several language helpers mixed into languages object
|
|
399
|
+
const getCodeLanguages = () => Object.keys(Prism.languages).filter( // Prism has several language helpers mixed into languages object
|
|
345
400
|
// so filtering them out here to get langs list
|
|
346
401
|
language => typeof Prism.languages[language] !== 'function').sort();
|
|
347
|
-
|
|
348
402
|
/** @noInheritDoc */
|
|
403
|
+
|
|
349
404
|
class CodeHighlightNode extends lexical.TextNode {
|
|
350
405
|
/** @internal */
|
|
351
|
-
|
|
352
406
|
constructor(text, highlightType, key) {
|
|
353
407
|
super(text, key);
|
|
354
408
|
this.__highlightType = highlightType;
|
|
355
409
|
}
|
|
410
|
+
|
|
356
411
|
static getType() {
|
|
357
412
|
return 'code-highlight';
|
|
358
413
|
}
|
|
414
|
+
|
|
359
415
|
static clone(node) {
|
|
360
416
|
return new CodeHighlightNode(node.__text, node.__highlightType || undefined, node.__key);
|
|
361
417
|
}
|
|
418
|
+
|
|
362
419
|
getHighlightType() {
|
|
363
420
|
const self = this.getLatest();
|
|
364
421
|
return self.__highlightType;
|
|
365
422
|
}
|
|
423
|
+
|
|
366
424
|
createDOM(config) {
|
|
367
425
|
const element = super.createDOM(config);
|
|
368
426
|
const className = getHighlightThemeClass(config.theme, this.__highlightType);
|
|
369
427
|
utils.addClassNamesToElement(element, className);
|
|
370
428
|
return element;
|
|
371
429
|
}
|
|
430
|
+
|
|
372
431
|
updateDOM(prevNode, dom, config) {
|
|
373
432
|
const update = super.updateDOM(prevNode, dom, config);
|
|
374
433
|
const prevClassName = getHighlightThemeClass(config.theme, prevNode.__highlightType);
|
|
375
434
|
const nextClassName = getHighlightThemeClass(config.theme, this.__highlightType);
|
|
435
|
+
|
|
376
436
|
if (prevClassName !== nextClassName) {
|
|
377
437
|
if (prevClassName) {
|
|
378
438
|
utils.removeClassNamesFromElement(dom, prevClassName);
|
|
379
439
|
}
|
|
440
|
+
|
|
380
441
|
if (nextClassName) {
|
|
381
442
|
utils.addClassNamesToElement(dom, nextClassName);
|
|
382
443
|
}
|
|
383
444
|
}
|
|
445
|
+
|
|
384
446
|
return update;
|
|
385
447
|
}
|
|
448
|
+
|
|
386
449
|
static importJSON(serializedNode) {
|
|
387
450
|
const node = $createCodeHighlightNode(serializedNode.text, serializedNode.highlightType);
|
|
388
451
|
node.setFormat(serializedNode.format);
|
|
@@ -391,29 +454,34 @@ class CodeHighlightNode extends lexical.TextNode {
|
|
|
391
454
|
node.setStyle(serializedNode.style);
|
|
392
455
|
return node;
|
|
393
456
|
}
|
|
457
|
+
|
|
394
458
|
exportJSON() {
|
|
395
|
-
return {
|
|
396
|
-
...super.exportJSON(),
|
|
459
|
+
return { ...super.exportJSON(),
|
|
397
460
|
highlightType: this.getHighlightType(),
|
|
398
461
|
type: 'code-highlight',
|
|
399
462
|
version: 1
|
|
400
463
|
};
|
|
401
|
-
}
|
|
464
|
+
} // Prevent formatting (bold, underline, etc)
|
|
465
|
+
|
|
402
466
|
|
|
403
|
-
// Prevent formatting (bold, underline, etc)
|
|
404
467
|
setFormat(format) {
|
|
405
468
|
return this;
|
|
406
469
|
}
|
|
470
|
+
|
|
407
471
|
isParentRequired() {
|
|
408
472
|
return true;
|
|
409
473
|
}
|
|
474
|
+
|
|
410
475
|
createParentElementNode() {
|
|
411
476
|
return $createCodeNode();
|
|
412
477
|
}
|
|
478
|
+
|
|
413
479
|
}
|
|
480
|
+
|
|
414
481
|
function getHighlightThemeClass(theme, highlightType) {
|
|
415
482
|
return highlightType && theme && theme.codeHighlight && theme.codeHighlight[highlightType];
|
|
416
483
|
}
|
|
484
|
+
|
|
417
485
|
function $createCodeHighlightNode(text, highlightType) {
|
|
418
486
|
return lexical.$applyNodeReplacement(new CodeHighlightNode(text, highlightType));
|
|
419
487
|
}
|
|
@@ -424,30 +492,38 @@ function getFirstCodeHighlightNodeOfLine(anchor) {
|
|
|
424
492
|
let currentNode = null;
|
|
425
493
|
const previousSiblings = anchor.getPreviousSiblings();
|
|
426
494
|
previousSiblings.push(anchor);
|
|
495
|
+
|
|
427
496
|
while (previousSiblings.length > 0) {
|
|
428
497
|
const node = previousSiblings.pop();
|
|
498
|
+
|
|
429
499
|
if ($isCodeHighlightNode(node)) {
|
|
430
500
|
currentNode = node;
|
|
431
501
|
}
|
|
502
|
+
|
|
432
503
|
if (lexical.$isLineBreakNode(node)) {
|
|
433
504
|
break;
|
|
434
505
|
}
|
|
435
506
|
}
|
|
507
|
+
|
|
436
508
|
return currentNode;
|
|
437
509
|
}
|
|
438
510
|
function getLastCodeHighlightNodeOfLine(anchor) {
|
|
439
511
|
let currentNode = null;
|
|
440
512
|
const nextSiblings = anchor.getNextSiblings();
|
|
441
513
|
nextSiblings.unshift(anchor);
|
|
514
|
+
|
|
442
515
|
while (nextSiblings.length > 0) {
|
|
443
516
|
const node = nextSiblings.shift();
|
|
517
|
+
|
|
444
518
|
if ($isCodeHighlightNode(node)) {
|
|
445
519
|
currentNode = node;
|
|
446
520
|
}
|
|
521
|
+
|
|
447
522
|
if (lexical.$isLineBreakNode(node)) {
|
|
448
523
|
break;
|
|
449
524
|
}
|
|
450
525
|
}
|
|
526
|
+
|
|
451
527
|
return currentNode;
|
|
452
528
|
}
|
|
453
529
|
|
|
@@ -460,19 +536,25 @@ function getLastCodeHighlightNodeOfLine(anchor) {
|
|
|
460
536
|
*/
|
|
461
537
|
const PrismTokenizer = {
|
|
462
538
|
defaultLanguage: DEFAULT_CODE_LANGUAGE,
|
|
539
|
+
|
|
463
540
|
tokenize(code, language) {
|
|
464
541
|
return Prism.tokenize(code, Prism.languages[language || ''] || Prism.languages[this.defaultLanguage]);
|
|
465
542
|
}
|
|
543
|
+
|
|
466
544
|
};
|
|
545
|
+
|
|
467
546
|
function isSpaceOrTabChar(char) {
|
|
468
547
|
return char === ' ' || char === '\t';
|
|
469
548
|
}
|
|
549
|
+
|
|
470
550
|
function findFirstNotSpaceOrTabCharAtText(text, isForward) {
|
|
471
551
|
const length = text.length;
|
|
472
552
|
let offset = -1;
|
|
553
|
+
|
|
473
554
|
if (isForward) {
|
|
474
555
|
for (let i = 0; i < length; i++) {
|
|
475
556
|
const char = text[i];
|
|
557
|
+
|
|
476
558
|
if (!isSpaceOrTabChar(char)) {
|
|
477
559
|
offset = i;
|
|
478
560
|
break;
|
|
@@ -481,51 +563,64 @@ function findFirstNotSpaceOrTabCharAtText(text, isForward) {
|
|
|
481
563
|
} else {
|
|
482
564
|
for (let i = length - 1; i > -1; i--) {
|
|
483
565
|
const char = text[i];
|
|
566
|
+
|
|
484
567
|
if (!isSpaceOrTabChar(char)) {
|
|
485
568
|
offset = i;
|
|
486
569
|
break;
|
|
487
570
|
}
|
|
488
571
|
}
|
|
489
572
|
}
|
|
573
|
+
|
|
490
574
|
return offset;
|
|
491
575
|
}
|
|
576
|
+
|
|
492
577
|
function getStartOfCodeInLine(anchor) {
|
|
493
578
|
let currentNode = null;
|
|
494
579
|
let currentNodeOffset = -1;
|
|
495
580
|
const previousSiblings = anchor.getPreviousSiblings();
|
|
496
581
|
previousSiblings.push(anchor);
|
|
582
|
+
|
|
497
583
|
while (previousSiblings.length > 0) {
|
|
498
584
|
const node = previousSiblings.pop();
|
|
585
|
+
|
|
499
586
|
if ($isCodeHighlightNode(node)) {
|
|
500
587
|
const text = node.getTextContent();
|
|
501
588
|
const offset = findFirstNotSpaceOrTabCharAtText(text, true);
|
|
589
|
+
|
|
502
590
|
if (offset !== -1) {
|
|
503
591
|
currentNode = node;
|
|
504
592
|
currentNodeOffset = offset;
|
|
505
593
|
}
|
|
506
594
|
}
|
|
595
|
+
|
|
507
596
|
if (lexical.$isLineBreakNode(node)) {
|
|
508
597
|
break;
|
|
509
598
|
}
|
|
510
599
|
}
|
|
600
|
+
|
|
511
601
|
if (currentNode === null) {
|
|
512
602
|
const nextSiblings = anchor.getNextSiblings();
|
|
603
|
+
|
|
513
604
|
while (nextSiblings.length > 0) {
|
|
514
605
|
const node = nextSiblings.shift();
|
|
606
|
+
|
|
515
607
|
if ($isCodeHighlightNode(node)) {
|
|
516
608
|
const text = node.getTextContent();
|
|
517
609
|
const offset = findFirstNotSpaceOrTabCharAtText(text, true);
|
|
610
|
+
|
|
518
611
|
if (offset !== -1) {
|
|
519
612
|
currentNode = node;
|
|
520
613
|
currentNodeOffset = offset;
|
|
521
614
|
break;
|
|
522
615
|
}
|
|
523
616
|
}
|
|
617
|
+
|
|
524
618
|
if (lexical.$isLineBreakNode(node)) {
|
|
525
619
|
break;
|
|
526
620
|
}
|
|
527
621
|
}
|
|
528
622
|
}
|
|
623
|
+
|
|
529
624
|
return {
|
|
530
625
|
node: currentNode,
|
|
531
626
|
offset: currentNodeOffset
|
|
@@ -536,47 +631,59 @@ function getEndOfCodeInLine(anchor) {
|
|
|
536
631
|
let currentNodeOffset = -1;
|
|
537
632
|
const nextSiblings = anchor.getNextSiblings();
|
|
538
633
|
nextSiblings.unshift(anchor);
|
|
634
|
+
|
|
539
635
|
while (nextSiblings.length > 0) {
|
|
540
636
|
const node = nextSiblings.shift();
|
|
637
|
+
|
|
541
638
|
if ($isCodeHighlightNode(node)) {
|
|
542
639
|
const text = node.getTextContent();
|
|
543
640
|
const offset = findFirstNotSpaceOrTabCharAtText(text, false);
|
|
641
|
+
|
|
544
642
|
if (offset !== -1) {
|
|
545
643
|
currentNode = node;
|
|
546
644
|
currentNodeOffset = offset + 1;
|
|
547
645
|
}
|
|
548
646
|
}
|
|
647
|
+
|
|
549
648
|
if (lexical.$isLineBreakNode(node)) {
|
|
550
649
|
break;
|
|
551
650
|
}
|
|
552
651
|
}
|
|
652
|
+
|
|
553
653
|
if (currentNode === null) {
|
|
554
654
|
const previousSiblings = anchor.getPreviousSiblings();
|
|
655
|
+
|
|
555
656
|
while (previousSiblings.length > 0) {
|
|
556
657
|
const node = previousSiblings.pop();
|
|
658
|
+
|
|
557
659
|
if ($isCodeHighlightNode(node)) {
|
|
558
660
|
const text = node.getTextContent();
|
|
559
661
|
const offset = findFirstNotSpaceOrTabCharAtText(text, false);
|
|
662
|
+
|
|
560
663
|
if (offset !== -1) {
|
|
561
664
|
currentNode = node;
|
|
562
665
|
currentNodeOffset = offset + 1;
|
|
563
666
|
break;
|
|
564
667
|
}
|
|
565
668
|
}
|
|
669
|
+
|
|
566
670
|
if (lexical.$isLineBreakNode(node)) {
|
|
567
671
|
break;
|
|
568
672
|
}
|
|
569
673
|
}
|
|
570
674
|
}
|
|
675
|
+
|
|
571
676
|
return {
|
|
572
677
|
node: currentNode,
|
|
573
678
|
offset: currentNodeOffset
|
|
574
679
|
};
|
|
575
680
|
}
|
|
681
|
+
|
|
576
682
|
function textNodeTransform(node, editor, tokenizer) {
|
|
577
683
|
// Since CodeNode has flat children structure we only need to check
|
|
578
684
|
// if node's parent is a code node and run highlighting if so
|
|
579
685
|
const parentNode = node.getParent();
|
|
686
|
+
|
|
580
687
|
if ($isCodeNode(parentNode)) {
|
|
581
688
|
codeNodeTransform(parentNode, editor, tokenizer);
|
|
582
689
|
} else if ($isCodeHighlightNode(node)) {
|
|
@@ -585,31 +692,35 @@ function textNodeTransform(node, editor, tokenizer) {
|
|
|
585
692
|
node.replace(lexical.$createTextNode(node.__text));
|
|
586
693
|
}
|
|
587
694
|
}
|
|
695
|
+
|
|
588
696
|
function updateCodeGutter(node, editor) {
|
|
589
697
|
const codeElement = editor.getElementByKey(node.getKey());
|
|
698
|
+
|
|
590
699
|
if (codeElement === null) {
|
|
591
700
|
return;
|
|
592
701
|
}
|
|
702
|
+
|
|
593
703
|
const children = node.getChildren();
|
|
594
|
-
const childrenLength = children.length;
|
|
595
|
-
|
|
704
|
+
const childrenLength = children.length; // @ts-ignore: internal field
|
|
705
|
+
|
|
596
706
|
if (childrenLength === codeElement.__cachedChildrenLength) {
|
|
597
707
|
// Avoid updating the attribute if the children length hasn't changed.
|
|
598
708
|
return;
|
|
599
|
-
}
|
|
600
|
-
|
|
709
|
+
} // @ts-ignore:: internal field
|
|
710
|
+
|
|
711
|
+
|
|
601
712
|
codeElement.__cachedChildrenLength = childrenLength;
|
|
602
713
|
let gutter = '1';
|
|
603
714
|
let count = 1;
|
|
715
|
+
|
|
604
716
|
for (let i = 0; i < childrenLength; i++) {
|
|
605
717
|
if (lexical.$isLineBreakNode(children[i])) {
|
|
606
718
|
gutter += '\n' + ++count;
|
|
607
719
|
}
|
|
608
720
|
}
|
|
609
|
-
codeElement.setAttribute('data-gutter', gutter);
|
|
610
|
-
}
|
|
611
721
|
|
|
612
|
-
|
|
722
|
+
codeElement.setAttribute('data-gutter', gutter);
|
|
723
|
+
} // Using `skipTransforms` to prevent extra transforms since reformatting the code
|
|
613
724
|
// will not affect code block content itself.
|
|
614
725
|
//
|
|
615
726
|
// Using extra cache (`nodesCurrentlyHighlighting`) since both CodeNode and CodeHighlightNode
|
|
@@ -617,28 +728,33 @@ function updateCodeGutter(node, editor) {
|
|
|
617
728
|
// in both cases we'll rerun whole reformatting over CodeNode, which is redundant.
|
|
618
729
|
// Especially when pasting code into CodeBlock.
|
|
619
730
|
|
|
731
|
+
|
|
620
732
|
const nodesCurrentlyHighlighting = new Set();
|
|
733
|
+
|
|
621
734
|
function codeNodeTransform(node, editor, tokenizer) {
|
|
622
735
|
const nodeKey = node.getKey();
|
|
736
|
+
|
|
623
737
|
if (nodesCurrentlyHighlighting.has(nodeKey)) {
|
|
624
738
|
return;
|
|
625
739
|
}
|
|
626
|
-
nodesCurrentlyHighlighting.add(nodeKey);
|
|
627
740
|
|
|
628
|
-
// When new code block inserted it might not have language selected
|
|
741
|
+
nodesCurrentlyHighlighting.add(nodeKey); // When new code block inserted it might not have language selected
|
|
742
|
+
|
|
629
743
|
if (node.getLanguage() === undefined) {
|
|
630
744
|
node.setLanguage(tokenizer.defaultLanguage);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// Using nested update call to pass `skipTransforms` since we don't want
|
|
745
|
+
} // Using nested update call to pass `skipTransforms` since we don't want
|
|
634
746
|
// each individual codehighlight node to be transformed again as it's already
|
|
635
747
|
// in its final state
|
|
748
|
+
|
|
749
|
+
|
|
636
750
|
editor.update(() => {
|
|
637
751
|
updateAndRetainSelection(nodeKey, () => {
|
|
638
752
|
const currentNode = lexical.$getNodeByKey(nodeKey);
|
|
753
|
+
|
|
639
754
|
if (!$isCodeNode(currentNode) || !currentNode.isAttached()) {
|
|
640
755
|
return false;
|
|
641
756
|
}
|
|
757
|
+
|
|
642
758
|
const code = currentNode.getTextContent();
|
|
643
759
|
const tokens = tokenizer.tokenize(code, currentNode.getLanguage() || tokenizer.defaultLanguage);
|
|
644
760
|
const highlightNodes = getHighlightNodes(tokens);
|
|
@@ -648,10 +764,12 @@ function codeNodeTransform(node, editor, tokenizer) {
|
|
|
648
764
|
to,
|
|
649
765
|
nodesForReplacement
|
|
650
766
|
} = diffRange;
|
|
767
|
+
|
|
651
768
|
if (from !== to || nodesForReplacement.length) {
|
|
652
769
|
node.splice(from, to - from, nodesForReplacement);
|
|
653
770
|
return true;
|
|
654
771
|
}
|
|
772
|
+
|
|
655
773
|
return false;
|
|
656
774
|
});
|
|
657
775
|
}, {
|
|
@@ -661,16 +779,20 @@ function codeNodeTransform(node, editor, tokenizer) {
|
|
|
661
779
|
skipTransforms: true
|
|
662
780
|
});
|
|
663
781
|
}
|
|
782
|
+
|
|
664
783
|
function getHighlightNodes(tokens) {
|
|
665
784
|
const nodes = [];
|
|
666
785
|
tokens.forEach(token => {
|
|
667
786
|
if (typeof token === 'string') {
|
|
668
787
|
const partials = token.split('\n');
|
|
788
|
+
|
|
669
789
|
for (let i = 0; i < partials.length; i++) {
|
|
670
790
|
const text = partials[i];
|
|
791
|
+
|
|
671
792
|
if (text.length) {
|
|
672
793
|
nodes.push($createCodeHighlightNode(text));
|
|
673
794
|
}
|
|
795
|
+
|
|
674
796
|
if (i < partials.length - 1) {
|
|
675
797
|
nodes.push(lexical.$createLineBreakNode());
|
|
676
798
|
}
|
|
@@ -679,6 +801,7 @@ function getHighlightNodes(tokens) {
|
|
|
679
801
|
const {
|
|
680
802
|
content
|
|
681
803
|
} = token;
|
|
804
|
+
|
|
682
805
|
if (typeof content === 'string') {
|
|
683
806
|
nodes.push($createCodeHighlightNode(content, token.type));
|
|
684
807
|
} else if (Array.isArray(content) && content.length === 1 && typeof content[0] === 'string') {
|
|
@@ -689,83 +812,98 @@ function getHighlightNodes(tokens) {
|
|
|
689
812
|
}
|
|
690
813
|
});
|
|
691
814
|
return nodes;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// Wrapping update function into selection retainer, that tries to keep cursor at the same
|
|
815
|
+
} // Wrapping update function into selection retainer, that tries to keep cursor at the same
|
|
695
816
|
// position as before.
|
|
817
|
+
|
|
818
|
+
|
|
696
819
|
function updateAndRetainSelection(nodeKey, updateFn) {
|
|
697
820
|
const node = lexical.$getNodeByKey(nodeKey);
|
|
821
|
+
|
|
698
822
|
if (!$isCodeNode(node) || !node.isAttached()) {
|
|
699
823
|
return;
|
|
700
824
|
}
|
|
701
|
-
|
|
702
|
-
// If it's not range selection (or null selection) there's no need to change it,
|
|
825
|
+
|
|
826
|
+
const selection = lexical.$getSelection(); // If it's not range selection (or null selection) there's no need to change it,
|
|
703
827
|
// but we can still run highlighting logic
|
|
828
|
+
|
|
704
829
|
if (!lexical.$isRangeSelection(selection)) {
|
|
705
830
|
updateFn();
|
|
706
831
|
return;
|
|
707
832
|
}
|
|
833
|
+
|
|
708
834
|
const anchor = selection.anchor;
|
|
709
835
|
const anchorOffset = anchor.offset;
|
|
710
836
|
const isNewLineAnchor = anchor.type === 'element' && lexical.$isLineBreakNode(node.getChildAtIndex(anchor.offset - 1));
|
|
711
|
-
let textOffset = 0;
|
|
837
|
+
let textOffset = 0; // Calculating previous text offset (all text node prior to anchor + anchor own text offset)
|
|
712
838
|
|
|
713
|
-
// Calculating previous text offset (all text node prior to anchor + anchor own text offset)
|
|
714
839
|
if (!isNewLineAnchor) {
|
|
715
840
|
const anchorNode = anchor.getNode();
|
|
716
841
|
textOffset = anchorOffset + anchorNode.getPreviousSiblings().reduce((offset, _node) => {
|
|
717
842
|
return offset + _node.getTextContentSize();
|
|
718
843
|
}, 0);
|
|
719
844
|
}
|
|
845
|
+
|
|
720
846
|
const hasChanges = updateFn();
|
|
847
|
+
|
|
721
848
|
if (!hasChanges) {
|
|
722
849
|
return;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// Non-text anchors only happen for line breaks, otherwise
|
|
850
|
+
} // Non-text anchors only happen for line breaks, otherwise
|
|
726
851
|
// selection will be within text node (code highlight node)
|
|
852
|
+
|
|
853
|
+
|
|
727
854
|
if (isNewLineAnchor) {
|
|
728
855
|
anchor.getNode().select(anchorOffset, anchorOffset);
|
|
729
856
|
return;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// If it was non-element anchor then we walk through child nodes
|
|
857
|
+
} // If it was non-element anchor then we walk through child nodes
|
|
733
858
|
// and looking for a position of original text offset
|
|
859
|
+
|
|
860
|
+
|
|
734
861
|
node.getChildren().some(_node => {
|
|
735
862
|
const isText = lexical.$isTextNode(_node);
|
|
863
|
+
|
|
736
864
|
if (isText || lexical.$isLineBreakNode(_node)) {
|
|
737
865
|
const textContentSize = _node.getTextContentSize();
|
|
866
|
+
|
|
738
867
|
if (isText && textContentSize >= textOffset) {
|
|
739
868
|
_node.select(textOffset, textOffset);
|
|
869
|
+
|
|
740
870
|
return true;
|
|
741
871
|
}
|
|
872
|
+
|
|
742
873
|
textOffset -= textContentSize;
|
|
743
874
|
}
|
|
875
|
+
|
|
744
876
|
return false;
|
|
745
877
|
});
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// Finds minimal diff range between two nodes lists. It returns from/to range boundaries of prevNodes
|
|
878
|
+
} // Finds minimal diff range between two nodes lists. It returns from/to range boundaries of prevNodes
|
|
749
879
|
// that needs to be replaced with `nodes` (subset of nextNodes) to make prevNodes equal to nextNodes.
|
|
880
|
+
|
|
881
|
+
|
|
750
882
|
function getDiffRange(prevNodes, nextNodes) {
|
|
751
883
|
let leadingMatch = 0;
|
|
884
|
+
|
|
752
885
|
while (leadingMatch < prevNodes.length) {
|
|
753
886
|
if (!isEqual(prevNodes[leadingMatch], nextNodes[leadingMatch])) {
|
|
754
887
|
break;
|
|
755
888
|
}
|
|
889
|
+
|
|
756
890
|
leadingMatch++;
|
|
757
891
|
}
|
|
892
|
+
|
|
758
893
|
const prevNodesLength = prevNodes.length;
|
|
759
894
|
const nextNodesLength = nextNodes.length;
|
|
760
895
|
const maxTrailingMatch = Math.min(prevNodesLength, nextNodesLength) - leadingMatch;
|
|
761
896
|
let trailingMatch = 0;
|
|
897
|
+
|
|
762
898
|
while (trailingMatch < maxTrailingMatch) {
|
|
763
899
|
trailingMatch++;
|
|
900
|
+
|
|
764
901
|
if (!isEqual(prevNodes[prevNodesLength - trailingMatch], nextNodes[nextNodesLength - trailingMatch])) {
|
|
765
902
|
trailingMatch--;
|
|
766
903
|
break;
|
|
767
904
|
}
|
|
768
905
|
}
|
|
906
|
+
|
|
769
907
|
const from = leadingMatch;
|
|
770
908
|
const to = prevNodesLength - trailingMatch;
|
|
771
909
|
const nodesForReplacement = nextNodes.slice(leadingMatch, nextNodesLength - trailingMatch);
|
|
@@ -775,45 +913,59 @@ function getDiffRange(prevNodes, nextNodes) {
|
|
|
775
913
|
to
|
|
776
914
|
};
|
|
777
915
|
}
|
|
916
|
+
|
|
778
917
|
function isEqual(nodeA, nodeB) {
|
|
779
918
|
// Only checking for code higlight nodes and linebreaks. If it's regular text node
|
|
780
919
|
// returning false so that it's transformed into code highlight node
|
|
781
920
|
if ($isCodeHighlightNode(nodeA) && $isCodeHighlightNode(nodeB)) {
|
|
782
921
|
return nodeA.__text === nodeB.__text && nodeA.__highlightType === nodeB.__highlightType;
|
|
783
922
|
}
|
|
923
|
+
|
|
784
924
|
if (lexical.$isLineBreakNode(nodeA) && lexical.$isLineBreakNode(nodeB)) {
|
|
785
925
|
return true;
|
|
786
926
|
}
|
|
927
|
+
|
|
787
928
|
return false;
|
|
788
929
|
}
|
|
930
|
+
|
|
789
931
|
function handleMultilineIndent(type) {
|
|
790
932
|
const selection = lexical.$getSelection();
|
|
933
|
+
|
|
791
934
|
if (!lexical.$isRangeSelection(selection) || selection.isCollapsed()) {
|
|
792
935
|
return false;
|
|
793
|
-
}
|
|
936
|
+
} // Only run multiline indent logic on selections exclusively composed of code highlights and linebreaks
|
|
937
|
+
|
|
794
938
|
|
|
795
|
-
// Only run multiline indent logic on selections exclusively composed of code highlights and linebreaks
|
|
796
939
|
const nodes = selection.getNodes();
|
|
940
|
+
|
|
797
941
|
for (let i = 0; i < nodes.length; i++) {
|
|
798
942
|
const node = nodes[i];
|
|
943
|
+
|
|
799
944
|
if (!$isCodeHighlightNode(node) && !lexical.$isLineBreakNode(node)) {
|
|
800
945
|
return false;
|
|
801
946
|
}
|
|
802
947
|
}
|
|
948
|
+
|
|
803
949
|
const startOfLine = getFirstCodeHighlightNodeOfLine(nodes[0]);
|
|
950
|
+
|
|
804
951
|
if (startOfLine != null) {
|
|
805
952
|
doIndent(startOfLine, type);
|
|
806
953
|
}
|
|
954
|
+
|
|
807
955
|
for (let i = 1; i < nodes.length; i++) {
|
|
808
956
|
const node = nodes[i];
|
|
957
|
+
|
|
809
958
|
if (lexical.$isLineBreakNode(nodes[i - 1]) && $isCodeHighlightNode(node)) {
|
|
810
959
|
doIndent(node, type);
|
|
811
960
|
}
|
|
812
961
|
}
|
|
962
|
+
|
|
813
963
|
return true;
|
|
814
964
|
}
|
|
965
|
+
|
|
815
966
|
function doIndent(node, type) {
|
|
816
967
|
const text = node.getTextContent();
|
|
968
|
+
|
|
817
969
|
if (type === lexical.INDENT_CONTENT_COMMAND) {
|
|
818
970
|
// If the codeblock node doesn't start with whitespace, we don't want to
|
|
819
971
|
// naively prepend a '\t'; Prism will then mangle all of our nodes when
|
|
@@ -838,15 +990,17 @@ function doIndent(node, type) {
|
|
|
838
990
|
}
|
|
839
991
|
}
|
|
840
992
|
}
|
|
993
|
+
|
|
841
994
|
function handleShiftLines(type, event) {
|
|
842
995
|
// We only care about the alt+arrow keys
|
|
843
996
|
const selection = lexical.$getSelection();
|
|
997
|
+
|
|
844
998
|
if (!lexical.$isRangeSelection(selection)) {
|
|
845
999
|
return false;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// I'm not quite sure why, but it seems like calling anchor.getNode() collapses the selection here
|
|
1000
|
+
} // I'm not quite sure why, but it seems like calling anchor.getNode() collapses the selection here
|
|
849
1001
|
// So first, get the anchor and the focus, then get their nodes
|
|
1002
|
+
|
|
1003
|
+
|
|
850
1004
|
const {
|
|
851
1005
|
anchor,
|
|
852
1006
|
focus
|
|
@@ -855,19 +1009,21 @@ function handleShiftLines(type, event) {
|
|
|
855
1009
|
const focusOffset = focus.offset;
|
|
856
1010
|
const anchorNode = anchor.getNode();
|
|
857
1011
|
const focusNode = focus.getNode();
|
|
858
|
-
const arrowIsUp = type === lexical.KEY_ARROW_UP_COMMAND;
|
|
1012
|
+
const arrowIsUp = type === lexical.KEY_ARROW_UP_COMMAND; // Ensure the selection is within the codeblock
|
|
859
1013
|
|
|
860
|
-
// Ensure the selection is within the codeblock
|
|
861
1014
|
if (!$isCodeHighlightNode(anchorNode) || !$isCodeHighlightNode(focusNode)) {
|
|
862
1015
|
return false;
|
|
863
1016
|
}
|
|
1017
|
+
|
|
864
1018
|
if (!event.altKey) {
|
|
865
1019
|
// Handle moving selection out of the code block, given there are no
|
|
866
1020
|
// sibling thats can natively take the selection.
|
|
867
1021
|
if (selection.isCollapsed()) {
|
|
868
1022
|
const codeNode = anchorNode.getParentOrThrow();
|
|
1023
|
+
|
|
869
1024
|
if (arrowIsUp && anchorOffset === 0 && anchorNode.getPreviousSibling() === null) {
|
|
870
1025
|
const codeNodeSibling = codeNode.getPreviousSibling();
|
|
1026
|
+
|
|
871
1027
|
if (codeNodeSibling === null) {
|
|
872
1028
|
codeNode.selectPrevious();
|
|
873
1029
|
event.preventDefault();
|
|
@@ -875,6 +1031,7 @@ function handleShiftLines(type, event) {
|
|
|
875
1031
|
}
|
|
876
1032
|
} else if (!arrowIsUp && anchorOffset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null) {
|
|
877
1033
|
const codeNodeSibling = codeNode.getNextSibling();
|
|
1034
|
+
|
|
878
1035
|
if (codeNodeSibling === null) {
|
|
879
1036
|
codeNode.selectNext();
|
|
880
1037
|
event.preventDefault();
|
|
@@ -882,39 +1039,50 @@ function handleShiftLines(type, event) {
|
|
|
882
1039
|
}
|
|
883
1040
|
}
|
|
884
1041
|
}
|
|
1042
|
+
|
|
885
1043
|
return false;
|
|
886
1044
|
}
|
|
1045
|
+
|
|
887
1046
|
const start = getFirstCodeHighlightNodeOfLine(anchorNode);
|
|
888
1047
|
const end = getLastCodeHighlightNodeOfLine(focusNode);
|
|
1048
|
+
|
|
889
1049
|
if (start == null || end == null) {
|
|
890
1050
|
return false;
|
|
891
1051
|
}
|
|
1052
|
+
|
|
892
1053
|
const range = start.getNodesBetween(end);
|
|
1054
|
+
|
|
893
1055
|
for (let i = 0; i < range.length; i++) {
|
|
894
1056
|
const node = range[i];
|
|
1057
|
+
|
|
895
1058
|
if (!$isCodeHighlightNode(node) && !lexical.$isLineBreakNode(node)) {
|
|
896
1059
|
return false;
|
|
897
1060
|
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// After this point, we know the selection is within the codeblock. We may not be able to
|
|
1061
|
+
} // After this point, we know the selection is within the codeblock. We may not be able to
|
|
901
1062
|
// actually move the lines around, but we want to return true either way to prevent
|
|
902
1063
|
// the event's default behavior
|
|
1064
|
+
|
|
1065
|
+
|
|
903
1066
|
event.preventDefault();
|
|
904
1067
|
event.stopPropagation(); // required to stop cursor movement under Firefox
|
|
905
1068
|
|
|
906
1069
|
const linebreak = arrowIsUp ? start.getPreviousSibling() : end.getNextSibling();
|
|
1070
|
+
|
|
907
1071
|
if (!lexical.$isLineBreakNode(linebreak)) {
|
|
908
1072
|
return true;
|
|
909
1073
|
}
|
|
1074
|
+
|
|
910
1075
|
const sibling = arrowIsUp ? linebreak.getPreviousSibling() : linebreak.getNextSibling();
|
|
1076
|
+
|
|
911
1077
|
if (sibling == null) {
|
|
912
1078
|
return true;
|
|
913
1079
|
}
|
|
1080
|
+
|
|
914
1081
|
const maybeInsertionPoint = arrowIsUp ? getFirstCodeHighlightNodeOfLine(sibling) : getLastCodeHighlightNodeOfLine(sibling);
|
|
915
1082
|
let insertionPoint = maybeInsertionPoint != null ? maybeInsertionPoint : sibling;
|
|
916
1083
|
linebreak.remove();
|
|
917
1084
|
range.forEach(node => node.remove());
|
|
1085
|
+
|
|
918
1086
|
if (type === lexical.KEY_ARROW_UP_COMMAND) {
|
|
919
1087
|
range.forEach(node => insertionPoint.insertBefore(node));
|
|
920
1088
|
insertionPoint.insertBefore(linebreak);
|
|
@@ -926,14 +1094,18 @@ function handleShiftLines(type, event) {
|
|
|
926
1094
|
insertionPoint = node;
|
|
927
1095
|
});
|
|
928
1096
|
}
|
|
1097
|
+
|
|
929
1098
|
selection.setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset);
|
|
930
1099
|
return true;
|
|
931
1100
|
}
|
|
1101
|
+
|
|
932
1102
|
function handleMoveTo(type, event) {
|
|
933
1103
|
const selection = lexical.$getSelection();
|
|
1104
|
+
|
|
934
1105
|
if (!lexical.$isRangeSelection(selection)) {
|
|
935
1106
|
return false;
|
|
936
1107
|
}
|
|
1108
|
+
|
|
937
1109
|
const {
|
|
938
1110
|
anchor,
|
|
939
1111
|
focus
|
|
@@ -941,11 +1113,14 @@ function handleMoveTo(type, event) {
|
|
|
941
1113
|
const anchorNode = anchor.getNode();
|
|
942
1114
|
const focusNode = focus.getNode();
|
|
943
1115
|
const isMoveToStart = type === lexical.MOVE_TO_START;
|
|
1116
|
+
|
|
944
1117
|
if (!$isCodeHighlightNode(anchorNode) || !$isCodeHighlightNode(focusNode)) {
|
|
945
1118
|
return false;
|
|
946
1119
|
}
|
|
1120
|
+
|
|
947
1121
|
let node;
|
|
948
1122
|
let offset;
|
|
1123
|
+
|
|
949
1124
|
if (isMoveToStart) {
|
|
950
1125
|
({
|
|
951
1126
|
node,
|
|
@@ -957,25 +1132,31 @@ function handleMoveTo(type, event) {
|
|
|
957
1132
|
offset
|
|
958
1133
|
} = getEndOfCodeInLine(focusNode));
|
|
959
1134
|
}
|
|
1135
|
+
|
|
960
1136
|
if (node !== null && offset !== -1) {
|
|
961
1137
|
selection.setTextNodeRange(node, offset, node, offset);
|
|
962
1138
|
}
|
|
1139
|
+
|
|
963
1140
|
event.preventDefault();
|
|
964
1141
|
event.stopPropagation();
|
|
965
1142
|
return true;
|
|
966
1143
|
}
|
|
1144
|
+
|
|
967
1145
|
function registerCodeHighlighting(editor, tokenizer) {
|
|
968
1146
|
if (!editor.hasNodes([CodeNode, CodeHighlightNode])) {
|
|
969
1147
|
throw new Error('CodeHighlightPlugin: CodeNode or CodeHighlightNode not registered on editor');
|
|
970
1148
|
}
|
|
1149
|
+
|
|
971
1150
|
if (tokenizer == null) {
|
|
972
1151
|
tokenizer = PrismTokenizer;
|
|
973
1152
|
}
|
|
1153
|
+
|
|
974
1154
|
return utils.mergeRegister(editor.registerMutationListener(CodeNode, mutations => {
|
|
975
1155
|
editor.update(() => {
|
|
976
1156
|
for (const [key, type] of mutations) {
|
|
977
1157
|
if (type !== 'destroyed') {
|
|
978
1158
|
const node = lexical.$getNodeByKey(key);
|
|
1159
|
+
|
|
979
1160
|
if (node !== null) {
|
|
980
1161
|
updateCodeGutter(node, editor);
|
|
981
1162
|
}
|
package/LexicalCode.prod.js
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
'use strict';var e=require("prismjs");require("prismjs/components/prism-clike");require("prismjs/components/prism-javascript");require("prismjs/components/prism-markup");require("prismjs/components/prism-markdown");require("prismjs/components/prism-c");require("prismjs/components/prism-css");require("prismjs/components/prism-objectivec");require("prismjs/components/prism-sql");require("prismjs/components/prism-python");require("prismjs/components/prism-rust");require("prismjs/components/prism-swift");
|
|
8
|
-
require("prismjs/components/prism-typescript");var n=require("@lexical/utils"),u=require("lexical");let v=a=>null!=a&&e.languages.hasOwnProperty(a)?a:void 0;function x(a,b){for(let c of a.childNodes){if(c
|
|
8
|
+
require("prismjs/components/prism-typescript");require("prismjs/components/prism-java");require("prismjs/components/prism-cpp");var n=require("@lexical/utils"),u=require("lexical");let v=a=>null!=a&&e.languages.hasOwnProperty(a)?a:void 0;function x(a,b){for(let c of a.childNodes){if(n.isHTMLElement(c)&&c.tagName===b)return!0;x(c,b)}return!1}
|
|
9
9
|
class y extends u.ElementNode{static getType(){return"code"}static clone(a){return new y(a.__language,a.__key)}constructor(a,b){super(b);this.__language=v(a)}createDOM(a){let b=document.createElement("code");n.addClassNamesToElement(b,a.theme.code);b.setAttribute("spellcheck","false");(a=this.getLanguage())&&b.setAttribute("data-highlight-language",a);return b}updateDOM(a,b){let c=this.__language;a=a.__language;c?c!==a&&b.setAttribute("data-highlight-language",c):a&&b.removeAttribute("data-highlight-language");
|
|
10
10
|
return!1}static importDOM(){return{code:a=>null!=a.textContent&&(/\r?\n/.test(a.textContent)||x(a,"BR"))?{conversion:z,priority:1}:null,div:()=>({conversion:aa,priority:1}),pre:()=>({conversion:z,priority:0}),table:a=>A(a)?{conversion:ba,priority:3}:null,td:a=>{let b=a.closest("table");return a.classList.contains("js-file-line")?{conversion:ca,priority:3}:b&&A(b)?{conversion:B,priority:3}:null},tr:a=>(a=a.closest("table"))&&A(a)?{conversion:B,priority:3}:null}}static importJSON(a){let b=C(a.language);
|
|
11
11
|
b.setFormat(a.format);b.setIndent(a.indent);b.setDirection(a.direction);return b}exportJSON(){return{...super.exportJSON(),language:this.getLanguage(),type:"code",version:1}}insertNewAfter(a,b=!0){var c=this.getChildren(),d=c.length;if(2<=d&&"\n"===c[d-1].getTextContent()&&"\n"===c[d-2].getTextContent()&&a.isCollapsed()&&a.anchor.key===this.__key&&a.anchor.offset===d)return c[d-1].remove(),c[d-2].remove(),a=u.$createParagraphNode(),this.insertAfter(a,b),a;b=a.anchor.getNode();d=D(b);if(null!=d){c=
|
|
12
12
|
0;for(d=d.getTextContent();c<d.length&&/[\t ]/.test(d[c]);)c+=1;if(0<c)return c=d.substring(0,c),c=E(c),b.insertAfter(c),a.insertNodes([u.$createLineBreakNode()]),c.select(),c}return null}canInsertTab(){let a=u.$getSelection();return u.$isRangeSelection(a)&&a.isCollapsed()?!0:!1}canIndent(){return!1}collapseAtStart(){let a=u.$createParagraphNode();this.getChildren().forEach(b=>a.append(b));this.replace(a);return!0}setLanguage(a){this.getWritable().__language=v(a)}getLanguage(){return this.getLatest().__language}}
|
|
13
13
|
function C(a){return u.$applyNodeReplacement(new y(a))}function F(a){return a instanceof y}function z(){return{node:C(),preformatted:!0}}function aa(a){let b=null!==a.style.fontFamily.match("monospace");return b||da(a)?{after:c=>{let d=a.parentNode;null!=d&&a!==d.lastChild&&c.push(u.$createLineBreakNode());return c},node:b?C():null,preformatted:b}:{node:null}}function ba(){return{node:C(),preformatted:!0}}function B(){return{node:null}}
|
|
14
14
|
function ca(a){return{after:b=>{a.parentNode&&a.parentNode.nextSibling&&b.push(u.$createLineBreakNode());return b},node:null}}function da(a){for(a=a.parentElement;null!==a;){if(null!==a.style.fontFamily.match("monospace"))return!0;a=a.parentElement}return!1}function A(a){return a.classList.contains("js-file-line-container")}
|
|
15
|
-
let G={c:"C",clike:"C-like",css:"CSS",html:"HTML",js:"JavaScript",markdown:"Markdown",objc:"Objective-C",plain:"Plain Text",py:"Python",rust:"Rust",sql:"SQL",swift:"Swift",typescript:"TypeScript",xml:"XML"},H={javascript:"js",md:"markdown",plaintext:"plain",python:"py",text:"plain",ts:"typescript"};function I(a){return H[a]||a}
|
|
15
|
+
let G={c:"C",clike:"C-like",cpp:"C++",css:"CSS",html:"HTML",java:"Java",js:"JavaScript",markdown:"Markdown",objc:"Objective-C",plain:"Plain Text",py:"Python",rust:"Rust",sql:"SQL",swift:"Swift",typescript:"TypeScript",xml:"XML"},H={cpp:"cpp",java:"java",javascript:"js",md:"markdown",plaintext:"plain",python:"py",text:"plain",ts:"typescript"};function I(a){return H[a]||a}
|
|
16
16
|
class J extends u.TextNode{constructor(a,b,c){super(a,c);this.__highlightType=b}static getType(){return"code-highlight"}static clone(a){return new J(a.__text,a.__highlightType||void 0,a.__key)}getHighlightType(){return this.getLatest().__highlightType}createDOM(a){let b=super.createDOM(a);a=K(a.theme,this.__highlightType);n.addClassNamesToElement(b,a);return b}updateDOM(a,b,c){let d=super.updateDOM(a,b,c);a=K(c.theme,a.__highlightType);c=K(c.theme,this.__highlightType);a!==c&&(a&&n.removeClassNamesFromElement(b,
|
|
17
17
|
a),c&&n.addClassNamesToElement(b,c));return d}static importJSON(a){let b=E(a.text,a.highlightType);b.setFormat(a.format);b.setDetail(a.detail);b.setMode(a.mode);b.setStyle(a.style);return b}exportJSON(){return{...super.exportJSON(),highlightType:this.getHighlightType(),type:"code-highlight",version:1}}setFormat(){return this}isParentRequired(){return!0}createParentElementNode(){return C()}}function K(a,b){return b&&a&&a.codeHighlight&&a.codeHighlight[b]}
|
|
18
18
|
function E(a,b){return u.$applyNodeReplacement(new J(a,b))}function L(a){return a instanceof J}function D(a){let b=null,c=a.getPreviousSiblings();for(c.push(a);0<c.length&&(a=c.pop(),L(a)&&(b=a),!u.$isLineBreakNode(a)););return b}function M(a){let b=null,c=a.getNextSiblings();for(c.unshift(a);0<c.length&&(a=c.shift(),L(a)&&(b=a),!u.$isLineBreakNode(a)););return b}let N={defaultLanguage:"javascript",tokenize(a,b){return e.tokenize(a,e.languages[b||""]||e.languages[this.defaultLanguage])}};
|
package/package.json
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"code"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "0.9.0",
|
|
12
12
|
"main": "LexicalCode.js",
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"lexical": "0.
|
|
14
|
+
"lexical": "0.9.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lexical/utils": "0.
|
|
17
|
+
"@lexical/utils": "0.9.0",
|
|
18
18
|
"prismjs": "^1.27.0"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|