From 018acb0d82d7e37c58376eaa0b95fe6a0abf26c5 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Fri, 10 Mar 2023 09:57:27 -0700 Subject: [PATCH] kiss-vscode-api haxelib --- haxelib.json | 19 ++++++ src/Util.kiss | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 haxelib.json create mode 100644 src/Util.kiss diff --git a/haxelib.json b/haxelib.json new file mode 100644 index 0000000..7910882 --- /dev/null +++ b/haxelib.json @@ -0,0 +1,19 @@ +{ + "main": "kiss_vscode_api.Main", + "name": "kiss-vscode-api", + "description": "API for kiss-vscode extensions", + "classPath": "src/", + "dependencies": { + "kiss": "", + "vscode": "", + "hxnodejs": "" + }, + "url": "https://github.com/NQNStudios/kisslang", + "contributors": [ + "NQNStudios" + ], + "version": "0.0.0", + "releasenote": "", + "tags": [], + "license": "LGPL" +} \ No newline at end of file diff --git a/src/Util.kiss b/src/Util.kiss new file mode 100644 index 0000000..90736c2 --- /dev/null +++ b/src/Util.kiss @@ -0,0 +1,158 @@ +// This has to be a macro so it can return from tryLoadConfig +// TODO this macro should use gensym +(defMacro trySpawnSync [command args options onError] + `(let [command ,command + args ,args + options ,options + result (ChildProcess.spawnSync command args options)] + (if result.error + { + (,onError "Error $result.error from $command ${args}: $result.stdout $result.stderr") + null + } + (case result.status + (0 (when result.stdout (.toString (the js.node.Buffer result.stdout)))) + (errCode + (,onError "Error code $errCode from $command ${args}: $result.stdout $result.stderr") + null) + (null + (,onError "result status is null from $command ${args}: $result.stdout $result.stderr") + null))))) + +(#unless test + /** + * Aliases + */ + + // output + (defAlias &call infoMessage Vscode.window.showInformationMessage) + (defAlias &call warningMessage Vscode.window.showWarningMessage) + (defAlias &call errorMessage Vscode.window.showErrorMessage) + + // input + (defAlias &call inputBox Vscode.window.showInputBox) + (defAlias &call _quickPick Vscode.window.showQuickPick) + + (function quickPickItem [label &opt description] + (object + label label + description description + detail null + picked null + alwaysShow null)) + + (function quickPick [:Array strings] + (awaitLet [chosenItem (_quickPick (for string strings (quickPickItem string)))] + (when chosenItem chosenItem.label))) + + // thanks https://stackoverflow.com/a/69842249 + (function autoSuggestPick [:Array strings] + (new js.lib.Promise + ->[resolve reject] (let [qp (Vscode.window.createQuickPick) + :Array items (for string strings (quickPickItem string))] + (set qp.items items) + (qp.onDidChangeValue + ->v (unlessLet [(Some _) (indexOf strings v)] + (set qp.items (concat [(quickPickItem v)] items)))) + (qp.onDidAccept + ->_ { + (resolve .label (first qp.activeItems)) + (qp.hide) + }) + (qp.show)))) + + (function :js.lib.Promise.Thenable quickPickMap [:Map stringMap] + (awaitLet [chosenItem (_quickPick (for =>key value stringMap (quickPickItem key (Std.string value))))] + (when chosenItem (dictGet stringMap chosenItem.label)))) + + (defAlias &call openDialog Vscode.window.showOpenDialog) + + // commands + (defAlias &call executeCommand Vscode.commands.executeCommand) + (function repeatCommand [command times] + (let [iteration + ->[&opt _] (executeCommand command) + &mut promise + (iteration)] + (doFor i (range (- times 1)) + (set promise (promise.then iteration))) + promise)) + + (defMacro awaitCommands [commandsAndArgs &builder b &body body] + (let [commandsAndArgs + (if (isListExp commandsAndArgs) + (.copy (expList commandsAndArgs)) + (throw (CompileError.fromExp commandsAndArgs "First argument to awaitCommands should be a list of commands with optional argument arrays"))) + bindings []] + (while commandsAndArgs + (bindings.push (b.symbol "_")) + (let [nextCommand (commandsAndArgs.shift)] + (bindings.push (b.callSymbol "executeCommand" + (concat [nextCommand] (if (and commandsAndArgs (isListExp (first commandsAndArgs))) (expList (commandsAndArgs.shift)) [])))))) + `(awaitLet ,bindings ,@body))) + + // Other + + (defAlias &call showTextDocument Vscode.window.showTextDocument) + (defAlias &call openTextDocument Vscode.workspace.openTextDocument) + + // Macros for implementing commands in Kiss + + (defMacro withValueOrInputBox [v &body body] + `{ + (if ,v + {,@body} + (awaitLet [,v (inputBox)] + ,@body)) + null + }) + + (defMacro withValueOrQuickPick [v options &body body] + `(if ,v + {,@body} + (awaitLet [,v (quickPick ,options)] + ,@body))) + + (defMacro withValueOrQuickPickMap [v options &body body] + `(if ,v + {,@body} + (awaitLet [,v (quickPickMap ,options)] + ,@body))) + + (function :Void chooseFileInDir [:String->Void openFile :Bool allowNew &opt :String dir] + (withValueOrInputBox dir + (set dir (dir.replace "\\" "/")) + (when (dir.endsWith "/") (set dir (substr dir 0 -1))) + (awaitLet [dirOrFile ((if allowNew autoSuggestPick quickPick) (cast (concat [".."] (sys.FileSystem.readDirectory dir))))] + (let [dirOrFile + (case dirOrFile + (".." + (substr dir 0 (dir.lastIndexOf "/"))) + (otherwise (joinPath dir dirOrFile)))] + (cond + ((sys.FileSystem.isDirectory dirOrFile) + (chooseFileInDir openFile allowNew dirOrFile)) + (true + (openFile dirOrFile))))))) + + (function :Void showCompileError [errorMessage] + (ifLet [compileErrors (R.distinctMatches + (R.group + (R.namedGroup "file" + (R.repeat (R.oneOf R.anyLetter R.anyDigit (R.escape "/")) 1) // filename + (R.escape ".kiss:") + (R.repeat R.anyDigit 1) // line + (R.escape ":") + (R.optional + (R.group + (R.repeat R.anyDigit 1) // column + (R.escape ":")))) + (R.repeat R.anyChar 1)) + errorMessage)] + { + (Vscode.window.showErrorMessage errorMessage) + (awaitLet [chosen (quickPickMap (for match compileErrors =>match.match match))] + (Vscode.window.showErrorMessage chosen.match) + (executeCommand "workbench.action.quickOpen" (substr (chosen.namedGroup "file") 0 -1))) + } + (Vscode.window.showErrorMessage errorMessage)))) \ No newline at end of file