Refactor Kiss-VSCode to use new KeyShortcutHandler

This commit is contained in:
2022-02-03 12:48:58 -07:00
parent afba4ddc26
commit ae871c38bc
5 changed files with 55 additions and 99 deletions

View File

@@ -8,6 +8,8 @@
(prop &mut :BadKeyHandler<T> onBadKey null)
(prop &mut :BadShortcutHandler<T> onBadShortcut null)
(defNew [])
(defMacro tryCall [handler &rest args]
`(whenLet [handler ,handler]
(handler ,@args)))
@@ -30,14 +32,16 @@
(method handleKey [:String key]
(unless currentMap (tryCallOrThrow onBadKey "Tried to handle key $key without calling start() first" key null))
(case (dictGet currentMap key)
((Final item)
(tryCall onSelectItem item)
(cancel))
((Prefix nextMap)
(_selectMap nextMap))
(otherwise
(tryCallOrThrow onBadKey "Key $key is not defined in $currentMap and no onBadKey event was given" key currentMap))))
(if (= cancelKey key)
(cancel)
(case (dictGet currentMap key)
((Final item)
(tryCall onSelectItem item)
(cancel))
((Prefix nextMap)
(_selectMap nextMap))
(otherwise
(tryCallOrThrow onBadKey "Key $key is not defined in $currentMap and no onBadKey event was given" key currentMap)))))
// Extract [k]eyboard [s]hortcuts from a string:
(method extractKeyboardShortcuts [str &opt :Stream stream :String shortcuts]

View File

@@ -5,6 +5,7 @@
-lib hscript
-lib tink_macro
-lib re-flex
-lib kiss-tools
-cp src
-js bin/extension.js
-D analyzer-optimize

View File

@@ -14,17 +14,13 @@ import sys.io.File;
import sys.FileSystem;
import ktxt2.KTxt2;
import re_flex.R;
import kiss_tools.KeyShortcutHandler;
using haxe.io.Path;
using StringTools;
typedef Command = (String) -> Void;
enum ShortcutKey {
Final(command:String);
Prefix(keys:Map<String, ShortcutKey>);
}
@:expose
@:build(kiss.Kiss.buildAll(["KissConfig.kiss", "Config.kiss"]))
class KissConfig {}

View File

@@ -29,7 +29,7 @@
*/
(var :Map<String,Command> commands (new Map))
(var :Map<String,ShortcutKey> commandShortcuts (new Map))
(var :KeyShortcutHandler<String> shortcutHandler (new KeyShortcutHandler))
(var &mut :String lastCommand null)
(var parser (new Parser))
@@ -74,56 +74,40 @@
(var &mut :vscode.WebviewPanel shortcutPanel null)
(var &mut :vscode.TextDocument documentBeforeShortcut null)
(var &mut :String selectedTextBeforeShortcut null)
(var &mut :Disposable keyListener null)
(function :Void showShortcutPanel [&opt :Map<String,ShortcutKey> prefixMap]
(function :Void closeShortcutPanel []
(set documentBeforeShortcut null)
(set selectedTextBeforeShortcut null)
(when shortcutPanel
(keyListener.dispose)
(shortcutPanel.dispose)
(set shortcutPanel null)))
(function :Void runChosenCommand [:String command]
(if documentBeforeShortcut
(awaitLet [_ (Vscode.window.showTextDocument documentBeforeShortcut)]
(_runCommand command selectedTextBeforeShortcut))
(_runCommand command selectedTextBeforeShortcut)))
(function :Void showShortcutPanel [:PrefixMap<String> prefixMap]
// Preserve the selected text and focused document before opening the webview:
(whenLet [text (selectedText)] (set selectedTextBeforeShortcut text))
(if activeTextEditor
(set documentBeforeShortcut activeTextEditor.document)
(unless prefixMap (set documentBeforeShortcut null)))
(when activeTextEditor
(set documentBeforeShortcut activeTextEditor.document))
// When called without a prefixMap, if a shortcut panel is still open, close it and start over:
(unless prefixMap
(when shortcutPanel
// TODO for some reason, method calling an object in (when [object] ...) context, resets the object's type to Any unless (the [Type]) is used
(.dispose (the WebviewPanel shortcutPanel))
(set shortcutPanel null))
(set prefixMap commandShortcuts))
(when shortcutPanel (shortcutPanel.dispose))
(closeShortcutPanel)
(set shortcutPanel (Vscode.window.createWebviewPanel
"kissShortcut"
"Kiss Shortcuts"
vscode.ViewColumn.Two
(object
enableScripts true)))
// The keyListener handler needs to have access to the Disposible object to dispose itself, hence this let/set
(let [&mut keyListener null]
(set keyListener (shortcutPanel.webview.onDidReceiveMessage
->:Void key
(case (the String key)
((when (prefixMap.exists key) key)
(keyListener.dispose)
(case (dictGet prefixMap key)
((Prefix innerMap)
(showShortcutPanel innerMap))
((Final command)
// TODO dispose() for some reason doesn't take effect until
// the awaitLet below finishes its work...
(shortcutPanel.dispose)
(if documentBeforeShortcut
(awaitLet [_ (Vscode.window.showTextDocument documentBeforeShortcut)]
(_runCommand command selectedTextBeforeShortcut))
(_runCommand command selectedTextBeforeShortcut)))
(otherwise)))
("Escape"
(shortcutPanel.dispose))
(otherwise (warningMessage "$key is not mapped to a shortcut in this context"))))))
"kissShortcut"
"Kiss Shortcuts"
vscode.ViewColumn.Two
(object
enableScripts true)))
(set keyListener (shortcutPanel.webview.onDidReceiveMessage ->:Void key (shortcutHandler.handleKey (the String key))))
(set shortcutPanel.webview.html (shortcutPanelHtml prefixMap))
(shortcutPanel.webview.postMessage (object command "focus")))
(function shortcutPanelHtml [:Map<String,ShortcutKey> prefixMap]
(function shortcutPanelHtml [:PrefixMap<String> prefixMap]
(let [&mut unusedKeys "abcdefghijklmnopqrstuvwxyz1234567890-/"
shortcutParagraphs
(for =>key shortcutKey prefixMap
@@ -163,49 +147,7 @@
</html>"))
(function :Void runKeyboardShortcut [&opt _]
(showShortcutPanel))
// Extract [k]eyboard [s]hortcuts from a string:
(function extractKeyboardShortcuts [str &opt :Stream stream :String shortcuts]
(unless stream (set stream (Stream.fromString str)))
(unless shortcuts (set shortcuts ""))
(case (stream.takeUntilAndDrop "[")
((Some _)
(case (stream.takeUntilAndDrop "]")
((Some newShortcuts)
(extractKeyboardShortcuts str stream (+ shortcuts (newShortcuts.toLowerCase))))
(otherwise
(warningMessage "unclosed [ in $str")
"")))
(otherwise
shortcuts)))
(function :Void registerShortcut [keys description &opt :Map<String,ShortcutKey> prefixMap]
(unless prefixMap (set prefixMap commandShortcuts))
(let [firstKey (keys.shift)]
(cond
((prefixMap.exists firstKey)
(let [existingKey (dictGet prefixMap firstKey)
conflictMessage "Keyboard shortcut for $description conflicts with $existingKey"]
(if keys
(case existingKey
((Final _)
(warningMessage conflictMessage))
((Prefix innerPrefixMap)
(registerShortcut keys description innerPrefixMap))
(otherwise))
(warningMessage conflictMessage))))
(true
(if keys
(let [innerPrefixMap (new Map)]
(dictSet prefixMap firstKey (Prefix innerPrefixMap))
(registerShortcut keys description innerPrefixMap))
(dictSet prefixMap firstKey (Final description)))))))
(function registerCommand [description command]
(dictSet commands description command)
(whenLet [keyboardShortcut (extractKeyboardShortcuts description)]
(registerShortcut (keyboardShortcut.split "") description)))
(shortcutHandler.start))
// Register a VSCode command (built-in, or from an extension)
(function registerExistingCommand [description command]
@@ -219,6 +161,14 @@
(var &mut :Command tryLoadConfig)
(function :Void registerBuiltins [&opt leaderKeys]
// Set up the KeyShortcutHandler
(set shortcutHandler.cancelKey "Escape")
(set shortcutHandler.onSelectPrefixMap showShortcutPanel)
(set shortcutHandler.onSelectItem runChosenCommand)
(set shortcutHandler.onFinishOrCancel closeShortcutPanel)
(set shortcutHandler.onBadKey ->:Void [key map] (warningMessage "$key is not mapped to a shortcut in $map"))
(set shortcutHandler.onBadShortcut ->:Void [key otherKey] (warningMessage "$key conflicts with $otherKey"))
(unless leaderKeys (set leaderKeys ""))
(let [prefix "Kiss-VSCode:$(if leaderKeys " [${leaderKeys}]" "")"
ktxt2Prefix "KTxt2:$(if leaderKeys " [${leaderKeys}]" "")"]
@@ -292,6 +242,10 @@
(awaitLet [_ (showTextDocument previousEditor.document)]
(if editor.document.isDirty (let [error "Input editor for $prompt was closed without saving."] (warningMessage error) (reject error)) (resolve (editor.document.getText))))))))))))
(function registerCommand [description :Command command]
(dictSet commands description command)
(shortcutHandler.registerItem description description))
(#unless test
(var :Array<KTxt2Conversion> conversions [])

View File

@@ -3,6 +3,7 @@ args.hxml
-lib vscode
-lib kiss
-lib kiss-vscode
-lib kiss-tools
-lib re-flex
KissConfig
-js config.js