Skip to content

Commit a84c5eb

Browse files
committed
Added support to revert changes in ranges
1 parent 14a836a commit a84c5eb

File tree

5 files changed

+206
-3
lines changed

5 files changed

+206
-3
lines changed

icons/dark/clean.svg

Lines changed: 1 addition & 0 deletions
Loading

icons/light/clean.svg

Lines changed: 1 addition & 0 deletions
Loading

package.json

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@
145145
},
146146
{
147147
"command": "svn.revert",
148-
"title": "Revert Selected",
148+
"title": "Revert Selected File",
149149
"category": "SVN"
150150
},
151151
{
@@ -172,6 +172,20 @@
172172
"command": "svn.log",
173173
"title": "Show commit messages",
174174
"category": "SVN"
175+
},
176+
{
177+
"command": "svn.revertChange",
178+
"title": "Revert Change",
179+
"category": "SVN",
180+
"icon": {
181+
"light": "icons/light/clean.svg",
182+
"dark": "icons/dark/clean.svg"
183+
}
184+
},
185+
{
186+
"command": "svn.revertSelectedRanges",
187+
"title": "Revert Selected Ranges",
188+
"category": "SVN"
175189
}
176190
],
177191
"menus": {
@@ -207,6 +221,14 @@
207221
{
208222
"command": "svn.commitWithMessage",
209223
"when": "false"
224+
},
225+
{
226+
"command": "svn.revertChange",
227+
"when": "false"
228+
},
229+
{
230+
"command": "svn.revertSelectedRanges",
231+
"when": "config.svn.enabled && svnOpenRepositoryCount != 0"
210232
}
211233
],
212234
"scm/title": [
@@ -322,6 +344,12 @@
322344
"group": "2_modification"
323345
}
324346
],
347+
"scm/change/title": [
348+
{
349+
"command": "svn.revertChange",
350+
"when": "config.svn.enabled && originalResourceScheme == svn"
351+
}
352+
],
325353
"editor/title": [
326354
{
327355
"command": "svn.openFile",
@@ -340,6 +368,12 @@
340368
"group": "navigation",
341369
"when":
342370
"config.svn.enabled && svnOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file"
371+
},
372+
{
373+
"command": "svn.revertSelectedRanges",
374+
"group": "2_svn@3",
375+
"when":
376+
"config.svn.enabled && svnOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff"
343377
}
344378
]
345379
},

src/commands.ts

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import {
99
SourceControlResourceGroup,
1010
ViewColumn,
1111
SourceControlResourceState,
12-
ProgressLocation
12+
ProgressLocation,
13+
LineChange,
14+
WorkspaceEdit,
15+
Range,
16+
Position,
17+
TextEditor
1318
} from "vscode";
1419
import { inputCommitMessage } from "./messages";
1520
import { Svn, Status, SvnErrorCodes } from "./svn";
@@ -21,9 +26,11 @@ import * as fs from "fs";
2126
import * as path from "path";
2227
import { start } from "repl";
2328
import { getConflictPickOptions } from "./conflictItems";
29+
import { applyLineChanges } from "./lineChanges";
2430

2531
interface CommandOptions {
2632
repository?: boolean;
33+
diff?: boolean;
2734
}
2835

2936
interface Command {
@@ -135,7 +142,11 @@ export class SvnCommands {
135142
constructor(private model: Model) {
136143
Commands.map(({ commandId, method, options }) => {
137144
const command = this.createCommand(method, options);
138-
commands.registerCommand(commandId, command);
145+
if (options.diff) {
146+
return commands.registerDiffInformationCommand(commandId, command);
147+
} else {
148+
return commands.registerCommand(commandId, command);
149+
}
139150
});
140151
}
141152

@@ -889,6 +900,108 @@ export class SvnCommands {
889900
}
890901
}
891902

903+
private async _revertChanges(
904+
textEditor: TextEditor,
905+
changes: LineChange[]
906+
): Promise<void> {
907+
const modifiedDocument = textEditor.document;
908+
const modifiedUri = modifiedDocument.uri;
909+
910+
if (modifiedUri.scheme !== "file") {
911+
return;
912+
}
913+
914+
const originalUri = toSvnUri(modifiedUri, "BASE");
915+
const originalDocument = await workspace.openTextDocument(originalUri);
916+
const basename = path.basename(modifiedUri.fsPath);
917+
const message = `Are you sure you want to revert the selected changes in ${basename}?`;
918+
const yes = "Revert Changes";
919+
const pick = await window.showWarningMessage(message, { modal: true }, yes);
920+
921+
if (pick !== yes) {
922+
return;
923+
}
924+
925+
const result = applyLineChanges(
926+
originalDocument,
927+
modifiedDocument,
928+
changes
929+
);
930+
const edit = new WorkspaceEdit();
931+
edit.replace(
932+
modifiedUri,
933+
new Range(
934+
new Position(0, 0),
935+
modifiedDocument.lineAt(modifiedDocument.lineCount - 1).range.end
936+
),
937+
result
938+
);
939+
workspace.applyEdit(edit);
940+
await modifiedDocument.save();
941+
}
942+
943+
@command("svn.revertChange")
944+
async revertChange(
945+
uri: Uri,
946+
changes: LineChange[],
947+
index: number
948+
): Promise<void> {
949+
const textEditor = window.visibleTextEditors.filter(
950+
e => e.document.uri.toString() === uri.toString()
951+
)[0];
952+
953+
if (!textEditor) {
954+
return;
955+
}
956+
957+
await this._revertChanges(textEditor, [
958+
...changes.slice(0, index),
959+
...changes.slice(index + 1)
960+
]);
961+
}
962+
963+
@command("svn.revertSelectedRanges", { diff: true })
964+
async revertSelectedRanges(changes: LineChange[]): Promise<void> {
965+
const textEditor = window.activeTextEditor;
966+
967+
if (!textEditor) {
968+
return;
969+
}
970+
971+
const modifiedDocument = textEditor.document;
972+
const selections = textEditor.selections;
973+
const selectedChanges = changes.filter(change => {
974+
const modifiedRange =
975+
change.modifiedEndLineNumber === 0
976+
? new Range(
977+
modifiedDocument.lineAt(
978+
change.modifiedStartLineNumber - 1
979+
).range.end,
980+
modifiedDocument.lineAt(
981+
change.modifiedStartLineNumber
982+
).range.start
983+
)
984+
: new Range(
985+
modifiedDocument.lineAt(
986+
change.modifiedStartLineNumber - 1
987+
).range.start,
988+
modifiedDocument.lineAt(
989+
change.modifiedEndLineNumber - 1
990+
).range.end
991+
);
992+
993+
return selections.every(
994+
selection => !selection.intersection(modifiedRange)
995+
);
996+
});
997+
998+
if (selectedChanges.length === changes.length) {
999+
return;
1000+
}
1001+
1002+
await this._revertChanges(textEditor, selectedChanges);
1003+
}
1004+
8921005
private getSCMResource(uri?: Uri): Resource | undefined {
8931006
uri = uri
8941007
? uri

src/lineChanges.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { TextDocument, LineChange, Range } from "vscode";
2+
3+
export function applyLineChanges(
4+
original: TextDocument,
5+
modified: TextDocument,
6+
diffs: LineChange[]
7+
): string {
8+
const result: string[] = [];
9+
let currentLine = 0;
10+
11+
for (let diff of diffs) {
12+
const isInsertion = diff.originalEndLineNumber === 0;
13+
const isDeletion = diff.modifiedEndLineNumber === 0;
14+
15+
result.push(
16+
original.getText(
17+
new Range(
18+
currentLine,
19+
0,
20+
isInsertion
21+
? diff.originalStartLineNumber
22+
: diff.originalStartLineNumber - 1,
23+
0
24+
)
25+
)
26+
);
27+
28+
if (!isDeletion) {
29+
let fromLine = diff.modifiedStartLineNumber - 1;
30+
let fromCharacter = 0;
31+
32+
if (isInsertion && diff.originalStartLineNumber === original.lineCount) {
33+
fromLine = original.lineCount - 1;
34+
fromCharacter = original.lineAt(fromLine).range.end.character;
35+
}
36+
37+
result.push(
38+
modified.getText(
39+
new Range(fromLine, fromCharacter, diff.modifiedEndLineNumber, 0)
40+
)
41+
);
42+
}
43+
44+
currentLine = isInsertion
45+
? diff.originalStartLineNumber
46+
: diff.originalEndLineNumber;
47+
}
48+
49+
result.push(
50+
original.getText(new Range(currentLine, 0, original.lineCount, 0))
51+
);
52+
53+
return result.join("");
54+
}

0 commit comments

Comments
 (0)