Skip to content

Commit

Permalink
Merge pull request #7 from rcoopr/multiline-selections
Browse files Browse the repository at this point in the history
Multi-line cursor empty selections
  • Loading branch information
mguellsegarra authored May 1, 2024
2 parents baba2fd + cbf2e0e commit 66e2db6
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 7 deletions.
39 changes: 33 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,39 @@ export function deactivate() {
vscode.commands.executeCommand("setContext", "highlightOnCopy.init", false);
}

function getSelections(
export function getSelections(
editor: vscode.TextEditor
): readonly vscode.Selection[] | vscode.Range[] {
const selections = editor.selections;
if (selections.length === 1 && selections[0].isEmpty) {
return [editor.document.lineAt(selections[0].anchor).range];
}
return selections;
let lastSelectionLine = -1;

// We need to sort the selections for the case where an additional cursor is inserted
// before the 'active' cursor of another selection on the same line.
const sortedSelections = [...editor.selections].sort((a, b) => {
if (a.active.line === b.active.line) {
return a.active.character - b.active.character;
}
return a.active.line - b.active.line;
});

const expandedSelections = sortedSelections.map((selection) => {
// With multiple cursors on a single line, any empty selection is ignored (after the first selection)
if (selection.isEmpty && lastSelectionLine === selection.active.line) {
// We don't worry about setting lastSelectionLine here as this branch is only for the matching line
return null;
}

lastSelectionLine = selection.active.line;

// Return the range of the line if the selection is empty (default copy behaviour)
if (selection.isEmpty) {
return editor.document.lineAt(selection.active).range;
}

// For non-empty selections, return the selection
return selection;
});

return expandedSelections.filter(
(selection) => selection !== null
) as vscode.Range[];
}
52 changes: 51 additions & 1 deletion src/test/extension.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as assert from "assert";
import * as vscode from "vscode";
import { getSelections } from "../extension";

suite("Extension Test Suite", () => {
vscode.window.showInformationMessage("Start all tests.");
Expand Down Expand Up @@ -45,7 +46,10 @@ suite("Extension Test Suite", () => {

// Select the full text in the editor
const firstLine = editor.document.lineAt(0);
editor.selection = new vscode.Selection(firstLine.range.start, firstLine.range.end);
editor.selection = new vscode.Selection(
firstLine.range.start,
firstLine.range.end
);

// Execute the command
await vscode.commands.executeCommand("highlightOnCopy.run");
Expand Down Expand Up @@ -92,4 +96,50 @@ suite("Extension Test Suite", () => {
"The clipboard content should match the combined text of the selected lines."
);
});

test("Expand selections correctly for multiline selections", async () => {
// Create a new text document with multiple lines
const document = await vscode.workspace.openTextDocument({
content: "First line to copy\nSecond line to copy\nThird line to copy",
});
const editor = await vscode.window.showTextDocument(document);

// Create two Selection objects, one for each line we want to select with the cursor
// Selection looks like this, with 'First' selected: |First| |line to copy
editor.selections = [
new vscode.Selection(
new vscode.Position(0, 0),
new vscode.Position(0, 5)
),
new vscode.Selection(
new vscode.Position(0, 6),
new vscode.Position(0, 6)
),
];

// 2nd selection should be ignored as it is an empty selection preceeded by another selection on the same line
assert.strictEqual(getSelections(editor).length, 1);

// Selection looks like this, with 'line' selected: |First |line| to copy
editor.selections = [
new vscode.Selection(
new vscode.Position(0, 0),
new vscode.Position(0, 0)
),
new vscode.Selection(
new vscode.Position(0, 6),
new vscode.Position(0, 10)
),
];

const expandedSelectionsLeadingEmpty = getSelections(editor);
// 2nd selection should be ignored as it is an empty selection preceeded by another selection on the same line
assert.strictEqual(expandedSelectionsLeadingEmpty.length, 2);
assert.strictEqual(
expandedSelectionsLeadingEmpty[0].contains(
editor.document.lineAt(0).range
),
true
);
});
});

0 comments on commit 66e2db6

Please sign in to comment.