Skip to content

Commit f6e8ee9

Browse files
hermannlooseHermann Loosealexr00
authored
Introduce themable colors for resolved and unresolved comments (#145230)
* Make `CommentThread.state` available to `ReviewZoneWidget` * Apply themable colors for resolved and unresolved comments * Update color properties in comment widget * PR feedback Co-authored-by: Hermann Loose <hermannloose@google.com> Co-authored-by: Alex Ross <alros@microsoft.com>
1 parent 22ff1d3 commit f6e8ee9

File tree

6 files changed

+102
-7
lines changed

6 files changed

+102
-7
lines changed

src/vs/editor/common/languages.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1647,7 +1647,13 @@ export enum CommentThreadCollapsibleState {
16471647
Expanded = 1
16481648
}
16491649

1650-
1650+
/**
1651+
* @internal
1652+
*/
1653+
export enum CommentThreadState {
1654+
Unresolved = 0,
1655+
Resolved = 1
1656+
}
16511657

16521658
/**
16531659
* @internal
@@ -1682,12 +1688,14 @@ export interface CommentThread {
16821688
comments: Comment[] | undefined;
16831689
onDidChangeComments: Event<Comment[] | undefined>;
16841690
collapsibleState?: CommentThreadCollapsibleState;
1691+
state?: CommentThreadState;
16851692
canReply: boolean;
16861693
input?: CommentInput;
16871694
onDidChangeInput: Event<CommentInput | undefined>;
16881695
onDidChangeRange: Event<IRange>;
16891696
onDidChangeLabel: Event<string | undefined>;
16901697
onDidChangeCollasibleState: Event<CommentThreadCollapsibleState | undefined>;
1698+
onDidChangeState: Event<CommentThreadState | undefined>;
16911699
onDidChangeCanReply: Event<boolean>;
16921700
isDisposed: boolean;
16931701
}

src/vs/workbench/api/browser/mainThreadComments.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,19 @@ export class MainThreadCommentThread implements languages.CommentThread {
120120
return this._isDisposed;
121121
}
122122

123+
private _state: languages.CommentThreadState | undefined;
124+
get state() {
125+
return this._state;
126+
}
127+
128+
set state(newState: languages.CommentThreadState | undefined) {
129+
this._state = newState;
130+
this._onDidChangeState.fire(this._state);
131+
}
132+
133+
private readonly _onDidChangeState = new Emitter<languages.CommentThreadState | undefined>();
134+
public onDidChangeState = this._onDidChangeState.event;
135+
123136
constructor(
124137
public commentThreadHandle: number,
125138
public controllerHandle: number,
@@ -142,6 +155,7 @@ export class MainThreadCommentThread implements languages.CommentThread {
142155
if (modified('comments')) { this._comments = changes.comments; }
143156
if (modified('collapseState')) { this._collapsibleState = changes.collapseState; }
144157
if (modified('canReply')) { this.canReply = changes.canReply!; }
158+
if (modified('state')) { this.state = changes.state!; }
145159
}
146160

147161
dispose() {
@@ -151,6 +165,7 @@ export class MainThreadCommentThread implements languages.CommentThread {
151165
this._onDidChangeInput.dispose();
152166
this._onDidChangeLabel.dispose();
153167
this._onDidChangeRange.dispose();
168+
this._onDidChangeState.dispose();
154169
}
155170

156171
toJSON(): any {

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export type CommentThreadChanges = Partial<{
121121
comments: CommentChanges[];
122122
collapseState: languages.CommentThreadCollapsibleState;
123123
canReply: boolean;
124+
state: languages.CommentThreadState;
124125
}>;
125126

126127
export interface MainThreadCommentsShape extends IDisposable {

src/vs/workbench/api/common/extHostComments.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
1616
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
1717
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
1818
import * as types from 'vs/workbench/api/common/extHostTypes';
19+
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
1920
import type * as vscode from 'vscode';
2021
import { ExtHostCommentsShape, IMainContext, MainContext, CommentThreadChanges, CommentChanges } from './extHost.protocol';
2122
import { ExtHostCommands } from './extHostCommands';
@@ -222,6 +223,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
222223
comments: vscode.Comment[];
223224
collapsibleState: vscode.CommentThreadCollapsibleState;
224225
canReply: boolean;
226+
state: vscode.CommentThreadState;
225227
}>;
226228

227229
class ExtHostCommentThread implements vscode.CommentThread {
@@ -325,6 +327,20 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
325327
this._onDidUpdateCommentThread.fire();
326328
}
327329

330+
private _state?: vscode.CommentThreadState;
331+
332+
get state(): vscode.CommentThreadState {
333+
checkProposedApiEnabled(this.extensionDescription, 'commentsResolvedState');
334+
return this._state!;
335+
}
336+
337+
set state(newState: vscode.CommentThreadState) {
338+
checkProposedApiEnabled(this.extensionDescription, 'commentsResolvedState');
339+
this._state = newState;
340+
this.modifications.state = newState;
341+
this._onDidUpdateCommentThread.fire();
342+
}
343+
328344
private _localDisposables: types.Disposable[];
329345

330346
private _isDiposed: boolean;
@@ -397,6 +413,8 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
397413
set contextValue(value: string | undefined) { that.contextValue = value; },
398414
get label() { return that.label; },
399415
set label(value: string | undefined) { that.label = value; },
416+
get state() { return that.state; },
417+
set state(value: vscode.CommentThreadState) { that.state = value; },
400418
dispose: () => {
401419
that.dispose();
402420
}
@@ -441,6 +459,9 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
441459
if (modified('canReply')) {
442460
formattedModifications.canReply = this.canReply;
443461
}
462+
if (modified('state')) {
463+
formattedModifications.state = convertToState(this._state);
464+
}
444465
this.modifications = {};
445466

446467
proxy.$updateCommentThread(
@@ -660,5 +681,17 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
660681
return languages.CommentThreadCollapsibleState.Collapsed;
661682
}
662683

684+
function convertToState(kind: vscode.CommentThreadState | undefined): languages.CommentThreadState {
685+
if (kind !== undefined) {
686+
switch (kind) {
687+
case types.CommentThreadState.Unresolved:
688+
return languages.CommentThreadState.Unresolved;
689+
case types.CommentThreadState.Resolved:
690+
return languages.CommentThreadState.Resolved;
691+
}
692+
}
693+
return languages.CommentThreadState.Unresolved;
694+
}
695+
663696
return new ExtHostCommentsImpl();
664697
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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 { Color } from 'vs/base/common/color';
7+
import * as languages from 'vs/editor/common/languages';
8+
import * as nls from 'vs/nls';
9+
import { contrastBorder, editorWarningForeground, editorWidgetForeground, registerColor } from 'vs/platform/theme/common/colorRegistry';
10+
import { IColorTheme } from 'vs/platform/theme/common/themeService';
11+
12+
export const resolvedCommentBorder = registerColor('comments.resolved.border', { dark: editorWidgetForeground, light: editorWidgetForeground, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('resolvedCommentBorder', 'Color of borders and arrow for resolved comments.'));
13+
export const unresolvedCommentBorder = registerColor('comments.unresolved.border', { dark: editorWarningForeground, light: editorWarningForeground, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentBorder', 'Color of borders and arrow for unresolved comments.'));
14+
15+
const commentThreadStateColors = new Map([
16+
[languages.CommentThreadState.Unresolved, unresolvedCommentBorder],
17+
[languages.CommentThreadState.Resolved, resolvedCommentBorder],
18+
]);
19+
20+
export const commentThreadStateColorVar = '--comment-thread-state-color';
21+
22+
export function getCommentThreadStateColor(thread: languages.CommentThread, theme: IColorTheme): Color | undefined {
23+
const colorId = thread.state !== undefined ? commentThreadStateColors.get(thread.state) : undefined;
24+
return colorId !== undefined ? theme.getColor(colorId) : undefined;
25+
}

src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { PANEL_BORDER } from 'vs/workbench/common/theme';
4848
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
4949
import { Codicon } from 'vs/base/common/codicons';
5050
import { MarshalledId } from 'vs/base/common/marshallingIds';
51+
import { commentThreadStateColorVar, getCommentThreadStateColor } from 'vs/workbench/contrib/comments/browser/commentColors';
5152

5253

5354
const collapseIcon = registerIcon('review-comment-collapse', Codicon.chevronUp, nls.localize('collapseIcon', 'Icon to collapse a review comment.'));
@@ -56,6 +57,9 @@ export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration';
5657
const COLLAPSE_ACTION_CLASS = 'expand-review-action ' + ThemeIcon.asClassName(collapseIcon);
5758
const COMMENT_SCHEME = 'comment';
5859

60+
function getCommentThreadWidgetStateColor(thread: languages.CommentThread, theme: IColorTheme): Color | undefined {
61+
return getCommentThreadStateColor(thread, theme) ?? theme.getColor(peekViewBorder);
62+
}
5963

6064
export function parseMouseDownInfoFromEvent(e: IEditorMouseEvent) {
6165
const range = e.target.range;
@@ -639,6 +643,16 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
639643
await this.update(this._commentThread);
640644
}));
641645

646+
this._commentThreadDisposables.push(this._commentThread.onDidChangeState(() => {
647+
const borderColor =
648+
getCommentThreadWidgetStateColor(this._commentThread, this.themeService.getColorTheme()) || Color.transparent;
649+
this.style({
650+
frameColor: borderColor,
651+
arrowColor: borderColor,
652+
});
653+
this.container?.style.setProperty(commentThreadStateColorVar, `${borderColor}`);
654+
}));
655+
642656
this._commentThreadDisposables.push(this._commentThread.onDidChangeLabel(_ => {
643657
this.createThreadLabel();
644658
}));
@@ -890,17 +904,16 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
890904
}
891905

892906
private _applyTheme(theme: IColorTheme) {
893-
const borderColor = theme.getColor(peekViewBorder);
907+
const borderColor = getCommentThreadWidgetStateColor(this._commentThread, theme) || Color.transparent;
894908
this.style({
895-
arrowColor: borderColor || Color.transparent,
896-
frameColor: borderColor || Color.transparent
909+
arrowColor: borderColor,
910+
frameColor: borderColor,
897911
});
898912

899913
const content: string[] = [];
900914

901-
if (borderColor) {
902-
content.push(`.monaco-editor .review-widget > .body { border-top: 1px solid ${borderColor} }`);
903-
}
915+
this.container?.style.setProperty(commentThreadStateColorVar, `${borderColor}`);
916+
content.push(`.monaco-editor .review-widget > .body { border-top: 1px solid var(${commentThreadStateColorVar}) }`);
904917

905918
const linkColor = theme.getColor(textLinkForeground);
906919
if (linkColor) {

0 commit comments

Comments
 (0)