@opensumi/ide-comments 3.5.1-next-1730882731.0 → 3.5.1-next-1730963433.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,8 @@
1
- import { action, autorun, computed, makeObservable, observable, runInAction } from 'mobx';
2
-
3
1
  import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
4
2
  import { Disposable, Emitter, IContextKeyService, IRange, URI, localize, uuid } from '@opensumi/ide-core-browser';
5
3
  import { ResourceContextKey } from '@opensumi/ide-core-browser/lib/contextkey/resource';
6
4
  import { EditorCollectionService, IEditor } from '@opensumi/ide-editor';
5
+ import { autorun, derived, observableValue, transaction } from '@opensumi/ide-monaco/lib/common/observable';
7
6
 
8
7
  import {
9
8
  CommentThreadCollapsibleState,
@@ -33,17 +32,10 @@ export class CommentsThread extends Disposable implements ICommentsThread {
33
32
  @Autowired(INJECTOR_TOKEN)
34
33
  private readonly injector: Injector;
35
34
 
36
- @observable.shallow
37
- public comments: IThreadComment[] = [];
38
-
39
- @observable
40
- public label: string | undefined;
41
-
42
- @observable
43
- private _readOnly = false;
44
-
45
- @observable
46
- public isCollapsed: boolean;
35
+ public readonly comments = observableValue<IThreadComment[]>(this, []);
36
+ public readonly label = observableValue<string | undefined>(this, undefined);
37
+ public readonly readOnly = observableValue<boolean>(this, false);
38
+ public readonly isCollapsed = observableValue<boolean>(this, false);
47
39
 
48
40
  public data: any;
49
41
 
@@ -78,7 +70,6 @@ export class CommentsThread extends Disposable implements ICommentsThread {
78
70
  public options: ICommentsThreadOptions,
79
71
  ) {
80
72
  super();
81
- makeObservable(this);
82
73
  this.updateComments(
83
74
  options.comments
84
75
  ? options.comments.map((comment) => ({
@@ -93,33 +84,40 @@ export class CommentsThread extends Disposable implements ICommentsThread {
93
84
  const resourceContext = new ResourceContextKey(this._contextKeyService);
94
85
  resourceContext.set(uri);
95
86
  this._contextKeyService.createKey<string>('thread', options.contextValue);
96
- this.readOnly = !!options.readOnly;
97
- this.label = options.label;
98
- this.isCollapsed = !!this.options.isCollapsed;
87
+ transaction((tx) => {
88
+ this.setReadOnly(!!options.readOnly);
89
+ this.label.set(options.label, tx);
90
+ this.isCollapsed.set(!!this.options.isCollapsed, tx);
91
+ });
99
92
  const threadsLengthContext = this._contextKeyService.createKey<number>(
100
93
  'threadsLength',
101
94
  this.commentsService.getThreadsByUri(uri).length,
102
95
  );
103
- const commentsLengthContext = this._contextKeyService.createKey<number>('commentsLength', this.comments.length);
96
+ const comments = this.comments.get();
97
+ const commentsLengthContext = this._contextKeyService.createKey<number>('commentsLength', comments.length);
104
98
  // vscode 用于判断 thread 是否为空
105
99
  const commentThreadIsEmptyContext = this._contextKeyService.createKey<boolean>(
106
100
  'commentThreadIsEmpty',
107
- !this.comments.length,
101
+ !comments.length,
108
102
  );
109
103
  // vscode 用于判断是否为当前 controller 注册
110
104
  this._contextKeyService.createKey<string>('commentController', providerId);
111
105
  // 监听 comments 的变化
112
- autorun(() => {
113
- commentsLengthContext.set(this.comments.length);
114
- commentThreadIsEmptyContext.set(!this.comments.length);
115
- });
116
- autorun(() => {
117
- if (this.isCollapsed) {
118
- this.hideAll();
119
- } else {
120
- this.showAll();
121
- }
122
- });
106
+ this.addDispose(
107
+ autorun((reader) => {
108
+ const length = this.comments.read(reader).length;
109
+
110
+ commentsLengthContext.set(length);
111
+ commentThreadIsEmptyContext.set(!length);
112
+
113
+ const isCollapsed = this.isCollapsed.read(reader);
114
+ if (isCollapsed) {
115
+ this.hideAll();
116
+ } else {
117
+ this.showAll();
118
+ }
119
+ }),
120
+ );
123
121
  // 监听每次 thread 的变化,重新设置 threadsLength
124
122
  this.addDispose(
125
123
  this.commentsService.onThreadsChanged((thread) => {
@@ -136,9 +134,10 @@ export class CommentsThread extends Disposable implements ICommentsThread {
136
134
  this.onDidChangeEmitter.fire();
137
135
  }
138
136
 
139
- @action
140
137
  updateComments(comments: IThreadComment[]) {
141
- this.comments = comments;
138
+ transaction((tx) => {
139
+ this.comments.set(comments, tx);
140
+ });
142
141
  }
143
142
 
144
143
  getWidgetByEditor(editor: IEditor): ICommentsZoneWidget | undefined {
@@ -153,28 +152,26 @@ export class CommentsThread extends Disposable implements ICommentsThread {
153
152
  return this._contextKeyService;
154
153
  }
155
154
 
156
- @computed
157
- get readOnly() {
158
- return this._readOnly;
159
- }
160
-
161
- set readOnly(readOnly: boolean) {
162
- runInAction(() => (this._readOnly = readOnly));
163
- this._contextKeyService.createKey<boolean>('readOnly', this._readOnly);
155
+ setReadOnly(readOnly: boolean) {
156
+ transaction((tx) => {
157
+ this.readOnly.set(readOnly, tx);
158
+ this._contextKeyService.createKey<boolean>('readOnly', readOnly);
159
+ });
164
160
  }
165
161
 
166
- @computed
167
- get threadHeaderTitle() {
168
- if (this.label) {
169
- return this.label;
162
+ readonly threadHeaderTitle = derived(this, (reader) => {
163
+ const label = this.label.read(reader);
164
+ if (label) {
165
+ return label;
170
166
  }
171
- if (this.comments.length) {
172
- const commentAuthors = new Set<string>(this.comments.map((comment) => `@${comment.author.name}`));
167
+ const comments = this.comments.read(reader);
168
+ if (comments.length) {
169
+ const commentAuthors = new Set<string>(comments.map((comment) => `@${comment.author.name}`));
173
170
  return `${localize('comments.participants')}: ` + [...commentAuthors].join(' ');
174
171
  } else {
175
172
  return localize('comments.zone.title');
176
173
  }
177
- }
174
+ });
178
175
 
179
176
  private getEditorsByUri(uri: URI): IEditor[] {
180
177
  return this.editorCollectionService.listEditors().filter((editor) => editor.currentUri?.isEqual(uri));
@@ -207,7 +204,8 @@ export class CommentsThread extends Disposable implements ICommentsThread {
207
204
  }
208
205
 
209
206
  public toggle = (editor: IEditor) => {
210
- if (this.comments.length > 0) {
207
+ const comments = this.comments.get();
208
+ if (comments.length > 0) {
211
209
  const widget = this.widgets.get(editor);
212
210
  if (widget) {
213
211
  widget.toggle();
@@ -283,18 +281,20 @@ export class CommentsThread extends Disposable implements ICommentsThread {
283
281
  }
284
282
  }
285
283
 
286
- @action
287
284
  public showAll() {
288
- this.isCollapsed = false;
285
+ transaction((tx) => {
286
+ this.isCollapsed.set(false, tx);
287
+ });
289
288
  for (const [, widget] of this.widgets) {
290
289
  widget.show();
291
290
  }
292
291
  this.onDidChangeCollapsibleStateEmitter.fire(CommentThreadCollapsibleState.Expanded);
293
292
  }
294
293
 
295
- @action
296
294
  public hideAll(isDospose?: boolean) {
297
- this.isCollapsed = true;
295
+ transaction((tx) => {
296
+ this.isCollapsed.set(true, tx);
297
+ });
298
298
  for (const [editor, widget] of this.widgets) {
299
299
  if (isDospose) {
300
300
  // 如果 thread 出现在当前 editor 则不隐藏
@@ -306,23 +306,32 @@ export class CommentsThread extends Disposable implements ICommentsThread {
306
306
  this.onDidChangeCollapsibleStateEmitter.fire(CommentThreadCollapsibleState.Collapsed);
307
307
  }
308
308
 
309
- @action
310
309
  public addComment(...comments: IComment[]) {
311
- this.comments.push(
312
- ...comments.map((comment) => ({
313
- ...comment,
314
- id: uuid(),
315
- })),
316
- );
310
+ const preComments = this.comments.get();
311
+
312
+ transaction((tx) => {
313
+ this.comments.set(
314
+ [
315
+ ...preComments,
316
+ ...comments.map((comment) => ({
317
+ ...comment,
318
+ id: uuid(),
319
+ })),
320
+ ],
321
+ tx,
322
+ );
323
+ });
324
+
317
325
  this.onDidChangeEmitter.fire();
318
326
  }
319
327
 
320
- @action
321
328
  public removeComment(comment: IComment) {
322
- const index = this.comments.findIndex((c) => c === comment);
323
- if (index !== -1) {
324
- this.comments.splice(index, 1);
325
- }
329
+ transaction((tx) => {
330
+ this.comments.set(
331
+ this.comments.get().filter((c) => c !== comment),
332
+ tx,
333
+ );
334
+ });
326
335
  this.onDidChangeEmitter.fire();
327
336
  }
328
337
 
@@ -1,10 +1,17 @@
1
1
  import cls from 'classnames';
2
- import { observer } from 'mobx-react-lite';
3
2
  import React from 'react';
4
3
  import ReactDOM from 'react-dom/client';
5
4
 
6
5
  import { Autowired, INJECTOR_TOKEN, Injectable } from '@opensumi/di';
7
- import { AppConfig, ConfigProvider, Emitter, Event, localize, useInjectable } from '@opensumi/ide-core-browser';
6
+ import {
7
+ AppConfig,
8
+ ConfigProvider,
9
+ Emitter,
10
+ Event,
11
+ localize,
12
+ useAutorun,
13
+ useInjectable,
14
+ } from '@opensumi/ide-core-browser';
8
15
  import { InlineActionBar } from '@opensumi/ide-core-browser/lib/components/actions';
9
16
  import { MenuId } from '@opensumi/ide-core-browser/lib/menu/next';
10
17
  import { IEditor } from '@opensumi/ide-editor';
@@ -28,8 +35,11 @@ export interface ICommentProps {
28
35
  widget: ICommentsZoneWidget;
29
36
  }
30
37
 
31
- const CommentsZone: React.FC<ICommentProps> = observer(({ thread, widget }) => {
32
- const { comments, threadHeaderTitle, contextKeyService } = thread;
38
+ const CommentsZone: React.FC<ICommentProps> = ({ thread, widget }) => {
39
+ const { contextKeyService } = thread;
40
+ const comments = useAutorun(thread.comments);
41
+ const threadHeaderTitle = useAutorun(thread.threadHeaderTitle);
42
+
33
43
  const injector = useInjectable(INJECTOR_TOKEN);
34
44
  const commentsZoneService: CommentsZoneService = injector.get(CommentsZoneService, [thread]);
35
45
  const commentsFeatureRegistry = useInjectable<ICommentsFeatureRegistry>(ICommentsFeatureRegistry);
@@ -154,7 +164,7 @@ const CommentsZone: React.FC<ICommentProps> = observer(({ thread, widget }) => {
154
164
  </div>
155
165
  </div>
156
166
  );
157
- });
167
+ };
158
168
 
159
169
  @Injectable({ multiple: true })
160
170
  export class CommentsZoneWidget extends ResizeZoneWidget implements ICommentsZoneWidget {
@@ -182,7 +192,7 @@ export class CommentsZoneWidget extends ResizeZoneWidget implements ICommentsZon
182
192
  });
183
193
  this._editor = editor;
184
194
  this._wrapper = document.createElement('div');
185
- this._isShow = !thread.isCollapsed;
195
+ this._isShow = !thread.isCollapsed.get();
186
196
  this._container.appendChild(this._wrapper);
187
197
  this.observeContainer(this._wrapper);
188
198
  const customRender = this.commentsFeatureRegistry.getZoneWidgetRender();
@@ -101,7 +101,7 @@ export class CommentsBrowserContribution
101
101
  {
102
102
  execute: (threadTitle: ICommentThreadTitle) => {
103
103
  const { thread, widget } = threadTitle;
104
- if (!thread.comments.length) {
104
+ if (!thread.comments.get().length) {
105
105
  thread.dispose();
106
106
  } else {
107
107
  if (widget.isShow) {
@@ -160,8 +160,8 @@ export class CommentsService extends Disposable implements ICommentsService {
160
160
  */
161
161
  private createThreadDecoration(thread: ICommentsThread): model.IModelDecorationOptions {
162
162
  // 对于新增的空的 thread,默认显示当前用户的头像,否则使用第一个用户的头像
163
- const avatar =
164
- thread.comments.length === 0 ? this.currentAuthorAvatar : thread.comments[0].author.iconPath?.toString();
163
+ const comments = thread.comments.get();
164
+ const avatar = comments.length === 0 ? this.currentAuthorAvatar : comments[0].author.iconPath?.toString();
165
165
  const icon = avatar
166
166
  ? `${this.iconService.fromIcon('', avatar, IconType.Background)} avatar-icon`
167
167
  : this.iconService.fromString('$(comment)');
@@ -190,7 +190,7 @@ export class CommentsService extends Disposable implements ICommentsService {
190
190
 
191
191
  if (thread) {
192
192
  const range = thread.range;
193
- if (!thread.isCollapsed) {
193
+ if (!thread.isCollapsed.get()) {
194
194
  this.currentThreadCollapseStateListener = thread.onDidChangeCollapsibleState((state) => {
195
195
  if (state === CommentThreadCollapsibleState.Collapsed) {
196
196
  this.updateActiveThreadDecoration(undefined);
@@ -375,7 +375,7 @@ export class CommentsService extends Disposable implements ICommentsService {
375
375
  if (
376
376
  !this.commentsThreads.some(
377
377
  (thread) =>
378
- thread.comments.length === 0 &&
378
+ thread.comments.get().length === 0 &&
379
379
  editor.currentUri &&
380
380
  thread.uri.isEqual(editor.currentUri) &&
381
381
  thread.range.startLineNumber === range.startLineNumber &&
@@ -399,7 +399,7 @@ export class CommentsService extends Disposable implements ICommentsService {
399
399
  if (
400
400
  !this.commentsThreads.some(
401
401
  (thread) =>
402
- thread.comments.length === 0 &&
402
+ thread.comments.get().length === 0 &&
403
403
  editor.currentUri &&
404
404
  thread.uri.isEqual(editor.currentUri) &&
405
405
  thread.range.startLineNumber === range.startLineNumber &&
@@ -568,7 +568,7 @@ export class CommentsService extends Disposable implements ICommentsService {
568
568
  const shouldShowComments = editor.currentUri ? this.shouldShowCommentsSchemes.has(editor.currentUri.scheme) : false;
569
569
 
570
570
  const hasComments = this.commentsThreads.some(
571
- (thread) => editor.currentUri && thread.uri.isEqual(editor.currentUri) && thread.comments.length > 0,
571
+ (thread) => editor.currentUri && thread.uri.isEqual(editor.currentUri) && thread.comments.get().length > 0,
572
572
  );
573
573
 
574
574
  const hasCommentsOrRanges = shouldShowComments || hasComments;
@@ -863,7 +863,7 @@ export class CommentsService extends Disposable implements ICommentsService {
863
863
  public handleCommentFileNode(parent: CommentRoot): CommentFileNode[] {
864
864
  const childs: CommentFileNode[] = [];
865
865
 
866
- const commentThreads = [...this.threads.values()].filter((thread) => thread.comments.length);
866
+ const commentThreads = [...this.threads.values()].filter((thread) => thread.comments.get().length);
867
867
  const threadUris = groupBy(commentThreads, (thread: ICommentsThread) => thread.uri);
868
868
  Object.keys(threadUris).map((uri) => {
869
869
  const threads: ICommentsThread[] = threadUris[uri];
@@ -893,7 +893,8 @@ export class CommentsService extends Disposable implements ICommentsService {
893
893
  const childs: CommentContentNode[] = [];
894
894
 
895
895
  for (const thread of (parent as CommentFileNode).threads) {
896
- const [first] = thread.comments;
896
+ const comments = thread.comments.get();
897
+ const [first] = comments;
897
898
  const comment = typeof first.body === 'string' ? first.body : first.body.value;
898
899
  let description = `[Ln ${thread.range.startLineNumber}]`;
899
900
  if (thread.range.startLineNumber !== thread.range.endLineNumber) {
@@ -922,7 +923,8 @@ export class CommentsService extends Disposable implements ICommentsService {
922
923
  const childs: CommentReplyNode[] = [];
923
924
 
924
925
  const thread = parent.thread;
925
- const [_, ...others] = thread.comments;
926
+ const comments = thread.comments.get();
927
+ const [_, ...others] = comments;
926
928
  const lastReply = others[others.length - 1].author.name;
927
929
  childs.push(
928
930
  new CommentReplyNode(
@@ -1020,7 +1022,7 @@ export class CommentsService extends Disposable implements ICommentsService {
1020
1022
  this.commentsThreads
1021
1023
  .map((thread) => {
1022
1024
  if (thread.uri.isEqual(uri)) {
1023
- if (thread.comments.length) {
1025
+ if (thread.comments.get().length) {
1024
1026
  // 存在评论内容 恢复之前的现场
1025
1027
  thread.showWidgetsIfShowed();
1026
1028
  }
@@ -11,6 +11,7 @@ import {
11
11
  URI,
12
12
  } from '@opensumi/ide-core-browser';
13
13
  import { IEditor } from '@opensumi/ide-editor';
14
+ import { IObservable, ISettableObservable } from '@opensumi/ide-monaco/lib/common/observable';
14
15
 
15
16
  import type { ITree, ITreeNode } from '@opensumi/ide-components';
16
17
  import type { IEditorDocumentModel } from '@opensumi/ide-editor/lib/common/editor';
@@ -470,7 +471,7 @@ export interface ICommentsThread extends IDisposable {
470
471
  /**
471
472
  * 评论
472
473
  */
473
- comments: IThreadComment[];
474
+ comments: IObservable<IThreadComment[]>;
474
475
  /**
475
476
  * 当前 thread 的 uri
476
477
  */
@@ -482,7 +483,7 @@ export interface ICommentsThread extends IDisposable {
482
483
  /**
483
484
  * 是否折叠,默认为 false
484
485
  */
485
- isCollapsed: boolean;
486
+ isCollapsed: ISettableObservable<boolean>;
486
487
  /**
487
488
  * 附属数据
488
489
  */
@@ -494,7 +495,7 @@ export interface ICommentsThread extends IDisposable {
494
495
  /**
495
496
  * 在 header 组件显示的文案
496
497
  */
497
- label?: string;
498
+ label?: ISettableObservable<string | undefined>;
498
499
  /**
499
500
  * thread 参数
500
501
  */
@@ -502,11 +503,11 @@ export interface ICommentsThread extends IDisposable {
502
503
  /**
503
504
  * thread 头部文案
504
505
  */
505
- threadHeaderTitle: string;
506
+ threadHeaderTitle: IObservable<string>;
506
507
  /**
507
508
  * 是否是只读
508
509
  */
509
- readOnly: boolean;
510
+ readOnly: IObservable<boolean>;
510
511
  /**
511
512
  * 评论面板的 context key service
512
513
  */
@@ -582,6 +583,10 @@ export interface ICommentsThread extends IDisposable {
582
583
  * dispise 时会执行
583
584
  */
584
585
  onDispose: Event<void>;
586
+ /**
587
+ * 设置只读状态
588
+ */
589
+ setReadOnly(readOnly: boolean): void;
585
590
  }
586
591
 
587
592
  export interface ICommentsThreadOptions {