Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show hover feedback on commentable lines #144834

Merged
merged 2 commits into from
Mar 10, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { coalesce, findFirstInSorted } from 'vs/base/common/arrays';
import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import 'vs/css!./media/review';
import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, isCodeEditor, isDiffEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
import { EditorAction, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
Expand Down Expand Up @@ -40,6 +40,7 @@ import { IViewsService } from 'vs/workbench/common/views';
import { COMMENTS_VIEW_ID } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { COMMENTS_SECTION, ICommentsConfiguration } from 'vs/workbench/contrib/comments/common/commentsConfiguration';
import { Emitter } from 'vs/base/common/event';

export const ID = 'editor.contrib.review';

Expand Down Expand Up @@ -67,7 +68,7 @@ class CommentingRangeDecoration {
return this._decorationId;
}

constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _label: string | undefined, private _range: IRange, commentingOptions: ModelDecorationOptions, private commentingRangesInfo: languages.CommentingRanges) {
constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _label: string | undefined, private _range: IRange, commentingOptions: ModelDecorationOptions, private commentingRangesInfo: languages.CommentingRanges, public readonly isHover: boolean = false) {
const startLineNumber = _range.startLineNumber;
const endLineNumber = _range.endLineNumber;
let commentingRangeDecorations = [{
Expand Down Expand Up @@ -100,8 +101,14 @@ class CommentingRangeDecoration {
}
class CommentingRangeDecorator {

private decorationOptions: ModelDecorationOptions;
private decorationOptions!: ModelDecorationOptions;
private hoverDecorationOptions!: ModelDecorationOptions;
private commentingRangeDecorations: CommentingRangeDecoration[] = [];
private _editor: ICodeEditor | undefined;
private _infos: ICommentInfo[] | undefined;
private _lastHover: number = -1;
private _onDidChangeDecorationsCount: Emitter<number> = new Emitter();
public readonly onDidChangeDecorationsCount = this._onDidChangeDecorationsCount.event;

constructor() {
const decorationOptions: IModelDecorationOptions = {
Expand All @@ -111,9 +118,30 @@ class CommentingRangeDecorator {
};

this.decorationOptions = ModelDecorationOptions.createDynamic(decorationOptions);

const hoverDecorationOptions: IModelDecorationOptions = {
description: 'commenting-range-decorator',
isWholeLine: true,
linesDecorationsClassName: `comment-range-glyph comment-diff-added line-hover`
};

this.hoverDecorationOptions = ModelDecorationOptions.createDynamic(hoverDecorationOptions);
}

public updateHover(hoverLine?: number) {
if (this._editor && this._infos && (hoverLine !== this._lastHover)) {
this._doUpdate(this._editor, this._infos, hoverLine);
}
this._lastHover = hoverLine ?? -1;
}

public update(editor: ICodeEditor, commentInfos: ICommentInfo[]) {
this._editor = editor;
this._infos = commentInfos;
this._doUpdate(editor, commentInfos);
}

private _doUpdate(editor: ICodeEditor, commentInfos: ICommentInfo[], hoverLine: number = -1) {
let model = editor.getModel();
if (!model) {
return;
Expand All @@ -122,22 +150,47 @@ class CommentingRangeDecorator {
let commentingRangeDecorations: CommentingRangeDecoration[] = [];
for (const info of commentInfos) {
info.commentingRanges.ranges.forEach(range => {
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, this.decorationOptions, info.commentingRanges));
if ((range.startLineNumber <= hoverLine) && (range.endLineNumber >= hoverLine)) {
const beforeRange = new Range(range.startLineNumber, 1, hoverLine, 1);
const hoverRange = new Range(hoverLine, 1, hoverLine, 1);
const afterRange = new Range(hoverLine, 1, range.endLineNumber, 1);
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, beforeRange, this.decorationOptions, info.commentingRanges, true));
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, hoverRange, this.hoverDecorationOptions, info.commentingRanges, true));
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, afterRange, this.decorationOptions, info.commentingRanges, true));
} else {
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, this.decorationOptions, info.commentingRanges));
}
});
}

let oldDecorations = this.commentingRangeDecorations.map(decoration => decoration.id);
editor.deltaDecorations(oldDecorations, []);

const rangesDifference = this.commentingRangeDecorations.length - commentingRangeDecorations.length;
this.commentingRangeDecorations = commentingRangeDecorations;
if (rangesDifference) {
this._onDidChangeDecorationsCount.fire(this.commentingRangeDecorations.length);
}
}

public getMatchedCommentAction(line: number) {
// keys is ownerId
const foundHoverActions = new Map<string, languages.CommentingRanges>();
let result = [];
for (const decoration of this.commentingRangeDecorations) {
const range = decoration.getActiveRange();
if (range && range.startLineNumber <= line && line <= range.endLineNumber) {
result.push(decoration.getCommentAction());
// We can have 3 commenting ranges that match from the same owner because of how
// the line hover decoration is done. We only want to use the action from 1 of them.
const action = decoration.getCommentAction();
if (decoration.isHover) {
if (foundHoverActions.get(action.ownerId) === action.commentingRangesInfo) {
continue;
} else {
foundHoverActions.set(action.ownerId, action.commentingRangesInfo);
}
}
result.push(action);
}
}

Expand All @@ -164,6 +217,7 @@ export class CommentController implements IEditorContribution {
private _computeCommentingRangePromise!: CancelablePromise<ICommentInfo[]> | null;
private _computeCommentingRangeScheduler!: Delayer<Array<ICommentInfo | null>> | null;
private _pendingCommentCache: { [key: string]: { [key: string]: string } };
private _editorMouseDisposable: IDisposable | undefined;

constructor(
editor: ICodeEditor,
Expand All @@ -187,6 +241,13 @@ export class CommentController implements IEditorContribution {
this.editor = editor;

this._commentingRangeDecorator = new CommentingRangeDecorator();
this.globalToDispose.add(this._commentingRangeDecorator.onDidChangeDecorationsCount(count => {
if (count === 0) {
this.clearMouseMoveListener();
} else if (!this._editorMouseDisposable) {
this.registerMouseMoveListener();
}
}));

this.globalToDispose.add(this.commentService.onDidDeleteDataProvider(ownerId => {
delete this._pendingCommentCache[ownerId];
Expand All @@ -207,6 +268,19 @@ export class CommentController implements IEditorContribution {
this.beginCompute();
}

private registerMouseMoveListener() {
this._editorMouseDisposable = this.editor.onMouseMove(e => this.onEditorMouseMove(e));
}

private clearMouseMoveListener() {
this._editorMouseDisposable?.dispose();
this._editorMouseDisposable = undefined;
}

private onEditorMouseMove(e: IEditorMouseEvent): void {
this._commentingRangeDecorator.updateHover(e.target.position?.lineNumber);
}

private beginCompute(): Promise<void> {
this._computePromise = createCancelablePromise(token => {
const editorURI = this.editor && this.editor.hasModel() && this.editor.getModel().uri;
Expand Down Expand Up @@ -323,6 +397,7 @@ export class CommentController implements IEditorContribution {
public dispose(): void {
this.globalToDispose.dispose();
this.localToDispose.dispose();
this._editorMouseDisposable?.dispose();

this._commentWidgets.forEach(widget => widget.dispose());

Expand All @@ -336,6 +411,10 @@ export class CommentController implements IEditorContribution {

this.localToDispose.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
this.localToDispose.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
if (this._editorMouseDisposable) {
this.clearMouseMoveListener();
this.registerMouseMoveListener();
}

this._computeCommentingRangeScheduler = new Delayer<ICommentInfo[]>(200);
this.localToDispose.add({
Expand Down
4 changes: 3 additions & 1 deletion src/vs/workbench/contrib/comments/browser/media/review.css
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ div.preview.inline .monaco-editor .comment-range-glyph {
}

.monaco-editor .margin-view-overlays > div:hover > .comment-range-glyph.comment-diff-added:before,
.monaco-editor .margin-view-overlays .comment-range-glyph.comment-diff-added.line-hover:before,
.monaco-editor .comment-range-glyph.comment-thread:before {
position: absolute;
height: 100%;
Expand All @@ -402,7 +403,8 @@ div.preview.inline .monaco-editor .comment-range-glyph {
justify-content: center;
}

.monaco-editor .margin-view-overlays > div:hover > .comment-range-glyph.comment-diff-added:before {
.monaco-editor .margin-view-overlays > div:hover > .comment-range-glyph.comment-diff-added:before,
.monaco-editor .margin-view-overlays .comment-range-glyph.comment-diff-added.line-hover:before {
content: "+";
}

Expand Down