diff --git a/apps/playground/src/pages/index.astro b/apps/playground/src/pages/index.astro --- a/apps/playground/src/pages/index.astro +++ b/apps/playground/src/pages/index.astro @@ -21,30 +21,31 @@ import { keymap } from "@codemirror/view"; import type { InkwellExtension } from "@inkwell/core/extensions/types"; - function dummyKeymap(tag) { - return keymap.of([ - { - key: "Enter", - run() { - console.log(this, tag); - return true; - }, - }, - ]); - } - - const ChildExtension: InkwellExtension = { - name: "child", - addCodeMirrorExtensions: () => [], - }; - const TestExtension: InkwellExtension = { name: "test", priority: 10, - addExtensions: () => [ChildExtension], - addCodeMirrorExtensions: ({ editor }) => { - return [dummyKeymap("a")]; - }, + addKeybinds() { + return [ + { + key: 'Mod-Enter', + run(view) { + const newContent = `Hello World!\n\n**This** is a great text _extension_!` + // insert "Hello World" at the current cursor position + view.dispatch({ + changes: { + from: view.state.selection.main.from, + to: view.state.selection.main.to, + insert: newContent, + }, + selection: { + anchor: view.state.selection.main.from + newContent.length + } + }) + return true + } + } + ] + } }; const editor = new Editor({ diff --git a/packages/core/src/editor/Editor.ts b/packages/core/src/editor/Editor.ts --- a/packages/core/src/editor/Editor.ts +++ b/packages/core/src/editor/Editor.ts @@ -58,16 +58,21 @@ const { element, content } = this.options; this._extensionManager = new ExtensionManager(this, this.options?.extensions ?? []); + const editorExtensions = [ + ...this._extensionManager.cmExtensions, + this._extensionManager.keybindings, + ]; + this.emit("beforeCreate", { editor: this }); const initialState = EditorState.create({ doc: content ?? "", + extensions: editorExtensions, }); this.view = new EditorView({ state: initialState, parent: element, - extensions: this._extensionManager.cmExtensions, }); this.setupDOM(); diff --git a/packages/core/src/extensions/ExtensionManager.ts b/packages/core/src/extensions/ExtensionManager.ts --- a/packages/core/src/extensions/ExtensionManager.ts +++ b/packages/core/src/extensions/ExtensionManager.ts @@ -1,4 +1,5 @@ import type { Extension as CMExtension } from "@codemirror/state"; +import { type KeyBinding, keymap } from "@codemirror/view"; import type { Editor } from "../editor/Editor.ts"; import type { InkwellExtension } from "./types.ts"; @@ -7,12 +8,20 @@ private _extensions: InkwellExtension[] = []; private _resolvedExtensions: InkwellExtension[] = []; private _cmExtensions: CMExtension[] = []; + private _keybindings: KeyBinding[] = []; constructor(editor: Editor, extensions: InkwellExtension[]) { this.editor = editor; this._extensions = extensions; this._resolvedExtensions = this.resolveExtensions(); - this._cmExtensions = this.toCodeMirrorExtensions(); + + for (const ext of this.sortExtensionsByPrio(this._resolvedExtensions)) { + if (ext.addNodes) this.bindNodes(ext.addNodes); + if (ext.addMarks) this.bindMarks(ext.addMarks); + if (ext.addCommands) this.bindCommands(ext.addCommands); + if (ext.addKeybinds) this.bindKeymaps(ext.addKeybinds); + if (ext.addCodeMirrorExtensions) this.bindCmExtensions(ext.addCodeMirrorExtensions); + } } get extensions(): InkwellExtension[] { @@ -25,6 +34,10 @@ get resolvedExtensions(): InkwellExtension[] { return [...this._resolvedExtensions]; + } + + get keybindings(): CMExtension { + return keymap.of(this._keybindings); } /** The resolved extension array, including child extensions */ @@ -60,16 +73,36 @@ return [...extensions].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); } - public toCodeMirrorExtensions(): CMExtension[] { - const cmExtensions: CMExtension[] = []; + private bindNodes(addNodes: NonNullable) { + console.log(addNodes({ editor: this.editor })); + // TODO: implement node binding logic, noop for now + } - for (const ext of this.sortExtensionsByPrio(this.resolvedExtensions)) { - if (ext.addCodeMirrorExtensions) { - const cmExts = ext.addCodeMirrorExtensions({ editor: this.editor }); - cmExtensions.push(...cmExts); - } - } + private bindMarks(addMarks: NonNullable) { + console.log(addMarks({ editor: this.editor })); + // TODO: implement mark binding logic, noop for now + } - return cmExtensions; + private bindCommands(addCommands: NonNullable) { + console.log(addCommands({ editor: this.editor })); + // TODO: implement command binding logic, noop for now + } + + /** + * Binds keymaps from all resolved extensions. + * @returns An array of KeyBinding objects from all resolved extensions. + */ + private bindKeymaps(addKeybinds: NonNullable) { + const keyBinds = + addKeybinds({ editor: this.editor })?.filter((kb): kb is KeyBinding => kb !== undefined) ?? + []; + this._keybindings.push(...keyBinds); + } + + private bindCmExtensions( + addCodeMirrorExtensions: NonNullable, + ) { + const cmExtensions: CMExtension[] = addCodeMirrorExtensions({ editor: this.editor }) ?? []; + this._cmExtensions.push(...cmExtensions); } } diff --git a/packages/core/src/extensions/types.ts b/packages/core/src/extensions/types.ts --- a/packages/core/src/extensions/types.ts +++ b/packages/core/src/extensions/types.ts @@ -32,6 +32,7 @@ addNodes?: (ctx: EditorContext) => NodeConfig[]; addMarks?: (ctx: EditorContext) => MarkConfig[]; + addCommands?: (ctx: EditorContext) => Record void>; addCodeMirrorExtensions?: (ctx: EditorContext) => CMExtension[]; addExtensions?: (ctx: EditorContext) => InkwellExtension[];