Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
PEZ committed Oct 6, 2019
2 parents 132097a + ac2d9bc commit 38d6286
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 38 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Change Log
Changes to Calva.

## When time allows, this will be worked on
- [Support for custom project/workflow commands](https://github.com/BetterThanTomorrow/calva/issues/281)

## [Unreleased]

## [2.0.44] - 05.10.2019
- [Support for custom project/workflow commands](https://github.com/BetterThanTomorrow/calva/issues/281)

## [2.0.43] - 03.10.2019
- [Insourcing @tonsky's Clojue Warrior, now named Calva Highlight](https://github.com/BetterThanTomorrow/calva/pull/362)
- [Update status bar when configuration changed](https://github.com/BetterThanTomorrow/calva/issues/358)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ Demo: switch between `clj` and `cljs` repl sessions for `cljc` files:
- Evaluate code from the editor to the REPL window: `ctrl+alt+c ctrl+alt+e` (`ctrl+alt+c ctrl+alt+v` on Windows)
- When editing `cljc` files, easily choose if REPL commands should go to the `clj` or `cljs` REPL by clicking the `cljc/clj[s]` indicator in the status bar.
- Selection of current form: `ctrl+alt+c s`. Auto-detected the same way as for evaluation. Will select the form preceding or following the cursor first, otherwise the form the cursor is inside. (Only when the cursor is directly adjacent to any bracket so far.)
- Configure and run custom commands, i.e. code snippets, at will: `ctrl+alt+c .`

Demo: Peek at definitions, etcetera:

Expand Down
3 changes: 2 additions & 1 deletion docs/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ Smaller changes:

When a VSIX is good enough for release, and someone authorized to commit to the `master` branch has _at least half an hour of spare time_, the following will bring it to the Marketplace:

1. With `dev` checked out: `git checkout -B master`. (This ”moves” `master` to where `dev`'s `HEAD` is pointing.)
1. With `dev` checked out: `git checkout master`.
1. `git merge dev`
1. Tag with `v<VERSION>`
1. Push `master` (Using `--follow-tags`).
* This will build the release VSIX, push a relase to GitHub, and publish it on the extension Marketplace.
Expand Down
54 changes: 52 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "Calva: Clojure & Clojurescript Interactive Programming",
"description": "Integrated REPL, formatter, Paredit, and more. Powered by nREPL.",
"icon": "assets/calva.png",
"version": "2.0.43",
"version": "2.0.44",
"publisher": "betterthantomorrow",
"author": {
"name": "Better Than Tomorrow",
Expand Down Expand Up @@ -215,6 +215,46 @@
"default": true,
"description": "Should Calva open the Figwheel app for you when Figwheel has been started?"
},
"calva.customREPLCommandSnippets": {
"type": "array",
"default": [],
"description": "Configuration for the command **Run Custom REPL Command**",
"$schema": "http://json-schema.org/draft-06/schema#",
"items": {
"title": "replCommand",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name this command so that it is easy to pick from the menu."
},
"snippet": {
"type": [
"string",
"array"
],
"description": "Command to send to the REPL"
},
"ns": {
"type": "string",
"description": "(optional) Namespace to evaluate the command in. If ommitted the command will be executed in whatever namespace the REPL window has at the moment, which probably is mostly useful for running code using definitions from the `user` namespace."
},
"repl": {
"type": "string",
"description": "Choose which REPL should the code should be evaluated in.",
"enum": [
"clj",
"cljs"
]
}
},
"required": [
"name",
"snippet",
"repl"
]
}
},
"calva.myLeinProfiles": {
"type": "array",
"description": "At Jack in, any profiles listed here will be added to the profiles found in the `project.clj` file.",
Expand Down Expand Up @@ -671,10 +711,16 @@
},
{
"command": "calva.evalCurrentTopLevelFormInREPLWindow",
"title": "Evaluate Top Level Form in REPL Window (defun)",
"title": "Evaluate Top Level Form (defun) in REPL Window",
"enablement": "calva:connected",
"category": "Calva"
},
{
"command": "calva.runCustomREPLCommand",
"title": "Run Custom REPL Command",
"category": "Calva",
"enablement": "calva:connected"
},
{
"command": "calva.switchCljsBuild",
"title": "Select CLJS Build Connection",
Expand Down Expand Up @@ -1022,6 +1068,10 @@
"command": "calva.evalCurrentTopLevelFormInREPLWindow",
"key": "ctrl+alt+c ctrl+alt+space"
},
{
"command": "calva.runCustomREPLCommand",
"key": "ctrl+alt+c ."
},
{
"command": "paredit.forwardSexp",
"key": "ctrl+alt+right",
Expand Down
6 changes: 3 additions & 3 deletions src/calva-fmt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ Some examples of what it can be like to use Calva Formatter:

### Format Current Form

![Format Current Form](/assets/format-current-form.gif)
![Format Current Form](/src/calva-fmt/assets/format-current-form.gif)

### Align Current Form

![Align Current Form](/assets/align-items.gif)
![Align Current Form](/src/calva-fmt/assets/align-items.gif)

### Parinfer

![Infer parens](/assets/parinfer.gif)
![Infer parens](/src/calva-fmt/assets/parinfer.gif)

## How to use

Expand Down
2 changes: 1 addition & 1 deletion src/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async function connectToHost(hostname, port, connectSequence: ReplConnectSequenc

if (connectSequence.afterCLJReplJackInCode) {
state.outputChannel().appendLine("Evaluating `afterCLJReplJackInCode` in CLJ REPL Window");
await sendTextToREPLWindow(connectSequence.afterCLJReplJackInCode, null, false);
await sendTextToREPLWindow("clj", connectSequence.afterCLJReplJackInCode, null, false);
}

//cljsSession = nClient.session;
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ function activate(context: vscode.ExtensionContext) {

for (const config of ["enableBracketColors", "bracketColors", "cycleBracketColors", "misplacedBracketStyle", "matchedBracketStyle", "commentFormStyle", "ignoredFormStyle"]) {
if (cwConfig.get(config) !== undefined) {
vscode.window.showWarningMessage("Legacy Clojure Warrior settings detected. These settings have changed prefix/namespace to from `clojureWarrior´ to `calva.highlight`. You should update `settings.json`.", ...["Roger that!"]);
vscode.window.showWarningMessage("Legacy Clojure Warrior settings detected. These settings have changed prefix/namespace from `clojureWarrior´ to `calva.highlight`. You should update `settings.json`.", ...["Roger that!"]);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/nrepl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class NReplSession {
const msgType: string = msgData.out? "stdout" : "stderr";

if (msgValue && this.replType) {
const window = await replWindow.openReplWindow(this.replType, true);
const window = replWindow.getReplWindow(this.replType);
const windowMsg = {
type: msgType,
value: msgValue
Expand Down
115 changes: 90 additions & 25 deletions src/repl-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as path from "path";
import * as state from "./state";
import status from "./status"
import * as fs from "fs";
import * as _ from "lodash";
import { NReplEvaluation, NReplSession } from "./nrepl";

import annotations from './providers/annotations';
Expand All @@ -25,9 +26,9 @@ export function activeReplWindow() {
export function isReplWindowOpen(mode: "clj" | "cljs" = "clj") {
// If we find `mode` in ythe `replWindows` dictionary, then it is open.
if (!replWindows[mode]) {
return(false);
return (false);
}
return(true);
return (true);
}

class REPLWindow {
Expand Down Expand Up @@ -174,7 +175,7 @@ class REPLWindow {
})
try {
this.postMessage({ type: "repl-response", value: await this.evaluation.value, ns: this.ns = ns || this.evaluation.ns || this.ns });
if(this.evaluation.ns && this.ns != this.evaluation.ns) {
if (this.evaluation.ns && this.ns != this.evaluation.ns) {
// the evaluation changed the namespace so set the new namespace.
this.setNamespace(this.evaluation.ns);
}
Expand All @@ -198,17 +199,25 @@ class REPLWindow {
let ctx: vscode.ExtensionContext

let replWindows: { [id: string]: REPLWindow } = {};
let replViewColum: { [id: string]: vscode.ViewColumn } = {"clj": vscode.ViewColumn.Two,
"cljs": vscode.ViewColumn.Two};
let replViewColum: { [id: string]: vscode.ViewColumn } = {
"clj": vscode.ViewColumn.Two,
"cljs": vscode.ViewColumn.Two
};

export function getReplWindow(mode: "clj" | "cljs") {
return replWindows[mode];
}

function getImageUrl(name: string) {
let imagepath = "";
if (!name)
imagepath = path.join(ctx.extensionPath, "assets/images/empty.svg");
else
imagepath = path.join(ctx.extensionPath, "assets/images/", name);
if (!name) {
imagepath = path.join(ctx.extensionPath, "assets/images/empty.svg");
}
else {
imagepath = path.join(ctx.extensionPath, "assets/images/", name);
}

if(!fs.existsSync(imagepath)) {
if (!fs.existsSync(imagepath)) {
imagepath = path.join(ctx.extensionPath, "assets/images/empty.svg");
}
return vscode.Uri.file(imagepath).with({ scheme: 'vscode-resource' }).toString()
Expand All @@ -223,7 +232,7 @@ export async function reconnectReplWindow(mode: "clj" | "cljs") {

export async function openClojureReplWindows() {
if (state.deref().get('connected')) {
if(util.getSession("clj")) {
if (util.getSession("clj")) {
openReplWindow("clj", true);
return;
}
Expand All @@ -233,7 +242,7 @@ export async function openClojureReplWindows() {

export async function openClojureScriptReplWindows() {
if (state.deref().get('connected')) {
if(util.getSession("cljs")) {
if (util.getSession("cljs")) {
openReplWindow("cljs", true);
return;
}
Expand All @@ -247,7 +256,7 @@ export async function openReplWindow(mode: "clj" | "cljs" = "clj", preserveFocus

if (!replWindows[mode]) {
await createReplWindow(session, mode);
} else if (!nreplClient.sessions[replWindows[mode].session.sessionId]) {
} else if (!nreplClient.sessions[replWindows[mode].session.sessionId]) {
replWindows[mode].session = await session.clone();
}

Expand Down Expand Up @@ -295,20 +304,31 @@ async function setREPLNamespaceCommand() {
await setREPLNamespace(util.getDocumentNamespace(), false).catch(r => { console.error(r) });
}

export async function sendTextToREPLWindow(text, ns: string, pprint: boolean) {
let wnd = await openReplWindow(util.getREPLSessionType(), true);
export async function sendTextToREPLWindow(sessionType: "clj" | "cljs", text: string, ns: string, pprint: boolean) {
const chan = state.outputChannel(),
wnd = await openReplWindow(sessionType, true);
if (wnd) {
let oldNs = wnd.ns;
if (ns && ns != oldNs)
await wnd.session.eval("(in-ns '" + ns + ")").value;
try {
wnd.evaluate(ns || oldNs, text);
await wnd.replEval(text, oldNs, pprint);
} finally {
if (ns && ns != oldNs) {
await wnd.session.eval("(in-ns '" + oldNs + ")").value;
const inNs = ns ? ns : wnd.ns;
if (ns && ns !== wnd.ns) {
try {
const requireEvaluation = wnd.session.eval(`(require '${ns})`)
await requireEvaluation.value;
const inNSEvaluation = wnd.session.eval(`(in-ns '${ns})`)
await inNSEvaluation.value;
if (inNSEvaluation) {
wnd.setNamespace(inNSEvaluation.ns);
}
} catch (e) {
vscode.window.showErrorMessage(`Error loading namespcace "${ns}" for command snippet: ${e}`, ...["OK"]);
return;
}
}
try {
wnd.evaluate(inNs, text);
await wnd.replEval(text, inNs, pprint);
} catch (e) {
vscode.window.showErrorMessage("Error running command snippet: " + e, ...["OK"]);
}
}
}

Expand Down Expand Up @@ -341,7 +361,7 @@ function evalCurrentFormInREPLWindow(topLevel: boolean, pprint: boolean) {
code = doc.getText(selection);
}
if (code !== "") {
sendTextToREPLWindow(code, util.getNamespace(doc), pprint)
sendTextToREPLWindow(util.getREPLSessionType(), code, util.getNamespace(doc), pprint)
}
}

Expand All @@ -353,6 +373,50 @@ function evalCurrentTopLevelFormInREPLWindowCommand() {
evalCurrentFormInREPLWindow(true, state.config().pprint);
}

export type customREPLCommandSnippet = { name: string, snippet: string, repl: string, ns?: string };

function sendCustomCommandSnippetToREPLCommand() {
let pickCounter = 1,
configErrors: {"name": string, "keys": string[]}[] = [];
const snippets = state.config().customREPLCommandSnippets as customREPLCommandSnippet[],
snippetPicks = _.map(snippets, (c: customREPLCommandSnippet) => {
const undefs = ["name", "snippet", "repl"].filter(k => {
return !c[k];
})
if (undefs.length > 0) {
configErrors.push({"name": c.name, "keys": undefs});
}
return `${pickCounter++}: ${c.name} (${c.repl})`;
}),
snippetsDict = {};
pickCounter = 1;

if (configErrors.length > 0) {
vscode.window.showErrorMessage("Errors found in the `calva.customREPLCommandSnippets` setting. Values missing for: " + JSON.stringify(configErrors), "OK");
return;
}
snippets.forEach((c: customREPLCommandSnippet) => {
snippetsDict[`${pickCounter++}: ${c.name} (${c.repl})`] = c;
});

if (snippets && snippets.length > 0) {
util.quickPickSingle({
values: snippetPicks,
placeHolder: "Choose a command to run at the REPL",
saveAs: "runCustomREPLCommand"
}).then(async (pick) => {
if (pick && snippetsDict[pick] && snippetsDict[pick].snippet) {
const command = snippetsDict[pick].snippet,
ns = snippetsDict[pick].ns ? snippetsDict[pick].ns : "user",
repl = snippetsDict[pick].repl ? snippetsDict[pick].repl : "clj";
sendTextToREPLWindow(repl ? repl : "clj", command, ns, false);
}
});
} else {
vscode.window.showInformationMessage("No snippets configured. Configure snippets in `calva.customREPLCommandSnippets`.", ...["OK"]);
}
}

export function activate(context: vscode.ExtensionContext) {
ctx = context;
context.subscriptions.push(vscode.commands.registerCommand('calva.openCljReplWindow', openClojureReplWindows));
Expand All @@ -361,6 +425,7 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('calva.setREPLNamespace', setREPLNamespaceCommand));
context.subscriptions.push(vscode.commands.registerCommand('calva.evalCurrentFormInREPLWindow', evalCurrentFormInREPLWindowCommand));
context.subscriptions.push(vscode.commands.registerCommand('calva.evalCurrentTopLevelFormInREPLWindow', evalCurrentTopLevelFormInREPLWindowCommand));
context.subscriptions.push(vscode.commands.registerCommand('calva.runCustomREPLCommand', sendCustomCommandSnippetToREPLCommand));
}

export function clearHistory() {
Expand Down
4 changes: 3 additions & 1 deletion src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ReplConnectSequence } from './nrepl/connectSequence';
import * as util from './utilities';
import * as path from 'path';
import * as fs from 'fs';
import { customREPLCommandSnippet } from './repl-window';

let extensionContext: vscode.ExtensionContext;
export function setExtensionContext(context: vscode.ExtensionContext) {
Expand Down Expand Up @@ -106,7 +107,8 @@ function config() {
myLeinProfiles: configOptions.get("myLeinProfiles", []).map(_trimAliasName) as string[],
myCljAliases: configOptions.get("myCljAliases", []).map(_trimAliasName) as string[],
pprint: configOptions.get("prettyPrint") as boolean,
asyncOutputDestination: configOptions.get("sendAsyncOutputTo") as string
asyncOutputDestination: configOptions.get("sendAsyncOutputTo") as string,
customREPLCommandSnippets: configOptions.get("customREPLCommandSnippets", []) as customREPLCommandSnippet[]
};
}

Expand Down

0 comments on commit 38d6286

Please sign in to comment.