@lexical/code-core 0.44.1-nightly.20260430.0 → 0.44.1-nightly.20260504.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/CodeIndentation.d.ts +54 -0
- package/FlatStructureUtils.d.ts +16 -1
- package/LexicalCodeCore.dev.js +456 -0
- package/LexicalCodeCore.dev.mjs +456 -3
- package/LexicalCodeCore.js.flow +18 -0
- package/LexicalCodeCore.mjs +4 -1
- package/LexicalCodeCore.node.mjs +4 -1
- package/LexicalCodeCore.prod.js +1 -1
- package/LexicalCodeCore.prod.mjs +1 -1
- package/index.d.ts +2 -1
- package/package.json +3 -3
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
*/
|
|
8
|
+
import type { LexicalEditor } from 'lexical';
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
* Register the keyboard and command handlers that drive code-block
|
|
12
|
+
* indentation: Tab / Shift+Tab, INDENT/OUTDENT_CONTENT_COMMAND,
|
|
13
|
+
* INSERT_TAB_COMMAND, alt+arrow line shifting, and Home/End movement.
|
|
14
|
+
*
|
|
15
|
+
* Both `@lexical/code-shiki` and `@lexical/code-prism` use this via
|
|
16
|
+
* {@link CodeIndentExtension}; callers using `registerCodeHighlighting`
|
|
17
|
+
* will implicitly call this with tabSize of `undefined`.
|
|
18
|
+
*
|
|
19
|
+
* @param editor The editor to register on.
|
|
20
|
+
* @param tabSize When set, OUTDENT_CONTENT_COMMAND (Shift+Tab) also strips
|
|
21
|
+
* up to that many leading spaces from a code line. See
|
|
22
|
+
* {@link $outdentLeadingSpaces}.
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerCodeIndentation(editor: LexicalEditor, tabSize?: number): () => void;
|
|
25
|
+
export interface CodeIndentConfig {
|
|
26
|
+
/**
|
|
27
|
+
* When true, the indent commands are not registered on the editor.
|
|
28
|
+
* This signal can be flipped at runtime to enable or disable indent
|
|
29
|
+
* handling without rebuilding the editor.
|
|
30
|
+
*/
|
|
31
|
+
disabled: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* When set, treats that many leading spaces on a code line as one indent
|
|
34
|
+
* level for the OUTDENT_CONTENT_COMMAND (Shift+Tab). See
|
|
35
|
+
* {@link registerCodeIndentation}. When undefined (the default), only
|
|
36
|
+
* TabNode removal is supported on outdent.
|
|
37
|
+
*
|
|
38
|
+
* Tab and INSERT_TAB_COMMAND continue to insert a TabNode regardless of
|
|
39
|
+
* this option.
|
|
40
|
+
*/
|
|
41
|
+
tabSize: number | undefined;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Adds keyboard-driven indentation to code blocks (Tab / Shift+Tab,
|
|
45
|
+
* alt+arrow line shifts, Home/End within a line). Both
|
|
46
|
+
* {@link "@lexical/code-shiki".CodeShikiExtension} and
|
|
47
|
+
* {@link "@lexical/code-prism".CodePrismExtension} declare this as a
|
|
48
|
+
* dependency, so it is activated automatically alongside either
|
|
49
|
+
* highlighter.
|
|
50
|
+
*
|
|
51
|
+
* Code blocks without syntax highlighting can use this extension on its
|
|
52
|
+
* own.
|
|
53
|
+
*/
|
|
54
|
+
export declare const CodeIndentExtension: import("lexical").LexicalExtension<CodeIndentConfig, "@lexical/code-indent", import("@lexical/extension").NamedSignalsOutput<CodeIndentConfig>, unknown>;
|
package/FlatStructureUtils.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
import type { CodeHighlightNode } from './CodeHighlightNode';
|
|
9
|
-
import type { LineBreakNode, TabNode } from 'lexical';
|
|
9
|
+
import type { LineBreakNode, RangeSelection, TabNode } from 'lexical';
|
|
10
10
|
export declare function $getFirstCodeNodeOfLine(anchor: CodeHighlightNode | TabNode | LineBreakNode): CodeHighlightNode | TabNode | LineBreakNode;
|
|
11
11
|
export declare function $getLastCodeNodeOfLine(anchor: CodeHighlightNode | TabNode | LineBreakNode): CodeHighlightNode | TabNode | LineBreakNode;
|
|
12
12
|
/**
|
|
@@ -23,3 +23,18 @@ export declare function $getStartOfCodeInLine(anchor: CodeHighlightNode | TabNod
|
|
|
23
23
|
offset: number;
|
|
24
24
|
};
|
|
25
25
|
export declare function $getEndOfCodeInLine(anchor: CodeHighlightNode | TabNode): CodeHighlightNode | TabNode;
|
|
26
|
+
/**
|
|
27
|
+
* Strip up to `tabSize` leading spaces from a {@link CodeHighlightNode} that
|
|
28
|
+
* starts a code line, to support outdenting space-indented code lines (e.g.
|
|
29
|
+
* code formatted with prettier). Returns true if any spaces were stripped.
|
|
30
|
+
*
|
|
31
|
+
* Best-effort: a line with fewer than `tabSize` leading spaces has all of
|
|
32
|
+
* them stripped, matching VS Code / IntelliJ behavior.
|
|
33
|
+
*
|
|
34
|
+
* Selection is preserved relative to line content. Anchor/focus offsets
|
|
35
|
+
* pointing into `node` shift left by the number of stripped characters
|
|
36
|
+
* (clamped to 0). The underlying TextNode mutation does not adjust
|
|
37
|
+
* selection offsets that already point into the old text, so we patch
|
|
38
|
+
* them up explicitly.
|
|
39
|
+
*/
|
|
40
|
+
export declare function $outdentLeadingSpaces(node: CodeHighlightNode, tabSize: number, selection: RangeSelection): boolean;
|
package/LexicalCodeCore.dev.js
CHANGED
|
@@ -192,6 +192,43 @@ function $getEndOfCodeInLine(anchor) {
|
|
|
192
192
|
return lastNode;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Strip up to `tabSize` leading spaces from a {@link CodeHighlightNode} that
|
|
197
|
+
* starts a code line, to support outdenting space-indented code lines (e.g.
|
|
198
|
+
* code formatted with prettier). Returns true if any spaces were stripped.
|
|
199
|
+
*
|
|
200
|
+
* Best-effort: a line with fewer than `tabSize` leading spaces has all of
|
|
201
|
+
* them stripped, matching VS Code / IntelliJ behavior.
|
|
202
|
+
*
|
|
203
|
+
* Selection is preserved relative to line content. Anchor/focus offsets
|
|
204
|
+
* pointing into `node` shift left by the number of stripped characters
|
|
205
|
+
* (clamped to 0). The underlying TextNode mutation does not adjust
|
|
206
|
+
* selection offsets that already point into the old text, so we patch
|
|
207
|
+
* them up explicitly.
|
|
208
|
+
*/
|
|
209
|
+
function $outdentLeadingSpaces(node, tabSize, selection) {
|
|
210
|
+
if (!Number.isInteger(tabSize) || tabSize <= 0) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
const text = node.getTextContent();
|
|
214
|
+
const leading = /^ +/.exec(text);
|
|
215
|
+
if (!leading) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
const stripCount = Math.min(tabSize, leading[0].length);
|
|
219
|
+
const lineKey = node.getKey();
|
|
220
|
+
const oldAnchorOffset = selection.anchor.key === lineKey && selection.anchor.type === 'text' ? selection.anchor.offset : null;
|
|
221
|
+
const oldFocusOffset = selection.focus.key === lineKey && selection.focus.type === 'text' ? selection.focus.offset : null;
|
|
222
|
+
node.spliceText(0, stripCount, '');
|
|
223
|
+
if (oldAnchorOffset !== null) {
|
|
224
|
+
selection.anchor.set(lineKey, Math.max(0, oldAnchorOffset - stripCount), 'text');
|
|
225
|
+
}
|
|
226
|
+
if (oldFocusOffset !== null) {
|
|
227
|
+
selection.focus.set(lineKey, Math.max(0, oldFocusOffset - stripCount), 'text');
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
195
232
|
/**
|
|
196
233
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
197
234
|
*
|
|
@@ -697,6 +734,422 @@ const CodeExtension = lexical.defineExtension({
|
|
|
697
734
|
}
|
|
698
735
|
});
|
|
699
736
|
|
|
737
|
+
function $isSelectionInCode(selection) {
|
|
738
|
+
if (!lexical.$isRangeSelection(selection)) {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
const anchorNode = selection.anchor.getNode();
|
|
742
|
+
const maybeAnchorCodeNode = $isCodeNode(anchorNode) ? anchorNode : anchorNode.getParent();
|
|
743
|
+
const focusNode = selection.focus.getNode();
|
|
744
|
+
const maybeFocusCodeNode = $isCodeNode(focusNode) ? focusNode : focusNode.getParent();
|
|
745
|
+
return $isCodeNode(maybeAnchorCodeNode) && maybeAnchorCodeNode.is(maybeFocusCodeNode);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Returns an Array of code lines
|
|
750
|
+
* Take the sequence of LineBreakNode | TabNode | CodeHighlightNode forming
|
|
751
|
+
* the selection and split it by LineBreakNode.
|
|
752
|
+
* If the selection ends at the start of the last line, it is considered empty.
|
|
753
|
+
* Empty lines are discarded.
|
|
754
|
+
*/
|
|
755
|
+
function $getCodeLines(selection) {
|
|
756
|
+
const nodes = selection.getNodes();
|
|
757
|
+
const lines = [];
|
|
758
|
+
if (nodes.length === 1 && $isCodeNode(nodes[0])) {
|
|
759
|
+
return lines;
|
|
760
|
+
}
|
|
761
|
+
let lastLine = [];
|
|
762
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
763
|
+
const node = nodes[i];
|
|
764
|
+
if (!($isCodeHighlightNode(node) || lexical.$isTabNode(node) || lexical.$isLineBreakNode(node))) {
|
|
765
|
+
formatDevErrorMessage(`Expected selection to be inside CodeBlock and consisting of CodeHighlightNode, TabNode and LineBreakNode`);
|
|
766
|
+
}
|
|
767
|
+
if (lexical.$isLineBreakNode(node)) {
|
|
768
|
+
if (lastLine.length > 0) {
|
|
769
|
+
lines.push(lastLine);
|
|
770
|
+
lastLine = [];
|
|
771
|
+
}
|
|
772
|
+
} else {
|
|
773
|
+
lastLine.push(node);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (lastLine.length > 0) {
|
|
777
|
+
const selectionEnd = selection.isBackward() ? selection.anchor : selection.focus;
|
|
778
|
+
|
|
779
|
+
// Discard the last line if the selection ends exactly at the
|
|
780
|
+
// start of the line (no real selection)
|
|
781
|
+
const lastPoint = lexical.$createPoint(lastLine[0].getKey(), 0, 'text');
|
|
782
|
+
if (!selectionEnd.is(lastPoint)) {
|
|
783
|
+
lines.push(lastLine);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return lines;
|
|
787
|
+
}
|
|
788
|
+
function $handleTab(shiftKey) {
|
|
789
|
+
const selection = lexical.$getSelection();
|
|
790
|
+
if (!lexical.$isRangeSelection(selection) || !$isSelectionInCode(selection)) {
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
const indentOrOutdent = !shiftKey ? lexical.INDENT_CONTENT_COMMAND : lexical.OUTDENT_CONTENT_COMMAND;
|
|
794
|
+
const tabOrOutdent = !shiftKey ? lexical.INSERT_TAB_COMMAND : lexical.OUTDENT_CONTENT_COMMAND;
|
|
795
|
+
const anchor = selection.anchor;
|
|
796
|
+
const focus = selection.focus;
|
|
797
|
+
|
|
798
|
+
// 1. early decision when there is no real selection
|
|
799
|
+
if (anchor.is(focus)) {
|
|
800
|
+
return tabOrOutdent;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// 2. If only empty lines or multiple non-empty lines are selected: indent/outdent
|
|
804
|
+
const codeLines = $getCodeLines(selection);
|
|
805
|
+
if (codeLines.length !== 1) {
|
|
806
|
+
return indentOrOutdent;
|
|
807
|
+
}
|
|
808
|
+
const codeLine = codeLines[0];
|
|
809
|
+
const codeLineLength = codeLine.length;
|
|
810
|
+
if (!(codeLineLength !== 0)) {
|
|
811
|
+
formatDevErrorMessage(`$getCodeLines only extracts non-empty lines`);
|
|
812
|
+
} // Take into account the direction of the selection
|
|
813
|
+
let selectionFirst;
|
|
814
|
+
let selectionLast;
|
|
815
|
+
if (selection.isBackward()) {
|
|
816
|
+
selectionFirst = focus;
|
|
817
|
+
selectionLast = anchor;
|
|
818
|
+
} else {
|
|
819
|
+
selectionFirst = anchor;
|
|
820
|
+
selectionLast = focus;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// find boundary elements of the line
|
|
824
|
+
// since codeLine only contains TabNode | CodeHighlightNode
|
|
825
|
+
// the result of these functions should is of Type TabNode | CodeHighlightNode
|
|
826
|
+
const firstOfLine = $getFirstCodeNodeOfLine(codeLine[0]);
|
|
827
|
+
const lastOfLine = $getLastCodeNodeOfLine(codeLine[0]);
|
|
828
|
+
const anchorOfLine = lexical.$createPoint(firstOfLine.getKey(), 0, 'text');
|
|
829
|
+
const focusOfLine = lexical.$createPoint(lastOfLine.getKey(), lastOfLine.getTextContentSize(), 'text');
|
|
830
|
+
|
|
831
|
+
// 3. multiline because selection started strictly before the line
|
|
832
|
+
if (selectionFirst.isBefore(anchorOfLine)) {
|
|
833
|
+
return indentOrOutdent;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// 4. multiline because the selection stops strictly after the line
|
|
837
|
+
if (focusOfLine.isBefore(selectionLast)) {
|
|
838
|
+
return indentOrOutdent;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// The selection if within the line.
|
|
842
|
+
// 4. If it does not touch both borders, it needs a tab
|
|
843
|
+
if (anchorOfLine.isBefore(selectionFirst) || selectionLast.isBefore(focusOfLine)) {
|
|
844
|
+
return tabOrOutdent;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// 5. Selection is matching a full line on non-empty code
|
|
848
|
+
return indentOrOutdent;
|
|
849
|
+
}
|
|
850
|
+
function $handleMultilineIndent(type, tabSize) {
|
|
851
|
+
const selection = lexical.$getSelection();
|
|
852
|
+
if (!lexical.$isRangeSelection(selection) || !$isSelectionInCode(selection)) {
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
const codeLines = $getCodeLines(selection);
|
|
856
|
+
const codeLinesLength = codeLines.length;
|
|
857
|
+
|
|
858
|
+
// Special Indent case
|
|
859
|
+
// Selection is collapsed at the beginning of a line
|
|
860
|
+
if (codeLinesLength === 0 && selection.isCollapsed()) {
|
|
861
|
+
if (type === lexical.INDENT_CONTENT_COMMAND) {
|
|
862
|
+
selection.insertNodes([lexical.$createTabNode()]);
|
|
863
|
+
}
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// Special Indent case
|
|
868
|
+
// Selection is matching only one LineBreak
|
|
869
|
+
if (codeLinesLength === 0 && type === lexical.INDENT_CONTENT_COMMAND && selection.getTextContent() === '\n') {
|
|
870
|
+
const tabNode = lexical.$createTabNode();
|
|
871
|
+
const lineBreakNode = lexical.$createLineBreakNode();
|
|
872
|
+
const direction = selection.isBackward() ? 'previous' : 'next';
|
|
873
|
+
selection.insertNodes([tabNode, lineBreakNode]);
|
|
874
|
+
lexical.$setSelectionFromCaretRange(lexical.$getCaretRangeInDirection(lexical.$getCaretRange(lexical.$getTextPointCaret(tabNode, 'next', 0), lexical.$normalizeCaret(lexical.$getSiblingCaret(lineBreakNode, 'next'))), direction));
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// Indent Non Empty Lines
|
|
879
|
+
for (let i = 0; i < codeLinesLength; i++) {
|
|
880
|
+
const line = codeLines[i];
|
|
881
|
+
// a line here is never empty
|
|
882
|
+
if (line.length > 0) {
|
|
883
|
+
let firstOfLine = line[0];
|
|
884
|
+
|
|
885
|
+
// make sure to consider the first node on the first line
|
|
886
|
+
// because the line might not be fully selected
|
|
887
|
+
if (i === 0) {
|
|
888
|
+
firstOfLine = $getFirstCodeNodeOfLine(firstOfLine);
|
|
889
|
+
}
|
|
890
|
+
if (type === lexical.INDENT_CONTENT_COMMAND) {
|
|
891
|
+
const tabNode = lexical.$createTabNode();
|
|
892
|
+
firstOfLine.insertBefore(tabNode);
|
|
893
|
+
// First real code line may need selection adjustment
|
|
894
|
+
// when firstOfLine is at the selection boundary
|
|
895
|
+
if (i === 0) {
|
|
896
|
+
const anchorKey = selection.isBackward() ? 'focus' : 'anchor';
|
|
897
|
+
const anchorLine = lexical.$createPoint(firstOfLine.getKey(), 0, 'text');
|
|
898
|
+
if (selection[anchorKey].is(anchorLine)) {
|
|
899
|
+
selection[anchorKey].set(tabNode.getKey(), 0, 'text');
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
} else if (lexical.$isTabNode(firstOfLine)) {
|
|
903
|
+
firstOfLine.remove();
|
|
904
|
+
} else if (tabSize !== undefined && $isCodeHighlightNode(firstOfLine)) {
|
|
905
|
+
// Outdent space-indented lines (e.g. code formatted with prettier).
|
|
906
|
+
$outdentLeadingSpaces(firstOfLine, tabSize, selection);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return true;
|
|
911
|
+
}
|
|
912
|
+
function $handleShiftLines(type, event) {
|
|
913
|
+
// We only care about the alt+arrow keys
|
|
914
|
+
const selection = lexical.$getSelection();
|
|
915
|
+
if (!lexical.$isRangeSelection(selection)) {
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// I'm not quite sure why, but it seems like calling anchor.getNode() collapses the selection here
|
|
920
|
+
// So first, get the anchor and the focus, then get their nodes
|
|
921
|
+
const {
|
|
922
|
+
anchor,
|
|
923
|
+
focus
|
|
924
|
+
} = selection;
|
|
925
|
+
const anchorOffset = anchor.offset;
|
|
926
|
+
const focusOffset = focus.offset;
|
|
927
|
+
const anchorNode = anchor.getNode();
|
|
928
|
+
const focusNode = focus.getNode();
|
|
929
|
+
const arrowIsUp = type === lexical.KEY_ARROW_UP_COMMAND;
|
|
930
|
+
|
|
931
|
+
// Ensure the selection is within the codeblock
|
|
932
|
+
if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || lexical.$isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || lexical.$isTabNode(focusNode))) {
|
|
933
|
+
return false;
|
|
934
|
+
}
|
|
935
|
+
if (!event.altKey) {
|
|
936
|
+
// Handle moving selection out of the code block, given there are no
|
|
937
|
+
// siblings that can natively take the selection.
|
|
938
|
+
if (selection.isCollapsed()) {
|
|
939
|
+
const codeNode = anchorNode.getParentOrThrow();
|
|
940
|
+
if (arrowIsUp && anchorOffset === 0 && anchorNode.getPreviousSibling() === null) {
|
|
941
|
+
const codeNodeSibling = codeNode.getPreviousSibling();
|
|
942
|
+
if (codeNodeSibling === null) {
|
|
943
|
+
codeNode.selectPrevious();
|
|
944
|
+
event.preventDefault();
|
|
945
|
+
return true;
|
|
946
|
+
}
|
|
947
|
+
} else if (!arrowIsUp && anchorOffset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null) {
|
|
948
|
+
const codeNodeSibling = codeNode.getNextSibling();
|
|
949
|
+
if (codeNodeSibling === null) {
|
|
950
|
+
codeNode.selectNext();
|
|
951
|
+
event.preventDefault();
|
|
952
|
+
return true;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
let start;
|
|
959
|
+
let end;
|
|
960
|
+
if (anchorNode.isBefore(focusNode)) {
|
|
961
|
+
start = $getFirstCodeNodeOfLine(anchorNode);
|
|
962
|
+
end = $getLastCodeNodeOfLine(focusNode);
|
|
963
|
+
} else {
|
|
964
|
+
start = $getFirstCodeNodeOfLine(focusNode);
|
|
965
|
+
end = $getLastCodeNodeOfLine(anchorNode);
|
|
966
|
+
}
|
|
967
|
+
if (start == null || end == null) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
const range = start.getNodesBetween(end);
|
|
971
|
+
for (let i = 0; i < range.length; i++) {
|
|
972
|
+
const node = range[i];
|
|
973
|
+
if (!$isCodeHighlightNode(node) && !lexical.$isTabNode(node) && !lexical.$isLineBreakNode(node)) {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// After this point, we know the selection is within the codeblock. We may not be able to
|
|
979
|
+
// actually move the lines around, but we want to return true either way to prevent
|
|
980
|
+
// the event's default behavior
|
|
981
|
+
event.preventDefault();
|
|
982
|
+
event.stopPropagation(); // required to stop cursor movement under Firefox
|
|
983
|
+
|
|
984
|
+
const linebreak = arrowIsUp ? start.getPreviousSibling() : end.getNextSibling();
|
|
985
|
+
if (!lexical.$isLineBreakNode(linebreak)) {
|
|
986
|
+
return true;
|
|
987
|
+
}
|
|
988
|
+
const sibling = arrowIsUp ? linebreak.getPreviousSibling() : linebreak.getNextSibling();
|
|
989
|
+
if (sibling == null) {
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
const maybeInsertionPoint = $isCodeHighlightNode(sibling) || lexical.$isTabNode(sibling) || lexical.$isLineBreakNode(sibling) ? arrowIsUp ? $getFirstCodeNodeOfLine(sibling) : $getLastCodeNodeOfLine(sibling) : null;
|
|
993
|
+
let insertionPoint = maybeInsertionPoint != null ? maybeInsertionPoint : sibling;
|
|
994
|
+
linebreak.remove();
|
|
995
|
+
range.forEach(node => node.remove());
|
|
996
|
+
if (type === lexical.KEY_ARROW_UP_COMMAND) {
|
|
997
|
+
range.forEach(node => insertionPoint.insertBefore(node));
|
|
998
|
+
insertionPoint.insertBefore(linebreak);
|
|
999
|
+
} else {
|
|
1000
|
+
insertionPoint.insertAfter(linebreak);
|
|
1001
|
+
insertionPoint = linebreak;
|
|
1002
|
+
range.forEach(node => {
|
|
1003
|
+
insertionPoint.insertAfter(node);
|
|
1004
|
+
insertionPoint = node;
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
selection.setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset);
|
|
1008
|
+
return true;
|
|
1009
|
+
}
|
|
1010
|
+
function $handleMoveTo(type, event) {
|
|
1011
|
+
const selection = lexical.$getSelection();
|
|
1012
|
+
if (!lexical.$isRangeSelection(selection)) {
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
const {
|
|
1016
|
+
anchor,
|
|
1017
|
+
focus
|
|
1018
|
+
} = selection;
|
|
1019
|
+
const anchorNode = anchor.getNode();
|
|
1020
|
+
const focusNode = focus.getNode();
|
|
1021
|
+
const isMoveToStart = type === lexical.MOVE_TO_START;
|
|
1022
|
+
|
|
1023
|
+
// Ensure the selection is within the codeblock
|
|
1024
|
+
if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || lexical.$isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || lexical.$isTabNode(focusNode))) {
|
|
1025
|
+
return false;
|
|
1026
|
+
}
|
|
1027
|
+
const focusLineNode = focusNode;
|
|
1028
|
+
const direction = $getCodeLineDirection(focusLineNode);
|
|
1029
|
+
const moveToStart = direction === 'rtl' ? !isMoveToStart : isMoveToStart;
|
|
1030
|
+
if (moveToStart) {
|
|
1031
|
+
const start = $getStartOfCodeInLine(focusLineNode, focus.offset);
|
|
1032
|
+
if (start !== null) {
|
|
1033
|
+
const {
|
|
1034
|
+
node,
|
|
1035
|
+
offset
|
|
1036
|
+
} = start;
|
|
1037
|
+
if (lexical.$isLineBreakNode(node)) {
|
|
1038
|
+
node.selectNext(0, 0);
|
|
1039
|
+
} else {
|
|
1040
|
+
selection.setTextNodeRange(node, offset, node, offset);
|
|
1041
|
+
}
|
|
1042
|
+
} else {
|
|
1043
|
+
focusLineNode.getParentOrThrow().selectStart();
|
|
1044
|
+
}
|
|
1045
|
+
} else {
|
|
1046
|
+
const node = $getEndOfCodeInLine(focusLineNode);
|
|
1047
|
+
node.select();
|
|
1048
|
+
}
|
|
1049
|
+
event.preventDefault();
|
|
1050
|
+
event.stopPropagation();
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* @internal
|
|
1056
|
+
* Register the keyboard and command handlers that drive code-block
|
|
1057
|
+
* indentation: Tab / Shift+Tab, INDENT/OUTDENT_CONTENT_COMMAND,
|
|
1058
|
+
* INSERT_TAB_COMMAND, alt+arrow line shifting, and Home/End movement.
|
|
1059
|
+
*
|
|
1060
|
+
* Both `@lexical/code-shiki` and `@lexical/code-prism` use this via
|
|
1061
|
+
* {@link CodeIndentExtension}; callers using `registerCodeHighlighting`
|
|
1062
|
+
* will implicitly call this with tabSize of `undefined`.
|
|
1063
|
+
*
|
|
1064
|
+
* @param editor The editor to register on.
|
|
1065
|
+
* @param tabSize When set, OUTDENT_CONTENT_COMMAND (Shift+Tab) also strips
|
|
1066
|
+
* up to that many leading spaces from a code line. See
|
|
1067
|
+
* {@link $outdentLeadingSpaces}.
|
|
1068
|
+
*/
|
|
1069
|
+
function registerCodeIndentation(editor, tabSize) {
|
|
1070
|
+
return lexical.mergeRegister(editor.registerCommand(lexical.KEY_TAB_COMMAND, event => {
|
|
1071
|
+
const command = $handleTab(event.shiftKey);
|
|
1072
|
+
if (command === null) {
|
|
1073
|
+
return false;
|
|
1074
|
+
}
|
|
1075
|
+
event.preventDefault();
|
|
1076
|
+
editor.dispatchCommand(command, undefined);
|
|
1077
|
+
return true;
|
|
1078
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.INSERT_TAB_COMMAND, () => {
|
|
1079
|
+
const selection = lexical.$getSelection();
|
|
1080
|
+
if (!$isSelectionInCode(selection)) {
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
lexical.$insertNodes([lexical.$createTabNode()]);
|
|
1084
|
+
return true;
|
|
1085
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.INDENT_CONTENT_COMMAND, () => $handleMultilineIndent(lexical.INDENT_CONTENT_COMMAND), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.OUTDENT_CONTENT_COMMAND, () => $handleMultilineIndent(lexical.OUTDENT_CONTENT_COMMAND, tabSize), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, event => {
|
|
1086
|
+
const selection = lexical.$getSelection();
|
|
1087
|
+
if (!lexical.$isRangeSelection(selection)) {
|
|
1088
|
+
return false;
|
|
1089
|
+
}
|
|
1090
|
+
const {
|
|
1091
|
+
anchor
|
|
1092
|
+
} = selection;
|
|
1093
|
+
const anchorNode = anchor.getNode();
|
|
1094
|
+
if (!$isSelectionInCode(selection)) {
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
// If at the start of a code block, prevent selection from moving out
|
|
1098
|
+
if (selection.isCollapsed() && anchor.offset === 0 && anchorNode.getPreviousSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
|
|
1099
|
+
event.preventDefault();
|
|
1100
|
+
return true;
|
|
1101
|
+
}
|
|
1102
|
+
return $handleShiftLines(lexical.KEY_ARROW_UP_COMMAND, event);
|
|
1103
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, event => {
|
|
1104
|
+
const selection = lexical.$getSelection();
|
|
1105
|
+
if (!lexical.$isRangeSelection(selection)) {
|
|
1106
|
+
return false;
|
|
1107
|
+
}
|
|
1108
|
+
const {
|
|
1109
|
+
anchor
|
|
1110
|
+
} = selection;
|
|
1111
|
+
const anchorNode = anchor.getNode();
|
|
1112
|
+
if (!$isSelectionInCode(selection)) {
|
|
1113
|
+
return false;
|
|
1114
|
+
}
|
|
1115
|
+
// If at the end of a code block, prevent selection from moving out
|
|
1116
|
+
if (selection.isCollapsed() && anchor.offset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
|
|
1117
|
+
event.preventDefault();
|
|
1118
|
+
return true;
|
|
1119
|
+
}
|
|
1120
|
+
return $handleShiftLines(lexical.KEY_ARROW_DOWN_COMMAND, event);
|
|
1121
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.MOVE_TO_START, event => $handleMoveTo(lexical.MOVE_TO_START, event), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.MOVE_TO_END, event => $handleMoveTo(lexical.MOVE_TO_END, event), lexical.COMMAND_PRIORITY_LOW));
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Adds keyboard-driven indentation to code blocks (Tab / Shift+Tab,
|
|
1125
|
+
* alt+arrow line shifts, Home/End within a line). Both
|
|
1126
|
+
* {@link "@lexical/code-shiki".CodeShikiExtension} and
|
|
1127
|
+
* {@link "@lexical/code-prism".CodePrismExtension} declare this as a
|
|
1128
|
+
* dependency, so it is activated automatically alongside either
|
|
1129
|
+
* highlighter.
|
|
1130
|
+
*
|
|
1131
|
+
* Code blocks without syntax highlighting can use this extension on its
|
|
1132
|
+
* own.
|
|
1133
|
+
*/
|
|
1134
|
+
const CodeIndentExtension = lexical.defineExtension({
|
|
1135
|
+
build: (editor, config) => extension.namedSignals(config),
|
|
1136
|
+
config: lexical.safeCast({
|
|
1137
|
+
disabled: false,
|
|
1138
|
+
tabSize: undefined
|
|
1139
|
+
}),
|
|
1140
|
+
dependencies: [CodeExtension],
|
|
1141
|
+
name: '@lexical/code-indent',
|
|
1142
|
+
register: (editor, config, state) => {
|
|
1143
|
+
const stores = state.getOutput();
|
|
1144
|
+
return extension.effect(() => {
|
|
1145
|
+
if (stores.disabled.value) {
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
return registerCodeIndentation(editor, stores.tabSize.value);
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
|
|
700
1153
|
exports.$createCodeHighlightNode = $createCodeHighlightNode;
|
|
701
1154
|
exports.$createCodeNode = $createCodeNode;
|
|
702
1155
|
exports.$getCodeLineDirection = $getCodeLineDirection;
|
|
@@ -706,8 +1159,11 @@ exports.$getLastCodeNodeOfLine = $getLastCodeNodeOfLine;
|
|
|
706
1159
|
exports.$getStartOfCodeInLine = $getStartOfCodeInLine;
|
|
707
1160
|
exports.$isCodeHighlightNode = $isCodeHighlightNode;
|
|
708
1161
|
exports.$isCodeNode = $isCodeNode;
|
|
1162
|
+
exports.$outdentLeadingSpaces = $outdentLeadingSpaces;
|
|
709
1163
|
exports.CodeExtension = CodeExtension;
|
|
710
1164
|
exports.CodeHighlightNode = CodeHighlightNode;
|
|
1165
|
+
exports.CodeIndentExtension = CodeIndentExtension;
|
|
711
1166
|
exports.CodeNode = CodeNode;
|
|
712
1167
|
exports.DEFAULT_CODE_LANGUAGE = DEFAULT_CODE_LANGUAGE;
|
|
713
1168
|
exports.getDefaultCodeLanguage = getDefaultCodeLanguage;
|
|
1169
|
+
exports.registerCodeIndentation = registerCodeIndentation;
|
package/LexicalCodeCore.dev.mjs
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { getTextDirection, $isElementNode, $isTabNode, $isLineBreakNode, $getSiblingCaret, $create, ElementNode, addClassNamesToElement, setDOMStyleFromCSS, $getEditor, $isTextNode, $createTabNode, $createLineBreakNode, $createParagraphNode, isHTMLElement, $applyNodeReplacement, TextNode, removeClassNamesFromElement, defineExtension, KEY_ENTER_COMMAND, $getSelection, $isRangeSelection, COMMAND_PRIORITY_LOW } from 'lexical';
|
|
10
|
-
import { getPeerDependencyFromEditor } from '@lexical/extension';
|
|
9
|
+
import { getTextDirection, $isElementNode, $isTabNode, $isLineBreakNode, $getSiblingCaret, $create, ElementNode, addClassNamesToElement, setDOMStyleFromCSS, $getEditor, $isTextNode, $createTabNode, $createLineBreakNode, $createParagraphNode, isHTMLElement, $applyNodeReplacement, TextNode, removeClassNamesFromElement, defineExtension, KEY_ENTER_COMMAND, $getSelection, $isRangeSelection, COMMAND_PRIORITY_LOW, safeCast, mergeRegister, KEY_TAB_COMMAND, INSERT_TAB_COMMAND, $insertNodes, INDENT_CONTENT_COMMAND, OUTDENT_CONTENT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, MOVE_TO_START, MOVE_TO_END, $createPoint, $setSelectionFromCaretRange, $getCaretRangeInDirection, $getCaretRange, $getTextPointCaret, $normalizeCaret } from 'lexical';
|
|
10
|
+
import { getPeerDependencyFromEditor, effect, namedSignals } from '@lexical/extension';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -190,6 +190,43 @@ function $getEndOfCodeInLine(anchor) {
|
|
|
190
190
|
return lastNode;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Strip up to `tabSize` leading spaces from a {@link CodeHighlightNode} that
|
|
195
|
+
* starts a code line, to support outdenting space-indented code lines (e.g.
|
|
196
|
+
* code formatted with prettier). Returns true if any spaces were stripped.
|
|
197
|
+
*
|
|
198
|
+
* Best-effort: a line with fewer than `tabSize` leading spaces has all of
|
|
199
|
+
* them stripped, matching VS Code / IntelliJ behavior.
|
|
200
|
+
*
|
|
201
|
+
* Selection is preserved relative to line content. Anchor/focus offsets
|
|
202
|
+
* pointing into `node` shift left by the number of stripped characters
|
|
203
|
+
* (clamped to 0). The underlying TextNode mutation does not adjust
|
|
204
|
+
* selection offsets that already point into the old text, so we patch
|
|
205
|
+
* them up explicitly.
|
|
206
|
+
*/
|
|
207
|
+
function $outdentLeadingSpaces(node, tabSize, selection) {
|
|
208
|
+
if (!Number.isInteger(tabSize) || tabSize <= 0) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const text = node.getTextContent();
|
|
212
|
+
const leading = /^ +/.exec(text);
|
|
213
|
+
if (!leading) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
const stripCount = Math.min(tabSize, leading[0].length);
|
|
217
|
+
const lineKey = node.getKey();
|
|
218
|
+
const oldAnchorOffset = selection.anchor.key === lineKey && selection.anchor.type === 'text' ? selection.anchor.offset : null;
|
|
219
|
+
const oldFocusOffset = selection.focus.key === lineKey && selection.focus.type === 'text' ? selection.focus.offset : null;
|
|
220
|
+
node.spliceText(0, stripCount, '');
|
|
221
|
+
if (oldAnchorOffset !== null) {
|
|
222
|
+
selection.anchor.set(lineKey, Math.max(0, oldAnchorOffset - stripCount), 'text');
|
|
223
|
+
}
|
|
224
|
+
if (oldFocusOffset !== null) {
|
|
225
|
+
selection.focus.set(lineKey, Math.max(0, oldFocusOffset - stripCount), 'text');
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
193
230
|
/**
|
|
194
231
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
195
232
|
*
|
|
@@ -695,4 +732,420 @@ const CodeExtension = defineExtension({
|
|
|
695
732
|
}
|
|
696
733
|
});
|
|
697
734
|
|
|
698
|
-
|
|
735
|
+
function $isSelectionInCode(selection) {
|
|
736
|
+
if (!$isRangeSelection(selection)) {
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
const anchorNode = selection.anchor.getNode();
|
|
740
|
+
const maybeAnchorCodeNode = $isCodeNode(anchorNode) ? anchorNode : anchorNode.getParent();
|
|
741
|
+
const focusNode = selection.focus.getNode();
|
|
742
|
+
const maybeFocusCodeNode = $isCodeNode(focusNode) ? focusNode : focusNode.getParent();
|
|
743
|
+
return $isCodeNode(maybeAnchorCodeNode) && maybeAnchorCodeNode.is(maybeFocusCodeNode);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Returns an Array of code lines
|
|
748
|
+
* Take the sequence of LineBreakNode | TabNode | CodeHighlightNode forming
|
|
749
|
+
* the selection and split it by LineBreakNode.
|
|
750
|
+
* If the selection ends at the start of the last line, it is considered empty.
|
|
751
|
+
* Empty lines are discarded.
|
|
752
|
+
*/
|
|
753
|
+
function $getCodeLines(selection) {
|
|
754
|
+
const nodes = selection.getNodes();
|
|
755
|
+
const lines = [];
|
|
756
|
+
if (nodes.length === 1 && $isCodeNode(nodes[0])) {
|
|
757
|
+
return lines;
|
|
758
|
+
}
|
|
759
|
+
let lastLine = [];
|
|
760
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
761
|
+
const node = nodes[i];
|
|
762
|
+
if (!($isCodeHighlightNode(node) || $isTabNode(node) || $isLineBreakNode(node))) {
|
|
763
|
+
formatDevErrorMessage(`Expected selection to be inside CodeBlock and consisting of CodeHighlightNode, TabNode and LineBreakNode`);
|
|
764
|
+
}
|
|
765
|
+
if ($isLineBreakNode(node)) {
|
|
766
|
+
if (lastLine.length > 0) {
|
|
767
|
+
lines.push(lastLine);
|
|
768
|
+
lastLine = [];
|
|
769
|
+
}
|
|
770
|
+
} else {
|
|
771
|
+
lastLine.push(node);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (lastLine.length > 0) {
|
|
775
|
+
const selectionEnd = selection.isBackward() ? selection.anchor : selection.focus;
|
|
776
|
+
|
|
777
|
+
// Discard the last line if the selection ends exactly at the
|
|
778
|
+
// start of the line (no real selection)
|
|
779
|
+
const lastPoint = $createPoint(lastLine[0].getKey(), 0, 'text');
|
|
780
|
+
if (!selectionEnd.is(lastPoint)) {
|
|
781
|
+
lines.push(lastLine);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return lines;
|
|
785
|
+
}
|
|
786
|
+
function $handleTab(shiftKey) {
|
|
787
|
+
const selection = $getSelection();
|
|
788
|
+
if (!$isRangeSelection(selection) || !$isSelectionInCode(selection)) {
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
const indentOrOutdent = !shiftKey ? INDENT_CONTENT_COMMAND : OUTDENT_CONTENT_COMMAND;
|
|
792
|
+
const tabOrOutdent = !shiftKey ? INSERT_TAB_COMMAND : OUTDENT_CONTENT_COMMAND;
|
|
793
|
+
const anchor = selection.anchor;
|
|
794
|
+
const focus = selection.focus;
|
|
795
|
+
|
|
796
|
+
// 1. early decision when there is no real selection
|
|
797
|
+
if (anchor.is(focus)) {
|
|
798
|
+
return tabOrOutdent;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// 2. If only empty lines or multiple non-empty lines are selected: indent/outdent
|
|
802
|
+
const codeLines = $getCodeLines(selection);
|
|
803
|
+
if (codeLines.length !== 1) {
|
|
804
|
+
return indentOrOutdent;
|
|
805
|
+
}
|
|
806
|
+
const codeLine = codeLines[0];
|
|
807
|
+
const codeLineLength = codeLine.length;
|
|
808
|
+
if (!(codeLineLength !== 0)) {
|
|
809
|
+
formatDevErrorMessage(`$getCodeLines only extracts non-empty lines`);
|
|
810
|
+
} // Take into account the direction of the selection
|
|
811
|
+
let selectionFirst;
|
|
812
|
+
let selectionLast;
|
|
813
|
+
if (selection.isBackward()) {
|
|
814
|
+
selectionFirst = focus;
|
|
815
|
+
selectionLast = anchor;
|
|
816
|
+
} else {
|
|
817
|
+
selectionFirst = anchor;
|
|
818
|
+
selectionLast = focus;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// find boundary elements of the line
|
|
822
|
+
// since codeLine only contains TabNode | CodeHighlightNode
|
|
823
|
+
// the result of these functions should is of Type TabNode | CodeHighlightNode
|
|
824
|
+
const firstOfLine = $getFirstCodeNodeOfLine(codeLine[0]);
|
|
825
|
+
const lastOfLine = $getLastCodeNodeOfLine(codeLine[0]);
|
|
826
|
+
const anchorOfLine = $createPoint(firstOfLine.getKey(), 0, 'text');
|
|
827
|
+
const focusOfLine = $createPoint(lastOfLine.getKey(), lastOfLine.getTextContentSize(), 'text');
|
|
828
|
+
|
|
829
|
+
// 3. multiline because selection started strictly before the line
|
|
830
|
+
if (selectionFirst.isBefore(anchorOfLine)) {
|
|
831
|
+
return indentOrOutdent;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// 4. multiline because the selection stops strictly after the line
|
|
835
|
+
if (focusOfLine.isBefore(selectionLast)) {
|
|
836
|
+
return indentOrOutdent;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// The selection if within the line.
|
|
840
|
+
// 4. If it does not touch both borders, it needs a tab
|
|
841
|
+
if (anchorOfLine.isBefore(selectionFirst) || selectionLast.isBefore(focusOfLine)) {
|
|
842
|
+
return tabOrOutdent;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// 5. Selection is matching a full line on non-empty code
|
|
846
|
+
return indentOrOutdent;
|
|
847
|
+
}
|
|
848
|
+
function $handleMultilineIndent(type, tabSize) {
|
|
849
|
+
const selection = $getSelection();
|
|
850
|
+
if (!$isRangeSelection(selection) || !$isSelectionInCode(selection)) {
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
const codeLines = $getCodeLines(selection);
|
|
854
|
+
const codeLinesLength = codeLines.length;
|
|
855
|
+
|
|
856
|
+
// Special Indent case
|
|
857
|
+
// Selection is collapsed at the beginning of a line
|
|
858
|
+
if (codeLinesLength === 0 && selection.isCollapsed()) {
|
|
859
|
+
if (type === INDENT_CONTENT_COMMAND) {
|
|
860
|
+
selection.insertNodes([$createTabNode()]);
|
|
861
|
+
}
|
|
862
|
+
return true;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Special Indent case
|
|
866
|
+
// Selection is matching only one LineBreak
|
|
867
|
+
if (codeLinesLength === 0 && type === INDENT_CONTENT_COMMAND && selection.getTextContent() === '\n') {
|
|
868
|
+
const tabNode = $createTabNode();
|
|
869
|
+
const lineBreakNode = $createLineBreakNode();
|
|
870
|
+
const direction = selection.isBackward() ? 'previous' : 'next';
|
|
871
|
+
selection.insertNodes([tabNode, lineBreakNode]);
|
|
872
|
+
$setSelectionFromCaretRange($getCaretRangeInDirection($getCaretRange($getTextPointCaret(tabNode, 'next', 0), $normalizeCaret($getSiblingCaret(lineBreakNode, 'next'))), direction));
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Indent Non Empty Lines
|
|
877
|
+
for (let i = 0; i < codeLinesLength; i++) {
|
|
878
|
+
const line = codeLines[i];
|
|
879
|
+
// a line here is never empty
|
|
880
|
+
if (line.length > 0) {
|
|
881
|
+
let firstOfLine = line[0];
|
|
882
|
+
|
|
883
|
+
// make sure to consider the first node on the first line
|
|
884
|
+
// because the line might not be fully selected
|
|
885
|
+
if (i === 0) {
|
|
886
|
+
firstOfLine = $getFirstCodeNodeOfLine(firstOfLine);
|
|
887
|
+
}
|
|
888
|
+
if (type === INDENT_CONTENT_COMMAND) {
|
|
889
|
+
const tabNode = $createTabNode();
|
|
890
|
+
firstOfLine.insertBefore(tabNode);
|
|
891
|
+
// First real code line may need selection adjustment
|
|
892
|
+
// when firstOfLine is at the selection boundary
|
|
893
|
+
if (i === 0) {
|
|
894
|
+
const anchorKey = selection.isBackward() ? 'focus' : 'anchor';
|
|
895
|
+
const anchorLine = $createPoint(firstOfLine.getKey(), 0, 'text');
|
|
896
|
+
if (selection[anchorKey].is(anchorLine)) {
|
|
897
|
+
selection[anchorKey].set(tabNode.getKey(), 0, 'text');
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
} else if ($isTabNode(firstOfLine)) {
|
|
901
|
+
firstOfLine.remove();
|
|
902
|
+
} else if (tabSize !== undefined && $isCodeHighlightNode(firstOfLine)) {
|
|
903
|
+
// Outdent space-indented lines (e.g. code formatted with prettier).
|
|
904
|
+
$outdentLeadingSpaces(firstOfLine, tabSize, selection);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return true;
|
|
909
|
+
}
|
|
910
|
+
function $handleShiftLines(type, event) {
|
|
911
|
+
// We only care about the alt+arrow keys
|
|
912
|
+
const selection = $getSelection();
|
|
913
|
+
if (!$isRangeSelection(selection)) {
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// I'm not quite sure why, but it seems like calling anchor.getNode() collapses the selection here
|
|
918
|
+
// So first, get the anchor and the focus, then get their nodes
|
|
919
|
+
const {
|
|
920
|
+
anchor,
|
|
921
|
+
focus
|
|
922
|
+
} = selection;
|
|
923
|
+
const anchorOffset = anchor.offset;
|
|
924
|
+
const focusOffset = focus.offset;
|
|
925
|
+
const anchorNode = anchor.getNode();
|
|
926
|
+
const focusNode = focus.getNode();
|
|
927
|
+
const arrowIsUp = type === KEY_ARROW_UP_COMMAND;
|
|
928
|
+
|
|
929
|
+
// Ensure the selection is within the codeblock
|
|
930
|
+
if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))) {
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
if (!event.altKey) {
|
|
934
|
+
// Handle moving selection out of the code block, given there are no
|
|
935
|
+
// siblings that can natively take the selection.
|
|
936
|
+
if (selection.isCollapsed()) {
|
|
937
|
+
const codeNode = anchorNode.getParentOrThrow();
|
|
938
|
+
if (arrowIsUp && anchorOffset === 0 && anchorNode.getPreviousSibling() === null) {
|
|
939
|
+
const codeNodeSibling = codeNode.getPreviousSibling();
|
|
940
|
+
if (codeNodeSibling === null) {
|
|
941
|
+
codeNode.selectPrevious();
|
|
942
|
+
event.preventDefault();
|
|
943
|
+
return true;
|
|
944
|
+
}
|
|
945
|
+
} else if (!arrowIsUp && anchorOffset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null) {
|
|
946
|
+
const codeNodeSibling = codeNode.getNextSibling();
|
|
947
|
+
if (codeNodeSibling === null) {
|
|
948
|
+
codeNode.selectNext();
|
|
949
|
+
event.preventDefault();
|
|
950
|
+
return true;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
let start;
|
|
957
|
+
let end;
|
|
958
|
+
if (anchorNode.isBefore(focusNode)) {
|
|
959
|
+
start = $getFirstCodeNodeOfLine(anchorNode);
|
|
960
|
+
end = $getLastCodeNodeOfLine(focusNode);
|
|
961
|
+
} else {
|
|
962
|
+
start = $getFirstCodeNodeOfLine(focusNode);
|
|
963
|
+
end = $getLastCodeNodeOfLine(anchorNode);
|
|
964
|
+
}
|
|
965
|
+
if (start == null || end == null) {
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
const range = start.getNodesBetween(end);
|
|
969
|
+
for (let i = 0; i < range.length; i++) {
|
|
970
|
+
const node = range[i];
|
|
971
|
+
if (!$isCodeHighlightNode(node) && !$isTabNode(node) && !$isLineBreakNode(node)) {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// After this point, we know the selection is within the codeblock. We may not be able to
|
|
977
|
+
// actually move the lines around, but we want to return true either way to prevent
|
|
978
|
+
// the event's default behavior
|
|
979
|
+
event.preventDefault();
|
|
980
|
+
event.stopPropagation(); // required to stop cursor movement under Firefox
|
|
981
|
+
|
|
982
|
+
const linebreak = arrowIsUp ? start.getPreviousSibling() : end.getNextSibling();
|
|
983
|
+
if (!$isLineBreakNode(linebreak)) {
|
|
984
|
+
return true;
|
|
985
|
+
}
|
|
986
|
+
const sibling = arrowIsUp ? linebreak.getPreviousSibling() : linebreak.getNextSibling();
|
|
987
|
+
if (sibling == null) {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
const maybeInsertionPoint = $isCodeHighlightNode(sibling) || $isTabNode(sibling) || $isLineBreakNode(sibling) ? arrowIsUp ? $getFirstCodeNodeOfLine(sibling) : $getLastCodeNodeOfLine(sibling) : null;
|
|
991
|
+
let insertionPoint = maybeInsertionPoint != null ? maybeInsertionPoint : sibling;
|
|
992
|
+
linebreak.remove();
|
|
993
|
+
range.forEach(node => node.remove());
|
|
994
|
+
if (type === KEY_ARROW_UP_COMMAND) {
|
|
995
|
+
range.forEach(node => insertionPoint.insertBefore(node));
|
|
996
|
+
insertionPoint.insertBefore(linebreak);
|
|
997
|
+
} else {
|
|
998
|
+
insertionPoint.insertAfter(linebreak);
|
|
999
|
+
insertionPoint = linebreak;
|
|
1000
|
+
range.forEach(node => {
|
|
1001
|
+
insertionPoint.insertAfter(node);
|
|
1002
|
+
insertionPoint = node;
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
selection.setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset);
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
function $handleMoveTo(type, event) {
|
|
1009
|
+
const selection = $getSelection();
|
|
1010
|
+
if (!$isRangeSelection(selection)) {
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
const {
|
|
1014
|
+
anchor,
|
|
1015
|
+
focus
|
|
1016
|
+
} = selection;
|
|
1017
|
+
const anchorNode = anchor.getNode();
|
|
1018
|
+
const focusNode = focus.getNode();
|
|
1019
|
+
const isMoveToStart = type === MOVE_TO_START;
|
|
1020
|
+
|
|
1021
|
+
// Ensure the selection is within the codeblock
|
|
1022
|
+
if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))) {
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
const focusLineNode = focusNode;
|
|
1026
|
+
const direction = $getCodeLineDirection(focusLineNode);
|
|
1027
|
+
const moveToStart = direction === 'rtl' ? !isMoveToStart : isMoveToStart;
|
|
1028
|
+
if (moveToStart) {
|
|
1029
|
+
const start = $getStartOfCodeInLine(focusLineNode, focus.offset);
|
|
1030
|
+
if (start !== null) {
|
|
1031
|
+
const {
|
|
1032
|
+
node,
|
|
1033
|
+
offset
|
|
1034
|
+
} = start;
|
|
1035
|
+
if ($isLineBreakNode(node)) {
|
|
1036
|
+
node.selectNext(0, 0);
|
|
1037
|
+
} else {
|
|
1038
|
+
selection.setTextNodeRange(node, offset, node, offset);
|
|
1039
|
+
}
|
|
1040
|
+
} else {
|
|
1041
|
+
focusLineNode.getParentOrThrow().selectStart();
|
|
1042
|
+
}
|
|
1043
|
+
} else {
|
|
1044
|
+
const node = $getEndOfCodeInLine(focusLineNode);
|
|
1045
|
+
node.select();
|
|
1046
|
+
}
|
|
1047
|
+
event.preventDefault();
|
|
1048
|
+
event.stopPropagation();
|
|
1049
|
+
return true;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* @internal
|
|
1054
|
+
* Register the keyboard and command handlers that drive code-block
|
|
1055
|
+
* indentation: Tab / Shift+Tab, INDENT/OUTDENT_CONTENT_COMMAND,
|
|
1056
|
+
* INSERT_TAB_COMMAND, alt+arrow line shifting, and Home/End movement.
|
|
1057
|
+
*
|
|
1058
|
+
* Both `@lexical/code-shiki` and `@lexical/code-prism` use this via
|
|
1059
|
+
* {@link CodeIndentExtension}; callers using `registerCodeHighlighting`
|
|
1060
|
+
* will implicitly call this with tabSize of `undefined`.
|
|
1061
|
+
*
|
|
1062
|
+
* @param editor The editor to register on.
|
|
1063
|
+
* @param tabSize When set, OUTDENT_CONTENT_COMMAND (Shift+Tab) also strips
|
|
1064
|
+
* up to that many leading spaces from a code line. See
|
|
1065
|
+
* {@link $outdentLeadingSpaces}.
|
|
1066
|
+
*/
|
|
1067
|
+
function registerCodeIndentation(editor, tabSize) {
|
|
1068
|
+
return mergeRegister(editor.registerCommand(KEY_TAB_COMMAND, event => {
|
|
1069
|
+
const command = $handleTab(event.shiftKey);
|
|
1070
|
+
if (command === null) {
|
|
1071
|
+
return false;
|
|
1072
|
+
}
|
|
1073
|
+
event.preventDefault();
|
|
1074
|
+
editor.dispatchCommand(command, undefined);
|
|
1075
|
+
return true;
|
|
1076
|
+
}, COMMAND_PRIORITY_LOW), editor.registerCommand(INSERT_TAB_COMMAND, () => {
|
|
1077
|
+
const selection = $getSelection();
|
|
1078
|
+
if (!$isSelectionInCode(selection)) {
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
$insertNodes([$createTabNode()]);
|
|
1082
|
+
return true;
|
|
1083
|
+
}, 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 => {
|
|
1084
|
+
const selection = $getSelection();
|
|
1085
|
+
if (!$isRangeSelection(selection)) {
|
|
1086
|
+
return false;
|
|
1087
|
+
}
|
|
1088
|
+
const {
|
|
1089
|
+
anchor
|
|
1090
|
+
} = selection;
|
|
1091
|
+
const anchorNode = anchor.getNode();
|
|
1092
|
+
if (!$isSelectionInCode(selection)) {
|
|
1093
|
+
return false;
|
|
1094
|
+
}
|
|
1095
|
+
// If at the start of a code block, prevent selection from moving out
|
|
1096
|
+
if (selection.isCollapsed() && anchor.offset === 0 && anchorNode.getPreviousSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
|
|
1097
|
+
event.preventDefault();
|
|
1098
|
+
return true;
|
|
1099
|
+
}
|
|
1100
|
+
return $handleShiftLines(KEY_ARROW_UP_COMMAND, event);
|
|
1101
|
+
}, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_DOWN_COMMAND, event => {
|
|
1102
|
+
const selection = $getSelection();
|
|
1103
|
+
if (!$isRangeSelection(selection)) {
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
const {
|
|
1107
|
+
anchor
|
|
1108
|
+
} = selection;
|
|
1109
|
+
const anchorNode = anchor.getNode();
|
|
1110
|
+
if (!$isSelectionInCode(selection)) {
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
// If at the end of a code block, prevent selection from moving out
|
|
1114
|
+
if (selection.isCollapsed() && anchor.offset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
|
|
1115
|
+
event.preventDefault();
|
|
1116
|
+
return true;
|
|
1117
|
+
}
|
|
1118
|
+
return $handleShiftLines(KEY_ARROW_DOWN_COMMAND, event);
|
|
1119
|
+
}, 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));
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Adds keyboard-driven indentation to code blocks (Tab / Shift+Tab,
|
|
1123
|
+
* alt+arrow line shifts, Home/End within a line). Both
|
|
1124
|
+
* {@link "@lexical/code-shiki".CodeShikiExtension} and
|
|
1125
|
+
* {@link "@lexical/code-prism".CodePrismExtension} declare this as a
|
|
1126
|
+
* dependency, so it is activated automatically alongside either
|
|
1127
|
+
* highlighter.
|
|
1128
|
+
*
|
|
1129
|
+
* Code blocks without syntax highlighting can use this extension on its
|
|
1130
|
+
* own.
|
|
1131
|
+
*/
|
|
1132
|
+
const CodeIndentExtension = defineExtension({
|
|
1133
|
+
build: (editor, config) => namedSignals(config),
|
|
1134
|
+
config: safeCast({
|
|
1135
|
+
disabled: false,
|
|
1136
|
+
tabSize: undefined
|
|
1137
|
+
}),
|
|
1138
|
+
dependencies: [CodeExtension],
|
|
1139
|
+
name: '@lexical/code-indent',
|
|
1140
|
+
register: (editor, config, state) => {
|
|
1141
|
+
const stores = state.getOutput();
|
|
1142
|
+
return effect(() => {
|
|
1143
|
+
if (stores.disabled.value) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
return registerCodeIndentation(editor, stores.tabSize.value);
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
export { $createCodeHighlightNode, $createCodeNode, $getCodeLineDirection, $getEndOfCodeInLine, $getFirstCodeNodeOfLine, $getLastCodeNodeOfLine, $getStartOfCodeInLine, $isCodeHighlightNode, $isCodeNode, $outdentLeadingSpaces, CodeExtension, CodeHighlightNode, CodeIndentExtension, CodeNode, DEFAULT_CODE_LANGUAGE, getDefaultCodeLanguage, registerCodeIndentation };
|
package/LexicalCodeCore.js.flow
CHANGED
|
@@ -73,6 +73,12 @@ declare export function $getLastCodeNodeOfLine(
|
|
|
73
73
|
anchor: CodeHighlightNode | TabNode | LineBreakNode,
|
|
74
74
|
): CodeHighlightNode | TabNode | LineBreakNode;
|
|
75
75
|
|
|
76
|
+
declare export function $outdentLeadingSpaces(
|
|
77
|
+
node: CodeHighlightNode,
|
|
78
|
+
tabSize: number,
|
|
79
|
+
selection: RangeSelection,
|
|
80
|
+
): boolean;
|
|
81
|
+
|
|
76
82
|
/**
|
|
77
83
|
* CodeNode
|
|
78
84
|
*/
|
|
@@ -105,3 +111,15 @@ declare export class CodeNode extends ElementNode {
|
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
declare export var CodeExtension: LexicalExtension<ExtensionConfigBase, "@lexical/code", unknown, unknown>;
|
|
114
|
+
|
|
115
|
+
export type CodeIndentConfig = {
|
|
116
|
+
disabled: boolean,
|
|
117
|
+
tabSize: number | void,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
declare export function registerCodeIndentation(
|
|
121
|
+
editor: LexicalEditor,
|
|
122
|
+
tabSize?: number,
|
|
123
|
+
): () => void;
|
|
124
|
+
|
|
125
|
+
declare export var CodeIndentExtension: LexicalExtension<CodeIndentConfig, "@lexical/code-indent", unknown, unknown>;
|
package/LexicalCodeCore.mjs
CHANGED
|
@@ -18,8 +18,11 @@ export const $getLastCodeNodeOfLine = mod.$getLastCodeNodeOfLine;
|
|
|
18
18
|
export const $getStartOfCodeInLine = mod.$getStartOfCodeInLine;
|
|
19
19
|
export const $isCodeHighlightNode = mod.$isCodeHighlightNode;
|
|
20
20
|
export const $isCodeNode = mod.$isCodeNode;
|
|
21
|
+
export const $outdentLeadingSpaces = mod.$outdentLeadingSpaces;
|
|
21
22
|
export const CodeExtension = mod.CodeExtension;
|
|
22
23
|
export const CodeHighlightNode = mod.CodeHighlightNode;
|
|
24
|
+
export const CodeIndentExtension = mod.CodeIndentExtension;
|
|
23
25
|
export const CodeNode = mod.CodeNode;
|
|
24
26
|
export const DEFAULT_CODE_LANGUAGE = mod.DEFAULT_CODE_LANGUAGE;
|
|
25
|
-
export const getDefaultCodeLanguage = mod.getDefaultCodeLanguage;
|
|
27
|
+
export const getDefaultCodeLanguage = mod.getDefaultCodeLanguage;
|
|
28
|
+
export const registerCodeIndentation = mod.registerCodeIndentation;
|
package/LexicalCodeCore.node.mjs
CHANGED
|
@@ -16,8 +16,11 @@ export const $getLastCodeNodeOfLine = mod.$getLastCodeNodeOfLine;
|
|
|
16
16
|
export const $getStartOfCodeInLine = mod.$getStartOfCodeInLine;
|
|
17
17
|
export const $isCodeHighlightNode = mod.$isCodeHighlightNode;
|
|
18
18
|
export const $isCodeNode = mod.$isCodeNode;
|
|
19
|
+
export const $outdentLeadingSpaces = mod.$outdentLeadingSpaces;
|
|
19
20
|
export const CodeExtension = mod.CodeExtension;
|
|
20
21
|
export const CodeHighlightNode = mod.CodeHighlightNode;
|
|
22
|
+
export const CodeIndentExtension = mod.CodeIndentExtension;
|
|
21
23
|
export const CodeNode = mod.CodeNode;
|
|
22
24
|
export const DEFAULT_CODE_LANGUAGE = mod.DEFAULT_CODE_LANGUAGE;
|
|
23
|
-
export const getDefaultCodeLanguage = mod.getDefaultCodeLanguage;
|
|
25
|
+
export const getDefaultCodeLanguage = mod.getDefaultCodeLanguage;
|
|
26
|
+
export const registerCodeIndentation = mod.registerCodeIndentation;
|
package/LexicalCodeCore.prod.js
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
"use strict";var e=require("lexical"),t=require("@lexical/extension");function n(e,...t){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",e);for(const e of t)r.append("v",e);throw n.search=r.toString(),Error(`Minified Lexical error #${e}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}function r(t,n){let r=t;for(let i=e.$getSiblingCaret(t,n);i&&(L(i.origin)||e.$isTabNode(i.origin));i=i.getAdjacentCaret())r=i.origin;return r}function i(e){return r(e,"previous")}function o(e){return r(e,"next")}const s="javascript";function l(t,n){for(const r of t.childNodes){if(e.isHTMLElement(r)&&r.tagName===n)return!0;l(r,n)}return!1}const a="data-language",g="data-highlight-language",u="data-theme",c=()=>{};class h extends e.ElementNode{__language;__theme;__isSyntaxHighlightSupported;static getType(){return"code"}static clone(e){return new h(e.__language,e.__key)}constructor(e,t){super(t),this.__language=e||void 0,this.__isSyntaxHighlightSupported=!1,this.__theme=void 0}afterCloneFrom(e){super.afterCloneFrom(e),this.__language=e.__language,this.__theme=e.__theme,this.__isSyntaxHighlightSupported=e.__isSyntaxHighlightSupported}createDOM(t){const n=document.createElement("code");e.addClassNamesToElement(n,t.theme.code),n.setAttribute("spellcheck","false");const r=this.getLanguage();r&&(n.setAttribute(a,r),this.getIsSyntaxHighlightSupported()&&n.setAttribute(g,r));const i=this.getTheme();i&&n.setAttribute(u,i);const o=this.getStyle();return o&&e.setDOMStyleFromCSS(n.style,o),n}updateDOM(t,n,r){const i=this.__language,o=t.__language;i?i!==o&&n.setAttribute(a,i):o&&n.removeAttribute(a);const s=this.__isSyntaxHighlightSupported;t.__isSyntaxHighlightSupported&&o?s&&i?i!==o&&n.setAttribute(g,i):n.removeAttribute(g):s&&i&&n.setAttribute(g,i);const l=this.__theme,c=t.__theme;l?l!==c&&n.setAttribute(u,l):c&&n.removeAttribute(u);const h=this.__style,d=t.__style;return h!==d&&e.setDOMStyleFromCSS(n.style,h,d),!1}exportDOM(t){const n=document.createElement("pre");e.addClassNamesToElement(n,t._config.theme.code),n.setAttribute("spellcheck","false");const r=this.getLanguage();r&&(n.setAttribute(a,r),this.getIsSyntaxHighlightSupported()&&n.setAttribute(g,r));const i=this.getTheme();i&&n.setAttribute(u,i);const o=this.getStyle();return o&&e.setDOMStyleFromCSS(n.style,o),{element:n}}static importDOM(){return{code:e=>null!=e.textContent&&(/\r?\n/.test(e.textContent)||l(e,"BR"))?{conversion:f,priority:1}:null,div:()=>({conversion:_,priority:1}),pre:()=>({conversion:f,priority:0}),table:e=>N(e)?{conversion:m,priority:3}:null,td:e=>{const t=e,n=t.closest("table");return t.classList.contains("js-file-line")||n&&N(n)?{conversion:x,priority:3}:null},tr:e=>{const t=e.closest("table");return t&&N(t)?{conversion:x,priority:3}:null}}}static importJSON(e){return d().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setLanguage(e.language).setTheme(e.theme)}exportJSON(){return{...super.exportJSON(),language:this.getLanguage(),theme:this.getTheme()}}insertNewAfter(n,r=!0){if(!t.getPeerDependencyFromEditor(e.$getEditor(),"@lexical/code")){c();const e=T(n);if(e)return e}const{anchor:o,focus:s}=n,l=(o.isBefore(s)?o:s).getNode();if(e.$isTextNode(l)){let t=i(l);const n=[];for(;;)if(e.$isTabNode(t))n.push(e.$createTabNode()),t=t.getNextSibling();else{if(!L(t))break;{let e=0;const r=t.getTextContent(),i=t.getTextContentSize();for(;e<i&&" "===r[e];)e++;if(0!==e&&n.push(b(" ".repeat(e))),e!==i)break;t=t.getNextSibling()}}const r=l.splitText(o.offset)[0],s=0===o.offset?0:1,a=r.getIndexWithinParent()+s,g=l.getParentOrThrow(),u=[e.$createLineBreakNode(),...n];g.splice(a,0,u);const c=n[n.length-1];c?c.select():0===o.offset?r.selectPrevious():r.getNextSibling().selectNext(0,0)}if(p(l)){const{offset:t}=n.anchor;l.splice(t,0,[e.$createLineBreakNode()]),l.select(t+1,t+1)}return null}canIndent(){return!1}collapseAtStart(){const t=e.$createParagraphNode();return this.getChildren().forEach(e=>t.append(e)),this.replace(t),!0}setLanguage(e){const t=this.getWritable();return t.__language=e||void 0,t}getLanguage(){return this.getLatest().__language}setIsSyntaxHighlightSupported(e){const t=this.getWritable();return t.__isSyntaxHighlightSupported=e,t}getIsSyntaxHighlightSupported(){return this.getLatest().__isSyntaxHighlightSupported}setTheme(e){const t=this.getWritable();return t.__theme=e||void 0,t}getTheme(){return this.getLatest().__theme}}function d(t,n){return e.$create(h).setLanguage(t).setTheme(n)}function p(e){return e instanceof h}function f(e){return{node:d(e.getAttribute(a))}}function _(e){const t=e,n=S(t);return n||function(e){let t=e.parentElement;for(;null!==t;){if(S(t))return!0;t=t.parentElement}return!1}(t)?{node:n?d():null}:{node:null}}function m(){return{node:d()}}function x(){return{node:null}}function S(e){return null!==e.style.fontFamily.match("monospace")}function N(e){return e.classList.contains("js-file-line-container")}function T(t){const{anchor:n}=t;if(t.isCollapsed()&&"element"===n.type){const t=n.getNode();if(p(t)){const r=t.getChildrenSize();if(r>=2&&n.offset===r){const n=t.getLastChild();if(e.$isLineBreakNode(n)&&e.$isLineBreakNode(n.getPreviousSibling())){const n=e.$createParagraphNode();return t.splice(r-2,2,[]).insertAfter(n,!1),n.select(),n}}}}return null}class y extends e.TextNode{__highlightType;constructor(e="",t,n){super(e,n),this.__highlightType=t}static getType(){return"code-highlight"}static clone(e){return new y(e.__text,e.__highlightType||void 0,e.__key)}afterCloneFrom(e){super.afterCloneFrom(e),this.__highlightType=e.__highlightType}getHighlightType(){return this.getLatest().__highlightType}setHighlightType(e){const t=this.getWritable();return t.__highlightType=e||void 0,t}canHaveFormat(){return!1}createDOM(t){const n=super.createDOM(t),r=C(t.theme,this.__highlightType);return e.addClassNamesToElement(n,r),n}updateDOM(t,n,r){const i=super.updateDOM(t,n,r),o=C(r.theme,t.__highlightType),s=C(r.theme,this.__highlightType);return o!==s&&(o&&e.removeClassNamesFromElement(n,o),s&&e.addClassNamesToElement(n,s)),i}static importJSON(e){return b().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setHighlightType(e.highlightType)}exportJSON(){return{...super.exportJSON(),highlightType:this.getHighlightType()}}setFormat(e){return this}isParentRequired(){return!0}createParentElementNode(){return d()}}function C(e,t){return t&&e&&e.codeHighlight&&e.codeHighlight[t]}function b(t="",n){return e.$applyNodeReplacement(new y(t,n))}function L(e){return e instanceof y}const O=e.defineExtension({name:"@lexical/code",nodes:()=>[h,y],register:t=>t.registerCommand(e.KEY_ENTER_COMMAND,t=>{const n=e.$getSelection();return!(!e.$isRangeSelection(n)||!T(n))&&(t.preventDefault(),!0)},e.COMMAND_PRIORITY_LOW)});exports.$createCodeHighlightNode=b,exports.$createCodeNode=d,exports.$getCodeLineDirection=function(t){const n=i(t),r=o(t);let s=n;for(;null!==s;){if(L(s)){const t=e.getTextDirection(s.getTextContent());if(null!==t)return t}if(s===r)break;s=s.getNextSibling()}const l=n.getParent();if(e.$isElementNode(l)){const e=l.getDirection();if("ltr"===e||"rtl"===e)return e}return null},exports.$getEndOfCodeInLine=function(t){const r=o(t);return e.$isLineBreakNode(r)&&n(168),r},exports.$getFirstCodeNodeOfLine=i,exports.$getLastCodeNodeOfLine=o,exports.$getStartOfCodeInLine=function(t,r){let i=null,o=null,s=t,l=r,a=t.getTextContent();for(;;){if(0===l){if(s=s.getPreviousSibling(),null===s)break;if(L(s)||e.$isTabNode(s)||e.$isLineBreakNode(s)||n(167),e.$isLineBreakNode(s)){i={node:s,offset:1};break}l=Math.max(0,s.getTextContentSize()-1),a=s.getTextContent()}else l--;const t=a[l];L(s)&&" "!==t&&(o={node:s,offset:l})}if(null!==o)return o;let g=null;if(r<t.getTextContentSize())L(t)&&(g=t.getTextContent()[r]);else{const e=t.getNextSibling();L(e)&&(g=e.getTextContent()[0])}if(null!==g&&" "!==g)return i;{const n=function(t,n){let r=t,i=n,o=t.getTextContent(),s=t.getTextContentSize();for(;;){if(!L(r)||i===s){if(r=r.getNextSibling(),null===r||e.$isLineBreakNode(r))return null;L(r)&&(i=0,o=r.getTextContent(),s=r.getTextContentSize())}if(L(r)){if(" "!==o[i])return{node:r,offset:i};i++}}}(t,r);return null!==n?n:i}},exports.$isCodeHighlightNode=L,exports.$isCodeNode=p,exports.CodeExtension=O,exports.CodeHighlightNode=y,exports.CodeNode=h,exports.DEFAULT_CODE_LANGUAGE=s,exports.getDefaultCodeLanguage=()=>s;
|
|
9
|
+
"use strict";var e=require("lexical"),t=require("@lexical/extension");function n(e,...t){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",e);for(const e of t)r.append("v",e);throw n.search=r.toString(),Error(`Minified Lexical error #${e}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}function r(t,n){let r=t;for(let i=e.$getSiblingCaret(t,n);i&&(A(i.origin)||e.$isTabNode(i.origin));i=i.getAdjacentCaret())r=i.origin;return r}function i(e){return r(e,"previous")}function o(e){return r(e,"next")}function s(t){const n=i(t),r=o(t);let s=n;for(;null!==s;){if(A(s)){const t=e.getTextDirection(s.getTextContent());if(null!==t)return t}if(s===r)break;s=s.getNextSibling()}const l=n.getParent();if(e.$isElementNode(l)){const e=l.getDirection();if("ltr"===e||"rtl"===e)return e}return null}function l(t,r){let i=null,o=null,s=t,l=r,a=t.getTextContent();for(;;){if(0===l){if(s=s.getPreviousSibling(),null===s)break;if(A(s)||e.$isTabNode(s)||e.$isLineBreakNode(s)||n(167),e.$isLineBreakNode(s)){i={node:s,offset:1};break}l=Math.max(0,s.getTextContentSize()-1),a=s.getTextContent()}else l--;const t=a[l];A(s)&&" "!==t&&(o={node:s,offset:l})}if(null!==o)return o;let g=null;if(r<t.getTextContentSize())A(t)&&(g=t.getTextContent()[r]);else{const e=t.getNextSibling();A(e)&&(g=e.getTextContent()[0])}if(null!==g&&" "!==g)return i;{const n=function(t,n){let r=t,i=n,o=t.getTextContent(),s=t.getTextContentSize();for(;;){if(!A(r)||i===s){if(r=r.getNextSibling(),null===r||e.$isLineBreakNode(r))return null;A(r)&&(i=0,o=r.getTextContent(),s=r.getTextContentSize())}if(A(r)){if(" "!==o[i])return{node:r,offset:i};i++}}}(t,r);return null!==n?n:i}}function a(t){const r=o(t);return e.$isLineBreakNode(r)&&n(168),r}function g(e,t,n){if(!Number.isInteger(t)||t<=0)return!1;const r=e.getTextContent(),i=/^ +/.exec(r);if(!i)return!1;const o=Math.min(t,i[0].length),s=e.getKey(),l=n.anchor.key===s&&"text"===n.anchor.type?n.anchor.offset:null,a=n.focus.key===s&&"text"===n.focus.type?n.focus.offset:null;return e.spliceText(0,o,""),null!==l&&n.anchor.set(s,Math.max(0,l-o),"text"),null!==a&&n.focus.set(s,Math.max(0,a-o),"text"),!0}const u="javascript";function c(t,n){for(const r of t.childNodes){if(e.isHTMLElement(r)&&r.tagName===n)return!0;c(r,n)}return!1}const d="data-language",h="data-highlight-language",f="data-theme",N=()=>{};class _ extends e.ElementNode{__language;__theme;__isSyntaxHighlightSupported;static getType(){return"code"}static clone(e){return new _(e.__language,e.__key)}constructor(e,t){super(t),this.__language=e||void 0,this.__isSyntaxHighlightSupported=!1,this.__theme=void 0}afterCloneFrom(e){super.afterCloneFrom(e),this.__language=e.__language,this.__theme=e.__theme,this.__isSyntaxHighlightSupported=e.__isSyntaxHighlightSupported}createDOM(t){const n=document.createElement("code");e.addClassNamesToElement(n,t.theme.code),n.setAttribute("spellcheck","false");const r=this.getLanguage();r&&(n.setAttribute(d,r),this.getIsSyntaxHighlightSupported()&&n.setAttribute(h,r));const i=this.getTheme();i&&n.setAttribute(f,i);const o=this.getStyle();return o&&e.setDOMStyleFromCSS(n.style,o),n}updateDOM(t,n,r){const i=this.__language,o=t.__language;i?i!==o&&n.setAttribute(d,i):o&&n.removeAttribute(d);const s=this.__isSyntaxHighlightSupported;t.__isSyntaxHighlightSupported&&o?s&&i?i!==o&&n.setAttribute(h,i):n.removeAttribute(h):s&&i&&n.setAttribute(h,i);const l=this.__theme,a=t.__theme;l?l!==a&&n.setAttribute(f,l):a&&n.removeAttribute(f);const g=this.__style,u=t.__style;return g!==u&&e.setDOMStyleFromCSS(n.style,g,u),!1}exportDOM(t){const n=document.createElement("pre");e.addClassNamesToElement(n,t._config.theme.code),n.setAttribute("spellcheck","false");const r=this.getLanguage();r&&(n.setAttribute(d,r),this.getIsSyntaxHighlightSupported()&&n.setAttribute(h,r));const i=this.getTheme();i&&n.setAttribute(f,i);const o=this.getStyle();return o&&e.setDOMStyleFromCSS(n.style,o),{element:n}}static importDOM(){return{code:e=>null!=e.textContent&&(/\r?\n/.test(e.textContent)||c(e,"BR"))?{conversion:C,priority:1}:null,div:()=>({conversion:O,priority:1}),pre:()=>({conversion:C,priority:0}),table:e=>M(e)?{conversion:m,priority:3}:null,td:e=>{const t=e,n=t.closest("table");return t.classList.contains("js-file-line")||n&&M(n)?{conversion:x,priority:3}:null},tr:e=>{const t=e.closest("table");return t&&M(t)?{conversion:x,priority:3}:null}}}static importJSON(e){return p().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setLanguage(e.language).setTheme(e.theme)}exportJSON(){return{...super.exportJSON(),language:this.getLanguage(),theme:this.getTheme()}}insertNewAfter(n,r=!0){if(!t.getPeerDependencyFromEditor(e.$getEditor(),"@lexical/code")){N();const e=$(n);if(e)return e}const{anchor:o,focus:s}=n,l=(o.isBefore(s)?o:s).getNode();if(e.$isTextNode(l)){let t=i(l);const n=[];for(;;)if(e.$isTabNode(t))n.push(e.$createTabNode()),t=t.getNextSibling();else{if(!A(t))break;{let e=0;const r=t.getTextContent(),i=t.getTextContentSize();for(;e<i&&" "===r[e];)e++;if(0!==e&&n.push(E(" ".repeat(e))),e!==i)break;t=t.getNextSibling()}}const r=l.splitText(o.offset)[0],s=0===o.offset?0:1,a=r.getIndexWithinParent()+s,g=l.getParentOrThrow(),u=[e.$createLineBreakNode(),...n];g.splice(a,0,u);const c=n[n.length-1];c?c.select():0===o.offset?r.selectPrevious():r.getNextSibling().selectNext(0,0)}if(T(l)){const{offset:t}=n.anchor;l.splice(t,0,[e.$createLineBreakNode()]),l.select(t+1,t+1)}return null}canIndent(){return!1}collapseAtStart(){const t=e.$createParagraphNode();return this.getChildren().forEach(e=>t.append(e)),this.replace(t),!0}setLanguage(e){const t=this.getWritable();return t.__language=e||void 0,t}getLanguage(){return this.getLatest().__language}setIsSyntaxHighlightSupported(e){const t=this.getWritable();return t.__isSyntaxHighlightSupported=e,t}getIsSyntaxHighlightSupported(){return this.getLatest().__isSyntaxHighlightSupported}setTheme(e){const t=this.getWritable();return t.__theme=e||void 0,t}getTheme(){return this.getLatest().__theme}}function p(t,n){return e.$create(_).setLanguage(t).setTheme(n)}function T(e){return e instanceof _}function C(e){return{node:p(e.getAttribute(d))}}function O(e){const t=e,n=S(t);return n||function(e){let t=e.parentElement;for(;null!==t;){if(S(t))return!0;t=t.parentElement}return!1}(t)?{node:n?p():null}:{node:null}}function m(){return{node:p()}}function x(){return{node:null}}function S(e){return null!==e.style.fontFamily.match("monospace")}function M(e){return e.classList.contains("js-file-line-container")}function $(t){const{anchor:n}=t;if(t.isCollapsed()&&"element"===n.type){const t=n.getNode();if(T(t)){const r=t.getChildrenSize();if(r>=2&&n.offset===r){const n=t.getLastChild();if(e.$isLineBreakNode(n)&&e.$isLineBreakNode(n.getPreviousSibling())){const n=e.$createParagraphNode();return t.splice(r-2,2,[]).insertAfter(n,!1),n.select(),n}}}}return null}class b extends e.TextNode{__highlightType;constructor(e="",t,n){super(e,n),this.__highlightType=t}static getType(){return"code-highlight"}static clone(e){return new b(e.__text,e.__highlightType||void 0,e.__key)}afterCloneFrom(e){super.afterCloneFrom(e),this.__highlightType=e.__highlightType}getHighlightType(){return this.getLatest().__highlightType}setHighlightType(e){const t=this.getWritable();return t.__highlightType=e||void 0,t}canHaveFormat(){return!1}createDOM(t){const n=super.createDOM(t),r=D(t.theme,this.__highlightType);return e.addClassNamesToElement(n,r),n}updateDOM(t,n,r){const i=super.updateDOM(t,n,r),o=D(r.theme,t.__highlightType),s=D(r.theme,this.__highlightType);return o!==s&&(o&&e.removeClassNamesFromElement(n,o),s&&e.addClassNamesToElement(n,s)),i}static importJSON(e){return E().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setHighlightType(e.highlightType)}exportJSON(){return{...super.exportJSON(),highlightType:this.getHighlightType()}}setFormat(e){return this}isParentRequired(){return!0}createParentElementNode(){return p()}}function D(e,t){return t&&e&&e.codeHighlight&&e.codeHighlight[t]}function E(t="",n){return e.$applyNodeReplacement(new b(t,n))}function A(e){return e instanceof b}const y=e.defineExtension({name:"@lexical/code",nodes:()=>[_,b],register:t=>t.registerCommand(e.KEY_ENTER_COMMAND,t=>{const n=e.$getSelection();return!(!e.$isRangeSelection(n)||!$(n))&&(t.preventDefault(),!0)},e.COMMAND_PRIORITY_LOW)});function R(t){if(!e.$isRangeSelection(t))return!1;const n=t.anchor.getNode(),r=T(n)?n:n.getParent(),i=t.focus.getNode(),o=T(i)?i:i.getParent();return T(r)&&r.is(o)}function v(t){const r=t.getNodes(),i=[];if(1===r.length&&T(r[0]))return i;let o=[];for(let t=0;t<r.length;t++){const s=r[t];A(s)||e.$isTabNode(s)||e.$isLineBreakNode(s)||n(169),e.$isLineBreakNode(s)?o.length>0&&(i.push(o),o=[]):o.push(s)}if(o.length>0){const n=t.isBackward()?t.anchor:t.focus,r=e.$createPoint(o[0].getKey(),0,"text");n.is(r)||i.push(o)}return i}function L(t,n){const r=e.$getSelection();if(!e.$isRangeSelection(r)||!R(r))return!1;const o=v(r),s=o.length;if(0===s&&r.isCollapsed())return t===e.INDENT_CONTENT_COMMAND&&r.insertNodes([e.$createTabNode()]),!0;if(0===s&&t===e.INDENT_CONTENT_COMMAND&&"\n"===r.getTextContent()){const t=e.$createTabNode(),n=e.$createLineBreakNode(),i=r.isBackward()?"previous":"next";return r.insertNodes([t,n]),e.$setSelectionFromCaretRange(e.$getCaretRangeInDirection(e.$getCaretRange(e.$getTextPointCaret(t,"next",0),e.$normalizeCaret(e.$getSiblingCaret(n,"next"))),i)),!0}for(let l=0;l<s;l++){const s=o[l];if(s.length>0){let o=s[0];if(0===l&&(o=i(o)),t===e.INDENT_CONTENT_COMMAND){const t=e.$createTabNode();if(o.insertBefore(t),0===l){const n=r.isBackward()?"focus":"anchor",i=e.$createPoint(o.getKey(),0,"text");r[n].is(i)&&r[n].set(t.getKey(),0,"text")}}else e.$isTabNode(o)?o.remove():void 0!==n&&A(o)&&g(o,n,r)}}return!0}function P(t,n){const r=e.$getSelection();if(!e.$isRangeSelection(r))return!1;const{anchor:s,focus:l}=r,a=s.offset,g=l.offset,u=s.getNode(),c=l.getNode(),d=t===e.KEY_ARROW_UP_COMMAND;if(!R(r)||!A(u)&&!e.$isTabNode(u)||!A(c)&&!e.$isTabNode(c))return!1;if(!n.altKey){if(r.isCollapsed()){const e=u.getParentOrThrow();if(d&&0===a&&null===u.getPreviousSibling()){if(null===e.getPreviousSibling())return e.selectPrevious(),n.preventDefault(),!0}else if(!d&&a===u.getTextContentSize()&&null===u.getNextSibling()){if(null===e.getNextSibling())return e.selectNext(),n.preventDefault(),!0}}return!1}let h,f;if(u.isBefore(c)?(h=i(u),f=o(c)):(h=i(c),f=o(u)),null==h||null==f)return!1;const N=h.getNodesBetween(f);for(let t=0;t<N.length;t++){const n=N[t];if(!A(n)&&!e.$isTabNode(n)&&!e.$isLineBreakNode(n))return!1}n.preventDefault(),n.stopPropagation();const _=d?h.getPreviousSibling():f.getNextSibling();if(!e.$isLineBreakNode(_))return!0;const p=d?_.getPreviousSibling():_.getNextSibling();if(null==p)return!0;const T=A(p)||e.$isTabNode(p)||e.$isLineBreakNode(p)?d?i(p):o(p):null;let C=null!=T?T:p;return _.remove(),N.forEach(e=>e.remove()),t===e.KEY_ARROW_UP_COMMAND?(N.forEach(e=>C.insertBefore(e)),C.insertBefore(_)):(C.insertAfter(_),C=_,N.forEach(e=>{C.insertAfter(e),C=e})),r.setTextNodeRange(u,a,c,g),!0}function I(t,n){const r=e.$getSelection();if(!e.$isRangeSelection(r))return!1;const{anchor:i,focus:o}=r,g=i.getNode(),u=o.getNode(),c=t===e.MOVE_TO_START;if(!R(r)||!A(g)&&!e.$isTabNode(g)||!A(u)&&!e.$isTabNode(u))return!1;const d=u;if("rtl"===s(d)?!c:c){const t=l(d,o.offset);if(null!==t){const{node:n,offset:i}=t;e.$isLineBreakNode(n)?n.selectNext(0,0):r.setTextNodeRange(n,i,n,i)}else d.getParentOrThrow().selectStart()}else{a(d).select()}return n.preventDefault(),n.stopPropagation(),!0}function B(t,r){return e.mergeRegister(t.registerCommand(e.KEY_TAB_COMMAND,r=>{const s=function(t){const r=e.$getSelection();if(!e.$isRangeSelection(r)||!R(r))return null;const s=t?e.OUTDENT_CONTENT_COMMAND:e.INDENT_CONTENT_COMMAND,l=t?e.OUTDENT_CONTENT_COMMAND:e.INSERT_TAB_COMMAND,a=r.anchor,g=r.focus;if(a.is(g))return l;const u=v(r);if(1!==u.length)return s;const c=u[0];let d,h;0===c.length&&n(285),r.isBackward()?(d=g,h=a):(d=a,h=g);const f=i(c[0]),N=o(c[0]),_=e.$createPoint(f.getKey(),0,"text"),p=e.$createPoint(N.getKey(),N.getTextContentSize(),"text");return d.isBefore(_)||p.isBefore(h)?s:_.isBefore(d)||h.isBefore(p)?l:s}(r.shiftKey);return null!==s&&(r.preventDefault(),t.dispatchCommand(s,void 0),!0)},e.COMMAND_PRIORITY_LOW),t.registerCommand(e.INSERT_TAB_COMMAND,()=>!!R(e.$getSelection())&&(e.$insertNodes([e.$createTabNode()]),!0),e.COMMAND_PRIORITY_LOW),t.registerCommand(e.INDENT_CONTENT_COMMAND,()=>L(e.INDENT_CONTENT_COMMAND),e.COMMAND_PRIORITY_LOW),t.registerCommand(e.OUTDENT_CONTENT_COMMAND,()=>L(e.OUTDENT_CONTENT_COMMAND,r),e.COMMAND_PRIORITY_LOW),t.registerCommand(e.KEY_ARROW_UP_COMMAND,t=>{const n=e.$getSelection();if(!e.$isRangeSelection(n))return!1;const{anchor:r}=n,i=r.getNode();return!!R(n)&&(n.isCollapsed()&&0===r.offset&&null===i.getPreviousSibling()&&T(i.getParentOrThrow())?(t.preventDefault(),!0):P(e.KEY_ARROW_UP_COMMAND,t))},e.COMMAND_PRIORITY_LOW),t.registerCommand(e.KEY_ARROW_DOWN_COMMAND,t=>{const n=e.$getSelection();if(!e.$isRangeSelection(n))return!1;const{anchor:r}=n,i=r.getNode();return!!R(n)&&(n.isCollapsed()&&r.offset===i.getTextContentSize()&&null===i.getNextSibling()&&T(i.getParentOrThrow())?(t.preventDefault(),!0):P(e.KEY_ARROW_DOWN_COMMAND,t))},e.COMMAND_PRIORITY_LOW),t.registerCommand(e.MOVE_TO_START,t=>I(e.MOVE_TO_START,t),e.COMMAND_PRIORITY_LOW),t.registerCommand(e.MOVE_TO_END,t=>I(e.MOVE_TO_END,t),e.COMMAND_PRIORITY_LOW))}const k=e.defineExtension({build:(e,n)=>t.namedSignals(n),config:e.safeCast({disabled:!1,tabSize:void 0}),dependencies:[y],name:"@lexical/code-indent",register:(e,n,r)=>{const i=r.getOutput();return t.effect(()=>{if(!i.disabled.value)return B(e,i.tabSize.value)})}});exports.$createCodeHighlightNode=E,exports.$createCodeNode=p,exports.$getCodeLineDirection=s,exports.$getEndOfCodeInLine=a,exports.$getFirstCodeNodeOfLine=i,exports.$getLastCodeNodeOfLine=o,exports.$getStartOfCodeInLine=l,exports.$isCodeHighlightNode=A,exports.$isCodeNode=T,exports.$outdentLeadingSpaces=g,exports.CodeExtension=y,exports.CodeHighlightNode=b,exports.CodeIndentExtension=k,exports.CodeNode=_,exports.DEFAULT_CODE_LANGUAGE=u,exports.getDefaultCodeLanguage=()=>u,exports.registerCodeIndentation=B;
|
package/LexicalCodeCore.prod.mjs
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import{getTextDirection as t,$isElementNode as e,$isTabNode as n,$isLineBreakNode as r,$getSiblingCaret as i,$create as o,ElementNode as s,addClassNamesToElement as l,setDOMStyleFromCSS as u,$getEditor as g,$isTextNode as a,$createTabNode as h,$createLineBreakNode as c,$createParagraphNode as p,isHTMLElement as f,$applyNodeReplacement as d,TextNode as _,removeClassNamesFromElement as m,defineExtension as S,KEY_ENTER_COMMAND as x,$getSelection as y,$isRangeSelection as T,COMMAND_PRIORITY_LOW as b}from"lexical";import{getPeerDependencyFromEditor as v}from"@lexical/extension";function C(t,...e){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",t);for(const t of e)r.append("v",t);throw n.search=r.toString(),Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}function N(t,e){let r=t;for(let o=i(t,e);o&&(X(o.origin)||n(o.origin));o=o.getAdjacentCaret())r=o.origin;return r}function O(t){return N(t,"previous")}function A(t){return N(t,"next")}function H(n){const r=O(n),i=A(n);let o=r;for(;null!==o;){if(X(o)){const e=t(o.getTextContent());if(null!==e)return e}if(o===i)break;o=o.getNextSibling()}const s=r.getParent();if(e(s)){const t=s.getDirection();if("ltr"===t||"rtl"===t)return t}return null}function L(t,e){let i=null,o=null,s=t,l=e,u=t.getTextContent();for(;;){if(0===l){if(s=s.getPreviousSibling(),null===s)break;if(X(s)||n(s)||r(s)||C(167),r(s)){i={node:s,offset:1};break}l=Math.max(0,s.getTextContentSize()-1),u=s.getTextContent()}else l--;const t=u[l];X(s)&&" "!==t&&(o={node:s,offset:l})}if(null!==o)return o;let g=null;if(e<t.getTextContentSize())X(t)&&(g=t.getTextContent()[e]);else{const e=t.getNextSibling();X(e)&&(g=e.getTextContent()[0])}if(null!==g&&" "!==g)return i;{const n=function(t,e){let n=t,i=e,o=t.getTextContent(),s=t.getTextContentSize();for(;;){if(!X(n)||i===s){if(n=n.getNextSibling(),null===n||r(n))return null;X(n)&&(i=0,o=n.getTextContent(),s=n.getTextContentSize())}if(X(n)){if(" "!==o[i])return{node:n,offset:i};i++}}}(t,e);return null!==n?n:i}}function F(t){const e=A(t);return r(e)&&C(168),e}const J="javascript",D=()=>J;function M(t,e){for(const n of t.childNodes){if(f(n)&&n.tagName===e)return!0;M(n,e)}return!1}const k="data-language",w="data-highlight-language",P="data-theme",E=()=>{};class z extends s{__language;__theme;__isSyntaxHighlightSupported;static getType(){return"code"}static clone(t){return new z(t.__language,t.__key)}constructor(t,e){super(e),this.__language=t||void 0,this.__isSyntaxHighlightSupported=!1,this.__theme=void 0}afterCloneFrom(t){super.afterCloneFrom(t),this.__language=t.__language,this.__theme=t.__theme,this.__isSyntaxHighlightSupported=t.__isSyntaxHighlightSupported}createDOM(t){const e=document.createElement("code");l(e,t.theme.code),e.setAttribute("spellcheck","false");const n=this.getLanguage();n&&(e.setAttribute(k,n),this.getIsSyntaxHighlightSupported()&&e.setAttribute(w,n));const r=this.getTheme();r&&e.setAttribute(P,r);const i=this.getStyle();return i&&u(e.style,i),e}updateDOM(t,e,n){const r=this.__language,i=t.__language;r?r!==i&&e.setAttribute(k,r):i&&e.removeAttribute(k);const o=this.__isSyntaxHighlightSupported;t.__isSyntaxHighlightSupported&&i?o&&r?r!==i&&e.setAttribute(w,r):e.removeAttribute(w):o&&r&&e.setAttribute(w,r);const s=this.__theme,l=t.__theme;s?s!==l&&e.setAttribute(P,s):l&&e.removeAttribute(P);const g=this.__style,a=t.__style;return g!==a&&u(e.style,g,a),!1}exportDOM(t){const e=document.createElement("pre");l(e,t._config.theme.code),e.setAttribute("spellcheck","false");const n=this.getLanguage();n&&(e.setAttribute(k,n),this.getIsSyntaxHighlightSupported()&&e.setAttribute(w,n));const r=this.getTheme();r&&e.setAttribute(P,r);const i=this.getStyle();return i&&u(e.style,i),{element:e}}static importDOM(){return{code:t=>null!=t.textContent&&(/\r?\n/.test(t.textContent)||M(t,"BR"))?{conversion:j,priority:1}:null,div:()=>({conversion:R,priority:1}),pre:()=>({conversion:j,priority:0}),table:t=>q(t)?{conversion:B,priority:3}:null,td:t=>{const e=t,n=e.closest("table");return e.classList.contains("js-file-line")||n&&q(n)?{conversion:U,priority:3}:null},tr:t=>{const e=t.closest("table");return e&&q(e)?{conversion:U,priority:3}:null}}}static importJSON(t){return I().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setLanguage(t.language).setTheme(t.theme)}exportJSON(){return{...super.exportJSON(),language:this.getLanguage(),theme:this.getTheme()}}insertNewAfter(t,e=!0){if(!v(g(),"@lexical/code")){E();const e=G(t);if(e)return e}const{anchor:r,focus:i}=t,o=(r.isBefore(i)?r:i).getNode();if(a(o)){let t=O(o);const e=[];for(;;)if(n(t))e.push(h()),t=t.getNextSibling();else{if(!X(t))break;{let n=0;const r=t.getTextContent(),i=t.getTextContentSize();for(;n<i&&" "===r[n];)n++;if(0!==n&&e.push(V(" ".repeat(n))),n!==i)break;t=t.getNextSibling()}}const i=o.splitText(r.offset)[0],s=0===r.offset?0:1,l=i.getIndexWithinParent()+s,u=o.getParentOrThrow(),g=[c(),...e];u.splice(l,0,g);const a=e[e.length-1];a?a.select():0===r.offset?i.selectPrevious():i.getNextSibling().selectNext(0,0)}if(W(o)){const{offset:e}=t.anchor;o.splice(e,0,[c()]),o.select(e+1,e+1)}return null}canIndent(){return!1}collapseAtStart(){const t=p();return this.getChildren().forEach(e=>t.append(e)),this.replace(t),!0}setLanguage(t){const e=this.getWritable();return e.__language=t||void 0,e}getLanguage(){return this.getLatest().__language}setIsSyntaxHighlightSupported(t){const e=this.getWritable();return e.__isSyntaxHighlightSupported=t,e}getIsSyntaxHighlightSupported(){return this.getLatest().__isSyntaxHighlightSupported}setTheme(t){const e=this.getWritable();return e.__theme=t||void 0,e}getTheme(){return this.getLatest().__theme}}function I(t,e){return o(z).setLanguage(t).setTheme(e)}function W(t){return t instanceof z}function j(t){return{node:I(t.getAttribute(k))}}function R(t){const e=t,n=$(e);return n||function(t){let e=t.parentElement;for(;null!==e;){if($(e))return!0;e=e.parentElement}return!1}(e)?{node:n?I():null}:{node:null}}function B(){return{node:I()}}function U(){return{node:null}}function $(t){return null!==t.style.fontFamily.match("monospace")}function q(t){return t.classList.contains("js-file-line-container")}function G(t){const{anchor:e}=t;if(t.isCollapsed()&&"element"===e.type){const t=e.getNode();if(W(t)){const n=t.getChildrenSize();if(n>=2&&e.offset===n){const e=t.getLastChild();if(r(e)&&r(e.getPreviousSibling())){const e=p();return t.splice(n-2,2,[]).insertAfter(e,!1),e.select(),e}}}}return null}class K extends _{__highlightType;constructor(t="",e,n){super(t,n),this.__highlightType=e}static getType(){return"code-highlight"}static clone(t){return new K(t.__text,t.__highlightType||void 0,t.__key)}afterCloneFrom(t){super.afterCloneFrom(t),this.__highlightType=t.__highlightType}getHighlightType(){return this.getLatest().__highlightType}setHighlightType(t){const e=this.getWritable();return e.__highlightType=t||void 0,e}canHaveFormat(){return!1}createDOM(t){const e=super.createDOM(t),n=Q(t.theme,this.__highlightType);return l(e,n),e}updateDOM(t,e,n){const r=super.updateDOM(t,e,n),i=Q(n.theme,t.__highlightType),o=Q(n.theme,this.__highlightType);return i!==o&&(i&&m(e,i),o&&l(e,o)),r}static importJSON(t){return V().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setHighlightType(t.highlightType)}exportJSON(){return{...super.exportJSON(),highlightType:this.getHighlightType()}}setFormat(t){return this}isParentRequired(){return!0}createParentElementNode(){return I()}}function Q(t,e){return e&&t&&t.codeHighlight&&t.codeHighlight[e]}function V(t="",e){return d(new K(t,e))}function X(t){return t instanceof K}const Y=S({name:"@lexical/code",nodes:()=>[z,K],register:t=>t.registerCommand(x,t=>{const e=y();return!(!T(e)||!G(e))&&(t.preventDefault(),!0)},b)});export{V as $createCodeHighlightNode,I as $createCodeNode,H as $getCodeLineDirection,F as $getEndOfCodeInLine,O as $getFirstCodeNodeOfLine,A as $getLastCodeNodeOfLine,L as $getStartOfCodeInLine,X as $isCodeHighlightNode,W as $isCodeNode,Y as CodeExtension,K as CodeHighlightNode,z as CodeNode,J as DEFAULT_CODE_LANGUAGE,D as getDefaultCodeLanguage};
|
|
9
|
+
import{getTextDirection as t,$isElementNode as e,$isTabNode as n,$isLineBreakNode as r,$getSiblingCaret as i,$create as o,ElementNode as s,addClassNamesToElement as l,setDOMStyleFromCSS as u,$getEditor as g,$isTextNode as c,$createTabNode as a,$createLineBreakNode as h,$createParagraphNode as f,isHTMLElement as p,$applyNodeReplacement as d,TextNode as m,removeClassNamesFromElement as x,defineExtension as _,KEY_ENTER_COMMAND as S,$getSelection as y,$isRangeSelection as b,COMMAND_PRIORITY_LOW as T,safeCast as v,mergeRegister as N,KEY_TAB_COMMAND as C,INSERT_TAB_COMMAND as O,$insertNodes as P,INDENT_CONTENT_COMMAND as A,OUTDENT_CONTENT_COMMAND as H,KEY_ARROW_UP_COMMAND as w,KEY_ARROW_DOWN_COMMAND as D,MOVE_TO_START as L,MOVE_TO_END as k,$createPoint as B,$setSelectionFromCaretRange as F,$getCaretRangeInDirection as M,$getCaretRange as J,$getTextPointCaret as z,$normalizeCaret as E}from"lexical";import{getPeerDependencyFromEditor as K,effect as I,namedSignals as R}from"@lexical/extension";function W(t,...e){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",t);for(const t of e)r.append("v",t);throw n.search=r.toString(),Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}function j(t,e){let r=t;for(let o=i(t,e);o&&(xt(o.origin)||n(o.origin));o=o.getAdjacentCaret())r=o.origin;return r}function U(t){return j(t,"previous")}function $(t){return j(t,"next")}function q(n){const r=U(n),i=$(n);let o=r;for(;null!==o;){if(xt(o)){const e=t(o.getTextContent());if(null!==e)return e}if(o===i)break;o=o.getNextSibling()}const s=r.getParent();if(e(s)){const t=s.getDirection();if("ltr"===t||"rtl"===t)return t}return null}function G(t,e){let i=null,o=null,s=t,l=e,u=t.getTextContent();for(;;){if(0===l){if(s=s.getPreviousSibling(),null===s)break;if(xt(s)||n(s)||r(s)||W(167),r(s)){i={node:s,offset:1};break}l=Math.max(0,s.getTextContentSize()-1),u=s.getTextContent()}else l--;const t=u[l];xt(s)&&" "!==t&&(o={node:s,offset:l})}if(null!==o)return o;let g=null;if(e<t.getTextContentSize())xt(t)&&(g=t.getTextContent()[e]);else{const e=t.getNextSibling();xt(e)&&(g=e.getTextContent()[0])}if(null!==g&&" "!==g)return i;{const n=function(t,e){let n=t,i=e,o=t.getTextContent(),s=t.getTextContentSize();for(;;){if(!xt(n)||i===s){if(n=n.getNextSibling(),null===n||r(n))return null;xt(n)&&(i=0,o=n.getTextContent(),s=n.getTextContentSize())}if(xt(n)){if(" "!==o[i])return{node:n,offset:i};i++}}}(t,e);return null!==n?n:i}}function Q(t){const e=$(t);return r(e)&&W(168),e}function V(t,e,n){if(!Number.isInteger(e)||e<=0)return!1;const r=t.getTextContent(),i=/^ +/.exec(r);if(!i)return!1;const o=Math.min(e,i[0].length),s=t.getKey(),l=n.anchor.key===s&&"text"===n.anchor.type?n.anchor.offset:null,u=n.focus.key===s&&"text"===n.focus.type?n.focus.offset:null;return t.spliceText(0,o,""),null!==l&&n.anchor.set(s,Math.max(0,l-o),"text"),null!==u&&n.focus.set(s,Math.max(0,u-o),"text"),!0}const X="javascript",Y=()=>X;function Z(t,e){for(const n of t.childNodes){if(p(n)&&n.tagName===e)return!0;Z(n,e)}return!1}const tt="data-language",et="data-highlight-language",nt="data-theme",rt=()=>{};class it extends s{__language;__theme;__isSyntaxHighlightSupported;static getType(){return"code"}static clone(t){return new it(t.__language,t.__key)}constructor(t,e){super(e),this.__language=t||void 0,this.__isSyntaxHighlightSupported=!1,this.__theme=void 0}afterCloneFrom(t){super.afterCloneFrom(t),this.__language=t.__language,this.__theme=t.__theme,this.__isSyntaxHighlightSupported=t.__isSyntaxHighlightSupported}createDOM(t){const e=document.createElement("code");l(e,t.theme.code),e.setAttribute("spellcheck","false");const n=this.getLanguage();n&&(e.setAttribute(tt,n),this.getIsSyntaxHighlightSupported()&&e.setAttribute(et,n));const r=this.getTheme();r&&e.setAttribute(nt,r);const i=this.getStyle();return i&&u(e.style,i),e}updateDOM(t,e,n){const r=this.__language,i=t.__language;r?r!==i&&e.setAttribute(tt,r):i&&e.removeAttribute(tt);const o=this.__isSyntaxHighlightSupported;t.__isSyntaxHighlightSupported&&i?o&&r?r!==i&&e.setAttribute(et,r):e.removeAttribute(et):o&&r&&e.setAttribute(et,r);const s=this.__theme,l=t.__theme;s?s!==l&&e.setAttribute(nt,s):l&&e.removeAttribute(nt);const g=this.__style,c=t.__style;return g!==c&&u(e.style,g,c),!1}exportDOM(t){const e=document.createElement("pre");l(e,t._config.theme.code),e.setAttribute("spellcheck","false");const n=this.getLanguage();n&&(e.setAttribute(tt,n),this.getIsSyntaxHighlightSupported()&&e.setAttribute(et,n));const r=this.getTheme();r&&e.setAttribute(nt,r);const i=this.getStyle();return i&&u(e.style,i),{element:e}}static importDOM(){return{code:t=>null!=t.textContent&&(/\r?\n/.test(t.textContent)||Z(t,"BR"))?{conversion:lt,priority:1}:null,div:()=>({conversion:ut,priority:1}),pre:()=>({conversion:lt,priority:0}),table:t=>ht(t)?{conversion:gt,priority:3}:null,td:t=>{const e=t,n=e.closest("table");return e.classList.contains("js-file-line")||n&&ht(n)?{conversion:ct,priority:3}:null},tr:t=>{const e=t.closest("table");return e&&ht(e)?{conversion:ct,priority:3}:null}}}static importJSON(t){return ot().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setLanguage(t.language).setTheme(t.theme)}exportJSON(){return{...super.exportJSON(),language:this.getLanguage(),theme:this.getTheme()}}insertNewAfter(t,e=!0){if(!K(g(),"@lexical/code")){rt();const e=ft(t);if(e)return e}const{anchor:r,focus:i}=t,o=(r.isBefore(i)?r:i).getNode();if(c(o)){let t=U(o);const e=[];for(;;)if(n(t))e.push(a()),t=t.getNextSibling();else{if(!xt(t))break;{let n=0;const r=t.getTextContent(),i=t.getTextContentSize();for(;n<i&&" "===r[n];)n++;if(0!==n&&e.push(mt(" ".repeat(n))),n!==i)break;t=t.getNextSibling()}}const i=o.splitText(r.offset)[0],s=0===r.offset?0:1,l=i.getIndexWithinParent()+s,u=o.getParentOrThrow(),g=[h(),...e];u.splice(l,0,g);const c=e[e.length-1];c?c.select():0===r.offset?i.selectPrevious():i.getNextSibling().selectNext(0,0)}if(st(o)){const{offset:e}=t.anchor;o.splice(e,0,[h()]),o.select(e+1,e+1)}return null}canIndent(){return!1}collapseAtStart(){const t=f();return this.getChildren().forEach(e=>t.append(e)),this.replace(t),!0}setLanguage(t){const e=this.getWritable();return e.__language=t||void 0,e}getLanguage(){return this.getLatest().__language}setIsSyntaxHighlightSupported(t){const e=this.getWritable();return e.__isSyntaxHighlightSupported=t,e}getIsSyntaxHighlightSupported(){return this.getLatest().__isSyntaxHighlightSupported}setTheme(t){const e=this.getWritable();return e.__theme=t||void 0,e}getTheme(){return this.getLatest().__theme}}function ot(t,e){return o(it).setLanguage(t).setTheme(e)}function st(t){return t instanceof it}function lt(t){return{node:ot(t.getAttribute(tt))}}function ut(t){const e=t,n=at(e);return n||function(t){let e=t.parentElement;for(;null!==e;){if(at(e))return!0;e=e.parentElement}return!1}(e)?{node:n?ot():null}:{node:null}}function gt(){return{node:ot()}}function ct(){return{node:null}}function at(t){return null!==t.style.fontFamily.match("monospace")}function ht(t){return t.classList.contains("js-file-line-container")}function ft(t){const{anchor:e}=t;if(t.isCollapsed()&&"element"===e.type){const t=e.getNode();if(st(t)){const n=t.getChildrenSize();if(n>=2&&e.offset===n){const e=t.getLastChild();if(r(e)&&r(e.getPreviousSibling())){const e=f();return t.splice(n-2,2,[]).insertAfter(e,!1),e.select(),e}}}}return null}class pt extends m{__highlightType;constructor(t="",e,n){super(t,n),this.__highlightType=e}static getType(){return"code-highlight"}static clone(t){return new pt(t.__text,t.__highlightType||void 0,t.__key)}afterCloneFrom(t){super.afterCloneFrom(t),this.__highlightType=t.__highlightType}getHighlightType(){return this.getLatest().__highlightType}setHighlightType(t){const e=this.getWritable();return e.__highlightType=t||void 0,e}canHaveFormat(){return!1}createDOM(t){const e=super.createDOM(t),n=dt(t.theme,this.__highlightType);return l(e,n),e}updateDOM(t,e,n){const r=super.updateDOM(t,e,n),i=dt(n.theme,t.__highlightType),o=dt(n.theme,this.__highlightType);return i!==o&&(i&&x(e,i),o&&l(e,o)),r}static importJSON(t){return mt().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setHighlightType(t.highlightType)}exportJSON(){return{...super.exportJSON(),highlightType:this.getHighlightType()}}setFormat(t){return this}isParentRequired(){return!0}createParentElementNode(){return ot()}}function dt(t,e){return e&&t&&t.codeHighlight&&t.codeHighlight[e]}function mt(t="",e){return d(new pt(t,e))}function xt(t){return t instanceof pt}const _t=_({name:"@lexical/code",nodes:()=>[it,pt],register:t=>t.registerCommand(S,t=>{const e=y();return!(!b(e)||!ft(e))&&(t.preventDefault(),!0)},T)});function St(t){if(!b(t))return!1;const e=t.anchor.getNode(),n=st(e)?e:e.getParent(),r=t.focus.getNode(),i=st(r)?r:r.getParent();return st(n)&&n.is(i)}function yt(t){const e=t.getNodes(),i=[];if(1===e.length&&st(e[0]))return i;let o=[];for(let t=0;t<e.length;t++){const s=e[t];xt(s)||n(s)||r(s)||W(169),r(s)?o.length>0&&(i.push(o),o=[]):o.push(s)}if(o.length>0){const e=t.isBackward()?t.anchor:t.focus,n=B(o[0].getKey(),0,"text");e.is(n)||i.push(o)}return i}function bt(t,e){const r=y();if(!b(r)||!St(r))return!1;const o=yt(r),s=o.length;if(0===s&&r.isCollapsed())return t===A&&r.insertNodes([a()]),!0;if(0===s&&t===A&&"\n"===r.getTextContent()){const t=a(),e=h(),n=r.isBackward()?"previous":"next";return r.insertNodes([t,e]),F(M(J(z(t,"next",0),E(i(e,"next"))),n)),!0}for(let i=0;i<s;i++){const s=o[i];if(s.length>0){let o=s[0];if(0===i&&(o=U(o)),t===A){const t=a();if(o.insertBefore(t),0===i){const e=r.isBackward()?"focus":"anchor",n=B(o.getKey(),0,"text");r[e].is(n)&&r[e].set(t.getKey(),0,"text")}}else n(o)?o.remove():void 0!==e&&xt(o)&&V(o,e,r)}}return!0}function Tt(t,e){const i=y();if(!b(i))return!1;const{anchor:o,focus:s}=i,l=o.offset,u=s.offset,g=o.getNode(),c=s.getNode(),a=t===w;if(!St(i)||!xt(g)&&!n(g)||!xt(c)&&!n(c))return!1;if(!e.altKey){if(i.isCollapsed()){const t=g.getParentOrThrow();if(a&&0===l&&null===g.getPreviousSibling()){if(null===t.getPreviousSibling())return t.selectPrevious(),e.preventDefault(),!0}else if(!a&&l===g.getTextContentSize()&&null===g.getNextSibling()){if(null===t.getNextSibling())return t.selectNext(),e.preventDefault(),!0}}return!1}let h,f;if(g.isBefore(c)?(h=U(g),f=$(c)):(h=U(c),f=$(g)),null==h||null==f)return!1;const p=h.getNodesBetween(f);for(let t=0;t<p.length;t++){const e=p[t];if(!xt(e)&&!n(e)&&!r(e))return!1}e.preventDefault(),e.stopPropagation();const d=a?h.getPreviousSibling():f.getNextSibling();if(!r(d))return!0;const m=a?d.getPreviousSibling():d.getNextSibling();if(null==m)return!0;const x=xt(m)||n(m)||r(m)?a?U(m):$(m):null;let _=null!=x?x:m;return d.remove(),p.forEach(t=>t.remove()),t===w?(p.forEach(t=>_.insertBefore(t)),_.insertBefore(d)):(_.insertAfter(d),_=d,p.forEach(t=>{_.insertAfter(t),_=t})),i.setTextNodeRange(g,l,c,u),!0}function vt(t,e){const i=y();if(!b(i))return!1;const{anchor:o,focus:s}=i,l=o.getNode(),u=s.getNode(),g=t===L;if(!St(i)||!xt(l)&&!n(l)||!xt(u)&&!n(u))return!1;const c=u;if("rtl"===q(c)?!g:g){const t=G(c,s.offset);if(null!==t){const{node:e,offset:n}=t;r(e)?e.selectNext(0,0):i.setTextNodeRange(e,n,e,n)}else c.getParentOrThrow().selectStart()}else{Q(c).select()}return e.preventDefault(),e.stopPropagation(),!0}function Nt(t,e){return N(t.registerCommand(C,e=>{const n=function(t){const e=y();if(!b(e)||!St(e))return null;const n=t?H:A,r=t?H:O,i=e.anchor,o=e.focus;if(i.is(o))return r;const s=yt(e);if(1!==s.length)return n;const l=s[0];let u,g;0===l.length&&W(285),e.isBackward()?(u=o,g=i):(u=i,g=o);const c=U(l[0]),a=$(l[0]),h=B(c.getKey(),0,"text"),f=B(a.getKey(),a.getTextContentSize(),"text");return u.isBefore(h)||f.isBefore(g)?n:h.isBefore(u)||g.isBefore(f)?r:n}(e.shiftKey);return null!==n&&(e.preventDefault(),t.dispatchCommand(n,void 0),!0)},T),t.registerCommand(O,()=>!!St(y())&&(P([a()]),!0),T),t.registerCommand(A,()=>bt(A),T),t.registerCommand(H,()=>bt(H,e),T),t.registerCommand(w,t=>{const e=y();if(!b(e))return!1;const{anchor:n}=e,r=n.getNode();return!!St(e)&&(e.isCollapsed()&&0===n.offset&&null===r.getPreviousSibling()&&st(r.getParentOrThrow())?(t.preventDefault(),!0):Tt(w,t))},T),t.registerCommand(D,t=>{const e=y();if(!b(e))return!1;const{anchor:n}=e,r=n.getNode();return!!St(e)&&(e.isCollapsed()&&n.offset===r.getTextContentSize()&&null===r.getNextSibling()&&st(r.getParentOrThrow())?(t.preventDefault(),!0):Tt(D,t))},T),t.registerCommand(L,t=>vt(L,t),T),t.registerCommand(k,t=>vt(k,t),T))}const Ct=_({build:(t,e)=>R(e),config:v({disabled:!1,tabSize:void 0}),dependencies:[_t],name:"@lexical/code-indent",register:(t,e,n)=>{const r=n.getOutput();return I(()=>{if(!r.disabled.value)return Nt(t,r.tabSize.value)})}});export{mt as $createCodeHighlightNode,ot as $createCodeNode,q as $getCodeLineDirection,Q as $getEndOfCodeInLine,U as $getFirstCodeNodeOfLine,$ as $getLastCodeNodeOfLine,G as $getStartOfCodeInLine,xt as $isCodeHighlightNode,st as $isCodeNode,V as $outdentLeadingSpaces,_t as CodeExtension,pt as CodeHighlightNode,Ct as CodeIndentExtension,it as CodeNode,X as DEFAULT_CODE_LANGUAGE,Y as getDefaultCodeLanguage,Nt as registerCodeIndentation};
|
package/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { CodeExtension } from './CodeExtension';
|
|
9
9
|
export { $createCodeHighlightNode, $isCodeHighlightNode, CodeHighlightNode, } from './CodeHighlightNode';
|
|
10
|
+
export { type CodeIndentConfig, CodeIndentExtension, registerCodeIndentation, } from './CodeIndentation';
|
|
10
11
|
export type { SerializedCodeNode } from './CodeNode';
|
|
11
12
|
export { $createCodeNode, $isCodeNode, CodeNode, DEFAULT_CODE_LANGUAGE, getDefaultCodeLanguage, } from './CodeNode';
|
|
12
|
-
export { $getCodeLineDirection, $getEndOfCodeInLine, $getFirstCodeNodeOfLine, $getLastCodeNodeOfLine, $getStartOfCodeInLine, } from './FlatStructureUtils';
|
|
13
|
+
export { $getCodeLineDirection, $getEndOfCodeInLine, $getFirstCodeNodeOfLine, $getLastCodeNodeOfLine, $getStartOfCodeInLine, $outdentLeadingSpaces, } from './FlatStructureUtils';
|
package/package.json
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"code"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.44.1-nightly.
|
|
11
|
+
"version": "0.44.1-nightly.20260504.0",
|
|
12
12
|
"main": "LexicalCodeCore.js",
|
|
13
13
|
"types": "index.d.ts",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@lexical/extension": "0.44.1-nightly.
|
|
16
|
-
"lexical": "0.44.1-nightly.
|
|
15
|
+
"@lexical/extension": "0.44.1-nightly.20260504.0",
|
|
16
|
+
"lexical": "0.44.1-nightly.20260504.0"
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|