From 6fcad6b3573d497db9e72755ab4e3422765435d9 Mon Sep 17 00:00:00 2001 From: Ross Cooper <37559715+Froskk@users.noreply.github.com> Date: Tue, 30 Apr 2024 19:58:08 +0100 Subject: [PATCH 1/2] fix: correct highlighting with empty selections in multiline selections --- src/extension.ts | 28 +++++++++++++++----- src/test/extension.test.ts | 52 +++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 9e9ef45..e7c970c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -45,12 +45,28 @@ 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; + const expandedSelections = editor.selections.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) { + 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[]; } diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts index ae606af..d353fe1 100644 --- a/src/test/extension.test.ts +++ b/src/test/extension.test.ts @@ -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."); @@ -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"); @@ -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 + ); + }); }); From cbf2e0eafa133abcb06733097e6879d16182bca3 Mon Sep 17 00:00:00 2001 From: Ross Cooper <37559715+Froskk@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:41:00 +0100 Subject: [PATCH 2/2] fix: edge cases with multiple selections on same line --- src/extension.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index e7c970c..5e239e2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -49,9 +49,20 @@ export function getSelections( editor: vscode.TextEditor ): readonly vscode.Selection[] | vscode.Range[] { let lastSelectionLine = -1; - const expandedSelections = editor.selections.map((selection) => { + + // 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; }