Skip to content

Commit

Permalink
Fixes BetterThanTomorrow#1024, paredit kill right
Browse files Browse the repository at this point in the history
  • Loading branch information
xfthhxk committed Sep 7, 2021
1 parent 014a6e9 commit b0d91e1
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changes to Calva.

## [Unreleased]
- Fix [The schema for the setting `calva.highlight.bracketColors` is broken](https://github.com/BetterThanTomorrow/calva/issues/1290)
- [Add Paredit kill right functionality](https://github.com/BetterThanTomorrow/calva/issues/1024)

## [2.0.211] - 2021-09-01
- [Add setting for letting Paredit Kill commands copy the deleted code to the clipboard](https://github.com/BetterThanTomorrow/calva/issues/1283)
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,11 @@
"key": "ctrl+shift+c",
"when": "calva:keybindingsEnabled && editorLangId == clojure && editorTextFocus && paredit:keyMap =~ /original|strict/"
},
{
"command": "paredit.killHybridSexpForward",
"key": "ctrl+k",
"when": "calva:keybindingsEnabled && editorLangId == clojure && editorTextFocus && paredit:keyMap =~ /original|strict/"
},
{
"command": "paredit.killSexpForward",
"key": "ctrl+shift+delete",
Expand Down
42 changes: 42 additions & 0 deletions src/cursor-doc/paredit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,48 @@ export function backwardListRange(doc: EditableDocument, start: number = doc.sel
return [cursor.offsetStart, start];
}


/**
* Modeled after paredit killRight or smartparens sp-kill-hybrid-sexp.
* Aims to find the end of the current form (list|string|vector etc) the
* first newline and delete up to the closing delimiter or newline.
* If deleting the new line would yield an invalid form then delete
* up to the end of the form that contains the newline.
* @param doc
* @param offset
* @param goPastWhitespace
* @returns [number, number]
*/
export function forwardHybridSexpRange(doc: EditableDocument, offset = Math.max(doc.selection.anchor, doc.selection.active), goPastWhitespace = false): [number, number] {
const cursor = doc.getTokenCursor(offset);
if (cursor.getToken().type === 'open') {
return forwardSexpRange(doc);
}
cursor.forwardList(); // move to the end of the current list|string|vector etc
const text = doc.model.getText(offset, cursor.offsetStart);
// should this be a regex that checks for \r, \n and/or \r\n?
const newLineIndex = text.indexOf("\n");
let end = cursor.offsetStart;
if (newLineIndex > 0) {
// deleting to the newline could leave an invalid form
// so go forwardList from the newline and compare if
// the cursor offsets are different
const cursor2 = doc.getTokenCursor(offset + newLineIndex);
cursor2.forwardList();
// no change offset? set end to be location of the newline
if (cursor.offsetStart === cursor2.offsetStart) {
end = offset + newLineIndex;
} else {
// offsets are different, so go to the end of cursor2.offsetEnd
// to include the closing delimiter
end = cursor2.offsetEnd;
}
}
return [offset, end];
}



export function rangeToForwardUpList(doc: EditableDocument, offset: number = Math.max(doc.selection.anchor, doc.selection.active), goPastWhitespace = false): [number, number] {
const cursor = doc.getTokenCursor(offset);
cursor.forwardList();
Expand Down
82 changes: 82 additions & 0 deletions src/extension-test/unit/cursor-doc/paredit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,88 @@ describe('paredit', () => {
});
})

describe('forwardHybridSexpRange', () => {
it('Finds end of string', () => {
const a = docFromTextNotation('"This |needs to find the end of the string."');
const b = docFromTextNotation('"This |needs to find the end of the string.|"');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds newline in multi line string', () => {
const a = docFromTextNotation('"This |needs to find the end\n of the string."');
const b = docFromTextNotation('"This |needs to find the end|\n of the string."');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds end of comment', () => {
const a = docFromTextNotation('(a |;; foo\n e)');
const b = docFromTextNotation('(a |;; foo|\n e)');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Maintains balanced delimiters', () => {
const a = docFromTextNotation('(a| b (c\n d) e)');
const b = docFromTextNotation('(a| b (c\n d)| e)');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds end of vectors', () => {
const a = docFromTextNotation('[a [b |c d e f] g h]');
const b = docFromTextNotation('[a [b |c d e f|] g h]');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds end of maps', () => {
const a = docFromTextNotation('{:a 1 |:b 2 :c 3}');
const b = docFromTextNotation('{:a 1 |:b 2 :c 3|}');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds end of line in multiline maps', () => {
const a = docFromTextNotation('{:a 1 |:b 2\n:c 3}');
const b = docFromTextNotation('{:a 1 |:b 2|:c 3}');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds end of expr in multiline maps', () => {
const a = docFromTextNotation('{:a 1 |:b (+\n 0\n 2\n) :c 3}');
const b = docFromTextNotation('{:a 1 |:b (+\n 0\n 2\n)| :c 3}');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds end of line in bindings', () => {
const a = docFromTextNotation('(let [|a (+ 1 2)\n b (+ 2 3)] (+ a b))');
const b = docFromTextNotation('(let [|a (+ 1 2)|\n b (+ 2 3)] (+ a b))');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});

it('Finds end of expr in multiline bindings', () => {
const a = docFromTextNotation('(let [|a (+\n 1 \n 2)\n b (+ 2 3)] (+ a b))');
const b = docFromTextNotation('(let [|a (+\n 1 \n 2)|\n b (+ 2 3)] (+ a b))');
const expected = textAndSelection(b)[1];
const actual = paredit.forwardHybridSexpRange(a);
expect(actual).toEqual(expected);
});
})

describe('moveToRangeRight', () => {
it('Places cursor at the right end of the selection', () => {
const a = docFromTextNotation('(def |>|foo|>| [vec])');
Expand Down
10 changes: 10 additions & 0 deletions src/paredit/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ const pareditCommands: PareditCommand[] = [
command: 'paredit.convolute',
handler: paredit.convolute
},
{
command: 'paredit.killHybridSexpForward',
handler: (doc: EditableDocument) => {
const range = paredit.forwardHybridSexpRange(doc);
if (shouldKillAlsoCutToClipboard()) {
copyRangeToClipboard(doc, range);
}
paredit.killRange(doc, range);
}
},
{
command: 'paredit.killSexpForward',
handler: (doc: EditableDocument) => {
Expand Down

0 comments on commit b0d91e1

Please sign in to comment.