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