Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 8 additions & 1 deletion packages/lexical-code-prism/flow/LexicalCodePrism.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
* LexicalCodePrism
*/

import type {LexicalEditor} from 'lexical';
import type {LexicalEditor, LexicalExtension} from 'lexical';
import type {NamedSignalsOutput} from '@lexical/extension';

type TokenContent = string | Token | (string | Token)[];
export interface Token {
Expand All @@ -29,6 +30,12 @@ declare export function registerCodeHighlighting(
tokenizer?: Tokenizer,
): () => void;

export type CodePrismConfig = {
disabled: boolean,
tokenizer: Tokenizer,
};
declare export var CodePrismExtension: LexicalExtension<CodePrismConfig, "@lexical/code-prism", NamedSignalsOutput<CodePrismConfig>, void>;

declare export function normalizeCodeLanguage(lang: string): string;

declare export var getCodeLanguages: () => Array<string>;
Expand Down
1 change: 1 addition & 0 deletions packages/lexical-code-prism/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"types": "index.d.ts",
"dependencies": {
"@lexical/code-core": "workspace:*",
"@lexical/extension": "workspace:*",
"lexical": "workspace:*",
"prismjs": "^1.30.0"
},
Expand Down
40 changes: 40 additions & 0 deletions packages/lexical-code-prism/src/CodeHighlighterPrism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ import {
$getStartOfCodeInLine,
$isCodeHighlightNode,
$isCodeNode,
CodeExtension,
CodeHighlightNode,
CodeNode,
DEFAULT_CODE_LANGUAGE,
} from '@lexical/code-core';
import {effect, namedSignals} from '@lexical/extension';
import {
$createLineBreakNode,
$createPoint,
Expand All @@ -49,6 +51,7 @@ import {
$onUpdate,
$setSelectionFromCaretRange,
COMMAND_PRIORITY_LOW,
defineExtension,
INDENT_CONTENT_COMMAND,
INSERT_TAB_COMMAND,
KEY_ARROW_DOWN_COMMAND,
Expand All @@ -58,6 +61,7 @@ import {
MOVE_TO_END,
MOVE_TO_START,
OUTDENT_CONTENT_COMMAND,
safeCast,
TabNode,
TextNode,
} from 'lexical';
Expand Down Expand Up @@ -896,3 +900,39 @@ export function registerCodeHighlighting(

return mergeRegister(...registrations);
}

export interface CodePrismConfig {
/**
* When true, the Prism code highlighter is not registered on the editor.
* This signal can be flipped at runtime to enable or disable the
* highlighter, for example to switch between the Prism and Shiki
* highlighters without rebuilding the editor.
*/
disabled: boolean;
tokenizer: Tokenizer;
}

/**
* Add code highlighting support for code blocks with Prism.
*
* {@link CodeExtension} is a dependency, so the required `CodeNode` and
* `CodeHighlightNode` nodes are registered automatically.
*/
export const CodePrismExtension = defineExtension({
build: (editor, config) => namedSignals(config),
config: safeCast<CodePrismConfig>({
disabled: false,
tokenizer: PrismTokenizer,
}),
dependencies: [CodeExtension],
name: '@lexical/code-prism',
register: (editor, config, state) => {
const stores = state.getOutput();
return effect(() => {
if (stores.disabled.value) {
return;
}
return registerCodeHighlighting(editor, stores.tokenizer.value);
});
},
});
2 changes: 2 additions & 0 deletions packages/lexical-code-prism/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*/

export {
type CodePrismConfig,
CodePrismExtension,
PrismTokenizer,
registerCodeHighlighting,
type Tokenizer,
Expand Down
13 changes: 11 additions & 2 deletions packages/lexical-code-shiki/flow/LexicalCodeShiki.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
LexicalNode,
ExtensionConfigBase,
} from 'lexical';
import type {NamedSignalsOutput} from '@lexical/extension';
import type {CodeNode} from '@lexical/code';

/**
Expand All @@ -22,6 +23,14 @@ export type Tokenizer = {
defaultTheme: string;
$tokenize: (codeNode: CodeNode, language?: string) => LexicalNode[];
}
export type CodeHighlighterShikiConfig = Tokenizer;
export type CodeShikiConfig = {
disabled: boolean,
tokenizer: Tokenizer,
};
declare export var ShikiTokenizer: Tokenizer;
declare export var CodeHighlighterShikiExtension: LexicalExtension<CodeHighlighterShikiConfig, "@lexical/code-shiki", void, void>;
declare export var CodeShikiExtension: LexicalExtension<CodeShikiConfig, "@lexical/code-shiki", NamedSignalsOutput<CodeShikiConfig>, void>;

/** @deprecated Use CodeShikiExtension instead */
export type CodeHighlighterShikiConfig = Tokenizer;
/** @deprecated Use CodeShikiExtension instead */
declare export var CodeHighlighterShikiExtension: LexicalExtension<CodeHighlighterShikiConfig, "@lexical/code-shiki/legacy", void, void>;
1 change: 1 addition & 0 deletions packages/lexical-code-shiki/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"types": "index.d.ts",
"dependencies": {
"@lexical/code-core": "workspace:*",
"@lexical/extension": "workspace:*",
"@shikijs/core": "^4.0.2",
"@shikijs/engine-javascript": "^4.0.2",
"@shikijs/langs": "^4.0.2",
Expand Down
62 changes: 58 additions & 4 deletions packages/lexical-code-shiki/src/CodeHighlighterShiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
CodeNode,
DEFAULT_CODE_LANGUAGE,
} from '@lexical/code-core';
import {effect, namedSignals} from '@lexical/extension';
import {
$createLineBreakNode,
$createPoint,
Expand Down Expand Up @@ -923,14 +924,67 @@ export function registerCodeHighlighting(
return mergeRegister(...registrations);
}

export interface CodeShikiConfig {
/**
* When true, the Shiki code highlighter is not registered on the editor.
* This signal can be flipped at runtime to enable or disable the
* highlighter, for example to switch between the Prism and Shiki
* highlighters without rebuilding the editor.
*/
disabled: boolean;
tokenizer: Tokenizer;
}

/**
* Add code highlighting support for code blocks with Shiki.
*
* {@link CodeExtension} is a dependency, so the required `CodeNode` and
* `CodeHighlightNode` nodes are registered automatically.
*/
export const CodeShikiExtension = defineExtension({
build: (editor, config) => namedSignals(config),
config: safeCast<CodeShikiConfig>({
disabled: false,
tokenizer: ShikiTokenizer,
}),
dependencies: [CodeExtension],
name: '@lexical/code-shiki',
register: (editor, config, state) => {
const stores = state.getOutput();
return effect(() => {
if (stores.disabled.value) {
return;
}
return registerCodeHighlighting(editor, stores.tokenizer.value);
});
},
});

/**
* @deprecated Use {@link CodeShikiExtension} instead. This type is a
* flat alias for {@link Tokenizer} kept for backward compatibility with
* {@link CodeHighlighterShikiExtension}.
*/
export type CodeHighlighterShikiConfig = Tokenizer;

/**
* Add code highlighting support for code blocks with Shiki
* @deprecated Use {@link CodeShikiExtension} instead.
*
* This is a thin backward-compatibility shim that preserves the original
* flat {@link Tokenizer} config API. It depends on
* {@link CodeShikiExtension} and routes its configured tokenizer to the
* underlying extension during `init` (before `CodeShikiExtension` builds),
* so consumers using
* `configExtension(CodeHighlighterShikiExtension, customTokenizer)`
* continue to work without modification.
*/
export const CodeHighlighterShikiExtension = defineExtension({
config: safeCast<CodeHighlighterShikiConfig>(ShikiTokenizer),
dependencies: [CodeExtension],
name: '@lexical/code-shiki',
register: (editor, config) => registerCodeHighlighting(editor, config),
dependencies: [CodeShikiExtension],
init: (editorConfig, config, state) => {
// Forward the flat Tokenizer config to CodeShikiExtension's `tokenizer`
// field before it builds.
state.getDependency(CodeShikiExtension).config.tokenizer = config;
},
name: '@lexical/code-shiki/legacy',
});
3 changes: 3 additions & 0 deletions packages/lexical-code-shiki/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
*/

export {
type CodeHighlighterShikiConfig,
CodeHighlighterShikiExtension,
type CodeShikiConfig,
CodeShikiExtension,
registerCodeHighlighting,
ShikiTokenizer,
type Tokenizer,
Expand Down
16 changes: 16 additions & 0 deletions packages/lexical-playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,24 @@ import logo from './images/logo.svg';
import {KeywordsExtension} from './nodes/KeywordNode';
import PlaygroundNodes from './nodes/PlaygroundNodes';
import {PlaygroundAutoLinkExtension} from './plugins/AutoLinkExtension';
import {CodeHighlightExtension} from './plugins/CodeHighlightExtension';
import {CollapsibleExtension} from './plugins/CollapsibleExtension';
import {DateTimeExtension} from './plugins/DateTimeExtension';
import DocsPlugin from './plugins/DocsPlugin';
import {DragDropPasteExtension} from './plugins/DragDropPasteExtension';
import {EmojisExtension} from './plugins/EmojisExtension';
import {FigmaExtension} from './plugins/FigmaExtension';
import {ImagesExtension} from './plugins/ImagesExtension';
import {PlaygroundMarkdownShortcutsExtension} from './plugins/MarkdownShortcutsExtension';
import {MaxLengthExtension} from './plugins/MaxLengthPlugin';
import {PageBreakExtension} from './plugins/PageBreakExtension';
import PasteLogPlugin from './plugins/PasteLogPlugin';
import {SpecialTextExtension} from './plugins/SpecialTextExtension';
import {TabFocusExtension} from './plugins/TabFocusExtension';
import TestRecorderPlugin from './plugins/TestRecorderPlugin';
import {TwitterExtension} from './plugins/TwitterExtension';
import TypingPerfPlugin from './plugins/TypingPerfPlugin';
import {YouTubeExtension} from './plugins/YouTubeExtension';
import Settings from './Settings';
import PlaygroundEditorTheme from './themes/PlaygroundEditorTheme';
import {validateUrl} from './utils/url';
Expand Down Expand Up @@ -157,6 +165,13 @@ const PlaygroundRichTextExtension = defineExtension({
RichTextExtension,
ImagesExtension,
HorizontalRuleExtension,
PageBreakExtension,
TwitterExtension,
YouTubeExtension,
FigmaExtension,
TabFocusExtension,
CollapsibleExtension,
CodeHighlightExtension,
configExtension(ListExtension, {shouldPreserveNumbering: false}),
CheckListExtension,
PlaygroundMarkdownShortcutsExtension,
Expand All @@ -174,6 +189,7 @@ const AppExtension = defineExtension({
HashtagExtension,
DateTimeExtension,
MaxLengthExtension,
SpecialTextExtension,
DragDropPasteExtension,
EmojisExtension,
configExtension(LinkExtension, {validateUrl}),
Expand Down
34 changes: 12 additions & 22 deletions packages/lexical-playground/src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,23 @@ import ActionsPlugin from './plugins/ActionsPlugin';
import AutocompletePlugin from './plugins/AutocompletePlugin';
import AutoEmbedPlugin from './plugins/AutoEmbedPlugin';
import CodeActionMenuPlugin from './plugins/CodeActionMenuPlugin';
import CodeHighlightPrismPlugin from './plugins/CodeHighlightPrismPlugin';
import CodeHighlightShikiPlugin from './plugins/CodeHighlightShikiPlugin';
import CollapsiblePlugin from './plugins/CollapsiblePlugin';
import {CodeHighlightExtension} from './plugins/CodeHighlightExtension';
import CommentPlugin from './plugins/CommentPlugin';
import ComponentPickerPlugin from './plugins/ComponentPickerPlugin';
import ContextMenuPlugin from './plugins/ContextMenuPlugin';
import DraggableBlockPlugin from './plugins/DraggableBlockPlugin';
import EmojiPickerPlugin from './plugins/EmojiPickerPlugin';
import EquationsPlugin from './plugins/EquationsPlugin';
import ExcalidrawPlugin from './plugins/ExcalidrawPlugin';
import FigmaPlugin from './plugins/FigmaPlugin';
import FloatingLinkEditorPlugin from './plugins/FloatingLinkEditorPlugin';
import FloatingTextFormatToolbarPlugin from './plugins/FloatingTextFormatToolbarPlugin';
import {LayoutPlugin} from './plugins/LayoutPlugin/LayoutPlugin';
import {MaxLengthExtension} from './plugins/MaxLengthPlugin';
import MentionsPlugin from './plugins/MentionsPlugin';
import PageBreakPlugin from './plugins/PageBreakPlugin';
import PollPlugin from './plugins/PollPlugin';
import ShortcutsPlugin from './plugins/ShortcutsPlugin';
import SpecialTextPlugin from './plugins/SpecialTextPlugin';
import {SpecialTextExtension} from './plugins/SpecialTextExtension';
import SpeechToTextPlugin from './plugins/SpeechToTextPlugin';
import TabFocusPlugin from './plugins/TabFocusPlugin';
import TableCellActionMenuPlugin from './plugins/TableActionMenuPlugin';
import TableCellResizer from './plugins/TableCellResizer';
import TableFitNestedTablePlugin from './plugins/TableFitNestedTablePlugin';
Expand All @@ -72,9 +67,7 @@ import TableOfContentsPlugin from './plugins/TableOfContentsPlugin';
import TableScrollShadowPlugin from './plugins/TableScrollShadowPlugin';
import ToolbarPlugin from './plugins/ToolbarPlugin';
import TreeViewPlugin from './plugins/TreeViewPlugin';
import TwitterPlugin from './plugins/TwitterPlugin';
import {VersionsPlugin} from './plugins/VersionsPlugin';
import YouTubePlugin from './plugins/YouTubePlugin';
import ContentEditable from './ui/ContentEditable';

const COLLAB_DOC_ID = 'main';
Expand Down Expand Up @@ -150,6 +143,16 @@ export default function Editor(): JSX.Element {
};

useSyncExtensionSignal(MaxLengthExtension, 'disabled', !isMaxLength);
useSyncExtensionSignal(
CodeHighlightExtension,
'mode',
!isCodeHighlighted ? 'off' : isCodeShiki ? 'shiki' : 'prism',
);
useSyncExtensionSignal(
SpecialTextExtension,
'disabled',
!shouldAllowHighlightingWithBrackets,
);
useSyncExtensionSignal(
LinkExtension,
'attributes',
Expand Down Expand Up @@ -239,12 +242,6 @@ export default function Editor(): JSX.Element {
<ContentEditable placeholder={placeholder} />
</div>
</div>
{isCodeHighlighted &&
(isCodeShiki ? (
<CodeHighlightShikiPlugin />
) : (
<CodeHighlightPrismPlugin />
))}
<TablePlugin
hasCellMerge={tableCellMerge}
hasCellBackgroundColor={tableCellBackgroundColor}
Expand All @@ -255,15 +252,9 @@ export default function Editor(): JSX.Element {
<TableCellResizer />
<TableScrollShadowPlugin />
<PollPlugin />
<TwitterPlugin />
<YouTubePlugin />
<FigmaPlugin />
<EquationsPlugin />
<ExcalidrawPlugin />
<TabFocusPlugin />
<TabIndentationPlugin maxIndent={7} />
<CollapsiblePlugin />
<PageBreakPlugin />
<LayoutPlugin />
{floatingAnchorElem && (
<>
Expand Down Expand Up @@ -302,7 +293,6 @@ export default function Editor(): JSX.Element {
{isAutocomplete && <AutocompletePlugin />}
<div>{showTableOfContents && <TableOfContentsPlugin />}</div>
{shouldUseLexicalContextMenu && <ContextMenuPlugin />}
{shouldAllowHighlightingWithBrackets && <SpecialTextPlugin />}
<ActionsPlugin
shouldPreserveNewLinesInMarkdown={shouldPreserveNewLinesInMarkdown}
useCollabV2={useCollabV2}
Expand Down
6 changes: 3 additions & 3 deletions packages/lexical-playground/src/nodes/PlaygroundNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import {OverflowNode} from '@lexical/overflow';
import {HeadingNode, QuoteNode} from '@lexical/rich-text';
import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';

import {CollapsibleContainerNode} from '../plugins/CollapsiblePlugin/CollapsibleContainerNode';
import {CollapsibleContentNode} from '../plugins/CollapsiblePlugin/CollapsibleContentNode';
import {CollapsibleTitleNode} from '../plugins/CollapsiblePlugin/CollapsibleTitleNode';
import {CollapsibleContainerNode} from '../plugins/CollapsibleExtension/CollapsibleContainerNode';
import {CollapsibleContentNode} from '../plugins/CollapsibleExtension/CollapsibleContentNode';
import {CollapsibleTitleNode} from '../plugins/CollapsibleExtension/CollapsibleTitleNode';
import {AutocompleteNode} from './AutocompleteNode';
import {DateTimeNode} from './DateTimeNode/DateTimeNode';
import {EmojiNode} from './EmojiNode';
Expand Down
Loading
Loading