prism-code-editor
Version:
Lightweight, extensible code editor component for the web using Prism
159 lines (158 loc) • 7.77 kB
TypeScript
import { BracketMatcher } from "./extensions/matchBrackets/index.js";
import { TagMatcher } from "./extensions/matchTags.js";
import { Cursor } from "./extensions/cursor.js";
import { SearchWidget } from "./extensions/search/widget.js";
import { IndentGuides } from "./extensions/guides.js";
import { ReadOnlyCodeFolding } from "./extensions/folding/index.js";
import { TokenStream } from "./prism/types.js";
import { EditHistory } from "./extensions/commands.js";
export type EditorOptions = {
/** Language used for syntax highlighting. */
language: string;
/** Tabsize for the editor. @default 2 */
tabSize?: number | undefined;
/** Whether the editor should insert spaces for indentation. @default true */
insertSpaces?: boolean | undefined;
/** Whether line numbers should be shown. @default true */
lineNumbers?: boolean | undefined;
/** Whether the editor should be read only. @default false */
readOnly?: boolean | undefined;
/** Whether the editor should have word wrap. @default false */
wordWrap?: boolean | undefined;
/** Code to display in the editor. */
value: string;
/** @experimental Whether the editor uses right to left directionality. @default false */
rtl?: boolean;
/** Function called when the code of the editor changes. */
onUpdate?: EditorEventMap["update"] | null;
/** Function called when the selection changes in the editor. */
onSelectionChange?: EditorEventMap["selectionChange"] | null;
/** Function called after `after-tokenize` Prism hooks, but before the tokens are stringified. */
onTokenize?: EditorEventMap["tokenize"] | null;
};
export type CommentTokens = {
line?: string;
block?: [string, string];
};
export type Language = {
/** Comment tokens used by the language. */
comments?: CommentTokens;
/**
* Method called when a user executes a comment toggling command.
* @param editor The editor the user is interacting with.
* @param position Where in the code the comment is being toggled.
* @param value Current code in the editor.
* @returns The comment tokens that should be used for this command.
*/
getComments?(editor: PrismEditor, position: number, value: string): CommentTokens;
/**
* Callbacks controlling the automatic indentation on new lines.
* First function should return whether indentation should be increased.
* Second function should return whether to add an extra line after the cursor.
*/
autoIndent?: [
((selection: InputSelection, value: string, editor: PrismEditor) => boolean)?,
((selection: InputSelection, value: string, editor: PrismEditor) => boolean)?
];
/**
* Function called when the user types `>`. Intended to auto close tags.
* @returns string which will get inserted behind the cursor.
*/
autoCloseTags?(selection: InputSelection, value: string, editor: PrismEditor): string | undefined;
};
/**
* Function called when a certain key is pressed.
* If true is returned, `e.preventDefault()` and `e.stopImmediatePropagation()` is called automatically.
*/
export type KeyCommandCallback = (e: KeyboardEvent, selection: InputSelection, value: string) => void | boolean;
/**
* Function called when a certain input is typed.
* If true is returned, `e.preventDefault()` and `e.stopImmediatePropagation()` is called automatically.
*/
export type InputCommandCallback = (e: InputEvent, selection: InputSelection, value: string) => void | boolean;
export type InputSelection = [number, number, "forward" | "backward" | "none"];
export interface Extension {
/** Function called when the extension is added or the options of the editor change. */
update(editor: PrismEditor, options: EditorOptions): any;
}
export interface BasicExtension {
(editor: PrismEditor, options: EditorOptions): any;
}
export type EditorExtension = Extension | BasicExtension;
export type EditorEventMap = {
update(this: PrismEditor, value: string): any;
selectionChange(this: PrismEditor, selection: InputSelection, value: string): any;
tokenize(this: PrismEditor, tokens: TokenStream, language: string, value: string): any;
};
export interface EventHandler<EventMap extends Record<string, (...args: any) => any>> {
/** Adds a listener for events with the specified name. */
addListener<T extends keyof EventMap>(name: T, listener: EventMap[T]): void;
/** Removes a listener for events with the specified name. */
removeListener<T extends keyof EventMap>(name: T, listener: EventMap[T]): void;
}
export interface PrismEditor extends EventHandler<EditorEventMap> {
/** This is the outermost element of the editor. */
readonly scrollContainer: HTMLDivElement;
/** Element wrapping the lines and overlays. */
readonly wrapper: HTMLDivElement;
/**
* Element containing overlays that are absolutely positioned ontop or behind the code.
* It is completely safe to append your own overlays to this element, but they will get
* some default styles.
*/
readonly overlays: HTMLDivElement;
/** Underlying `<textarea>` in the editor. */
readonly textarea: HTMLTextAreaElement;
/** The line the cursor is currently on. */
readonly activeLine: HTMLDivElement;
/** The line number of the active line. */
readonly activeLineNumber: number;
/** Whether the `textarea` is focused. */
readonly focused: boolean;
/** Current code in the editor. Same as `textarea.value`. */
readonly value: string;
/**
* Current options for the editor. The event handlers can be changed by
* mutating this object. Use `setOptions` to change the other options.
*/
readonly options: EditorOptions;
/** Record mapping an input to a function called when that input is typed. */
readonly inputCommandMap: Record<string, InputCommandCallback | null | undefined>;
/** Record mapping KeyboardEvent.key to a function called when that key is pressed. */
readonly keyCommandMap: Record<string, KeyCommandCallback | null | undefined>;
/** True if the remove method has been called. */
readonly removed: boolean;
/** Tokens currently displayed in the editor. */
readonly tokens: TokenStream;
/** Object storing some of the extensions added to the editor. */
readonly extensions: {
matchBrackets?: BracketMatcher;
matchTags?: TagMatcher;
cursor?: Cursor;
searchWidget?: SearchWidget;
indentGuides?: IndentGuides;
codeFold?: ReadOnlyCodeFolding;
history?: EditHistory;
};
/**
* Set new options for the editor. Ommitted properties will use their old value.
* @param options New options for the editor
*/
setOptions(options: Partial<EditorOptions>): void;
/** Forces the editor to update. Can be useful after adding a tokenize listener or modifying a grammar. */
update(): void;
/** Gets `selectionStart`, `selectionEnd` and `selectionDirection` for the `textarea`. */
getSelection(): InputSelection;
/**
* Sets the selection for the `textarea` and synchronously runs the selectionChange listeners.
* If you don't want to synchronously run the listeners, use `textarea.setSelectionRange` instead.
* @param start New selectionStart.
* @param end New selectionEnd. Defaults to `start`.
* @param direction New direction.
*/
setSelection(start: number, end?: number, direction?: "backward" | "forward" | "none"): void;
/** Adds extensions to the editor and calls their update methods. */
addExtensions(...extensions: EditorExtension[]): void;
/** Removes the editor from the DOM and marks the editor as removed. */
remove(): void;
}