Skip to content

Commit

Permalink
Merge pull request #119480 from codeclown/delete-duplicates
Browse files Browse the repository at this point in the history
Feature: New command - Delete Duplicate Lines
  • Loading branch information
alexdima authored Oct 27, 2021
2 parents e040003 + fee6bbc commit 9264622
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 1 deletion.
69 changes: 69 additions & 0 deletions src/vs/editor/contrib/linesOperations/linesOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,74 @@ export class SortLinesDescendingAction extends AbstractSortLinesAction {
}
}

export class DeleteDuplicateLinesAction extends EditorAction {
constructor() {
super({
id: 'editor.action.removeDuplicateLines',
label: nls.localize('lines.deleteDuplicates', "Delete Duplicate Lines"),
alias: 'Delete Duplicate Lines',
precondition: EditorContextKeys.writable
});
}

public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}

let model: ITextModel = editor.getModel();
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
return;
}

let edits: IIdentifiedSingleEditOperation[] = [];
let endCursorState: Selection[] = [];

let linesDeleted = 0;

for (let selection of editor.getSelections()) {
let uniqueLines = new Set();
let lines = [];

for (let i = selection.startLineNumber; i <= selection.endLineNumber; i++) {
let line = model.getLineContent(i);

if (uniqueLines.has(line)) {
continue;
}

lines.push(line);
uniqueLines.add(line);
}


let selectionToReplace = new Selection(
selection.startLineNumber,
1,
selection.endLineNumber,
model.getLineMaxColumn(selection.endLineNumber)
);

let adjustedSelectionStart = selection.startLineNumber - linesDeleted;
let finalSelection = new Selection(
adjustedSelectionStart,
1,
adjustedSelectionStart + lines.length - 1,
lines[lines.length - 1].length
);

edits.push(EditOperation.replace(selectionToReplace, lines.join('\n')));
endCursorState.push(finalSelection);

linesDeleted += (selection.endLineNumber - selection.startLineNumber + 1) - lines.length;
}

editor.pushUndoStop();
editor.executeEdits(this.id, edits, endCursorState);
editor.pushUndoStop();
}
}

export class TrimTrailingWhitespaceAction extends EditorAction {

public static readonly ID = 'editor.action.trimTrailingWhitespace';
Expand Down Expand Up @@ -1111,6 +1179,7 @@ registerEditorAction(MoveLinesUpAction);
registerEditorAction(MoveLinesDownAction);
registerEditorAction(SortLinesAscendingAction);
registerEditorAction(SortLinesDescendingAction);
registerEditorAction(DeleteDuplicateLinesAction);
registerEditorAction(TrimTrailingWhitespaceAction);
registerEditorAction(DeleteLinesAction);
registerEditorAction(IndentLinesAction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { Handler } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { DeleteAllLeftAction, DeleteAllRightAction, DeleteLinesAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SnakeCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TitleCaseAction, TransposeAction, UpperCaseAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { DeleteAllLeftAction, DeleteAllRightAction, DeleteDuplicateLinesAction, DeleteLinesAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SnakeCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TitleCaseAction, TransposeAction, UpperCaseAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';

Expand Down Expand Up @@ -143,6 +143,67 @@ suite('Editor Contrib - Line Operations', () => {
});
});

suite('DeleteDuplicateLinesAction', () => {
test('should remove duplicate lines', function () {
withTestCodeEditor(
[
'alpha',
'beta',
'beta',
'beta',
'alpha',
'omicron',
], {}, (editor) => {
let model = editor.getModel()!;
let deleteDuplicateLinesAction = new DeleteDuplicateLinesAction();

editor.setSelection(new Selection(1, 3, 6, 4));
executeAction(deleteDuplicateLinesAction, editor);
assert.deepStrictEqual(model.getLinesContent(), [
'alpha',
'beta',
'omicron',
]);
assertSelection(editor, new Selection(1, 1, 3, 7));
});
});

test('should remove duplicate lines in multiple selections', function () {
withTestCodeEditor(
[
'alpha',
'beta',
'beta',
'omicron',
'',
'alpha',
'alpha',
'beta'
], {}, (editor) => {
let model = editor.getModel()!;
let deleteDuplicateLinesAction = new DeleteDuplicateLinesAction();

editor.setSelections([new Selection(1, 2, 4, 3), new Selection(6, 2, 8, 3)]);
executeAction(deleteDuplicateLinesAction, editor);
assert.deepStrictEqual(model.getLinesContent(), [
'alpha',
'beta',
'omicron',
'',
'alpha',
'beta'
]);
let expectedSelections = [
new Selection(1, 1, 3, 7),
new Selection(5, 1, 6, 4)
];
editor.getSelections()!.forEach((actualSelection, index) => {
assert.deepStrictEqual(actualSelection.toString(), expectedSelections[index].toString());
});
});
});
});


suite('DeleteAllLeftAction', () => {
test('should delete to the left of the cursor', function () {
Expand Down

0 comments on commit 9264622

Please sign in to comment.