Skip to content

Commit 889edb3

Browse files
Copilotalexr00
andauthored
Show commit count as decoration on Commits tree node (#7959)
* Initial plan * Add commit count decoration to Commits tree node Co-authored-by: alexr00 <[email protected]> * Use file decoration instead of description for commit count Co-authored-by: alexr00 <[email protected]> * Address review feedback: add owner/repo, use scheme constant, listen for timeline changes Co-authored-by: alexr00 <[email protected]> * Remove commit count from URI, fetch from PR model instead Co-authored-by: alexr00 <[email protected]> * Clean up --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: alexr00 <[email protected]>
1 parent 8a8a4db commit 889edb3

File tree

5 files changed

+101
-3
lines changed

5 files changed

+101
-3
lines changed

src/common/uri.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,36 @@ export function createPRNodeUri(
520520
});
521521
}
522522

523+
export interface CommitsNodeUriParams {
524+
owner: string;
525+
repo: string;
526+
prNumber: number;
527+
}
528+
529+
export function createCommitsNodeUri(owner: string, repo: string, prNumber: number): vscode.Uri {
530+
const params: CommitsNodeUriParams = {
531+
owner,
532+
repo,
533+
prNumber
534+
};
535+
536+
return vscode.Uri.parse(`${Schemes.CommitsNode}:${owner}/${repo}/${prNumber}`).with({
537+
scheme: Schemes.CommitsNode,
538+
query: JSON.stringify(params)
539+
});
540+
}
541+
542+
export function fromCommitsNodeUri(uri: vscode.Uri): CommitsNodeUriParams | undefined {
543+
if (uri.scheme !== Schemes.CommitsNode) {
544+
return undefined;
545+
}
546+
try {
547+
return JSON.parse(uri.query) as CommitsNodeUriParams;
548+
} catch (e) {
549+
return undefined;
550+
}
551+
}
552+
523553
export interface NotificationUriParams {
524554
key: string;
525555
}
@@ -750,7 +780,8 @@ export enum Schemes {
750780
Repo = 'repo', // New issue file for passing data
751781
Git = 'git', // File content from the git extension
752782
PRQuery = 'prquery', // PR query tree item
753-
GitHubCommit = 'githubcommit' // file content from GitHub for a commit
783+
GitHubCommit = 'githubcommit', // file content from GitHub for a commit
784+
CommitsNode = 'commitsnode' // Commits tree node, for decorations
754785
}
755786

756787
export function resolvePath(from: vscode.Uri, to: string) {

src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { NotificationsProvider } from './notifications/notificationsProvider';
4141
import { ThemeWatcher } from './themeWatcher';
4242
import { resumePendingCheckout, UriHandler } from './uriHandler';
4343
import { CommentDecorationProvider } from './view/commentDecorationProvider';
44+
import { CommitsDecorationProvider } from './view/commitsDecorationProvider';
4445
import { CompareChanges } from './view/compareChangesTreeDataProvider';
4546
import { CreatePullRequestHelper } from './view/createPullRequestHelper';
4647
import { EmojiCompletionProvider } from './view/emojiCompletionProvider';
@@ -171,7 +172,7 @@ async function init(
171172
);
172173
const treeDecorationProviders = new TreeDecorationProviders(reposManager);
173174
context.subscriptions.push(treeDecorationProviders);
174-
treeDecorationProviders.registerProviders([new FileTypeDecorationProvider(), new CommentDecorationProvider(reposManager)]);
175+
treeDecorationProviders.registerProviders([new FileTypeDecorationProvider(), new CommentDecorationProvider(reposManager), new CommitsDecorationProvider(reposManager)]);
175176

176177
const notificationsProvider = new NotificationsProvider(credentialStore, reposManager);
177178
context.subscriptions.push(notificationsProvider);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { TreeDecorationProvider } from './treeDecorationProviders';
8+
import { createCommitsNodeUri, fromCommitsNodeUri, Schemes } from '../common/uri';
9+
import { FolderRepositoryManager } from '../github/folderRepositoryManager';
10+
import { PullRequestModel } from '../github/pullRequestModel';
11+
import { RepositoriesManager } from '../github/repositoriesManager';
12+
13+
export class CommitsDecorationProvider extends TreeDecorationProvider {
14+
15+
constructor(private readonly _repositoriesManager: RepositoriesManager) {
16+
super();
17+
}
18+
19+
registerPullRequestPropertyChangedListeners(_folderManager: FolderRepositoryManager, model: PullRequestModel): vscode.Disposable {
20+
return model.onDidChange(e => {
21+
if (e.timeline) {
22+
// Timeline changed, which may include new commits, so update the decoration
23+
const uri = createCommitsNodeUri(model.remote.owner, model.remote.repositoryName, model.number);
24+
this._onDidChangeFileDecorations.fire(uri);
25+
}
26+
});
27+
}
28+
29+
provideFileDecoration(
30+
uri: vscode.Uri,
31+
_token: vscode.CancellationToken,
32+
): vscode.ProviderResult<vscode.FileDecoration> {
33+
if (uri.scheme !== Schemes.CommitsNode) {
34+
return undefined;
35+
}
36+
37+
const params = fromCommitsNodeUri(uri);
38+
if (!params) {
39+
return undefined;
40+
}
41+
42+
const folderManager = this._repositoriesManager.getManagerForRepository(params.owner, params.repo);
43+
44+
if (folderManager) {
45+
const repo = folderManager.findExistingGitHubRepository({ owner: params.owner, repositoryName: params.repo });
46+
if (repo) {
47+
const pr = repo.getExistingPullRequestModel(params.prNumber);
48+
if (pr) {
49+
const commitsCount = pr.item.commits.length;
50+
return {
51+
badge: commitsCount.toString(),
52+
tooltip: vscode.l10n.t('{0} commits', commitsCount)
53+
};
54+
}
55+
}
56+
}
57+
58+
return undefined;
59+
}
60+
61+
}

src/view/treeDecorationProviders.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { PullRequestModel } from '../github/pullRequestModel';
1111
import { RepositoriesManager } from '../github/repositoriesManager';
1212

1313
export abstract class TreeDecorationProvider extends Disposable implements vscode.FileDecorationProvider {
14-
private _onDidChangeFileDecorations: vscode.EventEmitter<vscode.Uri | vscode.Uri[]> = this._register(new vscode.EventEmitter<
14+
protected _onDidChangeFileDecorations: vscode.EventEmitter<vscode.Uri | vscode.Uri[]> = this._register(new vscode.EventEmitter<
1515
vscode.Uri | vscode.Uri[]
1616
>());
1717
onDidChangeFileDecorations?: vscode.Event<vscode.Uri | vscode.Uri[] | undefined> | undefined = this._onDidChangeFileDecorations.event;

src/view/treeNodes/commitsCategoryNode.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import * as vscode from 'vscode';
77
import { CommitNode } from './commitNode';
88
import { TreeNode, TreeNodeParent } from './treeNode';
99
import Logger, { PR_TREE } from '../../common/logger';
10+
import { createCommitsNodeUri } from '../../common/uri';
1011
import { FolderRepositoryManager } from '../../github/folderRepositoryManager';
1112
import { PullRequestModel } from '../../github/pullRequestModel';
1213

1314
export class CommitsNode extends TreeNode implements vscode.TreeItem {
1415
public collapsibleState: vscode.TreeItemCollapsibleState;
16+
public resourceUri: vscode.Uri;
1517
private _folderRepoManager: FolderRepositoryManager;
1618
private _pr: PullRequestModel;
19+
public tooltip?: string;
1720

1821
constructor(
1922
parent: TreeNodeParent,
@@ -25,6 +28,8 @@ export class CommitsNode extends TreeNode implements vscode.TreeItem {
2528
this._pr = pr;
2629
this._folderRepoManager = reposManager;
2730
this.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
31+
this.resourceUri = createCommitsNodeUri(pr.remote.owner, pr.remote.repositoryName, pr.number);
32+
this.tooltip = vscode.l10n.t('Commits');
2833

2934
this.childrenDisposables = [];
3035
this.childrenDisposables.push(this._pr.onDidChangeReviewThreads(() => {

0 commit comments

Comments
 (0)