Repository: microsoft/vscode (TypeScript)
Date: 2021-10-14 14:38:25+00:00
Task type: Bug Fix

Issue:
When cutting multiple lines (ctrl x), the last line will not be erased
<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- 🕮 Read our guide about submitting issues: https://github.com/microsoft/vscode/wiki/Submitting-Bugs-and-Suggestions -->
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
<!-- 🧪 Test using the latest Insiders build to see if your issue has already been fixed: https://code.visualstudio.com/insiders/ -->
<!-- 💡 Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. -->
<!-- 🔧 Launch with `code --disable-extensions` to check. -->
Does this issue occur when all extensions are disabled?: Yes/No
Yes
<!-- 🪓 If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. -->
<!-- 📣 Issues caused by an extension need to be reported directly to the extension publisher. The 'Help > Report Issue' dialog can assist with this. -->
- VS Code Version:

```txt
版本: 1.58.1
提交: 2aeda6b18e13c4f4f9edf6667158a6b8d408874b
日期: 2021-07-13T06:22:33.013Z
Electron: 12.0.13
Chrome: 89.0.4389.128
Node.js: 14.16.0
V8: 8.9.255.25-electron.0
OS: Darwin x64 20.5.0
```

Steps to Reproduce:

1. Create a new page (cmd N), and copy the following content

```txt
system01.dbf
sysaux01.dbf
undotbs01.dbf
users01.dbf
orcl/users01.dbf

pdbseed/system01.dbf
pdbseed/sysaux01.dbf
pdbseed/undotbs01.dbf
orcl/system01.dbf
orcl/sysaux01.dbf
orcl/undotbs01.dbf
orcl/users01.dbf
```

2. Use `cmd D` to select the first `orcl`, then continue `cmd D` to select the rest
3. Then use the left and right keys to move the cursor to unselect the character
4. Then use `cmd x` to cut multiple lines. Will find that the last line does not disappear, like this: 

```txt
system01.dbf
sysaux01.dbf
undotbs01.dbf
users01.dbf

pdbseed/system01.dbf
pdbseed/sysaux01.dbf
pdbseed/undotbs01.dbf
orcl/undotbs01.dbf
```

`But if the last line has extra blank lines, this will not happen`

When cutting multiple lines (ctrl x), the last line will not be erased
<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- 🕮 Read our guide about submitting issues: https://github.com/microsoft/vscode/wiki/Submitting-Bugs-and-Suggestions -->
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
<!-- 🧪 Test using the latest Insiders build to see if your issue has already been fixed: https://code.visualstudio.com/insiders/ -->
<!-- 💡 Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. -->
<!-- 🔧 Launch with `code --disable-extensions` to check. -->
Does this issue occur when all extensions are disabled?: Yes/No
Yes
<!-- 🪓 If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. -->
<!-- 📣 Issues caused by an extension need to be reported directly to the extension publisher. The 'Help > Report Issue' dialog can assist with this. -->
- VS Code Version:

```txt
版本: 1.58.1
提交: 2aeda6b18e13c4f4f9edf6667158a6b8d408874b
日期: 2021-07-13T06:22:33.013Z
Electron: 12.0.13
Chrome: 89.0.4389.128
Node.js: 14.16.0
V8: 8.9.255.25-electron.0
OS: Darwin x64 20.5.0
```

Steps to Reproduce:

1. Create a new page (cmd N), and copy the following content

```txt
system01.dbf
sysaux01.dbf
undotbs01.dbf
users01.dbf
orcl/users01.dbf

pdbseed/system01.dbf
pdbseed/sysaux01.dbf
pdbseed/undotbs01.dbf
orcl/system01.dbf
orcl/sysaux01.dbf
orcl/undotbs01.dbf
orcl/users01.dbf
```

2. Use `cmd D` to select the first `orcl`, then continue `cmd D` to select the rest
3. Then use the left and right keys to move the cursor to unselect the character
4. Then use `cmd x` to cut multiple lines. Will find that the last line does not disappear, like this: 

```txt
system01.dbf
sysaux01.dbf
undotbs01.dbf
users01.dbf

pdbseed/system01.dbf
pdbseed/sysaux01.dbf
pdbseed/undotbs01.dbf
orcl/undotbs01.dbf
```

`But if the last line has extra blank lines, this will not happen`



Issue discussion:
I can reproduce:

![recording](https://user-images.githubusercontent.com/2931520/137326670-87beab6e-d845-4b2a-95b0-49b759908593.gif)

Interestingly the last line made it to the clipboard.
I can reproduce:

![recording](https://user-images.githubusercontent.com/2931520/137326670-87beab6e-d845-4b2a-95b0-49b759908593.gif)

Interestingly the last line made it to the clipboard.

Solution patch:
diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts
--- a/src/vs/editor/common/controller/cursorDeleteOperations.ts
+++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts
@@ -212,6 +212,8 @@ export class DeleteOperations {
 
 	public static cut(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): EditOperationResult {
 		let commands: Array<ICommand | null> = [];
+		let lastCutRange: Range | null = null;
+		selections.sort((a, b) => Position.compare(a.getStartPosition(), b.getEndPosition()));
 		for (let i = 0, len = selections.length; i < len; i++) {
 			const selection = selections[i];
 
@@ -232,8 +234,8 @@ export class DeleteOperations {
 						startColumn = 1;
 						endLineNumber = position.lineNumber + 1;
 						endColumn = 1;
-					} else if (position.lineNumber > 1) {
-						// Cutting the last line & there are more than 1 lines in the model
+					} else if (position.lineNumber > 1 && lastCutRange?.endLineNumber !== position.lineNumber) {
+						// Cutting the last line & there are more than 1 lines in the model & a previous cut operation does not touch the current cut operation
 						startLineNumber = position.lineNumber - 1;
 						startColumn = model.getLineMaxColumn(position.lineNumber - 1);
 						endLineNumber = position.lineNumber;
@@ -252,6 +254,7 @@ export class DeleteOperations {
 						endLineNumber,
 						endColumn
 					);
+					lastCutRange = deleteSelection;
 
 					if (!deleteSelection.isEmpty()) {
 						commands[i] = new ReplaceCommand(deleteSelection, '');


Test patch:
diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts
--- a/src/vs/editor/test/browser/controller/cursor.test.ts
+++ b/src/vs/editor/test/browser/controller/cursor.test.ts
@@ -1628,6 +1628,26 @@ suite('Editor Controller - Regression tests', () => {
 		});
 	});
 
+	test('issue #128602: When cutting multiple lines (ctrl x), the last line will not be erased', () => {
+		withTestCodeEditor([
+			'a1',
+			'a2',
+			'a3'
+		], {}, (editor, viewModel) => {
+			const model = editor.getModel()!;
+
+			viewModel.setSelections('test', [
+				new Selection(1, 1, 1, 1),
+				new Selection(2, 1, 2, 1),
+				new Selection(3, 1, 3, 1),
+			]);
+
+			viewModel.cut('keyboard');
+			assert.strictEqual(model.getLineCount(), 1);
+			assert.strictEqual(model.getLineContent(1), '');
+		});
+	});
+
 	test('Bug #11476: Double bracket surrounding + undo is broken', () => {
 		let mode = new SurroundingMode();
 		usingCursor({