Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions packages/comments/__test__/browser/comment.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { Injector } from '@opensumi/di';
import {
CommentContentNode,
CommentFileNode,
CommentReplyNode,
CommentRoot,
} from '@opensumi/ide-comments/lib/browser/tree/tree-node.defined';
import { IContextKeyService } from '@opensumi/ide-core-browser';
import { URI, positionToRange, Disposable } from '@opensumi/ide-core-common';
import { URI, positionToRange, Disposable, Emitter } from '@opensumi/ide-core-common';
import { IEditor, EditorCollectionService, ResourceService } from '@opensumi/ide-editor';
import { IEditorDecorationCollectionService } from '@opensumi/ide-editor/lib/browser';
import { ResourceServiceImpl } from '@opensumi/ide-editor/lib/browser/resource.service';
Expand Down Expand Up @@ -35,6 +34,18 @@ describe('comment service test', () => {
minimapWidth: 10,
minimapLeft: 10,
}),
getOption: () => 10,
createDecorationsCollection(decorations) {
return {
onDidChange: new Emitter().event,
clear: () => {},
length: 0,
set: () => {},
getRange: () => null,
getRanges: () => [],
has: () => true,
};
},
});
currentEditor = mockService({ monacoEditor });
injector = createBrowserInjector(
Expand Down
2 changes: 1 addition & 1 deletion packages/comments/src/browser/comments-thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export class CommentsThread extends Disposable implements ICommentsThread {
}

private addWidgetByEditor(editor: IEditor) {
const widget = this.injector.get(CommentsZoneWidget, [editor, this]);
const widget = this.injector.get(CommentsZoneWidget, [editor, this, { arrowColor: 'var(--peekView-border)' }]);
// 如果当前 widget 发生高度变化,通知同一个 同一个 editor 的其他 range 相同的 thread 也重新计算一下高度
this.addDispose(
widget.onChangeZoneWidget(() => {
Expand Down
6 changes: 3 additions & 3 deletions packages/comments/src/browser/comments-zone.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ConfigProvider, localize, AppConfig, useInjectable, Event, Emitter } fr
import { InlineActionBar } from '@opensumi/ide-core-browser/lib/components/actions';
import { MenuId } from '@opensumi/ide-core-browser/lib/menu/next';
import { IEditor } from '@opensumi/ide-editor';
import { ResizeZoneWidget } from '@opensumi/ide-monaco-enhance';
import { ResizeZoneWidget, IOptions } from '@opensumi/ide-monaco-enhance';

import {
ICommentReply,
Expand Down Expand Up @@ -152,8 +152,8 @@ export class CommentsZoneWidget extends ResizeZoneWidget implements ICommentsZon
private _onHide = new Emitter<void>();
public onHide: Event<void> = this._onHide.event;

constructor(editor: IEditor, thread: ICommentsThread) {
super(editor.monacoEditor, thread.range);
constructor(editor: IEditor, thread: ICommentsThread, options?: IOptions) {
super(editor.monacoEditor, thread.range, options);
this._editor = editor;
this._wrapper = document.createElement('div');
this._isShow = !thread.isCollapsed;
Expand Down
4 changes: 3 additions & 1 deletion packages/comments/src/browser/comments.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ export class CommentsService extends Disposable implements ICommentsService {
// 对于新增的空的 thread,默认显示当前用户的头像,否则使用第一个用户的头像
const avatar =
thread.comments.length === 0 ? this.currentAuthorAvatar : thread.comments[0].author.iconPath?.toString();
const icon = avatar ? this.iconService.fromIcon('', avatar, IconType.Background) : getIcon('message');
const icon = avatar
? this.iconService.fromIcon('', avatar, IconType.Background)
: this.iconService.fromString('$(comment-unresolved)');
const decorationOptions: model.IModelDecorationOptions = {
description: 'comments-thread-decoration',
// 创建评论显示在 glyph margin 处
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Disposable, IFileServiceClient } from '@opensumi/ide-core-browser';
import { IContextKeyService } from '@opensumi/ide-core-browser';
import { Emitter } from '@opensumi/ide-core-common';
import { DebugEditor, IDebugSessionManager } from '@opensumi/ide-debug';
import { DebugBreakpointWidget } from '@opensumi/ide-debug/lib/browser/editor';
import { createBrowserInjector } from '@opensumi/ide-dev-tool/src/injector-helper';
Expand All @@ -15,6 +16,18 @@ describe('Debug Breakpoint Widget', () => {
onDidLayoutChange: jest.fn(() => Disposable.create(() => {})),
getLayoutInfo: jest.fn(() => ({ width: 100, height: 100 })),
changeViewZones: jest.fn(() => Disposable.create(() => {})),
getOption: () => 10,
createDecorationsCollection(decorations) {
return {
onDidChange: new Emitter().event,
clear: () => {},
length: 0,
set: () => {},
getRange: () => null,
getRanges: () => [],
has: () => true,
};
},
};

beforeAll(() => {
Expand Down
129 changes: 112 additions & 17 deletions packages/monaco-enhance/src/browser/zone-widget.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { DomListener, IRange } from '@opensumi/ide-core-browser';
import { Disposable, IDisposable, Event, Emitter, uuid } from '@opensumi/ide-core-browser';
import type { ICodeEditor as IMonacoCodeEditor } from '@opensumi/ide-monaco/lib/browser/monaco-api/types';
import { IdGenerator } from '@opensumi/ide-core-common/lib/id-generator';
import type { ICodeEditor, ICodeEditor as IMonacoCodeEditor } from '@opensumi/ide-monaco/lib/browser/monaco-api/types';
import { createCSSRule, removeCSSRulesContainingSelector } from '@opensumi/monaco-editor-core/esm/vs/base/browser/dom';
import {
Sash,
IHorizontalSashLayoutProvider,
Expand All @@ -9,6 +11,7 @@ import {
ISashEvent,
} from '@opensumi/monaco-editor-core/esm/vs/base/browser/ui/sash/sash';
import { EditorOption } from '@opensumi/monaco-editor-core/esm/vs/editor/common/config/editorOptions';
import { TrackedRangeStickiness } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model';
import * as monaco from '@opensumi/monaco-editor-core/esm/vs/editor/editor.api';

export class ViewZoneDelegate implements monaco.editor.IViewZone {
Expand Down Expand Up @@ -68,6 +71,70 @@ export class OverlayWidgetDelegate extends Disposable implements monaco.editor.I

export interface IOptions {
isResizeable?: boolean;
arrowColor?: string;
}

class Arrow {
private static readonly _IdGenerator = new IdGenerator('.arrow-decoration-');

private readonly _ruleName = Arrow._IdGenerator.nextId();
private readonly _decorations = this._editor.createDecorationsCollection();
private _color: string | null = 'rgba(0, 122, 204)';

private _height = -1;

constructor(private readonly _editor: ICodeEditor) {}

dispose(): void {
this.hide();
removeCSSRulesContainingSelector(this._ruleName);
}

set color(value: string) {
if (this._color !== value) {
this._color = value;
this._updateStyle();
}
}

set height(value: number) {
if (this._height !== value) {
this._height = value;
this._updateStyle();
}
}

private _updateStyle(): void {
removeCSSRulesContainingSelector(this._ruleName);
createCSSRule(
`.monaco-editor ${this._ruleName}`,
`border-style: solid; border-color: transparent; border-bottom-color: ${this._color}; border-width: ${this._height}px; bottom: 0px; margin-left: -${this._height}px; width: 0px !important; left: 0px !important;`,
);
}

show(where: IRange): void {
this._updateStyle();

if (where.startColumn === 1) {
// the arrow isn't pretty at column 1 and we need to push it out a little
where = { ...where, startLineNumber: where.startLineNumber, startColumn: 2 };
}

this._decorations.set([
{
range: where,
options: {
description: 'zone-widget-arrow',
className: this._ruleName,
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
},
},
]);
}

hide(): void {
this._decorations.clear();
}
}

/**
Expand All @@ -77,6 +144,8 @@ export interface IOptions {
* dispose 负责回收。
*/
export abstract class ZoneWidget extends Disposable implements IHorizontalSashLayoutProvider {
private _arrow: Arrow | null = null;

protected _container: HTMLDivElement;
// 宽度和左定位不需要继承下去,完全交给父容器控制
private width = 0;
Expand All @@ -101,9 +170,12 @@ export abstract class ZoneWidget extends Disposable implements IHorizontalSashLa
protected abstract _fillContainer(container: HTMLElement): void;

private _showImpl(where: monaco.IRange, heightInLines: number) {
const position = where;
const { startLineNumber: lineNumber, startColumn: column } = where;
const viewZoneDomNode = document.createElement('div');
const layoutInfo = this.editor.getLayoutInfo();
const lineHeight = this.editor.getOption(EditorOption.lineHeight);

viewZoneDomNode.style.overflow = 'hidden';

this.editor.changeViewZones((accessor) => {
Expand All @@ -129,6 +201,20 @@ export abstract class ZoneWidget extends Disposable implements IHorizontalSashLa
this.editor.addOverlayWidget(this._overlay);
});

if (!this._arrow) {
this._arrow = new Arrow(this.editor);
this.disposables.push(this._arrow);
}

if (this.options?.arrowColor) {
const arrowColor = this.options?.arrowColor?.toString();
this._arrow.color = arrowColor;
}

const arrowHeight = Math.round(lineHeight / 3);
this._arrow.height = arrowHeight;
this._arrow.show(position);

this.layout(layoutInfo);
}

Expand All @@ -147,12 +233,33 @@ export abstract class ZoneWidget extends Disposable implements IHorizontalSashLa
this._showImpl(where, heightInLines);
}

hide() {}
private hideImpl() {
if (this._viewZone) {
this.editor.changeViewZones((accessor) => {
if (this._viewZone) {
accessor.removeZone(this._viewZone.id);
this._viewZone = null;
}
});
}
if (this._overlay) {
this.editor.removeOverlayWidget(this._overlay);
this._overlay = null;
}
this._container.remove();
this._arrow?.hide();
}

hide() {
this.hideImpl();
}

create(): void {
this._fillContainer(this._container);
this._initSash();
this.applyStyle();
this._arrow = new Arrow(this.editor);
this.disposables.push(this._arrow);
}

private _getLeft(info: monaco.editor.EditorLayoutInfo): number {
Expand Down Expand Up @@ -275,19 +382,7 @@ export abstract class ZoneWidget extends Disposable implements IHorizontalSashLa
}

dispose() {
if (this._viewZone) {
this.editor.changeViewZones((accessor) => {
if (this._viewZone) {
accessor.removeZone(this._viewZone.id);
this._viewZone = null;
}
});
}
if (this._overlay) {
this.editor.removeOverlayWidget(this._overlay);
this._overlay = null;
}
this._container.remove();
this.hideImpl();
super.dispose();
}
}
Expand All @@ -305,8 +400,8 @@ export abstract class ResizeZoneWidget extends ZoneWidget {
public onFirstDisplay = Event.once(this.onDomNodeTop);
protected _isShow = false;

constructor(protected readonly editor: IMonacoCodeEditor, private range: monaco.IRange) {
super(editor);
constructor(protected readonly editor: IMonacoCodeEditor, private range: monaco.IRange, readonly options?: IOptions) {
super(editor, options);
this.lineHeight = this.editor.getOption(monaco.editor.EditorOption.lineHeight);
this.addDispose(
this.editor.onDidChangeConfiguration((e) => {
Expand Down
11 changes: 10 additions & 1 deletion packages/monaco/__mocks__/monaco/editor/code-editor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Emitter, Event, Disposable, IDisposable } from '@opensumi/ide-core-common';
import { IModelDecorationsChangedEvent } from '@opensumi/monaco-editor-core/esm/vs/editor/common/textModelEvents';
import * as monaco from '@opensumi/monaco-editor-core/esm/vs/editor/editor.api';
import { ContextKeyValue } from '@opensumi/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey';

Expand Down Expand Up @@ -41,7 +42,15 @@ export class MockedCodeEditor extends Disposable implements monaco.editor.ICodeE
createDecorationsCollection(
decorations?: monaco.editor.IModelDeltaDecoration[] | undefined,
): monaco.editor.IEditorDecorationsCollection {
throw new Error('Method not implemented.');
return {
onDidChange: new Emitter<IModelDecorationsChangedEvent>().event,
clear: () => {},
length: 0,
set: () => {},
getRange: () => null,
getRanges: () => [],
has: () => true,
};
}
onDidChangeHiddenAreas: monaco.IEvent<void>;
getDecorationsInRange(range: monaco.Range): monaco.editor.IModelDecoration[] | null {
Expand Down