diff --git a/CHANGELOG.md b/CHANGELOG.md
index 866d65f4a..209524127 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ Changes to Calva.
## [Unreleased]
+- [A more flexible evaluate-to-cursor command](https://github.com/BetterThanTomorrow/calva/
+issues/1901)
+
## [2.0.307] - 2022-10-11
- [Support user level `~/.config/calva/config.edn`](https://github.com/BetterThanTomorrow/calva/issues/1887)
diff --git a/docs/site/evaluation.md b/docs/site/evaluation.md
index e72c6b324..74d45f7e9 100644
--- a/docs/site/evaluation.md
+++ b/docs/site/evaluation.md
@@ -66,9 +66,22 @@ An ”exception” is introduced by the `comment` form. It will create a new top
At the top level the selection of which form is the current top level form follows the same rules as those for [the current form](#current-form).
+### Evaluate Enclosing Form
+
+The default keyboard shortcut for evaluating the current enclosing form (the list the cursor is in) is `ctrl+shift+enter`.
+
+```clojure
+(let [foo :bar]
+ (when false (str| foo))) ; => ":bar"
+```
+
### Evaluate to Cursor
-There is also a command for evaluating the text from the start of the current list to where the cursor is. Convenient for checking intermediate results in thread or `doto`, or similar pipelines. The cursor is right behind `:d` in this form:
+There are several commands for evaluating a piece of code, closing brackets. It's good, especially in threads, but can also come in handy in other situations, for instance when you want to evaluate something that depends on bindings, such as in a `let` form.
+
+### Evaluate From Start of List to Cursor, Closing Brackets
+
+This command evaluates the text from the start of the current enclosing list to where the cursor is, and it adds the missing closing bracket for you. Convenient for checking intermediate results in thread or `doto`, or similar pipelines. The cursor is right behind `:d` in this form:
```clojure
(->> [1 1 2 3 5 8 13 21]
@@ -79,11 +92,40 @@ There is also a command for evaluating the text from the start of the current li
(Math/abs))
```
-The default shortcut for this command is `ctrl+alt+enter`.
+The default shortcut for this command is ctrl+alt+enter.
+
+### Evaluate Selection, Closing Brackets
+
+This is the most versatile of the ”evaluation, closing brackets” commands. It will do what it says. 😄 It's extra handy in combination with the command **Paredit: Select Backward Up Sexp/Form** (shift+ctrl+up). Consider this contrieved form (buggy code, because it was supposed to result in `42`, not `-42`):
+
+```clojure
+(defn fortytwo-from-thirty
+ []
+ (let [thirty 30]
+ (-> thirty
+ inc ;1
+ (send-off)
+ (+ 1 2 3)
+ (->>
+ (+ 2 2) ;2
+ (+))
+ list
+ (->>
+ (into [1])
+ (reduce + 1))
+ (- 1) ;3
+ (* -1))))
+```
+
+At `;1`, you can do **backward up sexp** (shift+ctrl+up) twice to select up to the `(let ..)`, then issue **Evaluate Selection, Closing Brackets**. It has the same default keybinding as the command for [evaluating the current list up to the cursor](#evaluate-from-start-of-list-to-cursor-closing-brackets): ctrl+alt+enter.
-### Evaluate Top Level Form to Cursor
+At `;2` you need select backwards up three times.
-This command has a default shortcut keybinding of `shift+alt+enter`. It will create a form from the start of the current top level form, up to the cursor, then fold the form, closing all brackets, and this will then be evaluated. Good for examining code blocks up to a certain point.
+`;3` is included because it is close to the bug. (Which was introduced when the thread-last, `->>` was added to make this example.) Please practice the **Evaluate Selection, Closing Brackets** command to fix the bug.
+
+### Evaluate From Start of Top Level Form to Cursor, Closing Brackets
+
+This command has a default shortcut keybinding of `shift+alt+enter`. It will create a form from the start of the current top level form, up to the cursor, close all brackets, and this will then be evaluated. Good for examining code blocks up to a certain point. Often comes in handy in Rich comments (`(comment ...)`).
Take this example and paste it in a file loaded into the REPL, then place the cursor in front of each line comment and try the command.
@@ -116,16 +158,11 @@ Take this example and paste it in a file loaded into the REPL, then place the cu
))))
```
-### Evaluate Enclosing Form
+### Evaluate From Start of File to Cursor, Closing Brackets
-The default keyboard shortcut for evaluating the current enclosing form (the list the cursor is in) is `ctrl+shift+enter`.
-
-```clojure
-(let [foo :bar]
- (when false (str| foo))) ; => ":bar"
-```
+Yup, that command also exists. 😄
-### Copying the inline results
+## Copying the inline results
There is a command called **Copy last evaluation results**, `ctrl+alt+c ctrl+c`.
diff --git a/package.json b/package.json
index 0a0e85a7c..f51a19d12 100644
--- a/package.json
+++ b/package.json
@@ -1133,19 +1133,25 @@
},
{
"command": "calva.evaluateToCursor",
- "title": "Evaluate From Start of List to Cursor",
- "enablement": "calva:connected",
+ "title": "Evaluate From Start of List to Cursor, Closing Brackets",
+ "enablement": "calva:connected && !editorHasSelection",
+ "category": "Calva"
+ },
+ {
+ "command": "calva.evaluateSelectionToSelectionEnd",
+ "title": "Evaluate Selection, Closing Brackets",
+ "enablement": "calva:connected && editorHasSelection",
"category": "Calva"
},
{
"command": "calva.evaluateTopLevelFormToCursor",
- "title": "Evaluate From Start of Top Level Form to Cursor",
+ "title": "Evaluate From Start of Top Level Form to Cursor, Closing Brackets",
"enablement": "calva:connected",
"category": "Calva"
},
{
"command": "calva.evaluateStartOfFileToCursor",
- "title": "Evaluate From Start of File to Cursor",
+ "title": "Evaluate From Start of File to Cursor, Closing Brackets",
"enablement": "calva:connected",
"category": "Calva"
},
@@ -1889,7 +1895,12 @@
{
"command": "calva.evaluateToCursor",
"key": "ctrl+alt+enter",
- "when": "calva:keybindingsEnabled && editorLangId == clojure && editorTextFocus"
+ "when": "calva:keybindingsEnabled && editorLangId == clojure && editorTextFocus && !editorHasSelection"
+ },
+ {
+ "command": "calva.evaluateSelectionToSelectionEnd",
+ "key": "ctrl+alt+enter",
+ "when": "calva:keybindingsEnabled && editorLangId == clojure && editorTextFocus && editorHasSelection"
},
{
"command": "calva.evaluateTopLevelFormToCursor",
diff --git a/src/evaluate.ts b/src/evaluate.ts
index a3dbd5c89..2821fcb51 100644
--- a/src/evaluate.ts
+++ b/src/evaluate.ts
@@ -377,7 +377,9 @@ function evaluateUsingTextAndSelectionGetter(
function evaluateToCursor(document = {}, options = {}) {
evaluateUsingTextAndSelectionGetter(
- getText.currentEnclosingFormToCursor,
+ vscode.window.activeTextEditor.selection.isEmpty
+ ? getText.currentEnclosingFormToCursor
+ : getText.selectionAddingBrackets,
(code) => `${code}`,
document,
options
diff --git a/src/extension.ts b/src/extension.ts
index dfd1fada0..294e02083 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -267,6 +267,9 @@ async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('calva.evaluateToCursor', eval.evaluateToCursor)
);
+ context.subscriptions.push(
+ vscode.commands.registerCommand('calva.evaluateSelectionToSelectionEnd', eval.evaluateToCursor)
+ );
context.subscriptions.push(
vscode.commands.registerCommand(
'calva.evaluateTopLevelFormToCursor',
diff --git a/src/util/cursor-get-text.ts b/src/util/cursor-get-text.ts
index a77f3b517..6d0400bac 100644
--- a/src/util/cursor-get-text.ts
+++ b/src/util/cursor-get-text.ts
@@ -39,14 +39,15 @@ export function currentTopLevelForm(doc: EditableDocument): RangeAndText {
return defunRange ? [defunRange, doc.model.getText(...defunRange)] : [undefined, ''];
}
-function rangeOrStartOfFileToCursor(
+function rangeToCursor(
doc: EditableDocument,
foldRange: [number, number],
- startFrom: number
+ startFrom: number,
+ active: number
): RangeAndText {
if (foldRange) {
const closeBrackets: string[] = [];
- const bracketCursor = doc.getTokenCursor(doc.selection.active);
+ const bracketCursor = doc.getTokenCursor(active);
bracketCursor.backwardWhitespace(true);
const rangeEnd = bracketCursor.offsetStart;
while (
@@ -65,17 +66,25 @@ function rangeOrStartOfFileToCursor(
export function currentEnclosingFormToCursor(doc: EditableDocument): RangeAndText {
const cursor = doc.getTokenCursor(doc.selection.active);
const enclosingRange = cursor.rangeForList(1);
- return rangeOrStartOfFileToCursor(doc, enclosingRange, enclosingRange[0]);
+ return rangeToCursor(doc, enclosingRange, enclosingRange[0], doc.selection.active);
}
export function currentTopLevelFormToCursor(doc: EditableDocument): RangeAndText {
const cursor = doc.getTokenCursor(doc.selection.active);
const defunRange = cursor.rangeForDefun(doc.selection.active);
- return rangeOrStartOfFileToCursor(doc, defunRange, defunRange[0]);
+ return rangeToCursor(doc, defunRange, defunRange[0], doc.selection.active);
}
export function startOfFileToCursor(doc: EditableDocument): RangeAndText {
const cursor = doc.getTokenCursor(doc.selection.active);
const defunRange = cursor.rangeForDefun(doc.selection.active, false);
- return rangeOrStartOfFileToCursor(doc, defunRange, 0);
+ return rangeToCursor(doc, defunRange, 0, doc.selection.active);
+}
+
+export function selectionAddingBrackets(doc: EditableDocument): RangeAndText {
+ const [left, right] = [doc.selection.anchor, doc.selection.active].sort();
+ const cursor = doc.getTokenCursor(left);
+ cursor.forwardSexp(true, true, true);
+ const rangeEnd = cursor.offsetStart;
+ return rangeToCursor(doc, [left, rangeEnd], left, right);
}
diff --git a/src/util/get-text.ts b/src/util/get-text.ts
index 751fe6838..864dca64c 100644
--- a/src/util/get-text.ts
+++ b/src/util/get-text.ts
@@ -105,6 +105,13 @@ export function currentTopLevelFormToCursor(
return selectionAndText(doc, cursorTextGetter.currentTopLevelFormToCursor, pos);
}
+export function selectionAddingBrackets(
+ doc: vscode.TextDocument,
+ pos: vscode.Position
+): SelectionAndText {
+ return selectionAndText(doc, cursorTextGetter.selectionAddingBrackets, pos);
+}
+
export function startOFileToCursor(
doc: vscode.TextDocument,
pos: vscode.Position