From 87c8daf6f21574e8dd362f553e70e6b5f6501499 Mon Sep 17 00:00:00 2001 From: nleve Date: Tue, 13 Jun 2023 18:48:14 -0400 Subject: [PATCH] add vim mode to script editor --- index.d.ts | 8 +++ package.json | 1 + src/ui/views/session/cmd-panel/actions.tsx | 5 +- src/ui/views/session/editor/ScriptEditor.tsx | 52 +++++++++++++++++++- tsconfig.json | 1 + yarn.lock | 5 ++ 6 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 index.d.ts diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 000000000..0508e0f3d --- /dev/null +++ b/index.d.ts @@ -0,0 +1,8 @@ +// monaco-vim does not have types, so we create a shallow module definition for our purposes +declare module "monaco-vim" { + interface VimMode { + dispose(); + } + + function initVimMode(editor: MonacoEditor.IStandaloneCodeEditor, statusBar: HTMLElement | null): VimMode; +} diff --git a/package.json b/package.json index 21a6fa7f4..15d5e2041 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "meshoptimizer": "^0.18.1", "monaco-editor": "^0.36.1", "monaco-themes": "^0.4.3", + "monaco-vim": "^0.4.0", "murmurhash-js": "^1.0.0", "optipng-js": "^0.1.2", "react": "^17.0.2", diff --git a/src/ui/views/session/cmd-panel/actions.tsx b/src/ui/views/session/cmd-panel/actions.tsx index 0e96d97ca..944d4aeed 100644 --- a/src/ui/views/session/cmd-panel/actions.tsx +++ b/src/ui/views/session/cmd-panel/actions.tsx @@ -21,9 +21,10 @@ import CrossIC from "../../../../../res/ic/cross.svg"; import { togglePhysicsDebug } from "../../../../plugins/thirdroom/thirdroom.main"; import { useDisableInput } from "../../../hooks/useDisableInput"; -enum ActionSection { +export enum ActionSection { Global = "Global", World = "World", + Editor = "Editor", } export const useUserProfileAction = () => { @@ -236,7 +237,7 @@ export const useToggleEditorAction = ( name: "Toggle Editor", shortcut: ["`"], keywords: "editor", - section: ActionSection.World, + section: ActionSection.Editor, icon: undefined, subtitle: undefined, perform: () => { diff --git a/src/ui/views/session/editor/ScriptEditor.tsx b/src/ui/views/session/editor/ScriptEditor.tsx index 507b6b804..895d70b91 100644 --- a/src/ui/views/session/editor/ScriptEditor.tsx +++ b/src/ui/views/session/editor/ScriptEditor.tsx @@ -1,9 +1,11 @@ -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useAtom } from "jotai"; import { BlobHandle, Room } from "@thirdroom/hydrogen-view-sdk"; import Editor, { Monaco, OnChange, useMonaco } from "@monaco-editor/react"; import { editor as MonacoEditor } from "monaco-editor"; +import { VimMode, initVimMode } from "monaco-vim"; import { useDrop } from "react-dnd"; +import { useRegisterActions } from "kbar"; import { Button } from "../../../atoms/button/Button"; import { Dots } from "../../../atoms/loading/Dots"; @@ -24,8 +26,10 @@ import { DnDItemTypes, NodeDragItem } from "./HierarchyPanel"; import { MainThreadResource, getLocalResource } from "../../../../engine/resource/resource.main"; import { useMainThreadContext } from "../../../hooks/useMainThread"; import { camelizeVariableName } from "../../../utils/common"; +import { ActionSection } from "../cmd-panel/actions"; const MONACO_THEME_KEY = "monaco_theme"; +const VIM_MODE_KEY = "vim_mode"; export function ScriptEditor({ room }: { room: Room }) { const { session, platform } = useHydrogen(true); @@ -40,12 +44,15 @@ export function ScriptEditor({ room }: { room: Room }) { // component state const [editorTheme, setEditorTheme] = useLocalStorage<"light" | "vs-dark">(MONACO_THEME_KEY, "light"); + const [vimModeSetting, setVimModeSetting] = useLocalStorage(VIM_MODE_KEY, false); const [reloading, setReloading] = useState(false); const [saved, setSavedState] = useState(true); const [showResetModal, setShowResetModal] = useState(false); const monaco = useMonaco(); const editorRef = useRef(null); + const vimModeRef = useRef(null); + /** * Set saved to true if active script is equal to persisted script */ @@ -174,6 +181,46 @@ export function ScriptEditor({ room }: { room: Room }) { monaco.languages.typescript.javascriptDefaults.addExtraLib(websgTypes, "websg.d.ts"); } + function setVimMode(newVimModeEnabled: boolean) { + if (editorRef.current) { + if (newVimModeEnabled) { + vimModeRef.current = initVimMode(editorRef.current, document.getElementById("ScriptEditor__statusBar")); + } else if (vimModeRef.current) { + vimModeRef.current.dispose(); + } + } + } + + function handleEditorMount(editor: MonacoEditor.IStandaloneCodeEditor) { + editorRef.current = editor; + setVimMode(vimModeSetting); + } + + const toggleVimMode = useCallback(() => { + const newVimModeEnabled = !vimModeSetting; + setVimModeSetting(newVimModeEnabled); + setVimMode(newVimModeEnabled); + }, [vimModeSetting, setVimModeSetting]); + + // Register vim mode toggle in kbar menu only while script editor is mounted + useRegisterActions( + [ + { + id: "vim-mode", + name: "Toggle Vim Mode", + keywords: "vim mode", + section: ActionSection.Editor, + icon: undefined, + subtitle: undefined, + perform: () => { + toggleVimMode(); + }, + parent: undefined, + }, + ], + [toggleVimMode] + ); + return ( <> (editorRef.current = editor)} + onMount={handleEditorMount} theme={editorTheme} />
@@ -238,6 +285,7 @@ export function ScriptEditor({ room }: { room: Room }) {
)} +
); diff --git a/tsconfig.json b/tsconfig.json index b46ac7036..48fd96e9e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,7 @@ "include": [ "./src", "./deps.d.ts", + "./index.d.ts", "./test", "./vite.config.ts", "./logviewer/src", diff --git a/yarn.lock b/yarn.lock index b827e5899..4e1017067 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5164,6 +5164,11 @@ monaco-themes@^0.4.3: dependencies: fast-plist "^0.1.2" +monaco-vim@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/monaco-vim/-/monaco-vim-0.4.0.tgz#060b89bd00f3731ca48586a06023954fd5beaaf2" + integrity sha512-+CsW0+Mvx2+eitkXS7OpUXIu57qXlqAL8oVkYhkPCEZ/c6+6gOp/IcG7w+Lb33YiZuTyvJ891+czkeJRPIEwVA== + mrmime@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz"