mirror of
https://github.com/facebook/lexical.git
synced 2025-05-21 09:07:44 +08:00
Make tabs flexible in code (#4520)
This commit is contained in:
@ -18,22 +18,23 @@ import type {
|
|||||||
LineBreakNode,
|
LineBreakNode,
|
||||||
SerializedElementNode,
|
SerializedElementNode,
|
||||||
SerializedTabNode,
|
SerializedTabNode,
|
||||||
|
TabNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
|
|
||||||
import {ElementNode, TextNode, TabNode} from 'lexical';
|
import {ElementNode, TextNode} from 'lexical';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CodeHighlighter
|
* CodeHighlighter
|
||||||
*/
|
*/
|
||||||
declare export function getEndOfCodeInLine(
|
declare export function getEndOfCodeInLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode,
|
anchor: CodeHighlightNode | TabNode,
|
||||||
): CodeHighlightNode | CodeTabNode;
|
): CodeHighlightNode | TabNode;
|
||||||
|
|
||||||
declare export function getStartOfCodeInLine(
|
declare export function getStartOfCodeInLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode,
|
anchor: CodeHighlightNode | TabNode,
|
||||||
offset: number,
|
offset: number,
|
||||||
): null | {
|
): null | {
|
||||||
node: CodeHighlightNode | CodeTabNode | LineBreakNode,
|
node: CodeHighlightNode | TabNode | LineBreakNode,
|
||||||
offset: number,
|
offset: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -91,14 +92,14 @@ declare export var getCodeLanguages: () => Array<string>;
|
|||||||
declare export var getDefaultCodeLanguage: () => string;
|
declare export var getDefaultCodeLanguage: () => string;
|
||||||
|
|
||||||
declare export function getFirstCodeNodeOfLine(
|
declare export function getFirstCodeNodeOfLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode | LineBreakNode,
|
anchor: CodeHighlightNode | TabNode | LineBreakNode,
|
||||||
): null | CodeHighlightNode | CodeTabNode | LineBreakNode;
|
): null | CodeHighlightNode | TabNode | LineBreakNode;
|
||||||
|
|
||||||
declare export function getLanguageFriendlyName(lang: string): string;
|
declare export function getLanguageFriendlyName(lang: string): string;
|
||||||
|
|
||||||
declare export function getLastCodeNodeOfLine(
|
declare export function getLastCodeNodeOfLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode | LineBreakNode,
|
anchor: CodeHighlightNode | TabNode | LineBreakNode,
|
||||||
): CodeHighlightNode | CodeTabNode | LineBreakNode;
|
): CodeHighlightNode | TabNode | LineBreakNode;
|
||||||
|
|
||||||
declare export function normalizeCodeLang(lang: string): string;
|
declare export function normalizeCodeLang(lang: string): string;
|
||||||
|
|
||||||
@ -127,34 +128,8 @@ declare export class CodeNode extends ElementNode {
|
|||||||
insertNewAfter(
|
insertNewAfter(
|
||||||
selection: RangeSelection,
|
selection: RangeSelection,
|
||||||
restoreSelection?: boolean,
|
restoreSelection?: boolean,
|
||||||
): null | ParagraphNode | CodeHighlightNode | CodeTabNode;
|
): null | ParagraphNode | CodeHighlightNode | TabNode;
|
||||||
collapseAtStart(): true;
|
collapseAtStart(): true;
|
||||||
setLanguage(language: string): void;
|
setLanguage(language: string): void;
|
||||||
getLanguage(): string | void;
|
getLanguage(): string | void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* CodeTabNode
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type SerializedCodeTabNode = SerializedTabNode;
|
|
||||||
|
|
||||||
declare export function $createCodeTabNode(): CodeTabNode;
|
|
||||||
|
|
||||||
declare export function $isCodeTabNode(
|
|
||||||
node: LexicalNode | null | void,
|
|
||||||
): boolean %checks(node instanceof CodeTabNode);
|
|
||||||
|
|
||||||
declare export class CodeTabNode extends TabNode {
|
|
||||||
static getType(): string;
|
|
||||||
// $FlowFixMe
|
|
||||||
static clone(node: CodeTabNode): CodeTabNode;
|
|
||||||
static importJSON(_serializedTabNode: SerializedCodeTabNode): CodeTabNode;
|
|
||||||
exportJSON(): SerializedTabNode;
|
|
||||||
createDOM(config: EditorConfig): HTMLElement;
|
|
||||||
updateDOM(
|
|
||||||
prevNode: TextNode,
|
|
||||||
dom: HTMLElement,
|
|
||||||
config: EditorConfig,
|
|
||||||
): boolean;
|
|
||||||
}
|
|
||||||
|
@ -14,6 +14,7 @@ import type {
|
|||||||
NodeKey,
|
NodeKey,
|
||||||
SerializedTextNode,
|
SerializedTextNode,
|
||||||
Spread,
|
Spread,
|
||||||
|
TabNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
|
|
||||||
import 'prismjs/components/prism-clike';
|
import 'prismjs/components/prism-clike';
|
||||||
@ -35,11 +36,15 @@ import {
|
|||||||
addClassNamesToElement,
|
addClassNamesToElement,
|
||||||
removeClassNamesFromElement,
|
removeClassNamesFromElement,
|
||||||
} from '@lexical/utils';
|
} from '@lexical/utils';
|
||||||
import {$applyNodeReplacement, ElementNode, TextNode} from 'lexical';
|
import {
|
||||||
|
$applyNodeReplacement,
|
||||||
|
$isTabNode,
|
||||||
|
ElementNode,
|
||||||
|
TextNode,
|
||||||
|
} from 'lexical';
|
||||||
import * as Prism from 'prismjs';
|
import * as Prism from 'prismjs';
|
||||||
|
|
||||||
import {$createCodeNode} from './CodeNode';
|
import {$createCodeNode} from './CodeNode';
|
||||||
import {$isCodeTabNode, CodeTabNode} from './CodeTabNode';
|
|
||||||
|
|
||||||
export const DEFAULT_CODE_LANGUAGE = 'javascript';
|
export const DEFAULT_CODE_LANGUAGE = 'javascript';
|
||||||
|
|
||||||
@ -229,11 +234,11 @@ export function $isCodeHighlightNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFirstCodeNodeOfLine(
|
export function getFirstCodeNodeOfLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode | LineBreakNode,
|
anchor: CodeHighlightNode | TabNode | LineBreakNode,
|
||||||
): null | CodeHighlightNode | CodeTabNode | LineBreakNode {
|
): null | CodeHighlightNode | TabNode | LineBreakNode {
|
||||||
let previousNode = anchor;
|
let previousNode = anchor;
|
||||||
let node: null | LexicalNode = anchor;
|
let node: null | LexicalNode = anchor;
|
||||||
while ($isCodeHighlightNode(node) || $isCodeTabNode(node)) {
|
while ($isCodeHighlightNode(node) || $isTabNode(node)) {
|
||||||
previousNode = node;
|
previousNode = node;
|
||||||
node = node.getPreviousSibling();
|
node = node.getPreviousSibling();
|
||||||
}
|
}
|
||||||
@ -241,11 +246,11 @@ export function getFirstCodeNodeOfLine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getLastCodeNodeOfLine(
|
export function getLastCodeNodeOfLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode | LineBreakNode,
|
anchor: CodeHighlightNode | TabNode | LineBreakNode,
|
||||||
): CodeHighlightNode | CodeTabNode | LineBreakNode {
|
): CodeHighlightNode | TabNode | LineBreakNode {
|
||||||
let nextNode = anchor;
|
let nextNode = anchor;
|
||||||
let node: null | LexicalNode = anchor;
|
let node: null | LexicalNode = anchor;
|
||||||
while ($isCodeHighlightNode(node) || $isCodeTabNode(node)) {
|
while ($isCodeHighlightNode(node) || $isTabNode(node)) {
|
||||||
nextNode = node;
|
nextNode = node;
|
||||||
node = node.getNextSibling();
|
node = node.getNextSibling();
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,14 @@ import {
|
|||||||
INDENT_CONTENT_COMMAND,
|
INDENT_CONTENT_COMMAND,
|
||||||
KEY_ARROW_DOWN_COMMAND,
|
KEY_ARROW_DOWN_COMMAND,
|
||||||
KEY_ARROW_UP_COMMAND,
|
KEY_ARROW_UP_COMMAND,
|
||||||
TabNode,
|
|
||||||
MOVE_TO_END,
|
MOVE_TO_END,
|
||||||
MOVE_TO_START,
|
MOVE_TO_START,
|
||||||
$insertNodes,
|
$insertNodes,
|
||||||
OUTDENT_CONTENT_COMMAND,
|
OUTDENT_CONTENT_COMMAND,
|
||||||
KEY_TAB_COMMAND,
|
KEY_TAB_COMMAND,
|
||||||
TextNode,
|
TextNode,
|
||||||
|
$isTabNode,
|
||||||
|
TabNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -70,7 +71,6 @@ import {
|
|||||||
|
|
||||||
import {$isCodeNode, CodeNode} from './CodeNode';
|
import {$isCodeNode, CodeNode} from './CodeNode';
|
||||||
import invariant from 'shared/invariant';
|
import invariant from 'shared/invariant';
|
||||||
import {CodeTabNode, $createCodeTabNode, $isCodeTabNode} from './CodeTabNode';
|
|
||||||
|
|
||||||
type TokenContent = string | Token | (string | Token)[];
|
type TokenContent = string | Token | (string | Token)[];
|
||||||
|
|
||||||
@ -95,18 +95,18 @@ export const PrismTokenizer: Tokenizer = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getStartOfCodeInLine(
|
export function getStartOfCodeInLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode,
|
anchor: CodeHighlightNode | TabNode,
|
||||||
offset: number,
|
offset: number,
|
||||||
): null | {
|
): null | {
|
||||||
node: CodeHighlightNode | CodeTabNode | LineBreakNode;
|
node: CodeHighlightNode | TabNode | LineBreakNode;
|
||||||
offset: number;
|
offset: number;
|
||||||
} {
|
} {
|
||||||
let last: null | {
|
let last: null | {
|
||||||
node: CodeHighlightNode | CodeTabNode | LineBreakNode;
|
node: CodeHighlightNode | TabNode | LineBreakNode;
|
||||||
offset: number;
|
offset: number;
|
||||||
} = null;
|
} = null;
|
||||||
let lastNonBlank: null | {node: CodeHighlightNode; offset: number} = null;
|
let lastNonBlank: null | {node: CodeHighlightNode; offset: number} = null;
|
||||||
let node: null | CodeHighlightNode | CodeTabNode | LineBreakNode = anchor;
|
let node: null | CodeHighlightNode | TabNode | LineBreakNode = anchor;
|
||||||
let nodeOffset = offset;
|
let nodeOffset = offset;
|
||||||
let nodeTextContent = anchor.getTextContent();
|
let nodeTextContent = anchor.getTextContent();
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
@ -118,9 +118,9 @@ export function getStartOfCodeInLine(
|
|||||||
}
|
}
|
||||||
invariant(
|
invariant(
|
||||||
$isCodeHighlightNode(node) ||
|
$isCodeHighlightNode(node) ||
|
||||||
$isCodeTabNode(node) ||
|
$isTabNode(node) ||
|
||||||
$isLineBreakNode(node),
|
$isLineBreakNode(node),
|
||||||
'Expected a valid Code Node: CodeHighlightNode, CodeTabNode, LineBreakNode',
|
'Expected a valid Code Node: CodeHighlightNode, TabNode, LineBreakNode',
|
||||||
);
|
);
|
||||||
if ($isLineBreakNode(node)) {
|
if ($isLineBreakNode(node)) {
|
||||||
last = {
|
last = {
|
||||||
@ -208,8 +208,8 @@ function findNextNonBlankInLine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getEndOfCodeInLine(
|
export function getEndOfCodeInLine(
|
||||||
anchor: CodeHighlightNode | CodeTabNode,
|
anchor: CodeHighlightNode | TabNode,
|
||||||
): CodeHighlightNode | CodeTabNode {
|
): CodeHighlightNode | TabNode {
|
||||||
const lastNode = getLastCodeNodeOfLine(anchor);
|
const lastNode = getLastCodeNodeOfLine(anchor);
|
||||||
invariant(
|
invariant(
|
||||||
!$isLineBreakNode(lastNode),
|
!$isLineBreakNode(lastNode),
|
||||||
@ -340,7 +340,7 @@ function getHighlightNodes(tokens: (string | Token)[]): LexicalNode[] {
|
|||||||
if (part === '\n' || part === '\r\n') {
|
if (part === '\n' || part === '\r\n') {
|
||||||
nodes.push($createLineBreakNode());
|
nodes.push($createLineBreakNode());
|
||||||
} else if (part === '\t') {
|
} else if (part === '\t') {
|
||||||
nodes.push($createCodeTabNode());
|
nodes.push($createTabNode());
|
||||||
} else if (part.length > 0) {
|
} else if (part.length > 0) {
|
||||||
nodes.push($createCodeHighlightNode(part));
|
nodes.push($createCodeHighlightNode(part));
|
||||||
}
|
}
|
||||||
@ -485,7 +485,7 @@ function isEqual(nodeA: LexicalNode, nodeB: LexicalNode): boolean {
|
|||||||
$isCodeHighlightNode(nodeB) &&
|
$isCodeHighlightNode(nodeB) &&
|
||||||
nodeA.__text === nodeB.__text &&
|
nodeA.__text === nodeB.__text &&
|
||||||
nodeA.__highlightType === nodeB.__highlightType) ||
|
nodeA.__highlightType === nodeB.__highlightType) ||
|
||||||
($isCodeTabNode(nodeA) && $isCodeTabNode(nodeB)) ||
|
($isTabNode(nodeA) && $isTabNode(nodeB)) ||
|
||||||
($isLineBreakNode(nodeA) && $isLineBreakNode(nodeB))
|
($isLineBreakNode(nodeA) && $isLineBreakNode(nodeB))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -507,20 +507,18 @@ function $isSelectionInCode(
|
|||||||
|
|
||||||
function $getCodeLines(
|
function $getCodeLines(
|
||||||
selection: RangeSelection,
|
selection: RangeSelection,
|
||||||
): Array<Array<CodeHighlightNode | CodeTabNode>> {
|
): Array<Array<CodeHighlightNode | TabNode>> {
|
||||||
const nodes = selection.getNodes();
|
const nodes = selection.getNodes();
|
||||||
const lines: Array<Array<CodeHighlightNode | CodeTabNode>> = [[]];
|
const lines: Array<Array<CodeHighlightNode | TabNode>> = [[]];
|
||||||
if (nodes.length === 1 && $isCodeNode(nodes[0])) {
|
if (nodes.length === 1 && $isCodeNode(nodes[0])) {
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
let lastLine: Array<CodeHighlightNode | CodeTabNode> = lines[0];
|
let lastLine: Array<CodeHighlightNode | TabNode> = lines[0];
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
const node = nodes[i];
|
const node = nodes[i];
|
||||||
invariant(
|
invariant(
|
||||||
$isCodeHighlightNode(node) ||
|
$isCodeHighlightNode(node) || $isTabNode(node) || $isLineBreakNode(node),
|
||||||
$isCodeTabNode(node) ||
|
'Expected selection to be inside CodeBlock and consisting of CodeHighlightNode, TabNode and LineBreakNode',
|
||||||
$isLineBreakNode(node),
|
|
||||||
'Expected selection to be inside CodeBlock and consisting of CodeHighlightNode, CodeTabNode and LineBreakNode',
|
|
||||||
);
|
);
|
||||||
if ($isLineBreakNode(node)) {
|
if ($isLineBreakNode(node)) {
|
||||||
if (i !== 0 && lastLine.length > 0) {
|
if (i !== 0 && lastLine.length > 0) {
|
||||||
@ -554,9 +552,9 @@ function handleTab(shiftKey: boolean): null | LexicalCommand<void> {
|
|||||||
invariant(
|
invariant(
|
||||||
$isCodeNode(firstNode) ||
|
$isCodeNode(firstNode) ||
|
||||||
$isCodeHighlightNode(firstNode) ||
|
$isCodeHighlightNode(firstNode) ||
|
||||||
$isCodeTabNode(firstNode) ||
|
$isTabNode(firstNode) ||
|
||||||
$isLineBreakNode(firstNode),
|
$isLineBreakNode(firstNode),
|
||||||
'Expected selection firstNode to be CodeHighlightNode or CodeTabNode',
|
'Expected selection firstNode to be CodeHighlightNode or TabNode',
|
||||||
);
|
);
|
||||||
if ($isCodeNode(firstNode)) {
|
if ($isCodeNode(firstNode)) {
|
||||||
return indentOrOutdent;
|
return indentOrOutdent;
|
||||||
@ -600,19 +598,16 @@ function handleMultilineIndent(type: LexicalCommand<void>): boolean {
|
|||||||
for (let i = 0; i < codeLinesLength; i++) {
|
for (let i = 0; i < codeLinesLength; i++) {
|
||||||
const line = codeLines[i];
|
const line = codeLines[i];
|
||||||
if (line.length > 0) {
|
if (line.length > 0) {
|
||||||
let firstOfLine:
|
let firstOfLine: null | CodeHighlightNode | TabNode | LineBreakNode =
|
||||||
| null
|
line[0];
|
||||||
| CodeHighlightNode
|
|
||||||
| CodeTabNode
|
|
||||||
| LineBreakNode = line[0];
|
|
||||||
// First and last lines might not be complete
|
// First and last lines might not be complete
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
firstOfLine = getFirstCodeNodeOfLine(firstOfLine);
|
firstOfLine = getFirstCodeNodeOfLine(firstOfLine);
|
||||||
}
|
}
|
||||||
if (firstOfLine !== null) {
|
if (firstOfLine !== null) {
|
||||||
if (type === INDENT_CONTENT_COMMAND) {
|
if (type === INDENT_CONTENT_COMMAND) {
|
||||||
firstOfLine.insertBefore($createCodeTabNode());
|
firstOfLine.insertBefore($createTabNode());
|
||||||
} else if ($isCodeTabNode(firstOfLine)) {
|
} else if ($isTabNode(firstOfLine)) {
|
||||||
firstOfLine.remove();
|
firstOfLine.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -626,14 +621,14 @@ function handleMultilineIndent(type: LexicalCommand<void>): boolean {
|
|||||||
invariant(
|
invariant(
|
||||||
$isCodeNode(firstNode) ||
|
$isCodeNode(firstNode) ||
|
||||||
$isCodeHighlightNode(firstNode) ||
|
$isCodeHighlightNode(firstNode) ||
|
||||||
$isCodeTabNode(firstNode) ||
|
$isTabNode(firstNode) ||
|
||||||
$isLineBreakNode(firstNode),
|
$isLineBreakNode(firstNode),
|
||||||
'Expected selection firstNode to be CodeHighlightNode or CodeTabNode',
|
'Expected selection firstNode to be CodeHighlightNode or CodeTabNode',
|
||||||
);
|
);
|
||||||
if ($isCodeNode(firstNode)) {
|
if ($isCodeNode(firstNode)) {
|
||||||
// CodeNode is empty
|
// CodeNode is empty
|
||||||
if (type === INDENT_CONTENT_COMMAND) {
|
if (type === INDENT_CONTENT_COMMAND) {
|
||||||
selection.insertNodes([$createCodeTabNode()]);
|
selection.insertNodes([$createTabNode()]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -644,11 +639,11 @@ function handleMultilineIndent(type: LexicalCommand<void>): boolean {
|
|||||||
);
|
);
|
||||||
if (type === INDENT_CONTENT_COMMAND) {
|
if (type === INDENT_CONTENT_COMMAND) {
|
||||||
if ($isLineBreakNode(firstOfLine)) {
|
if ($isLineBreakNode(firstOfLine)) {
|
||||||
firstOfLine.insertAfter($createCodeTabNode());
|
firstOfLine.insertAfter($createTabNode());
|
||||||
} else {
|
} else {
|
||||||
firstOfLine.insertBefore($createCodeTabNode());
|
firstOfLine.insertBefore($createTabNode());
|
||||||
}
|
}
|
||||||
} else if ($isCodeTabNode(firstOfLine)) {
|
} else if ($isTabNode(firstOfLine)) {
|
||||||
firstOfLine.remove();
|
firstOfLine.remove();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -676,8 +671,8 @@ function handleShiftLines(
|
|||||||
// Ensure the selection is within the codeblock
|
// Ensure the selection is within the codeblock
|
||||||
if (
|
if (
|
||||||
!$isSelectionInCode(selection) ||
|
!$isSelectionInCode(selection) ||
|
||||||
!($isCodeHighlightNode(anchorNode) || $isCodeTabNode(anchorNode)) ||
|
!($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) ||
|
||||||
!($isCodeHighlightNode(focusNode) || $isCodeTabNode(focusNode))
|
!($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -731,7 +726,7 @@ function handleShiftLines(
|
|||||||
const node = range[i];
|
const node = range[i];
|
||||||
if (
|
if (
|
||||||
!$isCodeHighlightNode(node) &&
|
!$isCodeHighlightNode(node) &&
|
||||||
!$isCodeTabNode(node) &&
|
!$isTabNode(node) &&
|
||||||
!$isLineBreakNode(node)
|
!$isLineBreakNode(node)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
@ -759,7 +754,7 @@ function handleShiftLines(
|
|||||||
|
|
||||||
const maybeInsertionPoint =
|
const maybeInsertionPoint =
|
||||||
$isCodeHighlightNode(sibling) ||
|
$isCodeHighlightNode(sibling) ||
|
||||||
$isCodeTabNode(sibling) ||
|
$isTabNode(sibling) ||
|
||||||
$isLineBreakNode(sibling)
|
$isLineBreakNode(sibling)
|
||||||
? arrowIsUp
|
? arrowIsUp
|
||||||
? getFirstCodeNodeOfLine(sibling)
|
? getFirstCodeNodeOfLine(sibling)
|
||||||
@ -801,8 +796,8 @@ function handleMoveTo(
|
|||||||
const isMoveToStart = type === MOVE_TO_START;
|
const isMoveToStart = type === MOVE_TO_START;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!($isCodeHighlightNode(anchorNode) || $isCodeTabNode(anchorNode)) ||
|
!($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) ||
|
||||||
!($isCodeHighlightNode(focusNode) || $isCodeTabNode(focusNode))
|
!($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -830,18 +825,6 @@ function handleMoveTo(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tabNodeTransform(node: TabNode): void {
|
|
||||||
if ($isCodeNode(node.getParent())) {
|
|
||||||
node.replace($createCodeTabNode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeTabNodeTransform(node: CodeTabNode): void {
|
|
||||||
if (!$isCodeNode(node.getParent())) {
|
|
||||||
node.replace($createTabNode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function registerCodeHighlighting(
|
export function registerCodeHighlighting(
|
||||||
editor: LexicalEditor,
|
editor: LexicalEditor,
|
||||||
tokenizer?: Tokenizer,
|
tokenizer?: Tokenizer,
|
||||||
@ -878,12 +861,6 @@ export function registerCodeHighlighting(
|
|||||||
editor.registerNodeTransform(CodeHighlightNode, (node) =>
|
editor.registerNodeTransform(CodeHighlightNode, (node) =>
|
||||||
textNodeTransform(node, editor, tokenizer as Tokenizer),
|
textNodeTransform(node, editor, tokenizer as Tokenizer),
|
||||||
),
|
),
|
||||||
editor.registerNodeTransform(TabNode, (node) => {
|
|
||||||
tabNodeTransform(node);
|
|
||||||
}),
|
|
||||||
editor.registerNodeTransform(CodeTabNode, (node) => {
|
|
||||||
codeTabNodeTransform(node);
|
|
||||||
}),
|
|
||||||
editor.registerCommand(
|
editor.registerCommand(
|
||||||
KEY_TAB_COMMAND,
|
KEY_TAB_COMMAND,
|
||||||
(event) => {
|
(event) => {
|
||||||
@ -904,7 +881,7 @@ export function registerCodeHighlighting(
|
|||||||
if (!$isSelectionInCode(selection)) {
|
if (!$isSelectionInCode(selection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$insertNodes([$createCodeTabNode()]);
|
$insertNodes([$createTabNode()]);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
COMMAND_PRIORITY_LOW,
|
COMMAND_PRIORITY_LOW,
|
||||||
|
@ -17,13 +17,9 @@ import type {
|
|||||||
RangeSelection,
|
RangeSelection,
|
||||||
SerializedElementNode,
|
SerializedElementNode,
|
||||||
Spread,
|
Spread,
|
||||||
|
TabNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import type {CodeHighlightNode, CodeTabNode} from '@lexical/code';
|
import type {CodeHighlightNode} from '@lexical/code';
|
||||||
import {
|
|
||||||
$isCodeHighlightNode,
|
|
||||||
$isCodeTabNode,
|
|
||||||
$createCodeTabNode,
|
|
||||||
} from '@lexical/code';
|
|
||||||
|
|
||||||
import 'prismjs/components/prism-clike';
|
import 'prismjs/components/prism-clike';
|
||||||
import 'prismjs/components/prism-javascript';
|
import 'prismjs/components/prism-javascript';
|
||||||
@ -46,8 +42,11 @@ import {
|
|||||||
$createLineBreakNode,
|
$createLineBreakNode,
|
||||||
$createParagraphNode,
|
$createParagraphNode,
|
||||||
ElementNode,
|
ElementNode,
|
||||||
|
$isTabNode,
|
||||||
|
$createTabNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import {
|
import {
|
||||||
|
$isCodeHighlightNode,
|
||||||
$createCodeHighlightNode,
|
$createCodeHighlightNode,
|
||||||
getFirstCodeNodeOfLine,
|
getFirstCodeNodeOfLine,
|
||||||
} from './CodeHighlightNode';
|
} from './CodeHighlightNode';
|
||||||
@ -222,7 +221,7 @@ export class CodeNode extends ElementNode {
|
|||||||
insertNewAfter(
|
insertNewAfter(
|
||||||
selection: RangeSelection,
|
selection: RangeSelection,
|
||||||
restoreSelection = true,
|
restoreSelection = true,
|
||||||
): null | ParagraphNode | CodeHighlightNode | CodeTabNode {
|
): null | ParagraphNode | CodeHighlightNode | TabNode {
|
||||||
const children = this.getChildren();
|
const children = this.getChildren();
|
||||||
const childrenLength = children.length;
|
const childrenLength = children.length;
|
||||||
|
|
||||||
@ -250,14 +249,14 @@ export class CodeNode extends ElementNode {
|
|||||||
const firstSelectionNode = firstPoint.getNode();
|
const firstSelectionNode = firstPoint.getNode();
|
||||||
if (
|
if (
|
||||||
$isCodeHighlightNode(firstSelectionNode) ||
|
$isCodeHighlightNode(firstSelectionNode) ||
|
||||||
$isCodeTabNode(firstSelectionNode)
|
$isTabNode(firstSelectionNode)
|
||||||
) {
|
) {
|
||||||
let node = getFirstCodeNodeOfLine(firstSelectionNode);
|
let node = getFirstCodeNodeOfLine(firstSelectionNode);
|
||||||
const insertNodes = [];
|
const insertNodes = [];
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
if ($isCodeTabNode(node)) {
|
if ($isTabNode(node)) {
|
||||||
insertNodes.push($createCodeTabNode());
|
insertNodes.push($createTabNode());
|
||||||
node = node.getNextSibling();
|
node = node.getNextSibling();
|
||||||
} else if ($isCodeHighlightNode(node)) {
|
} else if ($isCodeHighlightNode(node)) {
|
||||||
let spaces = 0;
|
let spaces = 0;
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type {
|
|
||||||
EditorConfig,
|
|
||||||
LexicalNode,
|
|
||||||
SerializedTabNode,
|
|
||||||
TextNode,
|
|
||||||
} from 'lexical';
|
|
||||||
|
|
||||||
import {$applyNodeReplacement, TabNode} from 'lexical';
|
|
||||||
|
|
||||||
export type SerializedCodeTabNode = SerializedTabNode;
|
|
||||||
|
|
||||||
/** @noInheritDoc */
|
|
||||||
export class CodeTabNode extends TabNode {
|
|
||||||
static getType(): string {
|
|
||||||
return 'code-tab';
|
|
||||||
}
|
|
||||||
|
|
||||||
static clone(node: CodeTabNode): CodeTabNode {
|
|
||||||
return new CodeTabNode(node.__key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static importJSON(_serializedTabNode: SerializedCodeTabNode): CodeTabNode {
|
|
||||||
return $createCodeTabNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
exportJSON(): SerializedTabNode {
|
|
||||||
return {
|
|
||||||
...super.exportJSON(),
|
|
||||||
type: 'code-tab',
|
|
||||||
version: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
createDOM(config: EditorConfig): HTMLElement {
|
|
||||||
const span = super.createDOM(config);
|
|
||||||
// TODO pass through theme
|
|
||||||
span.style.letterSpacing = '15px';
|
|
||||||
const text = span.firstChild;
|
|
||||||
if (text !== null) {
|
|
||||||
span.replaceChild(document.createTextNode(' '), text);
|
|
||||||
}
|
|
||||||
return span;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDOM(
|
|
||||||
prevNode: TextNode,
|
|
||||||
dom: HTMLElement,
|
|
||||||
config: EditorConfig,
|
|
||||||
): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function $createCodeTabNode(): CodeTabNode {
|
|
||||||
return $applyNodeReplacement(new CodeTabNode());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function $isCodeTabNode(
|
|
||||||
node: LexicalNode | null | undefined,
|
|
||||||
): node is CodeTabNode {
|
|
||||||
return node instanceof CodeTabNode;
|
|
||||||
}
|
|
@ -9,7 +9,6 @@
|
|||||||
import {
|
import {
|
||||||
$createCodeNode,
|
$createCodeNode,
|
||||||
$isCodeHighlightNode,
|
$isCodeHighlightNode,
|
||||||
$isCodeTabNode,
|
|
||||||
registerCodeHighlighting,
|
registerCodeHighlighting,
|
||||||
} from '@lexical/code';
|
} from '@lexical/code';
|
||||||
import {registerTabIndentation} from '@lexical/react/LexicalTabIndentationPlugin';
|
import {registerTabIndentation} from '@lexical/react/LexicalTabIndentationPlugin';
|
||||||
@ -24,6 +23,7 @@ import {
|
|||||||
$getSelection,
|
$getSelection,
|
||||||
$isLineBreakNode,
|
$isLineBreakNode,
|
||||||
$isRangeSelection,
|
$isRangeSelection,
|
||||||
|
$isTabNode,
|
||||||
$setSelection,
|
$setSelection,
|
||||||
KEY_ARROW_DOWN_COMMAND,
|
KEY_ARROW_DOWN_COMMAND,
|
||||||
KEY_ARROW_UP_COMMAND,
|
KEY_ARROW_UP_COMMAND,
|
||||||
@ -180,15 +180,15 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
});
|
});
|
||||||
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
||||||
expect(testEnv.innerHTML).toBe(
|
expect(testEnv.innerHTML).toBe(
|
||||||
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">function</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span></code>',
|
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">function</span><span data-lexical-text="true">\t</span></code>',
|
||||||
);
|
);
|
||||||
|
|
||||||
// CodeNode should only render diffs, make sure that the CodeTabNode is not cloned when
|
// CodeNode should only render diffs, make sure that the TabNode is not cloned when
|
||||||
// appending more text
|
// appending more text
|
||||||
let tabKey;
|
let tabKey;
|
||||||
await editor.update(() => {
|
await editor.update(() => {
|
||||||
tabKey = $dfs()
|
tabKey = $dfs()
|
||||||
.find(({node}) => $isCodeTabNode(node))
|
.find(({node}) => $isTabNode(node))
|
||||||
.node.getKey();
|
.node.getKey();
|
||||||
$getSelection().insertText('foo');
|
$getSelection().insertText('foo');
|
||||||
});
|
});
|
||||||
@ -198,7 +198,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
expect(testEnv.innerHTML).toBe(
|
expect(testEnv.innerHTML).toBe(
|
||||||
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">function</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">foo</span></code>',
|
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">function</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">foo</span></code>',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
});
|
});
|
||||||
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
||||||
expect(testEnv.innerHTML).toBe(
|
expect(testEnv.innerHTML).toBe(
|
||||||
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">f</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span></code>',
|
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">f</span><span data-lexical-text="true">\t</span></code>',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -244,7 +244,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
});
|
});
|
||||||
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
||||||
expect(testEnv.innerHTML).toBe(
|
expect(testEnv.innerHTML).toBe(
|
||||||
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">function</span></code>',
|
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">\t</span><span data-lexical-text="true">function</span></code>',
|
||||||
);
|
);
|
||||||
|
|
||||||
await editor.update(() => {
|
await editor.update(() => {
|
||||||
@ -280,7 +280,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
});
|
});
|
||||||
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
||||||
expect(testEnv.innerHTML).toBe(
|
expect(testEnv.innerHTML).toBe(
|
||||||
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">function</span></code>',
|
'<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1"><span data-lexical-text="true">\t</span><span data-lexical-text="true">function</span></code>',
|
||||||
);
|
);
|
||||||
|
|
||||||
await editor.update(() => {
|
await editor.update(() => {
|
||||||
@ -322,13 +322,13 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
||||||
expect(testEnv.innerHTML).toBe(
|
expect(testEnv.innerHTML).toBe(
|
||||||
`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
||||||
2"><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">hello</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">world</span><br><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">hello</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">world</span></code>`,
|
2"><span data-lexical-text="true">\t</span><span data-lexical-text="true">hello</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">world</span><br><span data-lexical-text="true">\t</span><span data-lexical-text="true">hello</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">world</span></code>`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await editor.dispatchCommand(KEY_TAB_COMMAND, shiftTabKeyboardEvent());
|
await editor.dispatchCommand(KEY_TAB_COMMAND, shiftTabKeyboardEvent());
|
||||||
expect(testEnv.innerHTML).toBe(
|
expect(testEnv.innerHTML).toBe(
|
||||||
`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
||||||
2"><span data-lexical-text="true">hello</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">world</span><br><span data-lexical-text="true">hello</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">world</span></code>`,
|
2"><span data-lexical-text="true">hello</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">world</span><br><span data-lexical-text="true">hello</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">world</span></code>`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
await editor.dispatchCommand(KEY_TAB_COMMAND, tabKeyboardEvent());
|
||||||
expect(testEnv.innerHTML)
|
expect(testEnv.innerHTML)
|
||||||
.toBe(`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
.toBe(`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
||||||
2"><span data-lexical-text="true">hello</span><br><span style="letter-spacing: 15px;" data-lexical-text="true"> </span></code>`);
|
2"><span data-lexical-text="true">hello</span><br><span data-lexical-text="true">\t</span></code>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can outdent at arbitrary points in the line (with tabs)', async () => {
|
test('can outdent at arbitrary points in the line (with tabs)', async () => {
|
||||||
@ -390,7 +390,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
await editor.dispatchCommand(KEY_ARROW_UP_COMMAND, keyEvent);
|
await editor.dispatchCommand(KEY_ARROW_UP_COMMAND, keyEvent);
|
||||||
expect(testEnv.innerHTML)
|
expect(testEnv.innerHTML)
|
||||||
.toBe(`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
.toBe(`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
||||||
2"><span data-lexical-text="true">ghi</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">jkl</span><br><span data-lexical-text="true">abc</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">def</span></code>`);
|
2"><span data-lexical-text="true">ghi</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">jkl</span><br><span data-lexical-text="true">abc</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">def</span></code>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('code blocks can shift multiple lines (with tab)', async () => {
|
test('code blocks can shift multiple lines (with tab)', async () => {
|
||||||
@ -424,7 +424,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(testEnv.innerHTML)
|
expect(testEnv.innerHTML)
|
||||||
.toBe(`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
.toBe(`<code spellcheck="false" data-highlight-language="javascript" dir="ltr" data-gutter="1
|
||||||
2
|
2
|
||||||
3"><span data-lexical-text="true">mno</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">pqr</span><br><span data-lexical-text="true">abc</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">def</span><br><span data-lexical-text="true">ghi</span><span style="letter-spacing: 15px;" data-lexical-text="true"> </span><span data-lexical-text="true">jkl</span></code>`);
|
3"><span data-lexical-text="true">mno</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">pqr</span><br><span data-lexical-text="true">abc</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">def</span><br><span data-lexical-text="true">ghi</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">jkl</span></code>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('arrows', () => {
|
describe('arrows', () => {
|
||||||
@ -493,7 +493,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(selection.isCollapsed()).toBe(true);
|
expect(selection.isCollapsed()).toBe(true);
|
||||||
if (moveTo === 'start') {
|
if (moveTo === 'start') {
|
||||||
if (tabOrSpaces === 'tab') {
|
if (tabOrSpaces === 'tab') {
|
||||||
expect($isCodeTabNode(selection.anchor.getNode())).toBe(true);
|
expect($isTabNode(selection.anchor.getNode())).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
$isCodeHighlightNode(
|
$isCodeHighlightNode(
|
||||||
selection.anchor.getNode().getNextSibling(),
|
selection.anchor.getNode().getNextSibling(),
|
||||||
@ -533,7 +533,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(selection.isCollapsed()).toBe(true);
|
expect(selection.isCollapsed()).toBe(true);
|
||||||
if (moveTo === 'start') {
|
if (moveTo === 'start') {
|
||||||
if (tabOrSpaces === 'tab') {
|
if (tabOrSpaces === 'tab') {
|
||||||
expect($isCodeTabNode(selection.anchor.getNode())).toBe(true);
|
expect($isTabNode(selection.anchor.getNode())).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
$isCodeHighlightNode(
|
$isCodeHighlightNode(
|
||||||
selection.anchor.getNode().getNextSibling(),
|
selection.anchor.getNode().getNextSibling(),
|
||||||
@ -645,7 +645,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(selection.isCollapsed()).toBe(true);
|
expect(selection.isCollapsed()).toBe(true);
|
||||||
if (moveTo === 'start') {
|
if (moveTo === 'start') {
|
||||||
if (tabOrSpaces === 'tab') {
|
if (tabOrSpaces === 'tab') {
|
||||||
expect($isCodeTabNode(selection.anchor.getNode())).toBe(true);
|
expect($isTabNode(selection.anchor.getNode())).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
$isCodeHighlightNode(
|
$isCodeHighlightNode(
|
||||||
selection.anchor.getNode().getNextSibling(),
|
selection.anchor.getNode().getNextSibling(),
|
||||||
@ -690,7 +690,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(selection.isCollapsed()).toBe(true);
|
expect(selection.isCollapsed()).toBe(true);
|
||||||
if (moveTo === 'start') {
|
if (moveTo === 'start') {
|
||||||
if (tabOrSpaces === 'tab') {
|
if (tabOrSpaces === 'tab') {
|
||||||
expect($isCodeTabNode(selection.anchor.getNode())).toBe(true);
|
expect($isTabNode(selection.anchor.getNode())).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
$isCodeHighlightNode(
|
$isCodeHighlightNode(
|
||||||
selection.anchor.getNode().getNextSibling(),
|
selection.anchor.getNode().getNextSibling(),
|
||||||
@ -731,7 +731,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(selection.isCollapsed()).toBe(true);
|
expect(selection.isCollapsed()).toBe(true);
|
||||||
if (moveTo === 'start') {
|
if (moveTo === 'start') {
|
||||||
if (tabOrSpaces === 'tab') {
|
if (tabOrSpaces === 'tab') {
|
||||||
expect($isCodeTabNode(selection.anchor.getNode())).toBe(true);
|
expect($isTabNode(selection.anchor.getNode())).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
$isCodeHighlightNode(
|
$isCodeHighlightNode(
|
||||||
selection.anchor.getNode().getNextSibling(),
|
selection.anchor.getNode().getNextSibling(),
|
||||||
@ -772,7 +772,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(selection.isCollapsed()).toBe(true);
|
expect(selection.isCollapsed()).toBe(true);
|
||||||
if (moveTo === 'start') {
|
if (moveTo === 'start') {
|
||||||
if (tabOrSpaces === 'tab') {
|
if (tabOrSpaces === 'tab') {
|
||||||
expect($isCodeTabNode(selection.anchor.getNode())).toBe(true);
|
expect($isTabNode(selection.anchor.getNode())).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
$isCodeHighlightNode(
|
$isCodeHighlightNode(
|
||||||
selection.anchor.getNode().getNextSibling(),
|
selection.anchor.getNode().getNextSibling(),
|
||||||
@ -818,7 +818,7 @@ describe('LexicalCodeNode tests', () => {
|
|||||||
expect(selection.isCollapsed()).toBe(true);
|
expect(selection.isCollapsed()).toBe(true);
|
||||||
if (moveTo === 'start') {
|
if (moveTo === 'start') {
|
||||||
if (tabOrSpaces === 'tab') {
|
if (tabOrSpaces === 'tab') {
|
||||||
expect($isCodeTabNode(selection.anchor.getNode())).toBe(true);
|
expect($isTabNode(selection.anchor.getNode())).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
$isCodeHighlightNode(
|
$isCodeHighlightNode(
|
||||||
selection.anchor.getNode().getNextSibling(),
|
selection.anchor.getNode().getNextSibling(),
|
||||||
|
@ -30,5 +30,3 @@ export {
|
|||||||
} from './CodeHighlightNode';
|
} from './CodeHighlightNode';
|
||||||
export type {SerializedCodeNode} from './CodeNode';
|
export type {SerializedCodeNode} from './CodeNode';
|
||||||
export {$createCodeNode, $isCodeNode, CodeNode} from './CodeNode';
|
export {$createCodeNode, $isCodeNode, CodeNode} from './CodeNode';
|
||||||
export type {SerializedCodeTabNode} from './CodeTabNode';
|
|
||||||
export {$createCodeTabNode, $isCodeTabNode, CodeTabNode} from './CodeTabNode';
|
|
||||||
|
@ -361,7 +361,7 @@ test.describe('CodeBlock', () => {
|
|||||||
;
|
;
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenFunction"
|
class="PlaygroundEditorTheme__tokenFunction"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -388,7 +388,7 @@ test.describe('CodeBlock', () => {
|
|||||||
;
|
;
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenPunctuation"
|
class="PlaygroundEditorTheme__tokenPunctuation"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -447,7 +447,7 @@ test.describe('CodeBlock', () => {
|
|||||||
{
|
{
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenFunction"
|
class="PlaygroundEditorTheme__tokenFunction"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -494,8 +494,8 @@ test.describe('CodeBlock', () => {
|
|||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
data-gutter="123"
|
data-gutter="123"
|
||||||
data-highlight-language="javascript">
|
data-highlight-language="javascript">
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenAttr"
|
class="PlaygroundEditorTheme__tokenAttr"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -520,9 +520,9 @@ test.describe('CodeBlock', () => {
|
|||||||
{
|
{
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenFunction"
|
class="PlaygroundEditorTheme__tokenFunction"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -544,8 +544,8 @@ test.describe('CodeBlock', () => {
|
|||||||
;
|
;
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenPunctuation"
|
class="PlaygroundEditorTheme__tokenPunctuation"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -567,7 +567,7 @@ test.describe('CodeBlock', () => {
|
|||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
data-gutter="123"
|
data-gutter="123"
|
||||||
data-highlight-language="javascript">
|
data-highlight-language="javascript">
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenAttr"
|
class="PlaygroundEditorTheme__tokenAttr"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -592,8 +592,8 @@ test.describe('CodeBlock', () => {
|
|||||||
{
|
{
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenFunction"
|
class="PlaygroundEditorTheme__tokenFunction"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -615,7 +615,7 @@ test.describe('CodeBlock', () => {
|
|||||||
;
|
;
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenPunctuation"
|
class="PlaygroundEditorTheme__tokenPunctuation"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
@ -1091,10 +1091,10 @@ test.describe('CodeBlock', () => {
|
|||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
data-gutter="12"
|
data-gutter="12"
|
||||||
data-highlight-language="javascript">
|
data-highlight-language="javascript">
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span data-lexical-text="true">a b</span>
|
<span data-lexical-text="true">a b</span>
|
||||||
<br />
|
<br />
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span data-lexical-text="true">c d</span>
|
<span data-lexical-text="true">c d</span>
|
||||||
</code>
|
</code>
|
||||||
`,
|
`,
|
||||||
|
@ -66,7 +66,7 @@ test.describe('Tab', () => {
|
|||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
data-gutter="1"
|
data-gutter="1"
|
||||||
data-highlight-language="javascript">
|
data-highlight-language="javascript">
|
||||||
<span style="letter-spacing: 15px" data-lexical-text="true"></span>
|
<span data-lexical-text="true"></span>
|
||||||
<span
|
<span
|
||||||
class="PlaygroundEditorTheme__tokenAttr"
|
class="PlaygroundEditorTheme__tokenAttr"
|
||||||
data-lexical-text="true">
|
data-lexical-text="true">
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import type {Klass, LexicalNode} from 'lexical';
|
import type {Klass, LexicalNode} from 'lexical';
|
||||||
|
|
||||||
import {CodeHighlightNode, CodeNode, CodeTabNode} from '@lexical/code';
|
import {CodeHighlightNode, CodeNode} from '@lexical/code';
|
||||||
import {HashtagNode} from '@lexical/hashtag';
|
import {HashtagNode} from '@lexical/hashtag';
|
||||||
import {AutoLinkNode, LinkNode} from '@lexical/link';
|
import {AutoLinkNode, LinkNode} from '@lexical/link';
|
||||||
import {ListItemNode, ListNode} from '@lexical/list';
|
import {ListItemNode, ListNode} from '@lexical/list';
|
||||||
@ -67,7 +67,6 @@ const PlaygroundNodes: Array<Klass<LexicalNode>> = [
|
|||||||
CollapsibleContainerNode,
|
CollapsibleContainerNode,
|
||||||
CollapsibleContentNode,
|
CollapsibleContentNode,
|
||||||
CollapsibleTitleNode,
|
CollapsibleTitleNode,
|
||||||
CodeTabNode,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default PlaygroundNodes;
|
export default PlaygroundNodes;
|
||||||
|
@ -101,6 +101,7 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
tab-size: 2;
|
||||||
}
|
}
|
||||||
.PlaygroundEditorTheme__code:before {
|
.PlaygroundEditorTheme__code:before {
|
||||||
content: attr(data-gutter);
|
content: attr(data-gutter);
|
||||||
|
@ -18,7 +18,7 @@ import type {
|
|||||||
SerializedTextNode,
|
SerializedTextNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
|
|
||||||
import {CodeHighlightNode, CodeNode, CodeTabNode} from '@lexical/code';
|
import {CodeHighlightNode, CodeNode} from '@lexical/code';
|
||||||
import {HashtagNode} from '@lexical/hashtag';
|
import {HashtagNode} from '@lexical/hashtag';
|
||||||
import {AutoLinkNode, LinkNode} from '@lexical/link';
|
import {AutoLinkNode, LinkNode} from '@lexical/link';
|
||||||
import {ListItemNode, ListNode} from '@lexical/list';
|
import {ListItemNode, ListNode} from '@lexical/list';
|
||||||
@ -422,7 +422,6 @@ const DEFAULT_NODES = [
|
|||||||
ListItemNode,
|
ListItemNode,
|
||||||
QuoteNode,
|
QuoteNode,
|
||||||
CodeNode,
|
CodeNode,
|
||||||
CodeTabNode,
|
|
||||||
TableNode,
|
TableNode,
|
||||||
TableCellNode,
|
TableCellNode,
|
||||||
TableRowNode,
|
TableRowNode,
|
||||||
|
Reference in New Issue
Block a user