Skip to content

Commit f3a3e43

Browse files
author
Sander Ronde
committed
Show reviewed-status of files and allow marking as reviewed/unreviewed (fixes #103)
1 parent 18ac36d commit f3a3e43

File tree

12 files changed

+297
-10
lines changed

12 files changed

+297
-10
lines changed

.vscode/tasks.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"version": "2.0.0",
55
"tasks": [
66
{
7-
"type": "npm",
7+
"type": "bun",
88
"script": "compile:debug",
99
"problemMatcher": "$tsc-watch",
1010
"isBackground": true,
@@ -17,4 +17,4 @@
1717
}
1818
}
1919
]
20-
}
20+
}

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@
161161
"command": "gerrit.openModified",
162162
"title": "Open Modified File"
163163
},
164+
{
165+
"command": "gerrit.markReviewed",
166+
"title": "Mark file as reviewed"
167+
},
168+
{
169+
"command": "gerrit.markUnreviewed",
170+
"title": "Mark file as unreviewed"
171+
},
164172
{
165173
"command": "gerrit.openOnline",
166174
"title": "Open file on Gerrit"
@@ -483,6 +491,14 @@
483491
"command": "gerrit.openModified",
484492
"when": "false"
485493
},
494+
{
495+
"command": "gerrit.markReviewed",
496+
"when": "false"
497+
},
498+
{
499+
"command": "gerrit.markUnreviewed",
500+
"when": "false"
501+
},
486502
{
487503
"command": "gerrit.openOnline",
488504
"when": "false"
@@ -785,6 +801,16 @@
785801
"when": "view == gerrit:changeExplorer && viewItem =~ /filechange/ && viewItem =~ /modified/",
786802
"group": "openFile@3"
787803
},
804+
{
805+
"command": "gerrit.markReviewed",
806+
"when": "view == gerrit:changeExplorer && viewItem =~ /filechange/",
807+
"group": "openFile@4"
808+
},
809+
{
810+
"command": "gerrit.markUnreviewed",
811+
"when": "view == gerrit:changeExplorer && viewItem =~ /filechange/",
812+
"group": "openFile@5"
813+
},
788814
{
789815
"command": "gerrit.openInReview",
790816
"when": "view == gerrit:changeExplorer && viewItem =~ /gerritchange/",

src/commands/command-names.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export enum GerritExtensionCommands {
1313
FILE_OPEN_ONLINE = 'gerrit.openOnline',
1414
FILE_OPEN_MODIFIED = 'gerrit.openModified',
1515
FILE_OPEN_ORIGINAL = 'gerrit.openOriginal',
16+
FILE_MARK_REVIEWED = 'gerrit.markReviewed',
17+
FILE_MARK_UNREVIEWED = 'gerrit.markUnreviewed',
1618
FETCH_MORE = 'gerrit.fetchMore',
1719
OPEN_CHANGE_SELECTOR = 'gerrit.openChangeSelector',
1820
// Duplicate so we can have multiple names in the command palette

src/commands/commands.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import { createAutoRegisterCommand } from 'vscode-generate-package-json';
5656
import { enterCredentials } from '../lib/credentials/enterCredentials';
5757
import { rebaseOntoParent, recursiveRebase } from '../lib/git/rebase';
5858
import { CommentThread, ExtensionContext, Uri, window } from 'vscode';
59+
import { GerritChange } from '../lib/gerrit/gerritAPI/gerritChange';
5960
import { focusChange } from '../lib/commandHandlers/focusChange';
6061
import { Repository } from '../types/vscode-extension-git';
6162
import { checkConnection } from '../lib/gerrit/gerritAPI';
@@ -193,6 +194,52 @@ export function registerCommands(
193194
(treeView: FileTreeView) => openModified(treeView)
194195
)
195196
);
197+
context.subscriptions.push(
198+
registerCommand(
199+
GerritExtensionCommands.FILE_MARK_REVIEWED,
200+
async (treeView: FileTreeView) => {
201+
const change = await GerritChange.getChangeOnce(
202+
treeView.file.changeID
203+
);
204+
if (!change) {
205+
return;
206+
}
207+
const currentRevisions = await change.currentRevisions();
208+
if (!currentRevisions) {
209+
return;
210+
}
211+
const revision =
212+
currentRevisions[treeView.file.currentRevision.id];
213+
if (!revision) {
214+
return;
215+
}
216+
await revision.setFileReviewed(treeView.file.filePath, true);
217+
}
218+
)
219+
);
220+
context.subscriptions.push(
221+
registerCommand(
222+
GerritExtensionCommands.FILE_MARK_UNREVIEWED,
223+
async (treeView: FileTreeView) => {
224+
const change = await GerritChange.getChangeOnce(
225+
treeView.file.changeID
226+
);
227+
if (!change) {
228+
return;
229+
}
230+
const currentRevisions = await change.currentRevisions();
231+
if (!currentRevisions) {
232+
return;
233+
}
234+
const revision =
235+
currentRevisions[treeView.file.currentRevision.id];
236+
if (!revision) {
237+
return;
238+
}
239+
await revision.setFileReviewed(treeView.file.filePath, false);
240+
}
241+
)
242+
);
196243
context.subscriptions.push(
197244
registerCommand(
198245
GerritExtensionCommands.FILE_OPEN_ORIGINAL,
@@ -218,6 +265,25 @@ export function registerCommands(
218265
...(diffCommand.arguments ?? [])
219266
);
220267
}
268+
269+
await (async () => {
270+
const change = await GerritChange.getChangeOnce(
271+
args.file.changeID
272+
);
273+
if (!change) {
274+
return;
275+
}
276+
const currentRevisions = await change.currentRevisions();
277+
if (!currentRevisions) {
278+
return;
279+
}
280+
const revision =
281+
currentRevisions[args.file.currentRevision.id];
282+
if (!revision) {
283+
return;
284+
}
285+
await revision.setFileReviewed(args.file.filePath, true);
286+
})();
221287
}
222288
)
223289
);

src/commands/defs.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ export const commands: {
177177
title: 'Open Modified File',
178178
inCommandPalette: false,
179179
},
180+
'gerrit.markReviewed': {
181+
title: 'Mark file as reviewed',
182+
inCommandPalette: false,
183+
},
184+
'gerrit.markUnreviewed': {
185+
title: 'Mark file as unreviewed',
186+
inCommandPalette: false,
187+
},
180188
'gerrit.openOnline': {
181189
title: 'Open file on Gerrit',
182190
inCommandPalette: false,
@@ -468,6 +476,20 @@ export const views: {
468476
viewItemContains(TREE_ITEM_WAS_MODIFIED)
469477
),
470478
},
479+
{
480+
command: GerritExtensionCommands.FILE_MARK_REVIEWED,
481+
when: and(
482+
IS_GERRIT_CHANGE_EXPLORER_VIEW,
483+
viewItemContains(TREE_ITEM_TYPE_FILE)
484+
),
485+
},
486+
{
487+
command: GerritExtensionCommands.FILE_MARK_UNREVIEWED,
488+
when: and(
489+
IS_GERRIT_CHANGE_EXPLORER_VIEW,
490+
viewItemContains(TREE_ITEM_TYPE_FILE)
491+
),
492+
},
471493
],
472494
change: [
473495
{

src/extension.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
startListeningForStreamEvents,
77
testEnableStreamEvents,
88
} from './lib/stream-events/stream-events';
9+
import { getReviewedStatusDecorationProvider } from './providers/reviewedStatusDecorationProvider';
910
import { FileModificationStatusProvider } from './providers/fileModificationStatusProvider';
1011
import { showQuickCheckoutStatusBarIcons } from './views/statusBar/quickCheckoutStatusBar';
1112
import { getOrCreateQuickCheckoutTreeProvider } from './views/activityBar/quickCheckout';
@@ -170,6 +171,13 @@ export async function activate(context: ExtensionContext): Promise<void> {
170171
window.registerFileDecorationProvider(getCommentDecorationProvider())
171172
);
172173

174+
// Register reviewed status decoration provider (eye icons)
175+
context.subscriptions.push(
176+
window.registerFileDecorationProvider(
177+
getReviewedStatusDecorationProvider()
178+
)
179+
);
180+
173181
// Register filetype decoration provider
174182
context.subscriptions.push(
175183
window.registerFileDecorationProvider(

src/lib/gerrit/gerritAPI/api.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ export class GerritAPI {
340340
// Non-get requests perform some remote action, we can't
341341
// just assume that that action only needs to happen once
342342
if (body && body.method !== 'GET') {
343+
log(`${body.method ?? 'GET'} request to "${url}"`);
343344
return GerritAPI.performRequest(url, body);
344345
}
345346

@@ -949,6 +950,69 @@ export class GerritAPI {
949950
);
950951
}
951952

953+
public getFileReviewStatus(
954+
changeID: string,
955+
revision: PatchsetDescription
956+
): Subscribable<Record<string, boolean>> {
957+
return APISubscriptionManager.fileReviewStatusSubscriptions.createFetcher(
958+
{
959+
changeID: changeID,
960+
revision,
961+
baseRevision: null,
962+
},
963+
async () => {
964+
const response = await this._tryRequest({
965+
path: `changes/${changeID}/revisions/${revision.id}/files`,
966+
method: 'GET',
967+
searchParams: {
968+
// Mutually exclusive with baseRevision and others so this needs
969+
// its own request
970+
reviewed: 'true',
971+
},
972+
});
973+
974+
const json = this._handleResponse<string[]>(response);
975+
if (!json) {
976+
return {};
977+
}
978+
979+
const byPath: Record<string, boolean> = {};
980+
for (const path of json) {
981+
byPath[path] = true;
982+
}
983+
984+
return byPath;
985+
}
986+
);
987+
}
988+
989+
public async setFileReviewed(
990+
changeID: string,
991+
revision: PatchsetDescription,
992+
path: string,
993+
reviewed: boolean
994+
): Promise<void> {
995+
if (reviewed) {
996+
await this._tryRequest({
997+
method: 'PUT',
998+
path: `changes/${changeID}/revisions/${revision.id}/files/${encodeURIComponent(path)}/reviewed`,
999+
body: JSON.stringify({
1000+
reviewed,
1001+
}),
1002+
});
1003+
} else {
1004+
await this._tryRequest({
1005+
method: 'DELETE',
1006+
path: `changes/${changeID}/revisions/${revision.id}/files/${encodeURIComponent(path)}/reviewed`,
1007+
});
1008+
}
1009+
await APISubscriptionManager.fileReviewStatusSubscriptions.invalidate({
1010+
changeID: changeID,
1011+
revision,
1012+
baseRevision: null,
1013+
});
1014+
}
1015+
9521016
public getFiles(
9531017
changeID: string,
9541018
changeProject: string,

src/lib/gerrit/gerritAPI/gerritRevision.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,32 @@ export class GerritRevision extends DynamicallyFetchable {
139139
}
140140
}
141141

142+
public async setFileReviewed(
143+
path: string,
144+
reviewed: boolean
145+
): Promise<void> {
146+
const api = await getAPIForSubscription();
147+
await api.setFileReviewed(
148+
this.changeID,
149+
{
150+
id: this.revisionID,
151+
number: this.number,
152+
},
153+
path,
154+
reviewed
155+
);
156+
}
157+
158+
public async getFileReviewStatus(): Promise<
159+
Subscribable<Record<string, boolean>>
160+
> {
161+
const api = await getAPIForSubscription();
162+
return api.getFileReviewStatus(this.changeID, {
163+
id: this.revisionID,
164+
number: this.number,
165+
});
166+
}
167+
142168
public detailedUploader(
143169
...additionalWith: GerritAPIWith[]
144170
): Promise<GerritDetailedUserResponse | null> {

src/lib/subscriptions/filesSubscription.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,21 @@ import {
55
WithMatchAny,
66
} from './baseSubscriptions';
77
import { PatchsetDescription } from '../../views/activityBar/changes/changeTreeView';
8-
import { GerritFile } from '../gerrit/gerritAPI/gerritFile';
98

109
interface ChangeSubscriptionsManagerConfig {
1110
changeID: string;
1211
revision: PatchsetDescription;
1312
baseRevision: PatchsetDescription | null;
1413
}
1514

16-
export class FilesSubscriptionsManager extends APISubSubscriptionManagerBase<
17-
Record<string, GerritFile>,
15+
export class FilesSubscriptionsManager<V> extends APISubSubscriptionManagerBase<
16+
V,
1817
ChangeSubscriptionsManagerConfig
1918
> {
2019
protected override refetchIntervalOnNull = null;
2120
protected override _getMatches(
2221
config: WithMatchAny<ChangeSubscriptionsManagerConfig>
23-
): APISubscriptionManagerEntry<
24-
ChangeSubscriptionsManagerConfig,
25-
Record<string, GerritFile>
26-
>[] {
22+
): APISubscriptionManagerEntry<ChangeSubscriptionsManagerConfig, V>[] {
2723
const allEntries = this._subscriptions.entries();
2824
if (config === MATCH_ANY) {
2925
return allEntries.map((e) => e[1]);

src/lib/subscriptions/subscriptions.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ChangesSubscriptionsManager } from './changesSubscriptions';
88
import { APISubSubscriptionManagerBase } from './baseSubscriptions';
99
import { GerritChange } from '../gerrit/gerritAPI/gerritChange';
1010
import { FilesSubscriptionsManager } from './filesSubscription';
11+
import { GerritFile } from '../gerrit/gerritAPI/gerritFile';
1112
import { Disposable } from 'vscode';
1213

1314
interface SubscriptionOptions {
@@ -48,7 +49,12 @@ export class APISubscriptionManager {
4849
Map<string, GerritDraftComment[]>
4950
>();
5051
public static changesSubscriptions = new ChangesSubscriptionsManager();
51-
public static filesSubscriptions = new FilesSubscriptionsManager();
52+
public static filesSubscriptions = new FilesSubscriptionsManager<
53+
Record<string, GerritFile>
54+
>();
55+
public static fileReviewStatusSubscriptions = new FilesSubscriptionsManager<
56+
Record<string, boolean>
57+
>();
5258
public static quickCheckoutSubscriptions =
5359
new QuickCheckoutSubscriptionsManager();
5460
public static readonly NO_OP = (): void => {};

0 commit comments

Comments
 (0)