From ae22459d827213a7555f38e46a496be9fea9df65 Mon Sep 17 00:00:00 2001 From: Hootan Hemmati Date: Sat, 10 Aug 2024 12:32:14 +0330 Subject: [PATCH] Add support for Vim keybindings Fixes #238 Add support for Vim keybindings to the text editor. * **BuiltinShortcuts**: - Add Vim keybindings to `BuiltinShortcuts` in `src/Apps/NetPad.Apps.App/App/src/core/@application/shortcuts/builtin-shortcuts.ts`. - Include keybindings for common Vim commands like `Escape`, `:w`, `:q`, and `:wq`. * **ITextEditorService**: - Update `ITextEditorService` interface in `src/Apps/NetPad.Apps.App/App/src/core/@application/editor/itext-editor-service.ts` to include methods for enabling and disabling Vim mode. * **TextEditor**: - Implement methods to enable and disable Vim mode in `TextEditor` class in `src/Apps/NetPad.Apps.App/App/src/core/@application/editor/text-editor.ts`. - Use `monaco-vim` library to integrate Vim keybindings with Monaco editor. --- .../editor/itext-editor-service.ts | 2 + .../core/@application/editor/text-editor.ts | 17 +++++ .../shortcuts/builtin-shortcuts.ts | 64 +++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/itext-editor-service.ts b/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/itext-editor-service.ts index 9c0c374e..e45cedd6 100644 --- a/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/itext-editor-service.ts +++ b/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/itext-editor-service.ts @@ -4,6 +4,8 @@ import {ITextEditor} from "@application/editor/text-editor"; export interface ITextEditorService { get active(): ITextEditor | undefined; create(host: HTMLElement): ITextEditor; + enableVimMode(): void; + disableVimMode(): void; } export const ITextEditorService = DI.createInterface(); diff --git a/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/text-editor.ts b/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/text-editor.ts index 3b3368ab..42521c6c 100644 --- a/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/text-editor.ts +++ b/src/Apps/NetPad.Apps.App/App/src/core/@application/editor/text-editor.ts @@ -5,6 +5,7 @@ import {WithDisposables} from "@common"; import {IEventBus, MonacoEditorUtil, Settings, ViewModelBase} from "@application"; import {TextEditorFocusedEvent} from "./events"; import {TextDocument} from "./text-document"; +import {initVimMode, VimMode} from "monaco-vim"; export const ITextEditor = DI.createInterface(); @@ -17,6 +18,8 @@ export interface ITextEditor extends WithDisposables { open(document: TextDocument): void; close(documentId: string): void; focus(): void; + enableVimMode(): void; + disableVimMode(): void; } export class TextEditor extends ViewModelBase implements ITextEditor { @@ -24,6 +27,7 @@ export class TextEditor extends ViewModelBase implements ITextEditor { public position?: monaco.Position | null; public active?: TextDocument | null; private element: HTMLElement; + private vimMode?: VimMode; private viewStates = new Map(); @@ -90,6 +94,19 @@ export class TextEditor extends ViewModelBase implements ITextEditor { setTimeout(() => this.monaco.focus(), 50); } + public enableVimMode() { + if (!this.vimMode) { + this.vimMode = initVimMode(this.monaco, this.element); + } + } + + public disableVimMode() { + if (this.vimMode) { + this.vimMode.dispose(); + this.vimMode = undefined; + } + } + private ensureEditorInitialized() { if (this.monaco) return; this.initializeEditor(); diff --git a/src/Apps/NetPad.Apps.App/App/src/core/@application/shortcuts/builtin-shortcuts.ts b/src/Apps/NetPad.Apps.App/App/src/core/@application/shortcuts/builtin-shortcuts.ts index 300405df..b334b0c8 100644 --- a/src/Apps/NetPad.Apps.App/App/src/core/@application/shortcuts/builtin-shortcuts.ts +++ b/src/Apps/NetPad.Apps.App/App/src/core/@application/shortcuts/builtin-shortcuts.ts @@ -18,6 +18,10 @@ export enum ShortcutIds { openExplorer = "shortcut.explorer.open", openNamespaces = "shortcut.namespaces.open", reloadWindow = "shortcut.window.reload", + vimMode = "shortcut.vim.mode", + vimSave = "shortcut.vim.save", + vimQuit = "shortcut.vim.quit", + vimSaveQuit = "shortcut.vim.savequit", } export const BuiltinShortcuts = [ @@ -170,4 +174,64 @@ export const BuiltinShortcuts = [ .captureDefaultKeyCombo() .configurable() .enabled(), + + new Shortcut(ShortcutIds.vimMode, "Toggle Vim Mode") + .withKey(KeyCode.Escape) + .hasAction(ctx => { + const editor = ctx.container.get(ITextEditorService).active?.monaco; + + if (!editor) return; + + editor.focus(); + editor.trigger("", "toggleVimMode", null); + }) + .captureDefaultKeyCombo() + .configurable() + .enabled(), + + new Shortcut(ShortcutIds.vimSave, "Vim Save") + .withKey(KeyCode.Colon) + .withKey(KeyCode.KeyW) + .hasAction(ctx => { + const editor = ctx.container.get(ITextEditorService).active?.monaco; + + if (!editor) return; + + editor.focus(); + editor.trigger("", "vimSave", null); + }) + .captureDefaultKeyCombo() + .configurable() + .enabled(), + + new Shortcut(ShortcutIds.vimQuit, "Vim Quit") + .withKey(KeyCode.Colon) + .withKey(KeyCode.KeyQ) + .hasAction(ctx => { + const editor = ctx.container.get(ITextEditorService).active?.monaco; + + if (!editor) return; + + editor.focus(); + editor.trigger("", "vimQuit", null); + }) + .captureDefaultKeyCombo() + .configurable() + .enabled(), + + new Shortcut(ShortcutIds.vimSaveQuit, "Vim Save and Quit") + .withKey(KeyCode.Colon) + .withKey(KeyCode.KeyW) + .withKey(KeyCode.KeyQ) + .hasAction(ctx => { + const editor = ctx.container.get(ITextEditorService).active?.monaco; + + if (!editor) return; + + editor.focus(); + editor.trigger("", "vimSaveQuit", null); + }) + .captureDefaultKeyCombo() + .configurable() + .enabled(), ];