Skip to content

Commit

Permalink
Added join lines action (#1901)
Browse files Browse the repository at this point in the history
`"join air"`
`"join block air"`

Fixes #50

## Checklist

- [x] I have added
[tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)
- [x] I have updated the
[docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and
[cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)
- [x] I have not broken the cheatsheet

---------

Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
  • Loading branch information
AndreasArvidsson and pokey authored Dec 6, 2023
1 parent 70cbd86 commit 740e3ef
Show file tree
Hide file tree
Showing 15 changed files with 362 additions and 0 deletions.
6 changes: 6 additions & 0 deletions changelog/2023-09-addedJoinLinesAction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
tags: [enhancement]
pullRequest: 1901
---

- Added `join` action. This action will join multiple lines together. eg `"join air"` or `"join three lines air"`.
1 change: 1 addition & 0 deletions cursorless-talon/src/spoken_forms.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"hover": "showHover",
"indent": "indentLine",
"inspect": "showDebugHover",
"join": "joinLines",
"post": "setSelectionAfter",
"pour": "editNewLineAfter",
"pre": "setSelectionBefore",
Expand Down
11 changes: 11 additions & 0 deletions docs/user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,17 @@ eg:

Extracts the function call containing the decorated 'a' into its own variable.

### Join

Join multiple lines together.

- `"join <TARGET>"`

eg:

- `join air`: Join the line with the token containing the letter 'a' with its next line.
- `join block air`: Joines all lines in the paragraph with the token containing the letter 'a' together into a single line.

## Paired delimiters

| Default spoken form | Delimiter name | Symbol inserted before target | Symbol inserted after target | Is wrapper? | Is selectable? |
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/types/command/ActionDescriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const simpleActionNames = [
"insertEmptyLineAfter",
"insertEmptyLineBefore",
"insertEmptyLinesAround",
"joinLines",
"outdentLine",
"randomizeTargets",
"remove",
Expand Down
2 changes: 2 additions & 0 deletions packages/cursorless-engine/src/actions/Actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
InsertEmptyLinesAround,
} from "./InsertEmptyLines";
import InsertSnippet from "./InsertSnippet";
import JoinLines from "./JoinLines";
import { PasteFromClipboard } from "./PasteFromClipboard";
import ShowParseTree from "./ShowParseTree";
import Remove from "./Remove";
Expand Down Expand Up @@ -111,6 +112,7 @@ export class Actions implements ActionRecord {
this,
this.modifierStageFactory,
);
joinLines = new JoinLines(this.rangeUpdater);
moveToTarget = new Move(this.rangeUpdater);
outdentLine = new OutdentLine(this.rangeUpdater);
pasteFromClipboard = new PasteFromClipboard(this.rangeUpdater, this);
Expand Down
69 changes: 69 additions & 0 deletions packages/cursorless-engine/src/actions/JoinLines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { FlashStyle, Range, TextEditor } from "@cursorless/common";
import { flatten, zip } from "lodash";
import type { RangeUpdater } from "../core/updateSelections/RangeUpdater";
import { performEditsAndUpdateRanges } from "../core/updateSelections/updateSelections";
import { ide } from "../singletons/ide.singleton";
import { Edit } from "../typings/Types";
import { Target } from "../typings/target.types";
import { flashTargets, runOnTargetsForEachEditor } from "../util/targetUtils";
import type { ActionReturnValue } from "./actions.types";
import { range as iterRange, map, pairwise } from "itertools";

export default class JoinLines {
constructor(private rangeUpdater: RangeUpdater) {
this.run = this.run.bind(this);
}

async run(targets: Target[]): Promise<ActionReturnValue> {
await flashTargets(ide(), targets, FlashStyle.pendingModification0);

const thatSelections = flatten(
await runOnTargetsForEachEditor(targets, async (editor, targets) => {
const contentRanges = targets.map(({ contentRange }) => contentRange);
const edits = getEdits(editor, contentRanges);

const [updatedRanges] = await performEditsAndUpdateRanges(
this.rangeUpdater,
ide().getEditableTextEditor(editor),
edits,
[contentRanges],
);

return zip(targets, updatedRanges).map(([target, range]) => ({
editor: target!.editor,
selection: range!.toSelection(target!.isReversed),
}));
}),
);

return { thatSelections };
}
}

function getEdits(editor: TextEditor, contentRanges: Range[]): Edit[] {
const { document } = editor;
const edits: Edit[] = [];

for (const range of contentRanges) {
const startLine = range.start.line;
const endLine = range.isSingleLine ? startLine + 1 : range.end.line;

const lineIter = map(iterRange(startLine, endLine + 1), (i) =>
document.lineAt(i),
);
for (const [line1, line2] of pairwise(lineIter)) {
edits.push({
range: new Range(
line1.range.end.line,
line1.lastNonWhitespaceCharacterIndex,
line2.range.start.line,
line2.firstNonWhitespaceCharacterIndex,
),
text: line2.isEmptyOrWhitespace ? "" : " ",
isReplace: true,
});
}
}

return edits;
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const actions = {
rewrapWithPairedDelimiter: "repack",
insertSnippet: "snippet",
pasteFromClipboard: "paste",
joinLines: "join",

["private.showParseTree"]: "parse tree",
["experimental.setInstanceReference"]: "from",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
languageId: plaintext
command:
version: 6
spokenForm: join air
action:
name: joinLines
target:
type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: a}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
aaa
bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks:
default.a:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
finalState:
documentContents: aaa bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
isReversed: false
hasExplicitRange: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
languageId: plaintext
command:
version: 6
spokenForm: join air
action:
name: joinLines
target:
type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: a}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
aaa
bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks:
default.a:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
finalState:
documentContents: aaa bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
isReversed: false
hasExplicitRange: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
languageId: plaintext
command:
version: 6
spokenForm: join air
action:
name: joinLines
target:
type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: a}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
aaa
bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks:
default.a:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
finalState:
documentContents: aaa bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
isReversed: false
hasExplicitRange: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
languageId: plaintext
command:
version: 6
spokenForm: join air
action:
name: joinLines
target:
type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: a}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
aaa
bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks:
default.a:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
finalState:
documentContents: aaa bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
isReversed: false
hasExplicitRange: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
languageId: plaintext
command:
version: 6
spokenForm: join block
action:
name: joinLines
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: paragraph}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
aaa
bbb
ccc
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: aaa bbb ccc
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 11}
isReversed: false
hasExplicitRange: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
languageId: plaintext
command:
version: 6
spokenForm: join file
action:
name: joinLines
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: document}
usePrePhraseSnapshot: true
initialState:
documentContents: |+
aaa
bbb
selections:
- anchor: {line: 5, character: 0}
active: {line: 5, character: 0}
marks: {}
finalState:
documentContents: aaa bbb
selections:
- anchor: {line: 0, character: 7}
active: {line: 0, character: 7}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 7}
isReversed: false
hasExplicitRange: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
languageId: plaintext
command:
version: 6
spokenForm: join this
action:
name: joinLines
target:
type: primitive
mark: {type: cursor}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
aaa
bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: |-
aaa
bbb
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 3}
isReversed: false
hasExplicitRange: true
Loading

0 comments on commit 740e3ef

Please sign in to comment.