From f492385642191d5599c237099ec470b1f426aa5c Mon Sep 17 00:00:00 2001 From: Rob Bertram Date: Tue, 22 Jul 2025 21:49:55 -0500 Subject: [PATCH 1/2] add vim mode setting to playground --- src/Playground.res | 38 ++++++++++++++++++++++++++++++++-- src/components/CodeMirror.res | 23 ++++++++++++++++++-- src/components/CodeMirror.resi | 8 +++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/Playground.res b/src/Playground.res index 3f7a16a5c..feb4e1e2f 100644 --- a/src/Playground.res +++ b/src/Playground.res @@ -12,6 +12,7 @@ %%raw(` if (typeof window !== "undefined" && typeof window.navigator !== "undefined") { require("codemirror/mode/javascript/javascript"); + require("codemirror/keymap/vim"); require("codemirror/addon/scroll/simplescrollbars"); require("plugins/cm-rescript-mode"); require("plugins/cm-reason-mode"); @@ -1005,8 +1006,10 @@ module Settings = { ~setConfig: Api.Config.t => unit, ~editorCode: React.ref, ~config: Api.Config.t, + ~keyMapState: (CodeMirror.KeyMap.t, (CodeMirror.KeyMap.t => CodeMirror.KeyMap.t) => unit), ) => { let {Api.Config.warn_flags: warn_flags} = config + let (keyMap, setKeyMap) = keyMapState let availableTargetLangs = Api.Version.availableLanguages(readyState.selected.apiVersion) @@ -1085,6 +1088,19 @@ module Settings = { onChange=onTargetLangSelect /> +
+
{React.string("Vim")}
+ + switch enabled { + | CodeMirror.KeyMap.Vim => "On" + | CodeMirror.KeyMap.Default => "Off" + }} + selected=keyMap + onChange={value => setKeyMap(_ => value)} + /> +
{React.string("Module-System")}
, + ~keyMapState: (CodeMirror.KeyMap.t, (CodeMirror.KeyMap.t => CodeMirror.KeyMap.t) => unit), ) => { /* We need the prevState to understand different @@ -1370,7 +1387,9 @@ module OutputPanel = { let config = ready.selected.config let setConfig = config => compilerDispatch(UpdateConfig(config)) - + | SetupFailed(msg) =>
{React.string("Setup failed: " ++ msg)}
| Init =>
{React.string("Initalizing Playground...")}
} @@ -1565,6 +1584,18 @@ let default = () => { let windowWidth = CodeMirror.useWindowWidth() + let (keyMap, setKeyMap) = React.useState(() => { + Dom.Storage2.localStorage + ->Dom.Storage2.getItem("vimMode") + ->Belt.Option.map(CodeMirror.KeyMap.fromString) + ->Belt.Option.getWithDefault(CodeMirror.KeyMap.Default) + }) + + React.useEffect1(() => { + Dom.Storage2.localStorage->Dom.Storage2.setItem("vimMode", CodeMirror.KeyMap.toString(keyMap)) + None + }, [keyMap]) + // The user can focus an error / warning on a specific line & column // which is stored in this ref and triggered by hover / click states // in the CodeMirror editor @@ -1712,13 +1743,16 @@ let default = () => { }} onMarkerFocus={rowCol => setFocusedRowCol(_prev => Some(rowCol))} onMarkerFocusLeave={_ => setFocusedRowCol(_ => None)} + keyMap />
1024 ? "56rem" : "100%", ())}> - +
+ switch keyMap { + | Default => "default" + | Vim => "vim" + } + + let fromString = (str: string) => + switch str { + | "vim" => Vim + | _ => Default + } +} + let useWindowWidth: unit => int = %raw(j` () => { const isClient = typeof window === 'object'; @@ -69,6 +84,8 @@ module CM = { fixedGutter: bool, @optional scrollbarStyle: string, + @optional + keyMap: string, } } @@ -597,6 +614,7 @@ let make = // props relevant for the react wrapper ~readOnly=false, ~lineNumbers=true, ~scrollbarStyle="overlay", + ~keyMap=KeyMap.Default, ~lineWrapping=false, ): React.element => { let inputElement = React.useRef(Js.Nullable.null) @@ -606,7 +624,7 @@ let make = // props relevant for the react wrapper let windowWidth = useWindowWidth() let (onMouseOver, onMouseOut, onMouseMove) = useHoverTooltip(~cmStateRef, ~cmRef, ()) - React.useEffect0(() => + React.useEffect1(() => { switch inputElement.current->Js.Nullable.toOption { | Some(input) => let options = CM.Options.t( @@ -617,6 +635,7 @@ let make = // props relevant for the react wrapper ~fixedGutter=false, ~readOnly, ~lineNumbers, + ~keyMap=KeyMap.toString(keyMap), ~scrollbarStyle, (), ) @@ -659,7 +678,7 @@ let make = // props relevant for the react wrapper Some(cleanup) | None => None } - ) + }, [keyMap]) React.useEffect1(() => { cmStateRef.current.hoverHints = hoverHints diff --git a/src/components/CodeMirror.resi b/src/components/CodeMirror.resi index fcf134cff..8ece8f602 100644 --- a/src/components/CodeMirror.resi +++ b/src/components/CodeMirror.resi @@ -1,3 +1,9 @@ +module KeyMap: { + type t = Default | Vim + let toString: t => string + let fromString: string => t +} + module Error: { type kind = [#Error | #Warning] @@ -38,6 +44,7 @@ module CM: { ~lineWrapping: bool=?, ~fixedGutter: bool=?, ~scrollbarStyle: string=?, + ~keyMap: string=?, unit, ) => t } @@ -62,5 +69,6 @@ let make: ( ~readOnly: bool=?, ~lineNumbers: bool=?, ~scrollbarStyle: string=?, + ~keyMap: KeyMap.t=?, ~lineWrapping: bool=?, ) => React.element From 3abae75a00bcabc32d99f43e52510cf13a14dfa8 Mon Sep 17 00:00:00 2001 From: Rob Bertram Date: Sat, 26 Jul 2025 01:39:06 -0400 Subject: [PATCH 2/2] use rescript core option instead of belt --- src/Playground.res | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Playground.res b/src/Playground.res index e1656dad9..f78c520f1 100644 --- a/src/Playground.res +++ b/src/Playground.res @@ -979,7 +979,7 @@ module Settings = { React.null }}
-
{React.string("Vim")}
+
{React.string("Use Vim Keymap")}
@@ -1485,8 +1485,8 @@ let make = (~versions: array) => { let (keyMap, setKeyMap) = React.useState(() => { Dom.Storage2.localStorage ->Dom.Storage2.getItem("vimMode") - ->Belt.Option.map(CodeMirror.KeyMap.fromString) - ->Belt.Option.getWithDefault(CodeMirror.KeyMap.Default) + ->Option.map(CodeMirror.KeyMap.fromString) + ->Option.getOr(CodeMirror.KeyMap.Default) }) React.useEffect1(() => {