Skip to content

Commit 6f27be3

Browse files
authored
Fix select all when inside jupyter cells (#418)
1 parent e819e6c commit 6f27be3

File tree

1 file changed

+15
-84
lines changed

1 file changed

+15
-84
lines changed

packages/lexical/src/plugins/JupyterInputOutputPlugin.tsx

Lines changed: 15 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
COMMAND_PRIORITY_EDITOR,
1414
INSERT_LINE_BREAK_COMMAND,
1515
COMMAND_PRIORITY_HIGH,
16+
COMMAND_PRIORITY_CRITICAL,
1617
KEY_ENTER_COMMAND,
1718
COMMAND_PRIORITY_LOW,
1819
$createLineBreakNode,
@@ -43,7 +44,6 @@ import {
4344
JupyterInputNode,
4445
$isJupyterInputNode,
4546
} from '../nodes/JupyterInputNode';
46-
import { $isJupyterInputHighlightNode } from '../nodes/JupyterInputHighlightNode';
4747
import { registerCodeHighlighting } from '../nodes/JupyterInputHighlighter';
4848
import {
4949
JupyterOutputNode,
@@ -451,101 +451,36 @@ export const JupyterInputOutputPlugin = (
451451
}, [editor, kernel]);
452452

453453
// Handle Ctrl+A to select all content within a Jupyter input cell
454-
// Use Lexical command system with HIGH priority to intercept before default behavior
454+
// Use CRITICAL priority to run before Lexical's internal select-all handler
455455
useEffect(() => {
456456
return editor.registerCommand<KeyboardEvent>(
457457
SELECT_ALL_COMMAND,
458458
(event: KeyboardEvent) => {
459-
console.log(
460-
'🎯 SELECT_ALL_COMMAND intercepted before default behavior',
461-
);
462-
463459
const selection = $getSelection();
464460
if (!$isRangeSelection(selection)) {
465-
console.log('❌ No range selection available');
466-
return false; // Let default behavior handle it
467-
}
468-
469-
// Debug: Log current selection details BEFORE default Ctrl+A
470-
console.log('📍 Current selection details (BEFORE default Ctrl+A):', {
471-
anchorKey: selection.anchor.key,
472-
anchorOffset: selection.anchor.offset,
473-
anchorType: selection.anchor.type,
474-
focusKey: selection.focus.key,
475-
focusOffset: selection.focus.offset,
476-
focusType: selection.focus.type,
477-
});
478-
479-
// Start from the anchor node and traverse up the hierarchy
480-
const anchorNode = selection.anchor.getNode();
481-
console.log('🔍 Anchor node details (BEFORE default Ctrl+A):', {
482-
type: anchorNode.getType(),
483-
key: anchorNode.getKey(),
484-
textContent: anchorNode.getTextContent?.()?.slice(0, 50) || 'N/A',
485-
parent: anchorNode.getParent()?.getType() || 'No parent',
486-
});
487-
488-
// Also check the focus node in case it's different
489-
const focusNode = selection.focus.getNode();
490-
if (focusNode !== anchorNode) {
491-
console.log('🔍 Focus node (different from anchor):', {
492-
type: focusNode.getType(),
493-
key: focusNode.getKey(),
494-
textContent: focusNode.getTextContent?.()?.slice(0, 50) || 'N/A',
495-
parent: focusNode.getParent()?.getType() || 'No parent',
496-
});
461+
return false;
497462
}
498463

499-
let currentNode: LexicalNode | null = anchorNode;
464+
// Traverse up from current node to find parent JupyterInputNode
465+
let currentNode: LexicalNode | null = selection.anchor.getNode();
500466
let jupyterInputNode: JupyterInputNode | null = null;
501467
let depth = 0;
502-
const maxDepth = 20; // Prevent infinite loops
468+
const maxDepth = 20;
503469

504-
// Traverse up the tree hierarchy
505470
while (currentNode && depth < maxDepth) {
506-
const nodeType = currentNode.getType();
507-
const nodeKey = currentNode.getKey();
508-
const isJupyterInput = $isJupyterInputNode(currentNode);
509-
const isJupyterHighlight = $isJupyterInputHighlightNode(currentNode);
510-
511-
console.log(
512-
`🔍 Depth ${depth}: ${nodeType} (${nodeKey}) - isJupyterInput: ${isJupyterInput}, isJupyterHighlight: ${isJupyterHighlight}`,
513-
);
514-
515-
// Direct check: Is this node a JupyterInputNode?
516-
if (isJupyterInput) {
471+
if ($isJupyterInputNode(currentNode)) {
517472
jupyterInputNode = currentNode as JupyterInputNode;
518-
console.log(`🎯 SUCCESS: Found JupyterInputNode at depth ${depth}`);
519-
break;
520-
}
521-
522-
// If this is a JupyterInputHighlightNode, continue traversing up
523-
// (the parent should be a JupyterInputNode)
524-
if (isJupyterHighlight) {
525-
console.log(
526-
`🔍 Found JupyterInputHighlightNode at depth ${depth}, continuing traversal...`,
527-
);
528-
}
529-
530-
// Move to parent
531-
const parentNode: LexicalNode | null = currentNode.getParent();
532-
if (parentNode) {
533-
currentNode = parentNode;
534-
depth++;
535-
} else {
536-
console.log(`🔍 No parent at depth ${depth}, reached root`);
537473
break;
538474
}
539-
}
540475

541-
if (depth >= maxDepth) {
542-
console.warn(`🔍 Max depth ${maxDepth} reached, stopping traversal`);
476+
currentNode = currentNode.getParent();
477+
depth++;
543478
}
544479

545480
if (jupyterInputNode) {
546-
console.log(
547-
'🎯 SUCCESS: Found JupyterInputNode, selecting all content within cell',
548-
);
481+
// Prevent default browser behavior only when we're handling the event
482+
event.preventDefault();
483+
event.stopPropagation();
549484

550485
// Select all content within the Jupyter input node
551486
const rangeSelection = $createRangeSelection();
@@ -556,16 +491,12 @@ export const JupyterInputOutputPlugin = (
556491
'element',
557492
);
558493
$setSelection(rangeSelection);
559-
560-
return true; // Prevent default Ctrl+A behavior
494+
return true; // Prevent default select-all behavior
561495
}
562496

563-
console.log(
564-
'❌ No JupyterInputNode found in hierarchy - allowing default Ctrl+A',
565-
);
566-
return false; // Let default behavior handle it
497+
return false; // Allow default select-all if not in Jupyter cell
567498
},
568-
COMMAND_PRIORITY_HIGH, // HIGH priority to intercept before default behavior
499+
COMMAND_PRIORITY_CRITICAL,
569500
);
570501
}, [editor]);
571502

0 commit comments

Comments
 (0)