Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
af8e4a1
insert paragraph from collaspsible-title if container are closed
levensta Apr 26, 2026
a87df47
add key handlers for creating fallback paragraphs
levensta Apr 26, 2026
f94fdb0
add tests for handlers
levensta Apr 26, 2026
d47874d
add config
levensta Apr 28, 2026
7176493
Merge remote-tracking branch 'upstream/main' into escape-from-code
levensta May 1, 2026
b7a9147
Merge remote-tracking branch 'upstream/main' into escape-from-code
levensta May 9, 2026
6fc33c1
move handlers to CodeIndentExtension and add backward-compatible tests
levensta May 10, 2026
39366f8
reorganize tests and move handlers to lexical-utils
levensta May 10, 2026
c06a236
Merge remote-tracking branch 'upstream/main' into escape-from-code
levensta May 10, 2026
60fb8b3
remove unused fn
levensta May 10, 2026
472a586
some refactor and disable escaping in the code mode
levensta May 13, 2026
d0a4fd8
Merge branch 'main' into escape-from-code
levensta May 13, 2026
09b3946
handle the edge of the ElementNode
levensta May 16, 2026
9e6138c
Merge branch 'main' into escape-from-code
levensta May 16, 2026
0aeba73
skip new tests in collab
levensta May 16, 2026
df21c2c
Merge remote-tracking branch 'upstream/main' into escape-from-code
levensta May 18, 2026
bdf3f65
Merge remote-tracking branch 'upstream/main' into escape-from-code
levensta May 19, 2026
60bbc44
trigger selection explicitly
levensta May 19, 2026
d4ad4ae
Merge remote-tracking branch 'upstream/main' into escape-from-code
levensta May 29, 2026
d984af0
Merge remote-tracking branch 'upstream/main' into escape-from-code
levensta May 31, 2026
b0b8c49
Merge branch 'main' into escape-from-code
levensta May 31, 2026
9bc369a
Merge branch 'main' into escape-from-code
etrepum Jun 1, 2026
f116ff1
add fallback for empty node
levensta Jun 2, 2026
43a825e
Merge branch 'main' into escape-from-code
levensta Jun 2, 2026
2aa1b6e
fix typo
levensta Jun 2, 2026
29fba45
Merge branch 'main' into escape-from-code
levensta Jun 3, 2026
8edb8d1
Merge branch 'main' into escape-from-code
levensta Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/lexical-code-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"types": "./dist/typescript-too-old.d.ts",
"dependencies": {
"@lexical/extension": "workspace:*",
"@lexical/utils": "workspace:*",
"@lexical/html": "workspace:*",
"@lexical/internal": "workspace:*",
"lexical": "workspace:*"
Expand Down
58 changes: 53 additions & 5 deletions packages/lexical-code-core/src/CodeIndentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {

import {effect, namedSignals} from '@lexical/extension';
import invariant from '@lexical/internal/invariant';
import {$onEscapeDown, $onEscapeUp} from '@lexical/utils';
import {
$createLineBreakNode,
$createPoint,
Expand All @@ -38,6 +39,8 @@ import {
INDENT_CONTENT_COMMAND,
INSERT_TAB_COMMAND,
KEY_ARROW_DOWN_COMMAND,
KEY_ARROW_LEFT_COMMAND,
KEY_ARROW_RIGHT_COMMAND,
KEY_ARROW_UP_COMMAND,
KEY_TAB_COMMAND,
mergeRegister,
Expand Down Expand Up @@ -328,7 +331,7 @@ function $handleShiftLines(
if (codeNodeSibling === null) {
codeNode.selectPrevious();
event.preventDefault();
return true;
return false;
}
} else if (
!arrowIsUp &&
Expand All @@ -339,7 +342,7 @@ function $handleShiftLines(
if (codeNodeSibling === null) {
codeNode.selectNext();
event.preventDefault();
return true;
return false;
}
}
}
Expand Down Expand Up @@ -502,8 +505,36 @@ function $handleMoveTo(
export function registerCodeIndentation(
editor: LexicalEditor,
tabSize?: number,
escapeWithArrows?: boolean,
): () => void {
return mergeRegister(
// When node is the last child pressing down/right or up/let arrow will insert paragraph
// below it to allow adding more content.
// These handlers must be executed before $handleShiftLines
...(escapeWithArrows

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are already up/down handlers in here you could just add this code to them in the right place

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to keep the group of handlers for the escapeWithArrows parameter together and not mix the responsibilities of two different functions into one handler

Otherwise, it would look like this

...(escapeWithArrows
  ? [
      editor.registerCommand(
        KEY_ARROW_RIGHT_COMMAND,
        () => $onEscapeDown($isCodeNode),
        COMMAND_PRIORITY_LOW,
      ),
      editor.registerCommand(
        KEY_ARROW_LEFT_COMMAND,
        () => $onEscapeUp($isCodeNode),
        COMMAND_PRIORITY_LOW,
      ),
    ]
  : []),
editor.registerCommand(
  KEY_ARROW_UP_COMMAND,
  event => {
    if (!event.altKey && escapeWithArrows) {
      $onEscapeUp($isCodeNode);
    }
    // ...
    return $handleShiftLines(KEY_ARROW_UP_COMMAND, event);
  },
  COMMAND_PRIORITY_LOW,
),
editor.registerCommand(
  KEY_ARROW_DOWN_COMMAND,
  event => {
    if (!event.altKey && escapeWithArrows) {
      $onEscapeUp($isCodeNode);
    }
    // ...
    return $handleShiftLines(KEY_ARROW_DOWN_COMMAND, event);
  },
  COMMAND_PRIORITY_LOW,
),

? [
editor.registerCommand(
KEY_ARROW_DOWN_COMMAND,
event => (event.altKey ? false : $onEscapeDown($isCodeNode)),
COMMAND_PRIORITY_LOW,
),
editor.registerCommand(
KEY_ARROW_RIGHT_COMMAND,
() => $onEscapeDown($isCodeNode),
COMMAND_PRIORITY_LOW,
),
editor.registerCommand(
KEY_ARROW_UP_COMMAND,
event => (event.altKey ? false : $onEscapeUp($isCodeNode)),
COMMAND_PRIORITY_LOW,
),
editor.registerCommand(
KEY_ARROW_LEFT_COMMAND,
() => $onEscapeUp($isCodeNode),
COMMAND_PRIORITY_LOW,
),
]
: []),
editor.registerCommand(
KEY_TAB_COMMAND,
event => {
Expand Down Expand Up @@ -552,11 +583,13 @@ export function registerCodeIndentation(
return false;
}
// If at the start of a code block, prevent selection from moving out
const parent = anchorNode.getParent();
if (
selection.isCollapsed() &&
anchor.offset === 0 &&
anchorNode.getPreviousSibling() === null &&
$isCodeNode(anchorNode.getParentOrThrow())
$isCodeNode(parent) &&
parent.getPreviousSibling() === null
) {
event.preventDefault();
return true;
Expand All @@ -582,7 +615,8 @@ export function registerCodeIndentation(
selection.isCollapsed() &&
anchor.offset === anchorNode.getTextContentSize() &&
anchorNode.getNextSibling() === null &&
$isCodeNode(anchorNode.getParentOrThrow())
$isCodeNode(anchorNode.getParentOrThrow()) &&
anchorNode.getParentOrThrow().getNextSibling() === null
) {
event.preventDefault();
return true;
Expand Down Expand Up @@ -621,6 +655,15 @@ export interface CodeIndentConfig {
* this option.
*/
tabSize: number | undefined;
/**
* When `true`, this enables the ability to exit a code block
* that has no adjacent elements using the ArrowLeft/ArrowUp keys
* if the cursor is at the beginning, or the ArrowRight/ArrowDown keys
* if the cursor is at the end.
* When `false` (default), pressing the arrow keys will not move the cursor
* if there are no adjacent elements around the code block
*/
escapeWithArrows: boolean;
}

/**
Expand All @@ -638,6 +681,7 @@ export const CodeIndentExtension = defineExtension({
build: (editor, config) => namedSignals(config),
config: safeCast<CodeIndentConfig>({
disabled: false,
escapeWithArrows: false,
tabSize: undefined,
}),
dependencies: [CodeExtension],
Expand All @@ -648,7 +692,11 @@ export const CodeIndentExtension = defineExtension({
if (stores.disabled.value) {
return;
}
return registerCodeIndentation(editor, stores.tabSize.value);
return registerCodeIndentation(
editor,
stores.tabSize.value,
stores.escapeWithArrows.value,
);
});
},
});
Loading
Loading