Skip to content

Commit

Permalink
Merge pull request #1902 from BetterThanTomorrow/1901-eval-selection-…
Browse files Browse the repository at this point in the history
…adding-brackets

Make eval-to-cursor selection aware
  • Loading branch information
PEZ authored Oct 18, 2022
2 parents d4c4af9 + 3cc247f commit ba172de
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 24 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
61 changes: 49 additions & 12 deletions docs/site/evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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 <kbd>ctrl+alt+enter</kbd>.

### 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** (<kbd>shift+ctrl+up</kbd>). 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** (<kbd>shift+ctrl+up</kbd>) 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): <kbd>ctrl+alt+enter</kbd>.

### 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.

Expand Down Expand Up @@ -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`.

Expand Down
21 changes: 16 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
21 changes: 15 additions & 6 deletions src/util/cursor-get-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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);
}
7 changes: 7 additions & 0 deletions src/util/get-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ba172de

Please sign in to comment.