From a0d765ed685cc63514f25e1c38694259c355c4b4 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Sat, 28 Dec 2024 22:17:40 -0800 Subject: [PATCH 1/9] add command option to custom repl commands --- docs/site/custom-commands.md | 22 ++++++++- src/custom-snippets.ts | 94 +++++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/docs/site/custom-commands.md b/docs/site/custom-commands.md index 9692ef238..91fc0251a 100644 --- a/docs/site/custom-commands.md +++ b/docs/site/custom-commands.md @@ -23,6 +23,7 @@ The `calva.customREPLCommandSnippets` is an array of objects with the following * `ns`: A namespace to evaluate the command in. If omitted the command will be executed in the namespace of the current editor. * `repl`: Which repl session to use for the evaluation. Either `"clj"` or `"cljs"`. Omit if you want to use the session of the current editor. * `evaluationSendCodeToOutputWindow`: (default `true`) Whether the evaluated code should be echoed to the Output/REPL window. +* `command`: (optional) A VS Code command to invoke after the snippet completes, providing the snippet's result as arguments. Supply and empty array `[]` if the result should include the command itself. There are also substitutions available, which will take elements from the current state of Calva and splice them in to the text of your command before executing it. They are @@ -86,7 +87,6 @@ And these **Workspace** settings: "snippet": "(start)" } ], - ``` Issuing **Run Custom REPL Command** will then render a VS Code menu with all the commands, where the Workspace configured commands will be listed first. @@ -170,3 +170,23 @@ A new experimental feature lets library authors ship snippets inside their jar f {:name "edn hover show val" :snippet (str "### EDN show val\n```clojure\n" (pr-str (eval (symbol (str "$ns" "/" "$hover-top-level-defined-symbol")))) "\n```")}]} ``` + +## Running a command as the result of evaluation + +Supplying a `command` will cause the result of evaluation to be treated as a command. +`command` is an array signature of the command. +If empty then the result will be treated as the command. +If supplied then the result will be treated as additional arguments. + +```json + { + "name": "Eval Current Form as Command", + "key": "a", + "repl": "clj", + "snippet": "$current-form", + "command": [] + }, +``` + +It's useful to know that there is a "runCommands" command, +so you can also chain multiple command/snippets together. diff --git a/src/custom-snippets.ts b/src/custom-snippets.ts index 2e7498859..d2e5ed8e7 100644 --- a/src/custom-snippets.ts +++ b/src/custom-snippets.ts @@ -8,27 +8,32 @@ import { getConfig } from './config'; import * as replSession from './nrepl/repl-session'; import evaluate from './evaluate'; import * as state from './state'; -import { getStateValue } from '../out/cljs-lib/cljs-lib'; +import { getStateValue, parseEdn } from '../out/cljs-lib/cljs-lib'; import * as output from './results-output/output'; +// the structure of custom REPL command snippets as configured by the user export type CustomREPLCommandSnippet = { name: string; key?: string; snippet: string; repl?: string; ns?: string; + command?: string | string[]; }; +// a snippet that is ready to be evaluated, with additional fields for internal use type SnippetDefinition = { snippet: string; ns: string; repl: string; evaluationSendCodeToOutputWindow?: boolean; + command?: string | string[]; }; export function evaluateCustomCodeSnippetCommand(codeOrKeyOrSnippet?: string | SnippetDefinition) { evaluateCodeOrKeyOrSnippet(codeOrKeyOrSnippet).catch((err) => { console.log('Failed to run snippet', err); + void vscode.window.showErrorMessage(`Failed to run snippet: ${err.message}`); }); } @@ -68,7 +73,33 @@ async function evaluateCodeOrKeyOrSnippet(codeOrKeyOrSnippet?: string | SnippetD : undefined; const context = makeContext(editor, snippetDefinition.ns, editorNS, snippetDefinition.repl); - await evaluateCodeInContext(editor, snippetDefinition.snippet, context, options); + const result = await evaluateCodeInContext(editor, snippetDefinition.snippet, context, options); + + if (snippetDefinition.command && typeof result === 'string') { + // the result of evaluation goes into the command array + const value = parseEdn(result); + const args = decodeArguments(value); + let commandArray: any[]; + + // make sure the command is an array + if (Array.isArray(snippetDefinition.command)) { + commandArray = [...snippetDefinition.command]; + } else { + commandArray = [snippetDefinition.command]; + } + + // append the results of evaluation + if (Array.isArray(args)) { + commandArray = [...commandArray, ...args]; + } else { + commandArray.push(args); + } + + // run the command + await vscode.commands.executeCommand(commandArray[0], ...commandArray.slice(1)); + } + + return result; } async function evaluateCodeInContext( @@ -112,6 +143,7 @@ async function getSnippetDefinition(codeOrKey: string, editorNS: string, editorR detail: `${entry.snippet}`, description: `${entry.repl}`, snippet: entry.snippet, + command: entry.command, }; snippetsMenuItems.push(item); if (!snippetsDict[entry.key]) { @@ -229,3 +261,61 @@ function interpolateCode(editor: vscode.TextEditor, code: string, context): stri .replace(/\$hover-tail/g, context.hovertail?.[1] ?? ''); } } + +function resolveRelativePath(path: string): string { + if (path.startsWith('/')) { + return path; + } + const workspaceFolders = vscode.workspace.workspaceFolders; + if (workspaceFolders && workspaceFolders.length > 0) { + const workspacePath = workspaceFolders[0].uri.fsPath; + return vscode.Uri.file(`${workspacePath}/${path}`).fsPath; + } + return path; +} + +function resolveFilePath(path: string): vscode.Uri { + return vscode.Uri.file(resolveRelativePath(path)); +} + +const decoders = { + File: resolveFilePath, + Uri: vscode.Uri.parse, +}; + +function decodeArgument(arg: any): any { + if (Array.isArray(arg) && arg.length > 1) { + if (decoders[arg[0]]) { + const Decoder = decoders[arg[0]]; + const decodedArgs = arg.slice(1).map(decodeArgument); + return Decoder(...decodedArgs); + } + + const parts = arg[0].split('.'); + if (parts.length > 0) { + let vscodeType: any = vscode; + for (const part of parts) { + vscodeType = vscodeType[part]; + if (!vscodeType) { + break; + } + } + if (typeof vscodeType === 'function') { + const decodedArgs = arg.slice(1).map(decodeArgument); + if (vscodeType.prototype) { + return new vscodeType(...decodedArgs); + } else { + return vscodeType(...decodedArgs); + } + } + } + } + return arg; +} + +function decodeArguments(value: any): any[] { + if (Array.isArray(value)) { + return value.map(decodeArgument); + } + return [decodeArgument(value)]; +} From 5569830bf8d8af8e73c09615f74492d96a170e2c Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Sun, 29 Dec 2024 18:38:43 -0800 Subject: [PATCH 2/9] load and watch resources/calva.export/config.edn --- src/config.ts | 11 +++++++++++ src/connector.ts | 9 +++++++++ src/custom-snippets.ts | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 2011ddca2..5b39d87b8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -161,6 +161,17 @@ watcher.onDidChange((uri: vscode.Uri) => { void updateCalvaConfigFromEdn(uri); }); +const exportsWatcher = vscode.workspace.createFileSystemWatcher( + '**/resources/calva.exports/config.edn', + false, + false, + false +); + +exportsWatcher.onDidChange((uri: vscode.Uri) => { + void updateCalvaConfigFromEdn(uri); +}); + // TODO find a way to validate the configs function getConfig() { const configOptions = vscode.workspace.getConfiguration('calva'); diff --git a/src/connector.ts b/src/connector.ts index af4abcb8d..22a9f1fff 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -53,6 +53,15 @@ async function readRuntimeConfigs() { if (element.endsWith('.jar')) { const edn = await getJarContents(element.concat('!/calva.exports/config.edn')); return [element, edn]; + } else if (element.endsWith('/resources')) { + const configUri = vscode.Uri.file(element.concat('/calva.exports/config.edn')); + try { + await vscode.workspace.fs.stat(configUri); + const edn = await vscode.workspace.fs.readFile(configUri); + return [element, edn]; + } catch { + // no config found + } } return [element, null]; diff --git a/src/custom-snippets.ts b/src/custom-snippets.ts index d2e5ed8e7..b35b1b3f0 100644 --- a/src/custom-snippets.ts +++ b/src/custom-snippets.ts @@ -56,7 +56,7 @@ async function evaluateCodeOrKeyOrSnippet(codeOrKeyOrSnippet?: string | SnippetD ? codeOrKeyOrSnippet : await getSnippetDefinition(codeOrKeyOrSnippet as string, editorNS, editorRepl); - snippetDefinition.repl = snippetDefinition.repl ?? editorRepl; + snippetDefinition.repl = snippetDefinition.repl ?? editorRepl ?? 'clj'; snippetDefinition.ns = snippetDefinition.ns ?? (editorRepl === snippetDefinition.repl ? editorNS : undefined); snippetDefinition.evaluationSendCodeToOutputWindow = From b325eda5eab22d673c05d4cdf23010038ed49c48 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Tue, 31 Dec 2024 21:51:31 -0800 Subject: [PATCH 3/9] add webview/info commands, load calva.exports --- package.json | 25 +++++++++++++++ src/connector.ts | 4 +-- src/custom-snippets.ts | 54 ++++++++++++++----------------- src/extension.ts | 5 +++ src/webview.ts | 73 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 32 deletions(-) create mode 100644 src/webview.ts diff --git a/package.json b/package.json index feaa41918..7ccfbafb4 100644 --- a/package.json +++ b/package.json @@ -2168,6 +2168,31 @@ "category": "Calva", "command": "calva.revealInspector", "title": "Reveal Inspector" + }, + { + "category": "Calva", + "command": "calva.revealJackInTerminal", + "title": "Reveal Jack-In Terminal" + }, + { + "category": "Calva", + "command": "calva.info", + "title": "Show Information Message" + }, + { + "category": "Calva", + "command": "calva.warn", + "title": "Show Warning Message" + }, + { + "category": "Calva", + "command": "calva.error", + "title": "Show Error Message" + }, + { + "category": "Calva", + "command": "calva.webview", + "title": "Show Webview" } ], "keybindings": [ diff --git a/src/connector.ts b/src/connector.ts index 22a9f1fff..41fb8dcf5 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -57,13 +57,13 @@ async function readRuntimeConfigs() { const configUri = vscode.Uri.file(element.concat('/calva.exports/config.edn')); try { await vscode.workspace.fs.stat(configUri); - const edn = await vscode.workspace.fs.readFile(configUri); + const ednBytes = await vscode.workspace.fs.readFile(configUri); + const edn = new TextDecoder('utf-8').decode(ednBytes); return [element, edn]; } catch { // no config found } } - return [element, null]; }); const files = await Promise.all(configs); diff --git a/src/custom-snippets.ts b/src/custom-snippets.ts index b35b1b3f0..6f08a4096 100644 --- a/src/custom-snippets.ts +++ b/src/custom-snippets.ts @@ -18,7 +18,8 @@ export type CustomREPLCommandSnippet = { snippet: string; repl?: string; ns?: string; - command?: string | string[]; + resultIsCommand?: boolean; + commandThen?: string; }; // a snippet that is ready to be evaluated, with additional fields for internal use @@ -27,7 +28,8 @@ type SnippetDefinition = { ns: string; repl: string; evaluationSendCodeToOutputWindow?: boolean; - command?: string | string[]; + resultIsCommand?: boolean; + commandThen?: string; }; export function evaluateCustomCodeSnippetCommand(codeOrKeyOrSnippet?: string | SnippetDefinition) { @@ -56,7 +58,12 @@ async function evaluateCodeOrKeyOrSnippet(codeOrKeyOrSnippet?: string | SnippetD ? codeOrKeyOrSnippet : await getSnippetDefinition(codeOrKeyOrSnippet as string, editorNS, editorRepl); - snippetDefinition.repl = snippetDefinition.repl ?? editorRepl ?? 'clj'; + if (!snippetDefinition) { + // user pressed ESC, not picking one + return undefined; + } + + snippetDefinition.repl = snippetDefinition.repl ?? editorRepl; snippetDefinition.ns = snippetDefinition.ns ?? (editorRepl === snippetDefinition.repl ? editorNS : undefined); snippetDefinition.evaluationSendCodeToOutputWindow = @@ -75,28 +82,21 @@ async function evaluateCodeOrKeyOrSnippet(codeOrKeyOrSnippet?: string | SnippetD const context = makeContext(editor, snippetDefinition.ns, editorNS, snippetDefinition.repl); const result = await evaluateCodeInContext(editor, snippetDefinition.snippet, context, options); - if (snippetDefinition.command && typeof result === 'string') { - // the result of evaluation goes into the command array + if (typeof result === 'string' && snippetDefinition.resultIsCommand) { const value = parseEdn(result); - const args = decodeArguments(value); - let commandArray: any[]; - - // make sure the command is an array - if (Array.isArray(snippetDefinition.command)) { - commandArray = [...snippetDefinition.command]; - } else { - commandArray = [snippetDefinition.command]; - } - - // append the results of evaluation - if (Array.isArray(args)) { - commandArray = [...commandArray, ...args]; + if (!Array.isArray(value) || !value[0]) { + void vscode.window.showErrorMessage('Result is not a command array'); + return result; } else { - commandArray.push(args); + const command = value[0]; + const args = value.slice(1).map(decodeArgument); + const commandResult = await vscode.commands.executeCommand(command, ...args); + if (snippetDefinition.commandThen) { + const code = `(${snippetDefinition.commandThen} ${commandResult})`; + return await evaluateCodeInContext(editor, code, context, options); + } + return commandResult; } - - // run the command - await vscode.commands.executeCommand(commandArray[0], ...commandArray.slice(1)); } return result; @@ -143,7 +143,8 @@ async function getSnippetDefinition(codeOrKey: string, editorNS: string, editorR detail: `${entry.snippet}`, description: `${entry.repl}`, snippet: entry.snippet, - command: entry.command, + resultIsCommand: entry.resultIsCommand, + commandThen: entry.commandThen, }; snippetsMenuItems.push(item); if (!snippetsDict[entry.key]) { @@ -312,10 +313,3 @@ function decodeArgument(arg: any): any { } return arg; } - -function decodeArguments(value: any): any[] { - if (Array.isArray(value)) { - return value.map(decodeArgument); - } - return [decodeArgument(value)]; -} diff --git a/src/extension.ts b/src/extension.ts index 368eb0f93..fc2812c33 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -45,6 +45,7 @@ import * as lsp from './lsp'; import * as fiddleFiles from './fiddle-files'; import * as output from './results-output/output'; import * as inspector from './providers/inspector'; +import * as webview from './webview'; function onDidChangeEditorOrSelection(editor: vscode.TextEditor) { replHistory.setReplHistoryCommandsActiveContext(editor); @@ -369,6 +370,10 @@ async function activate(context: vscode.ExtensionContext) { return inspectorTreeView.reveal(selectedItem, { select, focus, expand }); }, revealJackInTerminal: jackIn.revealJackInTerminal, + info: vscode.window.showInformationMessage, + warn: vscode.window.showWarningMessage, + error: vscode.window.showErrorMessage, + webview: webview.show, }; function registerCalvaCommand([command, callback]) { diff --git a/src/webview.ts b/src/webview.ts new file mode 100644 index 000000000..2d5ef2951 --- /dev/null +++ b/src/webview.ts @@ -0,0 +1,73 @@ +import * as vscode from 'vscode'; + +const defaultOpts = { + enableScripts: true, +}; + +// keep track of open webviews that have a key, +// so that they can be updated +const webviewRegistry: Record = {}; + +function setHtml(panel: vscode.WebviewPanel, title: string, html: string): vscode.WebviewPanel { + if (panel.title !== title) { + panel.title = title; + } + if (panel.webview.html !== html) { + panel.webview.html = html; + } + panel.reveal(); + return panel; +} + +function urlInIframe(uri: string): string { + return ` + + + + + + + +`; +} + +export function show({ + title = 'Webview', + html, + url, + key, + column = vscode.ViewColumn.Beside, + opts = defaultOpts, +}: { + title?: string; + html?: string; + url?: string; + key?: string; + column?: vscode.ViewColumn; + opts?: typeof defaultOpts; +}): vscode.WebviewPanel { + const finalHtml = url ? urlInIframe(url) : html || ''; + if (key) { + const existingPanel = webviewRegistry[key]; + if (existingPanel) { + return setHtml(existingPanel, title, finalHtml); + } + } + + const panel = vscode.window.createWebviewPanel('calva-webview', title, column, opts); + setHtml(panel, title, finalHtml); + + if (key) { + webviewRegistry[key] = panel; + panel.onDidDispose(() => delete webviewRegistry[key]); + } + + return panel; +} From 7724b8ad693b5d20a493fa311850769be074bebb Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Thu, 2 Jan 2025 16:45:37 -0800 Subject: [PATCH 4/9] add documentation for commands --- docs/site/commands-reference.md | 244 ++++++++++++++++++++++++++++++++ docs/site/commands.md | 13 +- docs/site/finding-commands.md | 2 + mkdocs.yml | 1 + package.json | 3 +- scripts/generate-commands-md.js | 95 +++++++++++++ src/webview.ts | 92 ++++++------ 7 files changed, 398 insertions(+), 52 deletions(-) create mode 100644 docs/site/commands-reference.md create mode 100644 scripts/generate-commands-md.js diff --git a/docs/site/commands-reference.md b/docs/site/commands-reference.md new file mode 100644 index 000000000..743cf157b --- /dev/null +++ b/docs/site/commands-reference.md @@ -0,0 +1,244 @@ +--- +title: Calva Command Reference +description: All Calva commands. +--- + +# Calva Command Reference + +Calva's commands are part of the [Calva API](api.md). +They often accept arguments of some type. + +## JackIn commands + +| Command | Title | +| :------ | :---- | +| `calva.startJoyrideReplAndConnect` | Start Joyride REPL and Connect | +| `calva.copyJackInCommandToClipboard` | Copy Jack-In Command Line to Clipboard | +| `calva.jackIn` | Start a Project REPL and Connect (aka Jack-In) | +| `calva.jackOut` | Stop/Kill the Project REPL started by Calva (aka Jack-Out) | +| `calva.connect` | Connect to a Running REPL Server in the Project | +| `calva.connectNonProjectREPL` | Connect to a Running REPL Server, not in Project | +| `calva.disconnect` | Disconnect from the REPL Server | +| `calva.startStandaloneHelloRepl` | Create a Getting Started REPL project | +| `calva.revealJackInTerminal` | Reveal Jack-In Terminal | + +## Evaluate commands + +| Command | Title | +| :------ | :---- | +| `calva.togglePrettyPrint` | Toggle Pretty Printing for All Evaluations | +| `calva.clearInlineResults` | Clear Inline Evaluation Results | +| `calva.interruptAllEvaluations` | Interrupt Running Evaluations | +| `calva.evaluateSelection` | Evaluate Current Form (or selection, if any) | +| `calva.evaluateEnclosingForm` | Evaluate Current Enclosing Form | +| `calva.evaluateToCursor` | Evaluate From Start of List to Cursor, Closing Brackets | +| `calva.evaluateSelectionToSelectionEnd` | Evaluate Selection, Closing Brackets | +| `calva.evaluateTopLevelFormToCursor` | Evaluate From Start of Top Level Form to Cursor, Closing Brackets | +| `calva.tapSelection` | Tap Current Form (or selection, if any) | +| `calva.evaluateCurrentTopLevelForm` | Evaluate Top Level Form (defun) | +| `calva.tapCurrentTopLevelForm` | Tap Current Top Level Form | +| `calva.evaluateSelectionAsComment` | Evaluate Current Form to Comment | +| `calva.evaluateTopLevelFormAsComment` | Evaluate Top Level Form (defun) to Comment | +| `calva.copyLastResults` | Copy Last Evaluation Result to Clipboard | +| `calva.loadFile` | Load/Evaluate Current File and its Requires/Dependencies | +| `calva.refresh` | Refresh Changed Namespaces | +| `calva.refreshAll` | Refresh All Namespaces | +| `calva.evaluateFiddleForSourceFile` | Evaluate Fiddle File for Current File | + +## Test commands + +| Command | Title | +| :------ | :---- | +| `calva.evaluateStartOfFileToCursor` | Evaluate From Start of File to Cursor, Closing Brackets | +| `calva.runNamespaceTests` | Run Tests for Current Namespace | +| `calva.loadTestFileForCurrentNamespace` | Load/Evaluate Test File (as saved on disk) for Current Namespace | +| `calva.runAllTests` | Run All Tests | +| `calva.rerunTests` | Run Failing Tests Again | +| `calva.runTestUnderCursor` | Run Current Test | +| `calva.toggleBetweenImplAndTest` | Toggle between implementation and test | + +## Paredit commands + +| Command | Title | +| :------ | :---- | +| `paredit.togglemode` | Toggle Paredit Mode | +| `paredit.forwardSexp` | Move Cursor Forward Sexp/Form | +| `paredit.backwardSexp` | Move Cursor Backward Sexp/Form | +| `paredit.forwardSexpOrUp` | Move Cursor Forward or Up Sexp/Form | +| `paredit.backwardSexpOrUp` | Move Cursor Backward or Up Sexp/Form | +| `paredit.forwardDownSexp` | Move Cursor Forward Down Sexp/Form | +| `paredit.backwardDownSexp` | Move Cursor Backward Down Sexp/Form | +| `paredit.backwardUpSexp` | Move Cursor Backward Up Sexp/Form | +| `paredit.forwardUpSexp` | Move Cursor Forward Up Sexp/Form | +| `paredit.closeList` | Move Cursor Forward to List End/Close | +| `paredit.selectForwardSexp` | Select Forward Sexp | +| `paredit.selectRight` | Select Right | +| `paredit.selectBackwardSexp` | Select Backward Sexp | +| `paredit.selectForwardDownSexp` | Select Forward Down Sexp | +| `paredit.selectBackwardDownSexp` | Select Backward Down Sexp | +| `paredit.selectBackwardUpSexp` | Select Backward Up Sexp | +| `paredit.selectForwardUpSexp` | Select Forward Up Sexp | +| `paredit.selectBackwardSexpOrUp` | Select Backward Or Up Sexp | +| `paredit.selectForwardSexpOrUp` | Select Forward Or Up Sexp | +| `paredit.selectCloseList` | Select Forward to List End/Close | +| `paredit.selectOpenList` | Select Backward to List Start/Open | +| `paredit.rangeForDefun` | Select Current Top Level (aka defun) Form | +| `paredit.sexpRangeExpansion` | Expand Selection | +| `paredit.sexpRangeContraction` | Shrink Selection | +| `paredit.slurpSexpForward` | Slurp Sexp Forward | +| `paredit.slurpSexpBackward` | Slurp Sexp Backward | +| `paredit.barfSexpForward` | Barf Sexp Forward | +| `paredit.barfSexpBackward` | Barf Sexp Backward | +| `paredit.spliceSexp` | Splice Sexp | +| `paredit.splitSexp` | Split Sexp | +| `paredit.joinSexp` | Join Sexp | +| `paredit.raiseSexp` | Raise Sexp | +| `paredit.transpose` | Transpose (Swap) the two Sexps Around the Cursor | +| `paredit.dragSexprBackward` | Drag Sexp Backward | +| `paredit.dragSexprForward` | Drag Sexp Forward | +| `paredit.dragSexprBackwardUp` | Drag Sexp Backward Up | +| `paredit.dragSexprForwardDown` | Drag Sexp Forward Down | +| `paredit.dragSexprForwardUp` | Drag Sexp Forward Up | +| `paredit.dragSexprBackwardDown` | Drag Sexp Backward Down | +| `paredit.convolute` | Convolute Sexp ¯\_(ツ)_/¯ | +| `paredit.killRight` | Kill/Delete Right | +| `paredit.killLeft` | Kill/Delete Left | +| `paredit.killSexpForward` | Kill/Delete Sexp Forward | +| `paredit.killSexpBackward` | Kill/Delete Sexp Backward | +| `paredit.killListForward` | Kill/Delete Forward to End of List | +| `paredit.killListBackward` | Kill/Delete Backward to Start of List | +| `paredit.spliceSexpKillForward` | Splice & Kill/Delete Forward | +| `paredit.spliceSexpKillBackward` | Splice & Kill/Delete Backward | +| `paredit.deleteForward` | Delete Forward | +| `paredit.deleteBackward` | Delete Backward | +| `paredit.forceDeleteForward` | Force Delete Forward | +| `paredit.forceDeleteBackward` | Force Delete Backward | +| `paredit.wrapAroundParens` | Wrap Around () | +| `paredit.wrapAroundSquare` | Wrap Around [] | +| `paredit.wrapAroundCurly` | Wrap Around {} | +| `paredit.wrapAroundQuote` | Wrap Around "" | +| `paredit.rewrapParens` | Rewrap () | +| `paredit.rewrapSquare` | Rewrap [] | +| `paredit.rewrapCurly` | Rewrap {} | +| `paredit.rewrapSet` | Rewrap #{} | +| `paredit.rewrapQuote` | Rewrap "" | +| `paredit.addRichComment` | Add Rich Comment | + +## LSP commands + +| Command | Title | +| :------ | :---- | +| `calva.clojureLsp.showClojureLspMenu` | Show clojure-lsp menu | +| `calva.clojureLsp.download` | Download the configured Clojure LSP Server version | +| `calva.clojureLsp.start` | Start the Clojure LSP Server | +| `calva.clojureLsp.stop` | Stop the Clojure LSP Server | +| `calva.clojureLsp.restart` | Restart the Clojure LSP Server | +| `calva.clojureLsp.manage` | Manage Clojure LSP Servers | +| `calva.diagnostics.openClojureLspLogFile` | Open Clojure LSP Log File | +| `calva.diagnostics.showLspTraceLevelSettings` | Show LSP Trace Level Settings | +| `clojureLsp.refactor.cleanNs` | Clean NS Form | +| `clojureLsp.refactor.addMissingLibspec` | Add Missing Require | +| `clojureLsp.dragBackward` | Drag Sexp Backward | +| `clojureLsp.dragForward` | Drag Sexp Forward | +| `clojureLsp.refactor.cyclePrivacy` | Cycle/Toggle Privacy | +| `clojureLsp.refactor.expandLet` | Expand Let | +| `clojureLsp.refactor.inlineSymbol` | Inline Symbol | +| `clojureLsp.refactor.threadFirst` | Thread First | +| `clojureLsp.refactor.threadFirstAll` | Thread First All | +| `clojureLsp.refactor.threadLast` | Thread Last | +| `clojureLsp.refactor.threadLastAll` | Thread Last All | +| `clojureLsp.refactor.unwindAll` | Unwind All | +| `clojureLsp.refactor.unwindThread` | Unwind Thread | +| `clojureLsp.refactor.introduceLet` | Introduce let | +| `clojureLsp.refactor.moveToLet` | Move to Previous let Box | +| `clojureLsp.refactor.extractFunction` | Extract to New Function | +| `calva.diagnostics.clojureLspServerInfo` | Clojure-lsp Server Info | + +## Formatting commands + +| Command | Title | +| :------ | :---- | +| `calva-fmt.formatCurrentForm` | Format Current Form | +| `calva-fmt.alignCurrentForm` | Format and Align Current Form (recursively, experimental) | +| `calva-fmt.trimCurrentFormWhiteSpace` | Format Current Form and trim space between forms | +| `calva-fmt.inferParens` | Infer Parens (from the indentation) | +| `calva-fmt.tabIndent` | Indent Line | +| `calva-fmt.tabDedent` | Dedent Line | + +## Conversion commands + +| Command | Title | +| :------ | :---- | +| `calva.convertJs2Cljs` | Convert JavaScript code to ClojureScript | +| `calva.convertDart2Clj` | Convert Dart code to Clojure/ClojureDart | +| `calva.convertHtml2Hiccup` | Convert HTML code to Hiccup | +| `calva.pasteHtmlAsHiccup` | Paste HTML code as Hiccup | +| `calva.copyHtmlAsHiccup` | Copy HTML code as Hiccup | + +## Inspector commands + +| Command | Title | +| :------ | :---- | +| `calva.clearInspector` | Clear All Inspector Items | +| `calva.clearInspectorItem` | Clear Inspector Item | +| `calva.copyInspectorItem` | Copy Inspector Item | +| `calva.inspectItem` | Inspect Item | +| `calva.pasteAsInspectorItem` | Paste as Inspector Item | +| `calva.addToInspector` | Add Selection or Current Form to Inspector | +| `calva.revealInspector` | Reveal Inspector | + +## Show commands + +| Command | Title | +| :------ | :---- | +| `calva.info` | Show Information Message | +| `calva.warn` | Show Warning Message | +| `calva.error` | Show Error Message | +| `calva.webview` | Show Webview | + +## REPL commands + +| Command | Title | +| :------ | :---- | +| `calva.prettyPrintReplaceCurrentForm` | Replace Current Form (or Selection) with Pretty Printed Form | +| `calva.openUserConfigEdn` | Open REPL snippets User config.edn | +| `calva.rereadUserConfigEdn` | Refresh REPL snippets from User config.edn | +| `calva.diagnostics.toggleNreplLoggingEnabled` | Toggle nREPL Logging Enabled | +| `calva.toggleEvaluationSendCodeToOutputWindow` | Toggle also sending evaluated code to the REPL Window | +| `calva.showReplMenu` | Open the REPL Menu (Start/Connect a REPL, etc.) | +| `calva.toggleCLJCSession` | Toggle the REPL Connection (clj or cljs) used for CLJC Files | +| `calva.evaluateSelectionReplace` | Evaluate Current Form and Replace it with the Result | +| `calva.printLastStacktrace` | Print Last Stacktrace to REPL Window | +| `calva.requireREPLUtilities` | Require (refer) REPL utilities, like (doc) etcetera, into Current Namespace | +| `calva.runCustomREPLCommand` | Run Custom REPL Command | +| `calva.showOutputChannel` | Show/Open the Calva says Output Channel | +| `calva.showOutputTerminal` | Show/Open the Calva Output Terminal | +| `calva.showResultOutputDestination` | Show/Open the result output destination | +| `calva.showReplWindow` | Show/Open REPL Window | +| `calva.showFileForOutputWindowNS` | Show File for the Current REPL Window Namespace | +| `calva.setOutputWindowNamespace` | Switch Namespace in REPL Window to Current Namespace | +| `calva.sendCurrentFormToOutputWindow` | Send Current Form to REPL Window | +| `calva.sendCurrentTopLevelFormToOutputWindow` | Send Current Top Level Form to REPL Window | +| `calva.showPreviousReplHistoryEntry` | Show Previous REPL History Entry | +| `calva.showNextReplHistoryEntry` | Show Next REPL History Entry | +| `calva.clearReplHistory` | Clear REPL History | + +## Miscellaneous commands + +| Command | Title | +| :------ | :---- | +| `calva.activateCalva` | Activate the Calva Extension | +| `calva.diagnostics.printTextNotationFromDocument` | Print TextNotation from the current document to Calva says | +| `calva.diagnostics.createDocumentFromTextNotation` | Create a new Clojure Document from TextNotation | +| `calva.linting.resolveMacroAs` | Resolve Macro As | +| `calva.openCalvaDocs` | Open Documentation (calva.io) | +| `calva.debug.instrument` | Instrument Top Level Form for Debugging | +| `calva.createMinimalProject` | Create a mini Clojure project | +| `calva.continueComment` | Continue Comment (add a commented line below). | +| `calva.switchCljsBuild` | Select CLJS Build Connection | +| `calva.toggleKeybindingsEnabled` | Toggle Keybindings Enabled | +| `calva.selectCurrentForm` | Select Current Form | +| `calva.openFiddleForSourceFile` | Open Fiddle File for Current File | +| `calva.openSourceFileForFiddle` | Open Source File for Current Fiddle File | +| `calva.printClojureDocsToOutputWindow` | Print clojuredocs.org examples to OutputWindow | +| `calva.printClojureDocsToRichComment` | Print clojuredocs.org examples to Rich Comment | diff --git a/docs/site/commands.md b/docs/site/commands.md index cdb2e3606..68cfda12b 100644 --- a/docs/site/commands.md +++ b/docs/site/commands.md @@ -5,9 +5,6 @@ description: A list of all (well, not by far yet) Calva commands. A part of Calv # Calva Commands -!!! Note "This list is totally incomplete" - If you want to help the Calva project, one way to do so is to help making this list of commands complete. - Calva's commands are part of the [Calva API](api.md). They often accept arguments of some type, which you can use from keybindings and from [Joyride](https://github.com/BetterThanTomorrow/joyride) (or another VS Code extension). Well behaved commands return a Promise, if it is async. You can utilize this with Joyride too, or with keybindings involving [`runCommands`](https://blog.agical.se/en/posts/vs-code-runcommands-for-multi-commands-keyboard-shortcuts/). ## Example shortcut bindings @@ -44,16 +41,14 @@ Here's another way to achieve something similar. } ``` -## REPL commands - -Commands that establishes or needs a REPL connection. +## Commands with arguments | Command | Title | Arguments | Notes | | :------ | :---- | :-------- | :---- | | `calva.refresh` | Refreshes changed namespaces | A JSON object with stuff from [cider-nrepl ops/refresh](https://github.com/clojure-emacs/cider-nrepl/blob/master/doc/modules/ROOT/pages/nrepl-api/ops.adoc#refresh) | Mostly meant for sending `:dirs`, `:after`, and `:before`. The print options may or may not work. | `calva.refreshAll` | Refreshes changed namespaces | A JSON object with stuff from [cider-nrepl ops/refresh-aa](https://github.com/clojure-emacs/cider-nrepl/blob/master/doc/modules/ROOT/pages/nrepl-api/ops.adoc#refresh-all) | Mostly meant for sending `:dirs`, `:after`, and `:before`. The print options may or may not work. +Unfortunatley the arguments are not well documented, so you may need to poke around in the code to discover them. +If you search for the command, you should find it in package.json, and be able to navigate to the function definition to see the arguments. -## Wait, where are all the commands? - -Told you the list is incomplete... Please consider helping with making this a complete list! 🙏 \ No newline at end of file +See [Commands Reference](commands-reference.md) for the complete list of commands available. diff --git a/docs/site/finding-commands.md b/docs/site/finding-commands.md index 153276ca7..7d3837e39 100644 --- a/docs/site/finding-commands.md +++ b/docs/site/finding-commands.md @@ -26,6 +26,8 @@ Did you know? There is a complete list of Calva settings and commands in the *Co ![The Calva Contributions Tab](https://user-images.githubusercontent.com/30010/66733740-c754b800-ee60-11e9-877b-962f6b920cd7.png) +See also the [Commands Reference](commands-reference.md) + ## Toggling Keyboard Shortcuts On/Off The command `calva.toggleKeybindingsEnabled` can be used to quickly enable and disable (almost) all keyboard shortcuts. This allows you to quickly toggle between Calva keybindings and other keybindings which would otherwise not be available when Calva is enabled. This is particularly useful with the Paredit keyboard shortcuts, whose default shortcuts conflict with the default VS Code shortcuts for textual (non-structural) editing. diff --git a/mkdocs.yml b/mkdocs.yml index fc8688eff..03eeaeb4c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -108,6 +108,7 @@ nav: - API: - api.md - commands.md + - commands-reference.md - Guides: - get-started-with-clojure.md - jack-in-guide.md diff --git a/package.json b/package.json index 7ccfbafb4..525858e9d 100644 --- a/package.json +++ b/package.json @@ -3367,7 +3367,8 @@ "preprettier-format-watch": "npm run prettier-format", "eslint": "npx eslint . --ext .js,.jsx,.ts,.tsx", "eslint-watch": "npx esw . --ext .js,.jsx,.ts,.tsx --watch", - "install-ys": "curl -sS https://yamlscript.org/install | PREFIX=/tmp/yamlscript VERSION=0.1.56 bash" + "install-ys": "curl -sS https://yamlscript.org/install | PREFIX=/tmp/yamlscript VERSION=0.1.56 bash", + "generate-commands-md": "node ./scripts/generate-commands-md.js" }, "dependencies": { "@vscode/debugadapter": "^1.64.0", diff --git a/scripts/generate-commands-md.js b/scripts/generate-commands-md.js new file mode 100644 index 000000000..f4dc0659d --- /dev/null +++ b/scripts/generate-commands-md.js @@ -0,0 +1,95 @@ +const fs = require('fs'); +const path = require('path'); +const packageJson = require('../package.json'); + +const commands = packageJson.contributes.commands; + +const sections = { + Miscellaneous: [], + REPL: [], + Paredit: [], + LSP: [], + Formatting: [], + Test: [], + Evaluate: [], + JackIn: [], + Inspector: [], + Conversion: [], + Show: [], +}; + +commands.forEach((c) => { + if (c.command.startsWith('paredit.')) { + sections.Paredit.push(c); + } else if (c.command.includes('Lsp')) { + sections.LSP.push(c); + } else if (c.command.startsWith('calva-fmt')) { + sections.Formatting.push(c); + } else if ( + c.command.toLowerCase().includes('jack') || + c.command.includes('.connect') || + c.command.includes('.start') || + c.command.includes('.disconnect') + ) { + sections.JackIn.push(c); + } else if ( + c.command.toLowerCase().includes('repl') || + c.title.toLowerCase().includes('repl') || + c.command.includes('.show') + ) { + sections.REPL.push(c); + } else if (c.command.toLowerCase().includes('test') || c.title.toLowerCase().includes('test')) { + sections.Test.push(c); + } else if ( + c.command.toLowerCase().includes('evalu') || + c.title.toLowerCase().includes('evalu') || + c.command.includes('refresh') || + c.command.includes('.interrupt') || + c.command.includes('.tap') + ) { + sections.Evaluate.push(c); + } else if (c.command.toLowerCase().includes('inspect')) { + sections.Inspector.push(c); + } else if (c.command.includes('HtmlAsHiccup') || c.command.includes('convert')) { + sections.Conversion.push(c); + } else if (c.title.startsWith('Show')) { + sections.Show.push(c); + } else { + sections.Miscellaneous.push(c); + } +}); + +const generateSection = (title, commands) => { + return `## ${title} commands + +| Command | Title | +| :------ | :---- | +${commands.map((cmd) => `| \`${cmd.command}\` | ${cmd.title} |`).join('\n')} +`; +}; + +const content = `--- +title: Calva Command Reference +description: All Calva commands. +--- + +# Calva Command Reference + +Calva's commands are part of the [Calva API](api.md). +They often accept arguments of some type. + +${generateSection('JackIn', sections.JackIn)} +${generateSection('Evaluate', sections.Evaluate)} +${generateSection('Test', sections.Test)} +${generateSection('Paredit', sections.Paredit)} +${generateSection('LSP', sections.LSP)} +${generateSection('Formatting', sections.Formatting)} +${generateSection('Conversion', sections.Conversion)} +${generateSection('Inspector', sections.Inspector)} +${generateSection('Show', sections.Show)} +${generateSection('REPL', sections.REPL)} +${generateSection('Miscellaneous', sections.Miscellaneous)}`; + +const filename = '../docs/site/commands-reference.md'; +fs.writeFileSync(path.join(__dirname, filename), content); +console.log(filename + ' has been generated.'); diff --git a/src/webview.ts b/src/webview.ts index 2d5ef2951..8bc176278 100644 --- a/src/webview.ts +++ b/src/webview.ts @@ -4,19 +4,61 @@ const defaultOpts = { enableScripts: true, }; -// keep track of open webviews that have a key, -// so that they can be updated -const webviewRegistry: Record = {}; +interface CalvaWebPanel extends vscode.WebviewPanel { + url?: string; +} + +// keep track of open webviews that have a key +// so that they can be updated in the future +const calvaWebPanels: Record = {}; + +export function show({ + title = 'Webview', + key, + html, + url, + reload = false, + reveal = true, + column = vscode.ViewColumn.Beside, + opts = defaultOpts, +}: { + title?: string; + key?: string; + html?: string; + url?: string; + reload?: boolean; + reveal?: boolean; + column?: vscode.ViewColumn; + opts?: typeof defaultOpts; +}): void { + let panel: CalvaWebPanel; + if (key) { + panel = calvaWebPanels[key]; + } + if (!panel) { + panel = vscode.window.createWebviewPanel('calva-webview', title, column, opts); + if (key) { + calvaWebPanels[key] = panel; + panel.onDidDispose(() => delete calvaWebPanels[key]); + } + } + + if (html && panel.webview.html != html) { + panel.webview.html = html; + } + + if (url && (url != panel.url || reload)) { + panel.url = url; + panel.webview.html = urlInIframe(url); + } -function setHtml(panel: vscode.WebviewPanel, title: string, html: string): vscode.WebviewPanel { if (panel.title !== title) { panel.title = title; } - if (panel.webview.html !== html) { - panel.webview.html = html; + + if (reveal) { + panel.reveal(); } - panel.reveal(); - return panel; } function urlInIframe(uri: string): string { @@ -37,37 +79,3 @@ function urlInIframe(uri: string): string { `; } - -export function show({ - title = 'Webview', - html, - url, - key, - column = vscode.ViewColumn.Beside, - opts = defaultOpts, -}: { - title?: string; - html?: string; - url?: string; - key?: string; - column?: vscode.ViewColumn; - opts?: typeof defaultOpts; -}): vscode.WebviewPanel { - const finalHtml = url ? urlInIframe(url) : html || ''; - if (key) { - const existingPanel = webviewRegistry[key]; - if (existingPanel) { - return setHtml(existingPanel, title, finalHtml); - } - } - - const panel = vscode.window.createWebviewPanel('calva-webview', title, column, opts); - setHtml(panel, title, finalHtml); - - if (key) { - webviewRegistry[key] = panel; - panel.onDidDispose(() => delete webviewRegistry[key]); - } - - return panel; -} From 498223c5b5baae9f022679bba5ef85b1c027f005 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Thu, 2 Jan 2025 21:14:50 -0800 Subject: [PATCH 5/9] add a test for custom snippets --- src/custom-snippets.ts | 2 +- .../integration/suite/custom-snippets-test.ts | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/extension-test/integration/suite/custom-snippets-test.ts diff --git a/src/custom-snippets.ts b/src/custom-snippets.ts index 6f08a4096..455d46285 100644 --- a/src/custom-snippets.ts +++ b/src/custom-snippets.ts @@ -23,7 +23,7 @@ export type CustomREPLCommandSnippet = { }; // a snippet that is ready to be evaluated, with additional fields for internal use -type SnippetDefinition = { +export type SnippetDefinition = { snippet: string; ns: string; repl: string; diff --git a/src/extension-test/integration/suite/custom-snippets-test.ts b/src/extension-test/integration/suite/custom-snippets-test.ts new file mode 100644 index 000000000..a02ac7f90 --- /dev/null +++ b/src/extension-test/integration/suite/custom-snippets-test.ts @@ -0,0 +1,31 @@ +import * as assert from 'assert'; +import * as customSnippets from '../../../custom-snippets'; + +suite('Custom Snippets Tests', () => { + test('evaluateCustomCodeSnippetCommand with resultIsCommand', () => { + const snippet: customSnippets.SnippetDefinition = { + snippet: '(+ 1 2)', + ns: 'user', + repl: 'clj', + resultIsCommand: true, + commandThen: 'identity', + }; + + const result = customSnippets.evaluateCustomCodeSnippetCommand(snippet); + + assert.strictEqual(result, '3'); + }); + + test('evaluateCustomCodeSnippetCommand without resultIsCommand', () => { + const snippet: customSnippets.SnippetDefinition = { + snippet: '(+ 1 2)', + ns: 'user', + repl: 'clj', + resultIsCommand: false, + }; + + const result = customSnippets.evaluateCustomCodeSnippetCommand(snippet); + + assert.strictEqual(result, '3'); + }); +}); From e3420ddf86139921e48999ec7b5856a090b472e7 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Thu, 2 Jan 2025 21:26:15 -0800 Subject: [PATCH 6/9] update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9b74cce7..ede4ce249 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,11 @@ Changes to Calva. ## [Unreleased] +- [Added resultAsCommand and commandThen options for custom snippets](https://github.com/BetterThanTomorrow/calva/issues/2683) + ## [2.0.482] - 2024-12-03 -- Fix: [Added 'replace-refer-all-with-alias' & 'replace-refer-all-with-refer' actions to calva.](https://github.com/BetterThanTomorrow/calva/issues/2667) +- Fix: [Added 'replace-refer-all-with-alias' & 'replace-refer-all-with-refer' actions to calva.](https://github.com/BetterThanTomorrow/calva/issues/2667) ## [2.0.481] - 2024-10-29 From 56827ad04f50d52a0491b51e83eec46861fce497 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Sun, 5 Jan 2025 12:39:36 -0800 Subject: [PATCH 7/9] move webview into custom-snippets for build CI fails when I add the new file webview.ts, so to work around that I'll just add the webview to snippets --- src/custom-snippets.ts | 80 +++++++++++++++++++++++++++++++++++++++++ src/extension.ts | 3 +- src/webview.ts | 81 ------------------------------------------ 3 files changed, 81 insertions(+), 83 deletions(-) delete mode 100644 src/webview.ts diff --git a/src/custom-snippets.ts b/src/custom-snippets.ts index 455d46285..cf8472133 100644 --- a/src/custom-snippets.ts +++ b/src/custom-snippets.ts @@ -313,3 +313,83 @@ function decodeArgument(arg: any): any { } return arg; } + +const defaultWebviewOptions = { + enableScripts: true, +}; + +interface CalvaWebPanel extends vscode.WebviewPanel { + url?: string; +} + +// keep track of open webviews that have a key +// so that they can be updated in the future +const calvaWebPanels: Record = {}; + +export function show({ + title = 'Webview', + key, + html, + url, + reload = false, + reveal = true, + column = vscode.ViewColumn.Beside, + opts = defaultWebviewOptions, +}: { + title?: string; + key?: string; + html?: string; + url?: string; + reload?: boolean; + reveal?: boolean; + column?: vscode.ViewColumn; + opts?: typeof defaultWebviewOptions; +}): void { + let panel: CalvaWebPanel; + if (key) { + panel = calvaWebPanels[key]; + } + if (!panel) { + panel = vscode.window.createWebviewPanel('calva-webview', title, column, opts); + if (key) { + calvaWebPanels[key] = panel; + panel.onDidDispose(() => delete calvaWebPanels[key]); + } + } + + if (html && panel.webview.html != html) { + panel.webview.html = html; + } + + if (url && (url != panel.url || reload)) { + panel.url = url; + panel.webview.html = urlInIframe(url); + } + + if (panel.title !== title) { + panel.title = title; + } + + if (reveal) { + panel.reveal(); + } +} + +function urlInIframe(uri: string): string { + return ` + + + + + + + +`; +} diff --git a/src/extension.ts b/src/extension.ts index fc2812c33..60e13554f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -45,7 +45,6 @@ import * as lsp from './lsp'; import * as fiddleFiles from './fiddle-files'; import * as output from './results-output/output'; import * as inspector from './providers/inspector'; -import * as webview from './webview'; function onDidChangeEditorOrSelection(editor: vscode.TextEditor) { replHistory.setReplHistoryCommandsActiveContext(editor); @@ -373,7 +372,7 @@ async function activate(context: vscode.ExtensionContext) { info: vscode.window.showInformationMessage, warn: vscode.window.showWarningMessage, error: vscode.window.showErrorMessage, - webview: webview.show, + webview: snippets.show, }; function registerCalvaCommand([command, callback]) { diff --git a/src/webview.ts b/src/webview.ts deleted file mode 100644 index 8bc176278..000000000 --- a/src/webview.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as vscode from 'vscode'; - -const defaultOpts = { - enableScripts: true, -}; - -interface CalvaWebPanel extends vscode.WebviewPanel { - url?: string; -} - -// keep track of open webviews that have a key -// so that they can be updated in the future -const calvaWebPanels: Record = {}; - -export function show({ - title = 'Webview', - key, - html, - url, - reload = false, - reveal = true, - column = vscode.ViewColumn.Beside, - opts = defaultOpts, -}: { - title?: string; - key?: string; - html?: string; - url?: string; - reload?: boolean; - reveal?: boolean; - column?: vscode.ViewColumn; - opts?: typeof defaultOpts; -}): void { - let panel: CalvaWebPanel; - if (key) { - panel = calvaWebPanels[key]; - } - if (!panel) { - panel = vscode.window.createWebviewPanel('calva-webview', title, column, opts); - if (key) { - calvaWebPanels[key] = panel; - panel.onDidDispose(() => delete calvaWebPanels[key]); - } - } - - if (html && panel.webview.html != html) { - panel.webview.html = html; - } - - if (url && (url != panel.url || reload)) { - panel.url = url; - panel.webview.html = urlInIframe(url); - } - - if (panel.title !== title) { - panel.title = title; - } - - if (reveal) { - panel.reveal(); - } -} - -function urlInIframe(uri: string): string { - return ` - - - - - - - -`; -} From e38c7a886e15cb7becdebc1b798d6b871e4eebeb Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Sun, 5 Jan 2025 13:10:07 -0800 Subject: [PATCH 8/9] remove custom-snippet-test.ts not working --- .../integration/suite/custom-snippets-test.ts | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/extension-test/integration/suite/custom-snippets-test.ts diff --git a/src/extension-test/integration/suite/custom-snippets-test.ts b/src/extension-test/integration/suite/custom-snippets-test.ts deleted file mode 100644 index a02ac7f90..000000000 --- a/src/extension-test/integration/suite/custom-snippets-test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as assert from 'assert'; -import * as customSnippets from '../../../custom-snippets'; - -suite('Custom Snippets Tests', () => { - test('evaluateCustomCodeSnippetCommand with resultIsCommand', () => { - const snippet: customSnippets.SnippetDefinition = { - snippet: '(+ 1 2)', - ns: 'user', - repl: 'clj', - resultIsCommand: true, - commandThen: 'identity', - }; - - const result = customSnippets.evaluateCustomCodeSnippetCommand(snippet); - - assert.strictEqual(result, '3'); - }); - - test('evaluateCustomCodeSnippetCommand without resultIsCommand', () => { - const snippet: customSnippets.SnippetDefinition = { - snippet: '(+ 1 2)', - ns: 'user', - repl: 'clj', - resultIsCommand: false, - }; - - const result = customSnippets.evaluateCustomCodeSnippetCommand(snippet); - - assert.strictEqual(result, '3'); - }); -}); From 4bbc7a7e3b1e04ac807bfe4975e430c0efe9bd12 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Sun, 5 Jan 2025 13:30:16 -0800 Subject: [PATCH 9/9] remove redundant title heading command reference --- docs/site/commands-reference.md | 22 +++++++++++----------- scripts/generate-commands-md.js | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/site/commands-reference.md b/docs/site/commands-reference.md index 743cf157b..f9a16ad96 100644 --- a/docs/site/commands-reference.md +++ b/docs/site/commands-reference.md @@ -8,7 +8,7 @@ description: All Calva commands. Calva's commands are part of the [Calva API](api.md). They often accept arguments of some type. -## JackIn commands +## JackIn | Command | Title | | :------ | :---- | @@ -22,7 +22,7 @@ They often accept arguments of some type. | `calva.startStandaloneHelloRepl` | Create a Getting Started REPL project | | `calva.revealJackInTerminal` | Reveal Jack-In Terminal | -## Evaluate commands +## Evaluate | Command | Title | | :------ | :---- | @@ -45,7 +45,7 @@ They often accept arguments of some type. | `calva.refreshAll` | Refresh All Namespaces | | `calva.evaluateFiddleForSourceFile` | Evaluate Fiddle File for Current File | -## Test commands +## Test | Command | Title | | :------ | :---- | @@ -57,7 +57,7 @@ They often accept arguments of some type. | `calva.runTestUnderCursor` | Run Current Test | | `calva.toggleBetweenImplAndTest` | Toggle between implementation and test | -## Paredit commands +## Paredit | Command | Title | | :------ | :---- | @@ -124,7 +124,7 @@ They often accept arguments of some type. | `paredit.rewrapQuote` | Rewrap "" | | `paredit.addRichComment` | Add Rich Comment | -## LSP commands +## LSP | Command | Title | | :------ | :---- | @@ -154,7 +154,7 @@ They often accept arguments of some type. | `clojureLsp.refactor.extractFunction` | Extract to New Function | | `calva.diagnostics.clojureLspServerInfo` | Clojure-lsp Server Info | -## Formatting commands +## Formatting | Command | Title | | :------ | :---- | @@ -165,7 +165,7 @@ They often accept arguments of some type. | `calva-fmt.tabIndent` | Indent Line | | `calva-fmt.tabDedent` | Dedent Line | -## Conversion commands +## Conversion | Command | Title | | :------ | :---- | @@ -175,7 +175,7 @@ They often accept arguments of some type. | `calva.pasteHtmlAsHiccup` | Paste HTML code as Hiccup | | `calva.copyHtmlAsHiccup` | Copy HTML code as Hiccup | -## Inspector commands +## Inspector | Command | Title | | :------ | :---- | @@ -187,7 +187,7 @@ They often accept arguments of some type. | `calva.addToInspector` | Add Selection or Current Form to Inspector | | `calva.revealInspector` | Reveal Inspector | -## Show commands +## Show | Command | Title | | :------ | :---- | @@ -196,7 +196,7 @@ They often accept arguments of some type. | `calva.error` | Show Error Message | | `calva.webview` | Show Webview | -## REPL commands +## REPL | Command | Title | | :------ | :---- | @@ -223,7 +223,7 @@ They often accept arguments of some type. | `calva.showNextReplHistoryEntry` | Show Next REPL History Entry | | `calva.clearReplHistory` | Clear REPL History | -## Miscellaneous commands +## Miscellaneous | Command | Title | | :------ | :---- | diff --git a/scripts/generate-commands-md.js b/scripts/generate-commands-md.js index f4dc0659d..841be96fa 100644 --- a/scripts/generate-commands-md.js +++ b/scripts/generate-commands-md.js @@ -60,7 +60,7 @@ commands.forEach((c) => { }); const generateSection = (title, commands) => { - return `## ${title} commands + return `## ${title} | Command | Title | | :------ | :---- |