@mx-space/cli 0.7.7 → 0.8.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/dist/{Lexical.dev-rcxzwpir.mjs → Lexical.dev-By5dOKBN.mjs} +1486 -356
- package/dist/{Lexical.node-BUsI9gWr.mjs → Lexical.node-fXM8Jp7p.mjs} +39 -17
- package/dist/{Lexical.prod-C8HtkSD6.mjs → Lexical.prod-BlCvXPGa.mjs} +2279 -1768
- package/dist/LexicalClipboard.dev--HxGoSkO.mjs +915 -0
- package/dist/{LexicalClipboard.node-CO-MCKx5.mjs → LexicalClipboard.node-C3mRbrmW.mjs} +10 -3
- package/dist/LexicalClipboard.prod-DNJzFMcT.mjs +371 -0
- package/dist/LexicalCodeCore.dev-DjSrd4A4.mjs +1109 -0
- package/dist/LexicalCodeCore.prod-DTDShkR3.mjs +642 -0
- package/dist/{LexicalDragon.dev-gBP8WSRS.mjs → LexicalDragon.dev-DwprRH0z.mjs} +3 -3
- package/dist/{LexicalDragon.node-OG6G55nl.mjs → LexicalDragon.node-BVhw4C7s.mjs} +2 -2
- package/dist/{LexicalDragon.prod-DP-_Q8cv.mjs → LexicalDragon.prod-nh4J-8vn.mjs} +3 -3
- package/dist/{LexicalExtension.dev-DVbjljrm.mjs → LexicalExtension.dev-BPNyvGqe.mjs} +335 -6
- package/dist/{LexicalExtension.node-CF3EAPN7.mjs → LexicalExtension.node-BVlcrgOx.mjs} +15 -8
- package/dist/{LexicalExtension.prod-DklHHErZ.mjs → LexicalExtension.prod-DFKbiNt-.mjs} +375 -265
- package/dist/{LexicalHeadless.dev-DdWLM7Nl.mjs → LexicalHeadless.dev-B-cj7Pr5.mjs} +2 -2
- package/dist/{LexicalHeadless.prod-CoiiTDzR.mjs → LexicalHeadless.prod-Byp1vNzT.mjs} +2 -2
- package/dist/LexicalHtml.dev-prBAwszN.mjs +2424 -0
- package/dist/LexicalHtml.node-BvNF96jZ.mjs +56 -0
- package/dist/LexicalHtml.prod-Sz3CW1ht.mjs +1207 -0
- package/dist/{LexicalLink.dev-BS1Bf-yD.mjs → LexicalLink.dev-NySTYhVR.mjs} +42 -8
- package/dist/{LexicalLink.prod-B_crkoHP.mjs → LexicalLink.prod-BuCM8gts.mjs} +209 -191
- package/dist/{LexicalList.dev-OMqmcnQ-.mjs → LexicalList.dev-DIMOvSFD.mjs} +252 -80
- package/dist/{LexicalList.prod-CpL1s8vS.mjs → LexicalList.prod-MVqfA6Vo.mjs} +364 -278
- package/dist/{LexicalMarkdown.dev-BHSdx_Gr.mjs → LexicalMarkdown.dev-C7aylvCG.mjs} +729 -552
- package/dist/LexicalMarkdown.prod-CfQSM406.mjs +868 -0
- package/dist/{LexicalRichText.dev-Bz9A_WbM.mjs → LexicalRichText.dev-DfICq6lU.mjs} +272 -77
- package/dist/{LexicalRichText.node-DDpwfE-P.mjs → LexicalRichText.node-Bg9jND3Q.mjs} +21 -8
- package/dist/LexicalRichText.prod-GsFzCK-o.mjs +550 -0
- package/dist/{LexicalSelection.dev-CQ380fnD.mjs → LexicalSelection.dev-DGjj12wW.mjs} +50 -38
- package/dist/{LexicalSelection.node-DesBMjIE.mjs → LexicalSelection.node-BU5L4193.mjs} +4 -4
- package/dist/{LexicalSelection.prod-D2jivCDB.mjs → LexicalSelection.prod-D-PwRbyK.mjs} +158 -140
- package/dist/{LexicalTable.dev-Ca7mQ7Jk.mjs → LexicalTable.dev-ChP7cugd.mjs} +215 -53
- package/dist/{LexicalTable.prod-DyPAcC7y.mjs → LexicalTable.prod-Bpr9YRXp.mjs} +668 -573
- package/dist/{LexicalUtils.dev-HxM8F0g6.mjs → LexicalUtils.dev-BiFU3789.mjs} +10 -47
- package/dist/{LexicalUtils.node-lCWjRjoG.mjs → LexicalUtils.node-j3Llm7Y3.mjs} +2 -2
- package/dist/{LexicalUtils.prod-Bti5w4ki.mjs → LexicalUtils.prod-DCvrA78p.mjs} +103 -105
- package/dist/bin/mxs.d.mts +1 -1
- package/dist/bin/mxs.mjs +1 -1
- package/dist/esm-By7trG9K.mjs +2 -0
- package/dist/{esm-CK9m5JKX.mjs → esm-Dg-WhPCe.mjs} +989 -989
- package/dist/index.d.mts +22 -2
- package/dist/index.mjs +1 -1
- package/dist/{mxs-vbxEu09D.mjs → mxs-DT0oYk6U.mjs} +10387 -8278
- package/package.json +18 -17
- package/dist/LexicalClipboard.dev-BpqRwTLP.mjs +0 -494
- package/dist/LexicalClipboard.prod-B_xdeiAC.mjs +0 -291
- package/dist/LexicalCodeCore.dev-7_cexm39.mjs +0 -537
- package/dist/LexicalCodeCore.prod-Bd6WFHcO.mjs +0 -381
- package/dist/LexicalHtml.dev-C3ZSWBFZ.mjs +0 -684
- package/dist/LexicalHtml.node-D0TD152E.mjs +0 -24
- package/dist/LexicalHtml.prod-CX47X0zl.mjs +0 -430
- package/dist/LexicalMarkdown.prod-C-S7Ii6-.mjs +0 -754
- package/dist/LexicalRichText.prod-C8qXACDL.mjs +0 -427
- package/dist/esm-DRbkkY3r.mjs +0 -2
- /package/dist/{mxs-CM8-6uAr.d.mts → mxs-iqJBopvE.d.mts} +0 -0
|
@@ -0,0 +1,1109 @@
|
|
|
1
|
+
import { d as getPeerDependencyFromEditor, f as namedSignals, l as effect } from "./LexicalExtension.node-BVlcrgOx.mjs";
|
|
2
|
+
import { $ as $isElementNode, $t as ElementNode, An as KEY_ARROW_UP_COMMAND, Ar as removeClassNamesFromElement, Bn as OUTDENT_CONTENT_COMMAND, Bt as COMMAND_PRIORITY_LOW, D as $getCaretRangeInDirection, Dn as KEY_ARROW_DOWN_COMMAND, E as $getCaretRange, F as $getEditor, In as KEY_TAB_COMMAND, J as $insertNodes, K as $getTextPointCaret, Mr as setDOMStyleFromCSS, Nn as KEY_ENTER_COMMAND, Or as mergeRegister, Rn as MOVE_TO_END, Sr as isHTMLElement, Tt as $setSelectionFromCaretRange, U as $getSelection, W as $getSiblingCaret, _r as isDOMDocumentNode, cr as defineExtension, dt as $isTabNode, er as TextNode, f as $createParagraphNode, ft as $isTextNode, g as $createTabNode, ht as $normalizeCaret, it as $isLineBreakNode, jr as safeCast, l as $create, ln as INSERT_TAB_COMMAND, mr as getTextDirection, n as $applyNodeReplacement, on as INDENT_CONTENT_COMMAND, p as $createPoint, rr as configExtension, st as $isRangeSelection, tr as addClassNamesToElement, u as $createLineBreakNode, x as $generateNodesFromRawText, yr as isDOMTextNode, zn as MOVE_TO_START } from "./Lexical.node-fXM8Jp7p.mjs";
|
|
3
|
+
import { a as CoreImportExtension, g as sel, m as defineOverlayRules, o as DOMImportExtension, p as defineImportRule, s as ImportOverlays } from "./LexicalHtml.node-BvNF96jZ.mjs";
|
|
4
|
+
//#region ../../node_modules/.pnpm/@lexical+code-core@0.45.0/node_modules/@lexical/code-core/dist/LexicalCodeCore.dev.mjs
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
*
|
|
8
|
+
* This source code is licensed under the MIT license found in the
|
|
9
|
+
* LICENSE file in the root directory of this source tree.
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
14
|
+
*
|
|
15
|
+
* This source code is licensed under the MIT license found in the
|
|
16
|
+
* LICENSE file in the root directory of this source tree.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
function warnOnlyOnce(message) {
|
|
20
|
+
{
|
|
21
|
+
let run = false;
|
|
22
|
+
return () => {
|
|
23
|
+
if (!run) console.warn(message);
|
|
24
|
+
run = true;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
30
|
+
*
|
|
31
|
+
* This source code is licensed under the MIT license found in the
|
|
32
|
+
* LICENSE file in the root directory of this source tree.
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
function formatDevErrorMessage(message) {
|
|
36
|
+
throw new Error(message);
|
|
37
|
+
}
|
|
38
|
+
function $getLastMatchingCodeNode(anchor, direction) {
|
|
39
|
+
let matchingNode = anchor;
|
|
40
|
+
for (let caret = $getSiblingCaret(anchor, direction); caret && ($isCodeHighlightNode(caret.origin) || $isTabNode(caret.origin)); caret = caret.getAdjacentCaret()) matchingNode = caret.origin;
|
|
41
|
+
return matchingNode;
|
|
42
|
+
}
|
|
43
|
+
function $getFirstCodeNodeOfLine(anchor) {
|
|
44
|
+
return $getLastMatchingCodeNode(anchor, "previous");
|
|
45
|
+
}
|
|
46
|
+
function $getLastCodeNodeOfLine(anchor) {
|
|
47
|
+
return $getLastMatchingCodeNode(anchor, "next");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Determines the visual writing direction of a code line.
|
|
51
|
+
*
|
|
52
|
+
* Scans the line segments (CodeHighlightNode/TabNode) from start to end
|
|
53
|
+
* and returns the first strong direction found ("ltr" or "rtl").
|
|
54
|
+
* If no strong character is found, falls back to the parent element's
|
|
55
|
+
* direction. Returns null if indeterminate.
|
|
56
|
+
*/
|
|
57
|
+
function $getCodeLineDirection(anchor) {
|
|
58
|
+
const start = $getFirstCodeNodeOfLine(anchor);
|
|
59
|
+
const end = $getLastCodeNodeOfLine(anchor);
|
|
60
|
+
let node = start;
|
|
61
|
+
while (node !== null) {
|
|
62
|
+
if ($isCodeHighlightNode(node)) {
|
|
63
|
+
const direction = getTextDirection(node.getTextContent());
|
|
64
|
+
if (direction !== null) return direction;
|
|
65
|
+
}
|
|
66
|
+
if (node === end) break;
|
|
67
|
+
node = node.getNextSibling();
|
|
68
|
+
}
|
|
69
|
+
const parent = start.getParent();
|
|
70
|
+
if ($isElementNode(parent)) {
|
|
71
|
+
const parentDirection = parent.getDirection();
|
|
72
|
+
if (parentDirection === "ltr" || parentDirection === "rtl") return parentDirection;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function $getStartOfCodeInLine(anchor, offset) {
|
|
77
|
+
let last = null;
|
|
78
|
+
let lastNonBlank = null;
|
|
79
|
+
let node = anchor;
|
|
80
|
+
let nodeOffset = offset;
|
|
81
|
+
let nodeTextContent = anchor.getTextContent();
|
|
82
|
+
while (true) {
|
|
83
|
+
if (nodeOffset === 0) {
|
|
84
|
+
node = node.getPreviousSibling();
|
|
85
|
+
if (node === null) break;
|
|
86
|
+
if (!($isCodeHighlightNode(node) || $isTabNode(node) || $isLineBreakNode(node))) formatDevErrorMessage(`Expected a valid Code Node: CodeHighlightNode, TabNode, LineBreakNode`);
|
|
87
|
+
if ($isLineBreakNode(node)) {
|
|
88
|
+
last = {
|
|
89
|
+
node,
|
|
90
|
+
offset: 1
|
|
91
|
+
};
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
nodeOffset = Math.max(0, node.getTextContentSize() - 1);
|
|
95
|
+
nodeTextContent = node.getTextContent();
|
|
96
|
+
} else nodeOffset--;
|
|
97
|
+
const character = nodeTextContent[nodeOffset];
|
|
98
|
+
if ($isCodeHighlightNode(node) && character !== " ") lastNonBlank = {
|
|
99
|
+
node,
|
|
100
|
+
offset: nodeOffset
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (lastNonBlank !== null) return lastNonBlank;
|
|
104
|
+
let codeCharacterAtAnchorOffset = null;
|
|
105
|
+
if (offset < anchor.getTextContentSize()) {
|
|
106
|
+
if ($isCodeHighlightNode(anchor)) codeCharacterAtAnchorOffset = anchor.getTextContent()[offset];
|
|
107
|
+
} else {
|
|
108
|
+
const nextSibling = anchor.getNextSibling();
|
|
109
|
+
if ($isCodeHighlightNode(nextSibling)) codeCharacterAtAnchorOffset = nextSibling.getTextContent()[0];
|
|
110
|
+
}
|
|
111
|
+
if (codeCharacterAtAnchorOffset !== null && codeCharacterAtAnchorOffset !== " ") return last;
|
|
112
|
+
else {
|
|
113
|
+
const nextNonBlank = findNextNonBlankInLine(anchor, offset);
|
|
114
|
+
if (nextNonBlank !== null) return nextNonBlank;
|
|
115
|
+
else return last;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function findNextNonBlankInLine(anchor, offset) {
|
|
119
|
+
let node = anchor;
|
|
120
|
+
let nodeOffset = offset;
|
|
121
|
+
let nodeTextContent = anchor.getTextContent();
|
|
122
|
+
let nodeTextContentSize = anchor.getTextContentSize();
|
|
123
|
+
while (true) {
|
|
124
|
+
if (!$isCodeHighlightNode(node) || nodeOffset === nodeTextContentSize) {
|
|
125
|
+
node = node.getNextSibling();
|
|
126
|
+
if (node === null || $isLineBreakNode(node)) return null;
|
|
127
|
+
if ($isCodeHighlightNode(node)) {
|
|
128
|
+
nodeOffset = 0;
|
|
129
|
+
nodeTextContent = node.getTextContent();
|
|
130
|
+
nodeTextContentSize = node.getTextContentSize();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if ($isCodeHighlightNode(node)) {
|
|
134
|
+
if (nodeTextContent[nodeOffset] !== " ") return {
|
|
135
|
+
node,
|
|
136
|
+
offset: nodeOffset
|
|
137
|
+
};
|
|
138
|
+
nodeOffset++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function $getEndOfCodeInLine(anchor) {
|
|
143
|
+
const lastNode = $getLastCodeNodeOfLine(anchor);
|
|
144
|
+
if (!!$isLineBreakNode(lastNode)) formatDevErrorMessage(`Unexpected lineBreakNode in getEndOfCodeInLine`);
|
|
145
|
+
return lastNode;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Plain split of code text into CodeHighlightNodes (with no highlight
|
|
149
|
+
* type) + LineBreakNodes + TabNodes. Used when the tokenizer opts out
|
|
150
|
+
* of a default language so a previously highlighted block still
|
|
151
|
+
* renders its `\n` / `\t` as real line breaks / tabs, while staying
|
|
152
|
+
* compatible with the indent / shift-lines handlers that only accept
|
|
153
|
+
* CodeHighlightNode + TabNode + LineBreakNode inside a CodeNode.
|
|
154
|
+
*/
|
|
155
|
+
function $plainifyCodeContent(text) {
|
|
156
|
+
const out = [];
|
|
157
|
+
text.split("\n").forEach((line, lineIdx) => {
|
|
158
|
+
if (lineIdx > 0) out.push($createLineBreakNode());
|
|
159
|
+
line.split(" ").forEach((part, partIdx) => {
|
|
160
|
+
if (partIdx > 0) out.push($createTabNode());
|
|
161
|
+
if (part.length > 0) out.push($createCodeHighlightNode(part));
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Strip up to `tabSize` leading spaces from a {@link CodeHighlightNode} that
|
|
168
|
+
* starts a code line, to support outdenting space-indented code lines (e.g.
|
|
169
|
+
* code formatted with prettier). Returns true if any spaces were stripped.
|
|
170
|
+
*
|
|
171
|
+
* Best-effort: a line with fewer than `tabSize` leading spaces has all of
|
|
172
|
+
* them stripped, matching VS Code / IntelliJ behavior.
|
|
173
|
+
*
|
|
174
|
+
* Selection is preserved relative to line content. Anchor/focus offsets
|
|
175
|
+
* pointing into `node` shift left by the number of stripped characters
|
|
176
|
+
* (clamped to 0). The underlying TextNode mutation does not adjust
|
|
177
|
+
* selection offsets that already point into the old text, so we patch
|
|
178
|
+
* them up explicitly.
|
|
179
|
+
*/
|
|
180
|
+
function $outdentLeadingSpaces(node, tabSize, selection) {
|
|
181
|
+
if (!Number.isInteger(tabSize) || tabSize <= 0) return false;
|
|
182
|
+
const text = node.getTextContent();
|
|
183
|
+
const leading = /^ +/.exec(text);
|
|
184
|
+
if (!leading) return false;
|
|
185
|
+
const stripCount = Math.min(tabSize, leading[0].length);
|
|
186
|
+
const lineKey = node.getKey();
|
|
187
|
+
const oldAnchorOffset = selection.anchor.key === lineKey && selection.anchor.type === "text" ? selection.anchor.offset : null;
|
|
188
|
+
const oldFocusOffset = selection.focus.key === lineKey && selection.focus.type === "text" ? selection.focus.offset : null;
|
|
189
|
+
node.spliceText(0, stripCount, "");
|
|
190
|
+
if (oldAnchorOffset !== null) selection.anchor.set(lineKey, Math.max(0, oldAnchorOffset - stripCount), "text");
|
|
191
|
+
if (oldFocusOffset !== null) selection.focus.set(lineKey, Math.max(0, oldFocusOffset - stripCount), "text");
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
196
|
+
*
|
|
197
|
+
* This source code is licensed under the MIT license found in the
|
|
198
|
+
* LICENSE file in the root directory of this source tree.
|
|
199
|
+
*
|
|
200
|
+
*/
|
|
201
|
+
const DEFAULT_CODE_LANGUAGE = "javascript";
|
|
202
|
+
/** @internal Configurable through the extensions. */
|
|
203
|
+
const getDefaultCodeLanguage = () => DEFAULT_CODE_LANGUAGE;
|
|
204
|
+
function hasChildDOMNodeTag(node, tagName) {
|
|
205
|
+
for (const child of node.childNodes) {
|
|
206
|
+
if (isHTMLElement(child) && child.tagName === tagName) return true;
|
|
207
|
+
if (hasChildDOMNodeTag(child, tagName)) return true;
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const LANGUAGE_DATA_ATTRIBUTE$1 = "data-language";
|
|
212
|
+
const HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE = "data-highlight-language";
|
|
213
|
+
const THEME_DATA_ATTRIBUTE = "data-theme";
|
|
214
|
+
const noExtensionDeprecation = warnOnlyOnce("Using CodeNode without CodeExtension is deprecated");
|
|
215
|
+
/** @noInheritDoc */
|
|
216
|
+
var CodeNode = class CodeNode extends ElementNode {
|
|
217
|
+
/** @internal */
|
|
218
|
+
__language;
|
|
219
|
+
/** @internal */
|
|
220
|
+
__theme;
|
|
221
|
+
/** @internal */
|
|
222
|
+
__isSyntaxHighlightSupported;
|
|
223
|
+
static getType() {
|
|
224
|
+
return "code";
|
|
225
|
+
}
|
|
226
|
+
static clone(node) {
|
|
227
|
+
return new CodeNode(node.__language, node.__key);
|
|
228
|
+
}
|
|
229
|
+
constructor(language, key) {
|
|
230
|
+
super(key);
|
|
231
|
+
this.__language = language || void 0;
|
|
232
|
+
this.__isSyntaxHighlightSupported = false;
|
|
233
|
+
this.__theme = void 0;
|
|
234
|
+
}
|
|
235
|
+
afterCloneFrom(prevNode) {
|
|
236
|
+
super.afterCloneFrom(prevNode);
|
|
237
|
+
this.__language = prevNode.__language;
|
|
238
|
+
this.__theme = prevNode.__theme;
|
|
239
|
+
this.__isSyntaxHighlightSupported = prevNode.__isSyntaxHighlightSupported;
|
|
240
|
+
}
|
|
241
|
+
createDOM(config) {
|
|
242
|
+
const element = document.createElement("code");
|
|
243
|
+
addClassNamesToElement(element, config.theme.code);
|
|
244
|
+
element.setAttribute("spellcheck", "false");
|
|
245
|
+
const language = this.getLanguage();
|
|
246
|
+
if (language) {
|
|
247
|
+
element.setAttribute(LANGUAGE_DATA_ATTRIBUTE$1, language);
|
|
248
|
+
if (this.getIsSyntaxHighlightSupported()) element.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
|
|
249
|
+
}
|
|
250
|
+
const theme = this.getTheme();
|
|
251
|
+
if (theme) element.setAttribute(THEME_DATA_ATTRIBUTE, theme);
|
|
252
|
+
const style = this.getStyle();
|
|
253
|
+
if (style) setDOMStyleFromCSS(element.style, style);
|
|
254
|
+
return element;
|
|
255
|
+
}
|
|
256
|
+
updateDOM(prevNode, dom, config) {
|
|
257
|
+
const language = this.__language;
|
|
258
|
+
const prevLanguage = prevNode.__language;
|
|
259
|
+
if (language) {
|
|
260
|
+
if (language !== prevLanguage) dom.setAttribute(LANGUAGE_DATA_ATTRIBUTE$1, language);
|
|
261
|
+
} else if (prevLanguage) dom.removeAttribute(LANGUAGE_DATA_ATTRIBUTE$1);
|
|
262
|
+
const isSyntaxHighlightSupported = this.__isSyntaxHighlightSupported;
|
|
263
|
+
if (prevNode.__isSyntaxHighlightSupported && prevLanguage) if (isSyntaxHighlightSupported && language) {
|
|
264
|
+
if (language !== prevLanguage) dom.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
|
|
265
|
+
} else dom.removeAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE);
|
|
266
|
+
else if (isSyntaxHighlightSupported && language) dom.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
|
|
267
|
+
const theme = this.__theme;
|
|
268
|
+
const prevTheme = prevNode.__theme;
|
|
269
|
+
if (theme) {
|
|
270
|
+
if (theme !== prevTheme) dom.setAttribute(THEME_DATA_ATTRIBUTE, theme);
|
|
271
|
+
} else if (prevTheme) dom.removeAttribute(THEME_DATA_ATTRIBUTE);
|
|
272
|
+
const style = this.__style;
|
|
273
|
+
const prevStyle = prevNode.__style;
|
|
274
|
+
if (style !== prevStyle) setDOMStyleFromCSS(dom.style, style, prevStyle);
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
exportDOM(editor) {
|
|
278
|
+
const element = document.createElement("pre");
|
|
279
|
+
addClassNamesToElement(element, editor._config.theme.code);
|
|
280
|
+
element.setAttribute("spellcheck", "false");
|
|
281
|
+
const language = this.getLanguage();
|
|
282
|
+
if (language) {
|
|
283
|
+
element.setAttribute(LANGUAGE_DATA_ATTRIBUTE$1, language);
|
|
284
|
+
if (this.getIsSyntaxHighlightSupported()) element.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
|
|
285
|
+
}
|
|
286
|
+
const theme = this.getTheme();
|
|
287
|
+
if (theme) element.setAttribute(THEME_DATA_ATTRIBUTE, theme);
|
|
288
|
+
const style = this.getStyle();
|
|
289
|
+
if (style) setDOMStyleFromCSS(element.style, style);
|
|
290
|
+
return { element };
|
|
291
|
+
}
|
|
292
|
+
static importDOM() {
|
|
293
|
+
return {
|
|
294
|
+
code: (node) => {
|
|
295
|
+
return node.textContent != null && (/\r?\n/.test(node.textContent) || hasChildDOMNodeTag(node, "BR")) ? {
|
|
296
|
+
conversion: $convertPreElement,
|
|
297
|
+
priority: 1
|
|
298
|
+
} : null;
|
|
299
|
+
},
|
|
300
|
+
div: () => ({
|
|
301
|
+
conversion: $convertDivElement,
|
|
302
|
+
priority: 1
|
|
303
|
+
}),
|
|
304
|
+
pre: () => ({
|
|
305
|
+
conversion: $convertPreElement,
|
|
306
|
+
priority: 0
|
|
307
|
+
}),
|
|
308
|
+
table: (node) => {
|
|
309
|
+
if (isGitHubCodeTable(node)) return {
|
|
310
|
+
conversion: $convertTableElement,
|
|
311
|
+
priority: 3
|
|
312
|
+
};
|
|
313
|
+
return null;
|
|
314
|
+
},
|
|
315
|
+
td: (node) => {
|
|
316
|
+
const td = node;
|
|
317
|
+
const table = td.closest("table");
|
|
318
|
+
if (isGitHubCodeCell(td) || table && isGitHubCodeTable(table)) return {
|
|
319
|
+
conversion: convertCodeNoop,
|
|
320
|
+
priority: 3
|
|
321
|
+
};
|
|
322
|
+
return null;
|
|
323
|
+
},
|
|
324
|
+
tr: (node) => {
|
|
325
|
+
const table = node.closest("table");
|
|
326
|
+
if (table && isGitHubCodeTable(table)) return {
|
|
327
|
+
conversion: convertCodeNoop,
|
|
328
|
+
priority: 3
|
|
329
|
+
};
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
static importJSON(serializedNode) {
|
|
335
|
+
return $createCodeNode().updateFromJSON(serializedNode);
|
|
336
|
+
}
|
|
337
|
+
updateFromJSON(serializedNode) {
|
|
338
|
+
return super.updateFromJSON(serializedNode).setLanguage(serializedNode.language).setTheme(serializedNode.theme);
|
|
339
|
+
}
|
|
340
|
+
exportJSON() {
|
|
341
|
+
return {
|
|
342
|
+
...super.exportJSON(),
|
|
343
|
+
language: this.getLanguage(),
|
|
344
|
+
theme: this.getTheme()
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
insertNewAfter(selection, restoreSelection = true) {
|
|
348
|
+
if (!getPeerDependencyFromEditor($getEditor(), "@lexical/code")) {
|
|
349
|
+
noExtensionDeprecation();
|
|
350
|
+
const el = $exitCodeNodeOnEnter(selection);
|
|
351
|
+
if (el) return el;
|
|
352
|
+
}
|
|
353
|
+
const { anchor, focus } = selection;
|
|
354
|
+
const firstSelectionNode = (anchor.isBefore(focus) ? anchor : focus).getNode();
|
|
355
|
+
if ($isTextNode(firstSelectionNode)) {
|
|
356
|
+
let node = $getFirstCodeNodeOfLine(firstSelectionNode);
|
|
357
|
+
const insertNodes = [];
|
|
358
|
+
while (true) if ($isTabNode(node)) {
|
|
359
|
+
insertNodes.push($createTabNode());
|
|
360
|
+
node = node.getNextSibling();
|
|
361
|
+
} else if ($isCodeHighlightNode(node)) {
|
|
362
|
+
let spaces = 0;
|
|
363
|
+
const text = node.getTextContent();
|
|
364
|
+
const textSize = node.getTextContentSize();
|
|
365
|
+
while (spaces < textSize && text[spaces] === " ") spaces++;
|
|
366
|
+
if (spaces !== 0) insertNodes.push($createCodeHighlightNode(" ".repeat(spaces)));
|
|
367
|
+
if (spaces !== textSize) break;
|
|
368
|
+
node = node.getNextSibling();
|
|
369
|
+
} else break;
|
|
370
|
+
const split = firstSelectionNode.splitText(anchor.offset)[0];
|
|
371
|
+
const x = anchor.offset === 0 ? 0 : 1;
|
|
372
|
+
const index = split.getIndexWithinParent() + x;
|
|
373
|
+
const codeNode = firstSelectionNode.getParentOrThrow();
|
|
374
|
+
const nodesToInsert = [$createLineBreakNode(), ...insertNodes];
|
|
375
|
+
codeNode.splice(index, 0, nodesToInsert);
|
|
376
|
+
const last = insertNodes[insertNodes.length - 1];
|
|
377
|
+
if (last) last.select();
|
|
378
|
+
else if (anchor.offset === 0) split.selectPrevious();
|
|
379
|
+
else split.getNextSibling().selectNext(0, 0);
|
|
380
|
+
}
|
|
381
|
+
if ($isCodeNode(firstSelectionNode)) {
|
|
382
|
+
const { offset } = selection.anchor;
|
|
383
|
+
firstSelectionNode.splice(offset, 0, [$createLineBreakNode()]);
|
|
384
|
+
firstSelectionNode.select(offset + 1, offset + 1);
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
canIndent() {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
collapseAtStart() {
|
|
392
|
+
const paragraph = $createParagraphNode();
|
|
393
|
+
this.getChildren().forEach((child) => paragraph.append(child));
|
|
394
|
+
this.replace(paragraph);
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
setLanguage(language) {
|
|
398
|
+
const writable = this.getWritable();
|
|
399
|
+
writable.__language = language || void 0;
|
|
400
|
+
return writable;
|
|
401
|
+
}
|
|
402
|
+
getLanguage() {
|
|
403
|
+
return this.getLatest().__language;
|
|
404
|
+
}
|
|
405
|
+
setIsSyntaxHighlightSupported(isSupported) {
|
|
406
|
+
const writable = this.getWritable();
|
|
407
|
+
writable.__isSyntaxHighlightSupported = isSupported;
|
|
408
|
+
return writable;
|
|
409
|
+
}
|
|
410
|
+
getIsSyntaxHighlightSupported() {
|
|
411
|
+
return this.getLatest().__isSyntaxHighlightSupported;
|
|
412
|
+
}
|
|
413
|
+
setTheme(theme) {
|
|
414
|
+
const writable = this.getWritable();
|
|
415
|
+
writable.__theme = theme || void 0;
|
|
416
|
+
return writable;
|
|
417
|
+
}
|
|
418
|
+
getTheme() {
|
|
419
|
+
return this.getLatest().__theme;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
function $createCodeNode(language, theme) {
|
|
423
|
+
return $create(CodeNode).setLanguage(language).setTheme(theme);
|
|
424
|
+
}
|
|
425
|
+
function $isCodeNode(node) {
|
|
426
|
+
return node instanceof CodeNode;
|
|
427
|
+
}
|
|
428
|
+
function $convertPreElement(domNode) {
|
|
429
|
+
return { node: $createCodeNode(domNode.getAttribute(LANGUAGE_DATA_ATTRIBUTE$1)) };
|
|
430
|
+
}
|
|
431
|
+
function $convertDivElement(domNode) {
|
|
432
|
+
const div = domNode;
|
|
433
|
+
const isCode = isCodeElement(div);
|
|
434
|
+
if (!isCode && !isCodeChildElement(div)) return { node: null };
|
|
435
|
+
return { node: isCode ? $createCodeNode() : null };
|
|
436
|
+
}
|
|
437
|
+
function $convertTableElement() {
|
|
438
|
+
return { node: $createCodeNode() };
|
|
439
|
+
}
|
|
440
|
+
function convertCodeNoop() {
|
|
441
|
+
return { node: null };
|
|
442
|
+
}
|
|
443
|
+
function isCodeElement(div) {
|
|
444
|
+
return div.style.fontFamily.match("monospace") !== null;
|
|
445
|
+
}
|
|
446
|
+
function isCodeChildElement(node) {
|
|
447
|
+
let parent = node.parentElement;
|
|
448
|
+
while (parent !== null) {
|
|
449
|
+
if (isCodeElement(parent)) return true;
|
|
450
|
+
parent = parent.parentElement;
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
function isGitHubCodeCell(cell) {
|
|
455
|
+
return cell.classList.contains("js-file-line");
|
|
456
|
+
}
|
|
457
|
+
function isGitHubCodeTable(table) {
|
|
458
|
+
return table.classList.contains("js-file-line-container");
|
|
459
|
+
}
|
|
460
|
+
function $exitCodeNodeOnEnter(selection) {
|
|
461
|
+
const { anchor } = selection;
|
|
462
|
+
if (selection.isCollapsed() && anchor.type === "element") {
|
|
463
|
+
const codeNode = anchor.getNode();
|
|
464
|
+
if ($isCodeNode(codeNode)) {
|
|
465
|
+
const childrenSize = codeNode.getChildrenSize();
|
|
466
|
+
if (childrenSize >= 2 && anchor.offset === childrenSize) {
|
|
467
|
+
const lastChild = codeNode.getLastChild();
|
|
468
|
+
if ($isLineBreakNode(lastChild) && $isLineBreakNode(lastChild.getPreviousSibling())) {
|
|
469
|
+
const newElement = $createParagraphNode();
|
|
470
|
+
codeNode.splice(childrenSize - 2, 2, []).insertAfter(newElement, false);
|
|
471
|
+
newElement.select();
|
|
472
|
+
return newElement;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
481
|
+
*
|
|
482
|
+
* This source code is licensed under the MIT license found in the
|
|
483
|
+
* LICENSE file in the root directory of this source tree.
|
|
484
|
+
*
|
|
485
|
+
*/
|
|
486
|
+
/** @noInheritDoc */
|
|
487
|
+
var CodeHighlightNode = class CodeHighlightNode extends TextNode {
|
|
488
|
+
/** @internal */
|
|
489
|
+
__highlightType;
|
|
490
|
+
constructor(text = "", highlightType, key) {
|
|
491
|
+
super(text, key);
|
|
492
|
+
this.__highlightType = highlightType;
|
|
493
|
+
}
|
|
494
|
+
static getType() {
|
|
495
|
+
return "code-highlight";
|
|
496
|
+
}
|
|
497
|
+
static clone(node) {
|
|
498
|
+
return new CodeHighlightNode(node.__text, node.__highlightType || void 0, node.__key);
|
|
499
|
+
}
|
|
500
|
+
afterCloneFrom(prevNode) {
|
|
501
|
+
super.afterCloneFrom(prevNode);
|
|
502
|
+
this.__highlightType = prevNode.__highlightType;
|
|
503
|
+
}
|
|
504
|
+
getHighlightType() {
|
|
505
|
+
return this.getLatest().__highlightType;
|
|
506
|
+
}
|
|
507
|
+
setHighlightType(highlightType) {
|
|
508
|
+
const self = this.getWritable();
|
|
509
|
+
self.__highlightType = highlightType || void 0;
|
|
510
|
+
return self;
|
|
511
|
+
}
|
|
512
|
+
canHaveFormat() {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
createDOM(config) {
|
|
516
|
+
const element = super.createDOM(config);
|
|
517
|
+
addClassNamesToElement(element, getHighlightThemeClass(config.theme, this.__highlightType));
|
|
518
|
+
return element;
|
|
519
|
+
}
|
|
520
|
+
updateDOM(prevNode, dom, config) {
|
|
521
|
+
const update = super.updateDOM(prevNode, dom, config);
|
|
522
|
+
const prevClassName = getHighlightThemeClass(config.theme, prevNode.__highlightType);
|
|
523
|
+
const nextClassName = getHighlightThemeClass(config.theme, this.__highlightType);
|
|
524
|
+
if (prevClassName !== nextClassName) {
|
|
525
|
+
if (prevClassName) removeClassNamesFromElement(dom, prevClassName);
|
|
526
|
+
if (nextClassName) addClassNamesToElement(dom, nextClassName);
|
|
527
|
+
}
|
|
528
|
+
return update;
|
|
529
|
+
}
|
|
530
|
+
static importJSON(serializedNode) {
|
|
531
|
+
return $createCodeHighlightNode().updateFromJSON(serializedNode);
|
|
532
|
+
}
|
|
533
|
+
updateFromJSON(serializedNode) {
|
|
534
|
+
return super.updateFromJSON(serializedNode).setHighlightType(serializedNode.highlightType);
|
|
535
|
+
}
|
|
536
|
+
exportJSON() {
|
|
537
|
+
return {
|
|
538
|
+
...super.exportJSON(),
|
|
539
|
+
highlightType: this.getHighlightType()
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
setFormat(format) {
|
|
543
|
+
return this;
|
|
544
|
+
}
|
|
545
|
+
isParentRequired() {
|
|
546
|
+
return true;
|
|
547
|
+
}
|
|
548
|
+
createParentElementNode() {
|
|
549
|
+
return $createCodeNode();
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
function getHighlightThemeClass(theme, highlightType) {
|
|
553
|
+
return highlightType && theme && theme.codeHighlight && theme.codeHighlight[highlightType];
|
|
554
|
+
}
|
|
555
|
+
function $createCodeHighlightNode(text = "", highlightType) {
|
|
556
|
+
return $applyNodeReplacement(new CodeHighlightNode(text, highlightType));
|
|
557
|
+
}
|
|
558
|
+
function $isCodeHighlightNode(node) {
|
|
559
|
+
return node instanceof CodeHighlightNode;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
563
|
+
*
|
|
564
|
+
* This source code is licensed under the MIT license found in the
|
|
565
|
+
* LICENSE file in the root directory of this source tree.
|
|
566
|
+
*
|
|
567
|
+
*/
|
|
568
|
+
/**
|
|
569
|
+
* Add code blocks to the editor (syntax highlighting provided separately)
|
|
570
|
+
*/
|
|
571
|
+
const CodeExtension = defineExtension({
|
|
572
|
+
name: "@lexical/code",
|
|
573
|
+
nodes: () => [CodeNode, CodeHighlightNode],
|
|
574
|
+
register(editor) {
|
|
575
|
+
return editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
|
|
576
|
+
const selection = $getSelection();
|
|
577
|
+
if ($isRangeSelection(selection) && $exitCodeNodeOnEnter(selection)) {
|
|
578
|
+
event.preventDefault();
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
return false;
|
|
582
|
+
}, COMMAND_PRIORITY_LOW);
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
/**
|
|
586
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
587
|
+
*
|
|
588
|
+
* This source code is licensed under the MIT license found in the
|
|
589
|
+
* LICENSE file in the root directory of this source tree.
|
|
590
|
+
*
|
|
591
|
+
*/
|
|
592
|
+
const LANGUAGE_DATA_ATTRIBUTE = "data-language";
|
|
593
|
+
/**
|
|
594
|
+
* True for elements whose `font-family` mentions `monospace` — the
|
|
595
|
+
* heuristic the legacy `<div>` rule uses to spot copy-pasted code blocks
|
|
596
|
+
* (e.g. Google Docs serializes a code block as a styled `<div>`).
|
|
597
|
+
*/
|
|
598
|
+
function isMonospaceElement(el) {
|
|
599
|
+
return el.style.fontFamily.match("monospace") !== null;
|
|
600
|
+
}
|
|
601
|
+
function isMonospaceDescendant(node) {
|
|
602
|
+
let parent = node.parentElement;
|
|
603
|
+
while (parent !== null) {
|
|
604
|
+
if (isMonospaceElement(parent)) return true;
|
|
605
|
+
parent = parent.parentElement;
|
|
606
|
+
}
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Overlay rules active only while {@link GitHubCodeTableRule} is
|
|
611
|
+
* processing its children. Inside the code-table subtree, every `<tr>`
|
|
612
|
+
* and `<td>` unwraps unconditionally — they never become table-row /
|
|
613
|
+
* table-cell nodes (even when `@lexical/table` registers its rules for
|
|
614
|
+
* those tags). Outside the subtree, this overlay isn't installed, so
|
|
615
|
+
* the cost of these rules is never paid against unrelated `<tr>` /
|
|
616
|
+
* `<td>` pastes.
|
|
617
|
+
*/
|
|
618
|
+
const GitHubCodeTableOverlayRules = defineOverlayRules([defineImportRule({
|
|
619
|
+
$import: (ctx, el) => ctx.$importChildren(el),
|
|
620
|
+
match: sel.tag("tr", "td"),
|
|
621
|
+
name: "@lexical/code/github-code-table/unwrap"
|
|
622
|
+
})]);
|
|
623
|
+
const PreRule = defineImportRule({
|
|
624
|
+
$import: (ctx, el) => [$createCodeNode(el.getAttribute(LANGUAGE_DATA_ATTRIBUTE)).splice(0, 0, ctx.$importChildren(el))],
|
|
625
|
+
match: sel.tag("pre"),
|
|
626
|
+
name: "@lexical/code/pre"
|
|
627
|
+
});
|
|
628
|
+
/**
|
|
629
|
+
* Multi-line `<code>` (containing newlines or `<br>`) is treated as a
|
|
630
|
+
* block code element — mirrors the legacy behavior. Single-line `<code>`
|
|
631
|
+
* defers to the inline-format rule from `CoreImportExtension` so it
|
|
632
|
+
* becomes a TextNode with IS_CODE.
|
|
633
|
+
*/
|
|
634
|
+
const MultilineCodeRule = defineImportRule({
|
|
635
|
+
$import: (ctx, el, $next) => {
|
|
636
|
+
const text = el.textContent || "";
|
|
637
|
+
if (!(/\r?\n/.test(text) || el.querySelector("br") !== null)) return $next();
|
|
638
|
+
return [$createCodeNode(el.getAttribute(LANGUAGE_DATA_ATTRIBUTE)).splice(0, 0, ctx.$importChildren(el))];
|
|
639
|
+
},
|
|
640
|
+
match: sel.tag("code"),
|
|
641
|
+
name: "@lexical/code/code-multiline"
|
|
642
|
+
});
|
|
643
|
+
/**
|
|
644
|
+
* True for elements carrying BOTH `font-family: …monospace…` and
|
|
645
|
+
* `white-space: pre*` inline — the shape VS Code uses for every line
|
|
646
|
+
* of a copied code block (on every per-line `<div>` on Safari, on
|
|
647
|
+
* the single outer wrapper on Chrome).
|
|
648
|
+
*/
|
|
649
|
+
function isMonospacePreElement(el) {
|
|
650
|
+
if (!isHTMLElement(el)) return false;
|
|
651
|
+
const ff = el.style.fontFamily;
|
|
652
|
+
const ws = el.style.whiteSpace;
|
|
653
|
+
return typeof ff === "string" && /monospace/i.test(ff) && typeof ws === "string" && ws.startsWith("pre");
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Split a monospace-pre wrapper element into logical code lines:
|
|
657
|
+
* `<div>` children contribute their text content as one line,
|
|
658
|
+
* `<br>` children contribute an empty line, inline children (spans
|
|
659
|
+
* and bare text) accumulate into the current line until the next
|
|
660
|
+
* block child.
|
|
661
|
+
*
|
|
662
|
+
* Returns `null` if `el` has no block children (i.e. it's a leaf
|
|
663
|
+
* line, not a wrapper) so the caller can leave it to the
|
|
664
|
+
* sibling-run pass.
|
|
665
|
+
*/
|
|
666
|
+
function splitMonospaceWrapperLines(el) {
|
|
667
|
+
let hasBlockChild = false;
|
|
668
|
+
const lines = [];
|
|
669
|
+
let acc = "";
|
|
670
|
+
let hasAcc = false;
|
|
671
|
+
const flush = () => {
|
|
672
|
+
if (hasAcc) {
|
|
673
|
+
lines.push(acc);
|
|
674
|
+
acc = "";
|
|
675
|
+
hasAcc = false;
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
for (const child of Array.from(el.childNodes)) if (isHTMLElement(child)) if (child.tagName === "DIV") {
|
|
679
|
+
flush();
|
|
680
|
+
lines.push(child.textContent || "");
|
|
681
|
+
hasBlockChild = true;
|
|
682
|
+
} else if (child.tagName === "BR") {
|
|
683
|
+
flush();
|
|
684
|
+
lines.push("");
|
|
685
|
+
hasBlockChild = true;
|
|
686
|
+
} else {
|
|
687
|
+
acc += child.textContent || "";
|
|
688
|
+
hasAcc = true;
|
|
689
|
+
}
|
|
690
|
+
else if (isDOMTextNode(child)) {
|
|
691
|
+
const t = child.textContent || "";
|
|
692
|
+
if (t.length > 0) {
|
|
693
|
+
acc += t;
|
|
694
|
+
hasAcc = true;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
flush();
|
|
698
|
+
return hasBlockChild ? lines : null;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Returns `true` if `root` contains the structural signature of a
|
|
702
|
+
* VS Code code-block paste:
|
|
703
|
+
*
|
|
704
|
+
* - a monospace+pre `<div>` wrapper with at least one block (`<div>` /
|
|
705
|
+
* `<br>`) child — the Chrome shape, or
|
|
706
|
+
* - two or more consecutive monospace+pre siblings — the Safari shape.
|
|
707
|
+
*
|
|
708
|
+
* Walked once in preprocess; the matching overlay is only installed
|
|
709
|
+
* when this returns `true` so an unrelated paste doesn't pay for the
|
|
710
|
+
* detection or rule cost.
|
|
711
|
+
*/
|
|
712
|
+
function looksLikeVscodePaste(root) {
|
|
713
|
+
for (const child of Array.from(root.children)) {
|
|
714
|
+
if (isHTMLElement(child) && isMonospacePreElement(child)) {
|
|
715
|
+
if (splitMonospaceWrapperLines(child) !== null) return true;
|
|
716
|
+
const next = child.nextElementSibling;
|
|
717
|
+
if (next && isMonospacePreElement(next)) return true;
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (looksLikeVscodePaste(child)) return true;
|
|
721
|
+
}
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
const VscodeCodePasteOverlay = defineOverlayRules([defineImportRule({
|
|
725
|
+
$import: (_ctx, el, $next) => {
|
|
726
|
+
if (!isMonospacePreElement(el) || isMonospaceDescendant(el)) return $next();
|
|
727
|
+
const lines = splitMonospaceWrapperLines(el);
|
|
728
|
+
if (lines === null || lines.length === 0) return $next();
|
|
729
|
+
return [$createCodeNode().splice(0, 0, $generateNodesFromRawText(lines.join("\n")))];
|
|
730
|
+
},
|
|
731
|
+
match: sel.tag("div"),
|
|
732
|
+
name: "@lexical/code/vscode-wrapper"
|
|
733
|
+
}), defineImportRule({
|
|
734
|
+
$import: (_ctx, el, $next) => {
|
|
735
|
+
if (!isMonospacePreElement(el) || isMonospaceDescendant(el)) return $next();
|
|
736
|
+
const prev = el.previousElementSibling;
|
|
737
|
+
if (prev && isMonospacePreElement(prev)) return [];
|
|
738
|
+
const lines = [];
|
|
739
|
+
let cur = el;
|
|
740
|
+
while (cur && isMonospacePreElement(cur)) {
|
|
741
|
+
lines.push(cur.tagName === "BR" ? "" : cur.textContent || "");
|
|
742
|
+
cur = cur.nextElementSibling;
|
|
743
|
+
}
|
|
744
|
+
if (lines.length < 2) return $next();
|
|
745
|
+
return [$createCodeNode().splice(0, 0, $generateNodesFromRawText(lines.join("\n")))];
|
|
746
|
+
},
|
|
747
|
+
match: sel.tag("div", "br"),
|
|
748
|
+
name: "@lexical/code/vscode-line-run"
|
|
749
|
+
})]);
|
|
750
|
+
/**
|
|
751
|
+
* VS Code → browser code-block pastes ship the block as either:
|
|
752
|
+
*
|
|
753
|
+
* - **Chrome**: one outer
|
|
754
|
+
* `<div style="font-family: …monospace…; white-space: pre">…</div>`
|
|
755
|
+
* wrapping per-line `<div>`s and `<br>`s.
|
|
756
|
+
* - **Safari**: a flat run of sibling
|
|
757
|
+
* `<div style="…monospace…; white-space: pre">…</div>` and
|
|
758
|
+
* `<br style="…monospace…; …">` elements with no wrapping
|
|
759
|
+
* monospace ancestor (the styles are duplicated onto every
|
|
760
|
+
* element).
|
|
761
|
+
*
|
|
762
|
+
* The legacy `<div>` rule (and {@link DivRule}) produces one CodeNode
|
|
763
|
+
* per `<div>` on Safari and concatenates inner divs without
|
|
764
|
+
* separating `\n`s on Chrome. This preprocess scans once for the
|
|
765
|
+
* structural signature and, only when it matches, pushes
|
|
766
|
+
* {@link VscodeCodePasteOverlay} onto {@link ImportOverlays} so the
|
|
767
|
+
* VS Code-specific rules participate in the walk. Pastes from other
|
|
768
|
+
* sources pay only the detection cost.
|
|
769
|
+
*
|
|
770
|
+
* @experimental
|
|
771
|
+
*/
|
|
772
|
+
const $installVscodeCodePasteOverlay = (dom, ctx, $next) => {
|
|
773
|
+
if (looksLikeVscodePaste(isDOMDocumentNode(dom) ? dom.body : dom)) ctx.session.update(ImportOverlays, (prev) => [...prev, VscodeCodePasteOverlay]);
|
|
774
|
+
$next();
|
|
775
|
+
};
|
|
776
|
+
/**
|
|
777
|
+
* A `<div style="font-family: …monospace…">` (Google-Docs-style code
|
|
778
|
+
* block) creates a CodeNode. Descendant elements inside a monospace
|
|
779
|
+
* wrapper just unwrap so their text content flows into the surrounding
|
|
780
|
+
* CodeNode.
|
|
781
|
+
*/
|
|
782
|
+
const DivRule = defineImportRule({
|
|
783
|
+
$import: (ctx, el, $next) => {
|
|
784
|
+
if (isMonospaceElement(el)) return [$createCodeNode().splice(0, 0, ctx.$importChildren(el))];
|
|
785
|
+
if (isMonospaceDescendant(el)) return ctx.$importChildren(el);
|
|
786
|
+
return $next();
|
|
787
|
+
},
|
|
788
|
+
match: sel.tag("div"),
|
|
789
|
+
name: "@lexical/code/div"
|
|
790
|
+
});
|
|
791
|
+
/**
|
|
792
|
+
* Import rules for {@link CodeNode}.
|
|
793
|
+
*
|
|
794
|
+
* Specific class-restricted rules (GitHub raw-file-view detectors) are
|
|
795
|
+
* registered before the generic `<table>` / `<tr>` / `<td>` rules so
|
|
796
|
+
* they win dispatch.
|
|
797
|
+
*
|
|
798
|
+
* @experimental
|
|
799
|
+
*/
|
|
800
|
+
const CodeImportRules = [
|
|
801
|
+
defineImportRule({
|
|
802
|
+
$import: (ctx, el) => [$createCodeNode().splice(0, 0, ctx.$importChildren(el, { rules: GitHubCodeTableOverlayRules }))],
|
|
803
|
+
match: sel.tag("table").classAll("js-file-line-container"),
|
|
804
|
+
name: "@lexical/code/github-code-table"
|
|
805
|
+
}),
|
|
806
|
+
defineImportRule({
|
|
807
|
+
$import: (ctx, el) => ctx.$importChildren(el),
|
|
808
|
+
match: sel.tag("td").classAll("js-file-line"),
|
|
809
|
+
name: "@lexical/code/github-code-cell-by-class"
|
|
810
|
+
}),
|
|
811
|
+
MultilineCodeRule,
|
|
812
|
+
PreRule,
|
|
813
|
+
DivRule
|
|
814
|
+
];
|
|
815
|
+
/**
|
|
816
|
+
* Bundles {@link CodeImportRules} (plus {@link CoreImportExtension}) into
|
|
817
|
+
* a single dependency. The legacy {@link CodeNode.importDOM} continues to
|
|
818
|
+
* work in parallel; depend on this extension to opt into the new
|
|
819
|
+
* pipeline.
|
|
820
|
+
*
|
|
821
|
+
* @experimental
|
|
822
|
+
*/
|
|
823
|
+
const CodeImportExtension = defineExtension({
|
|
824
|
+
dependencies: [
|
|
825
|
+
CoreImportExtension,
|
|
826
|
+
CodeExtension,
|
|
827
|
+
configExtension(DOMImportExtension, {
|
|
828
|
+
preprocess: [$installVscodeCodePasteOverlay],
|
|
829
|
+
rules: CodeImportRules
|
|
830
|
+
})
|
|
831
|
+
],
|
|
832
|
+
name: "@lexical/code/Import"
|
|
833
|
+
});
|
|
834
|
+
function $isSelectionInCode(selection) {
|
|
835
|
+
if (!$isRangeSelection(selection)) return false;
|
|
836
|
+
const anchorNode = selection.anchor.getNode();
|
|
837
|
+
const maybeAnchorCodeNode = $isCodeNode(anchorNode) ? anchorNode : anchorNode.getParent();
|
|
838
|
+
const focusNode = selection.focus.getNode();
|
|
839
|
+
const maybeFocusCodeNode = $isCodeNode(focusNode) ? focusNode : focusNode.getParent();
|
|
840
|
+
return $isCodeNode(maybeAnchorCodeNode) && maybeAnchorCodeNode.is(maybeFocusCodeNode);
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Returns an Array of code lines
|
|
844
|
+
* Take the sequence of LineBreakNode | TabNode | CodeHighlightNode forming
|
|
845
|
+
* the selection and split it by LineBreakNode.
|
|
846
|
+
* If the selection ends at the start of the last line, it is considered empty.
|
|
847
|
+
* Empty lines are discarded.
|
|
848
|
+
*/
|
|
849
|
+
function $getCodeLines(selection) {
|
|
850
|
+
const nodes = selection.getNodes();
|
|
851
|
+
const lines = [];
|
|
852
|
+
if (nodes.length === 1 && $isCodeNode(nodes[0])) return lines;
|
|
853
|
+
let lastLine = [];
|
|
854
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
855
|
+
const node = nodes[i];
|
|
856
|
+
if (!($isCodeHighlightNode(node) || $isTabNode(node) || $isLineBreakNode(node))) formatDevErrorMessage(`Expected selection to be inside CodeBlock and consisting of CodeHighlightNode, TabNode and LineBreakNode`);
|
|
857
|
+
if ($isLineBreakNode(node)) {
|
|
858
|
+
if (lastLine.length > 0) {
|
|
859
|
+
lines.push(lastLine);
|
|
860
|
+
lastLine = [];
|
|
861
|
+
}
|
|
862
|
+
} else lastLine.push(node);
|
|
863
|
+
}
|
|
864
|
+
if (lastLine.length > 0) {
|
|
865
|
+
const selectionEnd = selection.isBackward() ? selection.anchor : selection.focus;
|
|
866
|
+
const lastPoint = $createPoint(lastLine[0].getKey(), 0, "text");
|
|
867
|
+
if (!selectionEnd.is(lastPoint)) lines.push(lastLine);
|
|
868
|
+
}
|
|
869
|
+
return lines;
|
|
870
|
+
}
|
|
871
|
+
function $handleTab(shiftKey) {
|
|
872
|
+
const selection = $getSelection();
|
|
873
|
+
if (!$isRangeSelection(selection) || !$isSelectionInCode(selection)) return null;
|
|
874
|
+
const indentOrOutdent = !shiftKey ? INDENT_CONTENT_COMMAND : OUTDENT_CONTENT_COMMAND;
|
|
875
|
+
const tabOrOutdent = !shiftKey ? INSERT_TAB_COMMAND : OUTDENT_CONTENT_COMMAND;
|
|
876
|
+
const anchor = selection.anchor;
|
|
877
|
+
const focus = selection.focus;
|
|
878
|
+
if (anchor.is(focus)) return tabOrOutdent;
|
|
879
|
+
const codeLines = $getCodeLines(selection);
|
|
880
|
+
if (codeLines.length !== 1) return indentOrOutdent;
|
|
881
|
+
const codeLine = codeLines[0];
|
|
882
|
+
if (!(codeLine.length !== 0)) formatDevErrorMessage(`$getCodeLines only extracts non-empty lines`);
|
|
883
|
+
let selectionFirst;
|
|
884
|
+
let selectionLast;
|
|
885
|
+
if (selection.isBackward()) {
|
|
886
|
+
selectionFirst = focus;
|
|
887
|
+
selectionLast = anchor;
|
|
888
|
+
} else {
|
|
889
|
+
selectionFirst = anchor;
|
|
890
|
+
selectionLast = focus;
|
|
891
|
+
}
|
|
892
|
+
const firstOfLine = $getFirstCodeNodeOfLine(codeLine[0]);
|
|
893
|
+
const lastOfLine = $getLastCodeNodeOfLine(codeLine[0]);
|
|
894
|
+
const anchorOfLine = $createPoint(firstOfLine.getKey(), 0, "text");
|
|
895
|
+
const focusOfLine = $createPoint(lastOfLine.getKey(), lastOfLine.getTextContentSize(), "text");
|
|
896
|
+
if (selectionFirst.isBefore(anchorOfLine)) return indentOrOutdent;
|
|
897
|
+
if (focusOfLine.isBefore(selectionLast)) return indentOrOutdent;
|
|
898
|
+
if (anchorOfLine.isBefore(selectionFirst) || selectionLast.isBefore(focusOfLine)) return tabOrOutdent;
|
|
899
|
+
return indentOrOutdent;
|
|
900
|
+
}
|
|
901
|
+
function $handleMultilineIndent(type, tabSize) {
|
|
902
|
+
const selection = $getSelection();
|
|
903
|
+
if (!$isRangeSelection(selection) || !$isSelectionInCode(selection)) return false;
|
|
904
|
+
const codeLines = $getCodeLines(selection);
|
|
905
|
+
const codeLinesLength = codeLines.length;
|
|
906
|
+
if (codeLinesLength === 0 && selection.isCollapsed()) {
|
|
907
|
+
if (type === INDENT_CONTENT_COMMAND) selection.insertNodes([$createTabNode()]);
|
|
908
|
+
return true;
|
|
909
|
+
}
|
|
910
|
+
if (codeLinesLength === 0 && type === INDENT_CONTENT_COMMAND && selection.getTextContent() === "\n") {
|
|
911
|
+
const tabNode = $createTabNode();
|
|
912
|
+
const lineBreakNode = $createLineBreakNode();
|
|
913
|
+
const direction = selection.isBackward() ? "previous" : "next";
|
|
914
|
+
selection.insertNodes([tabNode, lineBreakNode]);
|
|
915
|
+
$setSelectionFromCaretRange($getCaretRangeInDirection($getCaretRange($getTextPointCaret(tabNode, "next", 0), $normalizeCaret($getSiblingCaret(lineBreakNode, "next"))), direction));
|
|
916
|
+
return true;
|
|
917
|
+
}
|
|
918
|
+
for (let i = 0; i < codeLinesLength; i++) {
|
|
919
|
+
const line = codeLines[i];
|
|
920
|
+
if (line.length > 0) {
|
|
921
|
+
let firstOfLine = line[0];
|
|
922
|
+
if (i === 0) firstOfLine = $getFirstCodeNodeOfLine(firstOfLine);
|
|
923
|
+
if (type === INDENT_CONTENT_COMMAND) {
|
|
924
|
+
const tabNode = $createTabNode();
|
|
925
|
+
firstOfLine.insertBefore(tabNode);
|
|
926
|
+
if (i === 0) {
|
|
927
|
+
const anchorKey = selection.isBackward() ? "focus" : "anchor";
|
|
928
|
+
const anchorLine = $createPoint(firstOfLine.getKey(), 0, "text");
|
|
929
|
+
if (selection[anchorKey].is(anchorLine)) selection[anchorKey].set(tabNode.getKey(), 0, "text");
|
|
930
|
+
}
|
|
931
|
+
} else if ($isTabNode(firstOfLine)) firstOfLine.remove();
|
|
932
|
+
else if (tabSize !== void 0 && $isCodeHighlightNode(firstOfLine)) $outdentLeadingSpaces(firstOfLine, tabSize, selection);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
937
|
+
function $handleShiftLines(type, event) {
|
|
938
|
+
const selection = $getSelection();
|
|
939
|
+
if (!$isRangeSelection(selection)) return false;
|
|
940
|
+
const { anchor, focus } = selection;
|
|
941
|
+
const anchorOffset = anchor.offset;
|
|
942
|
+
const focusOffset = focus.offset;
|
|
943
|
+
const anchorNode = anchor.getNode();
|
|
944
|
+
const focusNode = focus.getNode();
|
|
945
|
+
const arrowIsUp = type === KEY_ARROW_UP_COMMAND;
|
|
946
|
+
if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))) return false;
|
|
947
|
+
if (!event.altKey) {
|
|
948
|
+
if (selection.isCollapsed()) {
|
|
949
|
+
const codeNode = anchorNode.getParentOrThrow();
|
|
950
|
+
if (arrowIsUp && anchorOffset === 0 && anchorNode.getPreviousSibling() === null) {
|
|
951
|
+
if (codeNode.getPreviousSibling() === null) {
|
|
952
|
+
codeNode.selectPrevious();
|
|
953
|
+
event.preventDefault();
|
|
954
|
+
return true;
|
|
955
|
+
}
|
|
956
|
+
} else if (!arrowIsUp && anchorOffset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null) {
|
|
957
|
+
if (codeNode.getNextSibling() === null) {
|
|
958
|
+
codeNode.selectNext();
|
|
959
|
+
event.preventDefault();
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
let start;
|
|
967
|
+
let end;
|
|
968
|
+
if (anchorNode.isBefore(focusNode)) {
|
|
969
|
+
start = $getFirstCodeNodeOfLine(anchorNode);
|
|
970
|
+
end = $getLastCodeNodeOfLine(focusNode);
|
|
971
|
+
} else {
|
|
972
|
+
start = $getFirstCodeNodeOfLine(focusNode);
|
|
973
|
+
end = $getLastCodeNodeOfLine(anchorNode);
|
|
974
|
+
}
|
|
975
|
+
if (start == null || end == null) return false;
|
|
976
|
+
const range = start.getNodesBetween(end);
|
|
977
|
+
for (let i = 0; i < range.length; i++) {
|
|
978
|
+
const node = range[i];
|
|
979
|
+
if (!$isCodeHighlightNode(node) && !$isTabNode(node) && !$isLineBreakNode(node)) return false;
|
|
980
|
+
}
|
|
981
|
+
event.preventDefault();
|
|
982
|
+
event.stopPropagation();
|
|
983
|
+
const linebreak = arrowIsUp ? start.getPreviousSibling() : end.getNextSibling();
|
|
984
|
+
if (!$isLineBreakNode(linebreak)) return true;
|
|
985
|
+
const sibling = arrowIsUp ? linebreak.getPreviousSibling() : linebreak.getNextSibling();
|
|
986
|
+
if (sibling == null) return true;
|
|
987
|
+
const maybeInsertionPoint = $isCodeHighlightNode(sibling) || $isTabNode(sibling) || $isLineBreakNode(sibling) ? arrowIsUp ? $getFirstCodeNodeOfLine(sibling) : $getLastCodeNodeOfLine(sibling) : null;
|
|
988
|
+
let insertionPoint = maybeInsertionPoint != null ? maybeInsertionPoint : sibling;
|
|
989
|
+
linebreak.remove();
|
|
990
|
+
range.forEach((node) => node.remove());
|
|
991
|
+
if (type === KEY_ARROW_UP_COMMAND) {
|
|
992
|
+
range.forEach((node) => insertionPoint.insertBefore(node));
|
|
993
|
+
insertionPoint.insertBefore(linebreak);
|
|
994
|
+
} else {
|
|
995
|
+
insertionPoint.insertAfter(linebreak);
|
|
996
|
+
insertionPoint = linebreak;
|
|
997
|
+
range.forEach((node) => {
|
|
998
|
+
insertionPoint.insertAfter(node);
|
|
999
|
+
insertionPoint = node;
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
selection.setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset);
|
|
1003
|
+
return true;
|
|
1004
|
+
}
|
|
1005
|
+
function $handleMoveTo(type, event) {
|
|
1006
|
+
const selection = $getSelection();
|
|
1007
|
+
if (!$isRangeSelection(selection)) return false;
|
|
1008
|
+
const { anchor, focus } = selection;
|
|
1009
|
+
const anchorNode = anchor.getNode();
|
|
1010
|
+
const focusNode = focus.getNode();
|
|
1011
|
+
const isMoveToStart = type === MOVE_TO_START;
|
|
1012
|
+
if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))) return false;
|
|
1013
|
+
const focusLineNode = focusNode;
|
|
1014
|
+
const moveToStart = $getCodeLineDirection(focusLineNode) === "rtl" ? !isMoveToStart : isMoveToStart;
|
|
1015
|
+
const originalAnchorKey = anchor.key;
|
|
1016
|
+
const originalAnchorOffset = anchor.offset;
|
|
1017
|
+
const originalAnchorType = anchor.type;
|
|
1018
|
+
if (moveToStart) {
|
|
1019
|
+
const start = $getStartOfCodeInLine(focusLineNode, focus.offset);
|
|
1020
|
+
if (start !== null) {
|
|
1021
|
+
const { node, offset } = start;
|
|
1022
|
+
if ($isLineBreakNode(node)) node.selectNext(0, 0);
|
|
1023
|
+
else selection.setTextNodeRange(node, offset, node, offset);
|
|
1024
|
+
} else focusLineNode.getParentOrThrow().selectStart();
|
|
1025
|
+
} else $getEndOfCodeInLine(focusLineNode).select();
|
|
1026
|
+
if (event.shiftKey) selection.anchor.set(originalAnchorKey, originalAnchorOffset, originalAnchorType);
|
|
1027
|
+
event.preventDefault();
|
|
1028
|
+
event.stopPropagation();
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* @internal
|
|
1033
|
+
* Register the keyboard and command handlers that drive code-block
|
|
1034
|
+
* indentation: Tab / Shift+Tab, INDENT/OUTDENT_CONTENT_COMMAND,
|
|
1035
|
+
* INSERT_TAB_COMMAND, alt+arrow line shifting, and Home/End movement.
|
|
1036
|
+
*
|
|
1037
|
+
* Both `@lexical/code-shiki` and `@lexical/code-prism` use this via
|
|
1038
|
+
* {@link CodeIndentExtension}; callers using `registerCodeHighlighting`
|
|
1039
|
+
* will implicitly call this with tabSize of `undefined`.
|
|
1040
|
+
*
|
|
1041
|
+
* @param editor The editor to register on.
|
|
1042
|
+
* @param tabSize When set, OUTDENT_CONTENT_COMMAND (Shift+Tab) also strips
|
|
1043
|
+
* up to that many leading spaces from a code line. See
|
|
1044
|
+
* {@link $outdentLeadingSpaces}.
|
|
1045
|
+
*/
|
|
1046
|
+
function registerCodeIndentation(editor, tabSize) {
|
|
1047
|
+
return mergeRegister(editor.registerCommand(KEY_TAB_COMMAND, (event) => {
|
|
1048
|
+
const command = $handleTab(event.shiftKey);
|
|
1049
|
+
if (command === null) return false;
|
|
1050
|
+
event.preventDefault();
|
|
1051
|
+
editor.dispatchCommand(command, void 0);
|
|
1052
|
+
return true;
|
|
1053
|
+
}, COMMAND_PRIORITY_LOW), editor.registerCommand(INSERT_TAB_COMMAND, () => {
|
|
1054
|
+
if (!$isSelectionInCode($getSelection())) return false;
|
|
1055
|
+
$insertNodes([$createTabNode()]);
|
|
1056
|
+
return true;
|
|
1057
|
+
}, COMMAND_PRIORITY_LOW), editor.registerCommand(INDENT_CONTENT_COMMAND, () => $handleMultilineIndent(INDENT_CONTENT_COMMAND), COMMAND_PRIORITY_LOW), editor.registerCommand(OUTDENT_CONTENT_COMMAND, () => $handleMultilineIndent(OUTDENT_CONTENT_COMMAND, tabSize), COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_UP_COMMAND, (event) => {
|
|
1058
|
+
const selection = $getSelection();
|
|
1059
|
+
if (!$isRangeSelection(selection)) return false;
|
|
1060
|
+
const { anchor } = selection;
|
|
1061
|
+
const anchorNode = anchor.getNode();
|
|
1062
|
+
if (!$isSelectionInCode(selection)) return false;
|
|
1063
|
+
if (selection.isCollapsed() && anchor.offset === 0 && anchorNode.getPreviousSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
|
|
1064
|
+
event.preventDefault();
|
|
1065
|
+
return true;
|
|
1066
|
+
}
|
|
1067
|
+
return $handleShiftLines(KEY_ARROW_UP_COMMAND, event);
|
|
1068
|
+
}, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event) => {
|
|
1069
|
+
const selection = $getSelection();
|
|
1070
|
+
if (!$isRangeSelection(selection)) return false;
|
|
1071
|
+
const { anchor } = selection;
|
|
1072
|
+
const anchorNode = anchor.getNode();
|
|
1073
|
+
if (!$isSelectionInCode(selection)) return false;
|
|
1074
|
+
if (selection.isCollapsed() && anchor.offset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
|
|
1075
|
+
event.preventDefault();
|
|
1076
|
+
return true;
|
|
1077
|
+
}
|
|
1078
|
+
return $handleShiftLines(KEY_ARROW_DOWN_COMMAND, event);
|
|
1079
|
+
}, COMMAND_PRIORITY_LOW), editor.registerCommand(MOVE_TO_START, (event) => $handleMoveTo(MOVE_TO_START, event), COMMAND_PRIORITY_LOW), editor.registerCommand(MOVE_TO_END, (event) => $handleMoveTo(MOVE_TO_END, event), COMMAND_PRIORITY_LOW));
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Adds keyboard-driven indentation to code blocks (Tab / Shift+Tab,
|
|
1083
|
+
* alt+arrow line shifts, Home/End within a line). Both
|
|
1084
|
+
* {@link "@lexical/code-shiki".CodeShikiExtension} and
|
|
1085
|
+
* {@link "@lexical/code-prism".CodePrismExtension} declare this as a
|
|
1086
|
+
* dependency, so it is activated automatically alongside either
|
|
1087
|
+
* highlighter.
|
|
1088
|
+
*
|
|
1089
|
+
* Code blocks without syntax highlighting can use this extension on its
|
|
1090
|
+
* own.
|
|
1091
|
+
*/
|
|
1092
|
+
const CodeIndentExtension = defineExtension({
|
|
1093
|
+
build: (editor, config) => namedSignals(config),
|
|
1094
|
+
config: safeCast({
|
|
1095
|
+
disabled: false,
|
|
1096
|
+
tabSize: void 0
|
|
1097
|
+
}),
|
|
1098
|
+
dependencies: [CodeExtension],
|
|
1099
|
+
name: "@lexical/code-indent",
|
|
1100
|
+
register: (editor, config, state) => {
|
|
1101
|
+
const stores = state.getOutput();
|
|
1102
|
+
return effect(() => {
|
|
1103
|
+
if (stores.disabled.value) return;
|
|
1104
|
+
return registerCodeIndentation(editor, stores.tabSize.value);
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
//#endregion
|
|
1109
|
+
export { $createCodeHighlightNode, $createCodeNode, $getCodeLineDirection, $getEndOfCodeInLine, $getFirstCodeNodeOfLine, $getLastCodeNodeOfLine, $getStartOfCodeInLine, $isCodeHighlightNode, $isCodeNode, $outdentLeadingSpaces, $plainifyCodeContent, CodeExtension, CodeHighlightNode, CodeImportExtension, CodeImportRules, CodeIndentExtension, CodeNode, DEFAULT_CODE_LANGUAGE, getDefaultCodeLanguage, registerCodeIndentation };
|