Skip to content

Commit dfef005

Browse files
committed
Add settings to control the icons used in Issues and PR tree views
Implements the feature request #6641. Both tree views support an icon mode of `author` (default), `state`, or `generic`. `author` follows the existing logic prior to this setting, where we attempt to fetch a rounded avatar. PRs fall back to a GitHub icon and Issues fall back to state-specific icon. `state` will always use a state-specific icon that matches the Issue/PR type and state. `generic` will always use the GitHub icon for a PR and an uncolored issues theme icon for Issues.
1 parent e287ca8 commit dfef005

File tree

5 files changed

+99
-13
lines changed

5 files changed

+99
-13
lines changed

package.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,36 @@
733733
"default": false,
734734
"description": "%githubPullRequests.showPullRequestNumberInTree.description%"
735735
},
736+
"githubPullRequests.treeViewIconMode": {
737+
"type": "string",
738+
"enum": [
739+
"author",
740+
"state",
741+
"generic"
742+
],
743+
"enumDescriptions": [
744+
"%githubPullRequests.treeViewIconMode.author%",
745+
"%githubPullRequests.treeViewIconMode.state%",
746+
"%githubPullRequests.treeViewIconMode.generic%"
747+
],
748+
"default": "author",
749+
"description": "%githubPullRequests.treeViewIconMode.description%"
750+
},
751+
"githubIssues.treeViewIconMode": {
752+
"type": "string",
753+
"enum": [
754+
"author",
755+
"state",
756+
"generic"
757+
],
758+
"enumDescriptions": [
759+
"%githubIssues.treeViewIconMode.author%",
760+
"%githubIssues.treeViewIconMode.state%",
761+
"%githubIssues.treeViewIconMode.generic%"
762+
],
763+
"default": "author",
764+
"description": "%githubIssues.treeViewIconMode.description%"
765+
},
736766
"githubIssues.alwaysPromptForNewIssueRepo": {
737767
"type": "boolean",
738768
"default": false,

package.nls.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@
152152
"githubPullRequests.showPullRequestNumberInTree.description": "Shows the pull request number in the tree view.",
153153
"githubPullRequests.labelCreated.description": "Group of labels that you want to add to the pull request automatically. Labels that don't exist in the repository won't be added.",
154154
"githubPullRequests.labelCreated.label.description": "Each string element is value of label that you want to add",
155+
"githubPullRequests.treeViewIconMode.description": "Which icon to use in the pull request tree view",
156+
"githubPullRequests.treeViewIconMode.author": "Show the pull request author avatar",
157+
"githubPullRequests.treeViewIconMode.state": "Show the pull request type (draft or not) and state (open/closed/merged) as a colored icon",
158+
"githubPullRequests.treeViewIconMode.generic": "Show a GitHub icon",
159+
"githubIssues.treeViewIconMode.description": "Which icon to use in the issues tree view",
160+
"githubIssues.treeViewIconMode.author": "Show the issue author avatar",
161+
"githubIssues.treeViewIconMode.state": "Show the issue state (open/closed) as a colored icon",
162+
"githubIssues.treeViewIconMode.generic": "Show an issues icon regardless of state",
155163
"githubIssues.alwaysPromptForNewIssueRepo.description": "Enabling will always prompt which repository to create an issue in instead of basing off the current open file.",
156164
"view.github.pull.requests.name": "GitHub",
157165
"view.github.pull.request.name": "GitHub Pull Request",

src/common/settingKeys.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ export const EXPERIMENTAL_NOTIFICATIONS_PAGE_SIZE = 'experimental.notificationsV
5858
export const EXPERIMENTAL_NOTIFICATIONS_SCORE = 'experimental.notificationsScore';
5959
export const WEBVIEW_REFRESH_INTERVAL = 'webviewRefreshInterval';
6060

61+
export const TREE_VIEW_ICON_MODE = 'treeViewIconMode';
62+
export type TreeViewIconMode = 'author' | 'state' | 'generic';
63+
6164
// git
6265
export const GIT = 'git';
6366
export const PULL_BEFORE_CHECKOUT = 'pullBeforeCheckout';

src/issues/issuesView.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as path from 'path';
77
import * as vscode from 'vscode';
88
import { commands, contexts } from '../common/executeCommands';
9+
import { ISSUES_SETTINGS_NAMESPACE, TREE_VIEW_ICON_MODE, TreeViewIconMode } from '../common/settingKeys';
910
import { DataUri } from '../common/uri';
1011
import { groupBy } from '../common/utils';
1112
import { FolderRepositoryManager, ReposManagerState } from '../github/folderRepositoryManager';
@@ -59,6 +60,14 @@ export class IssuesTreeData
5960
this._onDidChangeTreeData.fire();
6061
}),
6162
);
63+
64+
context.subscriptions.push(
65+
vscode.workspace.onDidChangeConfiguration((e) => {
66+
if (e.affectsConfiguration(`${ISSUES_SETTINGS_NAMESPACE}.${TREE_VIEW_ICON_MODE}`)) {
67+
this._onDidChangeTreeData.fire();
68+
}
69+
}),
70+
);
6271
}
6372

6473
private getFolderRepoItem(element: FolderRepositoryManager): vscode.TreeItem {
@@ -77,10 +86,22 @@ export class IssuesTreeData
7786

7887
private async getIssueTreeItem(element: IssueItem): Promise<vscode.TreeItem> {
7988
const treeItem = new vscode.TreeItem(element.title, vscode.TreeItemCollapsibleState.None);
80-
treeItem.iconPath = (await DataUri.avatarCirclesAsImageDataUris(this.context, [element.author], 16, 16))[0] ??
81-
(element.isOpen
82-
? new vscode.ThemeIcon('issues', new vscode.ThemeColor('issues.open'))
83-
: new vscode.ThemeIcon('issue-closed', new vscode.ThemeColor('issues.closed')));
89+
90+
const iconMode = vscode.workspace.getConfiguration(ISSUES_SETTINGS_NAMESPACE).get<TreeViewIconMode>(TREE_VIEW_ICON_MODE, 'author');
91+
let iconPath: vscode.Uri | vscode.ThemeIcon = element.isOpen
92+
? new vscode.ThemeIcon('issues', new vscode.ThemeColor('issues.open'))
93+
: new vscode.ThemeIcon('issue-closed', new vscode.ThemeColor('issues.closed'));
94+
95+
if (iconMode === 'author') {
96+
const authorAvatar = (await DataUri.avatarCirclesAsImageDataUris(this.context, [element.author], 16, 16))[0];
97+
if (authorAvatar) {
98+
iconPath = authorAvatar;
99+
}
100+
} else if (iconMode === 'generic') {
101+
iconPath = new vscode.ThemeIcon('issues');
102+
}
103+
104+
treeItem.iconPath = iconPath;
84105

85106
treeItem.command = {
86107
command: 'issue.openDescription',

src/view/treeNodes/pullRequestNode.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import { COPILOT_ACCOUNTS } from '../../common/comment';
99
import { getCommentingRanges } from '../../common/commentingRanges';
1010
import { InMemFileChange, SlimFileChange } from '../../common/file';
1111
import Logger from '../../common/logger';
12-
import { FILE_LIST_LAYOUT, PR_SETTINGS_NAMESPACE, SHOW_PULL_REQUEST_NUMBER_IN_TREE } from '../../common/settingKeys';
12+
import { FILE_LIST_LAYOUT, PR_SETTINGS_NAMESPACE, SHOW_PULL_REQUEST_NUMBER_IN_TREE, TREE_VIEW_ICON_MODE, TreeViewIconMode } from '../../common/settingKeys';
1313
import { createPRNodeUri, DataUri, fromPRUri, Schemes } from '../../common/uri';
1414
import { FolderRepositoryManager } from '../../github/folderRepositoryManager';
1515
import { CopilotWorkingStatus } from '../../github/githubRepository';
16+
import { GithubItemStateEnum } from '../../github/interface';
1617
import { NotificationProvider } from '../../github/notifications';
1718
import { IResolvedPullRequestModel, PullRequestModel } from '../../github/pullRequestModel';
1819
import { InMemFileChangeModel, RemoteFileChangeModel } from '../fileChangeModel';
@@ -140,7 +141,7 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2
140141

141142
protected registerConfigurationChange() {
142143
this._register(vscode.workspace.onDidChangeConfiguration(e => {
143-
if (e.affectsConfiguration(`${PR_SETTINGS_NAMESPACE}.${SHOW_PULL_REQUEST_NUMBER_IN_TREE}`)) {
144+
if (e.affectsConfiguration(`${PR_SETTINGS_NAMESPACE}.${SHOW_PULL_REQUEST_NUMBER_IN_TREE}`) || e.affectsConfiguration(`${PR_SETTINGS_NAMESPACE}.${TREE_VIEW_ICON_MODE}`)) {
144145
this.refresh();
145146
}
146147
}));
@@ -271,11 +272,33 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2
271272
}
272273

273274
private async _getIcon(): Promise<vscode.Uri | vscode.ThemeIcon | { light: string | vscode.Uri; dark: string | vscode.Uri }> {
275+
const { author, state, isDraft } = this.pullRequestModel;
276+
const iconMode = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<TreeViewIconMode>(TREE_VIEW_ICON_MODE, 'author');
277+
let iconPath: vscode.Uri | vscode.ThemeIcon = new vscode.ThemeIcon('github');
278+
if (iconMode === 'author') {
279+
const authorAvatar = (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [author], 16, 16))[0];
280+
if (authorAvatar) {
281+
iconPath = authorAvatar;
282+
}
283+
} else if (iconMode === 'state') {
284+
iconPath = new vscode.ThemeIcon(
285+
state === GithubItemStateEnum.Closed ? 'git-pull-request-closed'
286+
: state === GithubItemStateEnum.Merged ? 'git-merge'
287+
: isDraft ? 'git-pull-request-draft'
288+
: 'git-pull-request',
289+
new vscode.ThemeColor(
290+
state === GithubItemStateEnum.Closed ? 'pullRequests.closed'
291+
: state === GithubItemStateEnum.Merged ? 'pullRequests.merged'
292+
: isDraft ? 'pullRequests.draft'
293+
: 'pullRequests.open'
294+
)
295+
);
296+
}
297+
274298
const copilotWorkingStatus = await this.pullRequestModel.copilotWorkingStatus(this.pullRequestModel);
275299
const theme = this._folderReposManager.themeWatcher.themeData;
276300
if (!theme || copilotWorkingStatus === CopilotWorkingStatus.NotCopilotIssue) {
277-
return (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [this.pullRequestModel.author], 16, 16))[0]
278-
?? new vscode.ThemeIcon('github');
301+
return iconPath;
279302
}
280303
switch (copilotWorkingStatus) {
281304
case CopilotWorkingStatus.InProgress:
@@ -294,17 +317,16 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2
294317
dark: DataUri.copilotErrorAsImageDataURI(getIconForeground(theme, 'dark'), getListErrorForeground(theme, 'dark'))
295318
};
296319
default:
297-
return (await DataUri.avatarCirclesAsImageDataUris(this._folderReposManager.context, [this.pullRequestModel.author], 16, 16))[0]
298-
?? new vscode.ThemeIcon('github');
320+
return iconPath;
299321
}
300322
}
301323

302324
async getTreeItem(): Promise<vscode.TreeItem> {
303325
const currentBranchIsForThisPR = this.pullRequestModel.equals(this._folderReposManager.activePullRequest);
304326

305327
const { title, number, author, isDraft, html_url } = this.pullRequestModel;
306-
let labelTitle = this.pullRequestModel.title.length > 50 ? `${this.pullRequestModel.title.substring(0, 50)}...` : this.pullRequestModel.title;
307-
if (COPILOT_ACCOUNTS[this.pullRequestModel.author.login]) {
328+
let labelTitle = title.length > 50 ? `${title.substring(0, 50)}...` : title;
329+
if (COPILOT_ACCOUNTS[author.login]) {
308330
labelTitle = labelTitle.replace('[WIP]', '');
309331
}
310332
const login = author.specialDisplayName ?? author.login;
@@ -322,7 +344,9 @@ export class PRNode extends TreeNode implements vscode.CommentingRangeProvider2
322344
labelPrefix += `#${formattedPRNumber}: `;
323345
}
324346

325-
const label = `${labelPrefix}${isDraft ? '| ' : ''}${labelTitle}`;
347+
const iconMode = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<TreeViewIconMode>(TREE_VIEW_ICON_MODE, 'author');
348+
349+
const label = `${labelPrefix}${isDraft && iconMode !== 'state' ? '[DRAFT] ' : ''}${labelTitle}`;
326350
const description = `by @${login}`;
327351
const command = {
328352
title: vscode.l10n.t('View Pull Request Description'),

0 commit comments

Comments
 (0)