Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/comments/browser/comments.contribution.ts
3296 views
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 nls from '../../../../nls.js';
7
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
8
import { Registry } from '../../../../platform/registry/common/platform.js';
9
import './commentsEditorContribution.js';
10
import { ICommentService, CommentService, IWorkspaceCommentThreadsEvent } from './commentService.js';
11
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../../platform/configuration/common/configurationRegistry.js';
12
import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
13
import { Disposable, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
14
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
15
import { Extensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from '../../../common/contributions.js';
16
import { IActivityService, NumberBadge } from '../../../services/activity/common/activity.js';
17
import { COMMENTS_VIEW_ID } from './commentsTreeViewer.js';
18
import { CommentThreadState } from '../../../../editor/common/languages.js';
19
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
20
import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
21
import { CONTEXT_KEY_HAS_COMMENTS, CONTEXT_KEY_SOME_COMMENTS_EXPANDED, CommentsPanel } from './commentsView.js';
22
import { ViewAction } from '../../../browser/parts/views/viewPane.js';
23
import { Codicon } from '../../../../base/common/codicons.js';
24
import { IEditorService } from '../../../services/editor/common/editorService.js';
25
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
26
import { revealCommentThread } from './commentsController.js';
27
import { MarshalledCommentThreadInternal } from '../../../common/comments.js';
28
import { accessibleViewCurrentProviderId, accessibleViewIsShown } from '../../accessibility/browser/accessibilityConfiguration.js';
29
import { AccessibleViewProviderId } from '../../../../platform/accessibility/browser/accessibleView.js';
30
import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
31
import { CommentsAccessibleView, CommentThreadAccessibleView } from './commentsAccessibleView.js';
32
import { CommentsAccessibilityHelp } from './commentsAccessibility.js';
33
34
registerAction2(class Collapse extends ViewAction<CommentsPanel> {
35
constructor() {
36
super({
37
viewId: COMMENTS_VIEW_ID,
38
id: 'comments.collapse',
39
title: nls.localize('collapseAll', "Collapse All"),
40
f1: false,
41
icon: Codicon.collapseAll,
42
menu: {
43
id: MenuId.ViewTitle,
44
group: 'navigation',
45
when: ContextKeyExpr.and(ContextKeyExpr.and(ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), CONTEXT_KEY_HAS_COMMENTS), CONTEXT_KEY_SOME_COMMENTS_EXPANDED),
46
order: 100
47
}
48
});
49
}
50
runInView(_accessor: ServicesAccessor, view: CommentsPanel) {
51
view.collapseAll();
52
}
53
});
54
55
registerAction2(class Expand extends ViewAction<CommentsPanel> {
56
constructor() {
57
super({
58
viewId: COMMENTS_VIEW_ID,
59
id: 'comments.expand',
60
title: nls.localize('expandAll', "Expand All"),
61
f1: false,
62
icon: Codicon.expandAll,
63
menu: {
64
id: MenuId.ViewTitle,
65
group: 'navigation',
66
when: ContextKeyExpr.and(ContextKeyExpr.and(ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), CONTEXT_KEY_HAS_COMMENTS), ContextKeyExpr.not(CONTEXT_KEY_SOME_COMMENTS_EXPANDED.key)),
67
order: 100
68
}
69
});
70
}
71
runInView(_accessor: ServicesAccessor, view: CommentsPanel) {
72
view.expandAll();
73
}
74
});
75
76
registerAction2(class Reply extends Action2 {
77
constructor() {
78
super({
79
id: 'comments.reply',
80
title: nls.localize('reply', "Reply"),
81
icon: Codicon.reply,
82
precondition: ContextKeyExpr.equals('canReply', true),
83
menu: [{
84
id: MenuId.CommentsViewThreadActions,
85
order: 100
86
},
87
{
88
id: MenuId.AccessibleView,
89
when: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.Comments)),
90
}]
91
});
92
}
93
94
override run(accessor: ServicesAccessor, marshalledCommentThread: MarshalledCommentThreadInternal): void {
95
const commentService = accessor.get(ICommentService);
96
const editorService = accessor.get(IEditorService);
97
const uriIdentityService = accessor.get(IUriIdentityService);
98
revealCommentThread(commentService, editorService, uriIdentityService, marshalledCommentThread.thread, marshalledCommentThread.thread.comments![marshalledCommentThread.thread.comments!.length - 1], true);
99
}
100
});
101
102
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
103
id: 'comments',
104
order: 20,
105
title: nls.localize('commentsConfigurationTitle', "Comments"),
106
type: 'object',
107
properties: {
108
'comments.openPanel': {
109
enum: ['neverOpen', 'openOnSessionStart', 'openOnSessionStartWithComments'],
110
default: 'openOnSessionStartWithComments',
111
description: nls.localize('openComments', "Controls when the comments panel should open."),
112
restricted: false,
113
markdownDeprecationMessage: nls.localize('comments.openPanel.deprecated', "This setting is deprecated in favor of `comments.openView`.")
114
},
115
'comments.openView': {
116
enum: ['never', 'file', 'firstFile', 'firstFileUnresolved'],
117
enumDescriptions: [nls.localize('comments.openView.never', "The comments view will never be opened."), nls.localize('comments.openView.file', "The comments view will open when a file with comments is active."), nls.localize('comments.openView.firstFile', "If the comments view has not been opened yet during this session it will open the first time during a session that a file with comments is active."), nls.localize('comments.openView.firstFileUnresolved', "If the comments view has not been opened yet during this session and the comment is not resolved, it will open the first time during a session that a file with comments is active.")],
118
default: 'firstFile',
119
description: nls.localize('comments.openView', "Controls when the comments view should open."),
120
restricted: false
121
},
122
'comments.useRelativeTime': {
123
type: 'boolean',
124
default: true,
125
description: nls.localize('useRelativeTime', "Determines if relative time will be used in comment timestamps (ex. '1 day ago').")
126
},
127
'comments.visible': {
128
type: 'boolean',
129
default: true,
130
description: nls.localize('comments.visible', "Controls the visibility of the comments bar and comment threads in editors that have commenting ranges and comments. Comments are still accessible via the Comments view and will cause commenting to be toggled on in the same way running the command \"Comments: Toggle Editor Commenting\" toggles comments.")
131
},
132
'comments.maxHeight': {
133
type: 'boolean',
134
default: true,
135
description: nls.localize('comments.maxHeight', "Controls whether the comments widget scrolls or expands.")
136
},
137
'comments.collapseOnResolve': {
138
type: 'boolean',
139
default: true,
140
description: nls.localize('collapseOnResolve', "Controls whether the comment thread should collapse when the thread is resolved.")
141
},
142
'comments.thread.confirmOnCollapse': {
143
type: 'string',
144
enum: ['whenHasUnsubmittedComments', 'never'],
145
enumDescriptions: [nls.localize('confirmOnCollapse.whenHasUnsubmittedComments', "Show a confirmation dialog when collapsing a comment thread with unsubmitted comments."), nls.localize('confirmOnCollapse.never', "Never show a confirmation dialog when collapsing a comment thread.")],
146
default: 'whenHasUnsubmittedComments',
147
description: nls.localize('confirmOnCollapse', "Controls whether a confirmation dialog is shown when collapsing a comment thread.")
148
}
149
}
150
});
151
152
registerSingleton(ICommentService, CommentService, InstantiationType.Delayed);
153
154
export class UnresolvedCommentsBadge extends Disposable implements IWorkbenchContribution {
155
private readonly activity = this._register(new MutableDisposable<IDisposable>());
156
private totalUnresolved = 0;
157
158
constructor(
159
@ICommentService private readonly _commentService: ICommentService,
160
@IActivityService private readonly activityService: IActivityService) {
161
super();
162
this._register(this._commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this));
163
this._register(this._commentService.onDidUpdateCommentThreads(this.onCommentsUpdated, this));
164
this._register(this._commentService.onDidDeleteDataProvider(this.onCommentsUpdated, this));
165
}
166
167
private onAllCommentsChanged(e: IWorkspaceCommentThreadsEvent): void {
168
let unresolved = 0;
169
for (const thread of e.commentThreads) {
170
if (thread.state === CommentThreadState.Unresolved) {
171
unresolved++;
172
}
173
}
174
this.updateBadge(unresolved);
175
}
176
177
private onCommentsUpdated(): void {
178
let unresolved = 0;
179
for (const resource of this._commentService.commentsModel.resourceCommentThreads) {
180
for (const thread of resource.commentThreads) {
181
if (thread.threadState === CommentThreadState.Unresolved) {
182
unresolved++;
183
}
184
}
185
}
186
this.updateBadge(unresolved);
187
}
188
189
private updateBadge(unresolved: number) {
190
if (unresolved === this.totalUnresolved) {
191
return;
192
}
193
194
this.totalUnresolved = unresolved;
195
const message = nls.localize('totalUnresolvedComments', '{0} Unresolved Comments', this.totalUnresolved);
196
this.activity.value = this.activityService.showViewActivity(COMMENTS_VIEW_ID, { badge: new NumberBadge(this.totalUnresolved, () => message) });
197
}
198
}
199
200
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(UnresolvedCommentsBadge, LifecyclePhase.Eventually);
201
202
AccessibleViewRegistry.register(new CommentsAccessibleView());
203
AccessibleViewRegistry.register(new CommentThreadAccessibleView());
204
AccessibleViewRegistry.register(new CommentsAccessibilityHelp());
205
206