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

Expose comments input as text document #209512

Merged
merged 5 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions src/vs/base/common/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ export namespace Schemas {
*/
export const vscodeSourceControl = 'vscode-scm';

/**
* Scheme used for input box for creating comments.
*/
export const commentsInput = 'vscode-comments-input';
mjbvz marked this conversation as resolved.
Show resolved Hide resolved

/**
* Scheme used for special rendering of settings in the release notes
*/
Expand Down
9 changes: 8 additions & 1 deletion src/vs/platform/markers/common/markerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { IMarker, IMarkerData, IMarkerService, IResourceMarker, MarkerSeverity, MarkerStatistics } from './markers';

export const unsupportedSchemas = new Set([Schemas.inMemory, Schemas.vscodeSourceControl, Schemas.walkThrough, Schemas.walkThroughSnippet, Schemas.vscodeChatCodeBlock]);
export const unsupportedSchemas = new Set([
Schemas.inMemory,
Schemas.vscodeSourceControl,
Schemas.walkThrough,
Schemas.walkThroughSnippet,
Schemas.vscodeChatCodeBlock,
Schemas.commentsInput,
]);

class DoubleResourceMap<V> {

Expand Down
42 changes: 22 additions & 20 deletions src/vs/workbench/contrib/comments/browser/commentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import * as dom from 'vs/base/browser/dom';
import * as languages from 'vs/editor/common/languages';
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action, IActionRunner, IAction, Separator, ActionRunner } from 'vs/base/common/actions';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, IReference, dispose } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/model';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
Expand Down Expand Up @@ -45,13 +42,14 @@ import { Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable';
import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { DomEmitter } from 'vs/base/browser/event';
import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys';
import { FileAccess } from 'vs/base/common/network';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { COMMENTS_SECTION, ICommentsConfiguration } from 'vs/workbench/contrib/comments/common/commentsConfiguration';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { MarshalledCommentThread } from 'vs/workbench/common/comments';
import { IHoverService } from 'vs/platform/hover/browser/hover';
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';

class CommentsActionRunner extends ActionRunner {
protected override async runAction(action: IAction, context: any[]): Promise<void> {
Expand All @@ -75,7 +73,7 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
private _reactionActionsContainer?: HTMLElement;
private _commentEditor: SimpleCommentEditor | null = null;
private _commentEditorDisposables: IDisposable[] = [];
private _commentEditorModel: ITextModel | null = null;
private _commentEditorModel: IReference<IResolvedTextEditorModel> | null = null;
private _editorHeight = MIN_EDITOR_HEIGHT;

private _isPendingLabel!: HTMLElement;
Expand Down Expand Up @@ -112,15 +110,14 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
private markdownRenderer: MarkdownRenderer,
@IInstantiationService private instantiationService: IInstantiationService,
@ICommentService private commentService: ICommentService,
@IModelService private modelService: IModelService,
@ILanguageService private languageService: ILanguageService,
@INotificationService private notificationService: INotificationService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService private configurationService: IConfigurationService,
@IHoverService private hoverService: IHoverService,
@IAccessibilityService private accessibilityService: IAccessibilityService,
@IKeybindingService private keybindingService: IKeybindingService
@IKeybindingService private keybindingService: IKeybindingService,
@ITextModelService private readonly textModelService: ITextModelService,
) {
super();

Expand Down Expand Up @@ -494,13 +491,18 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
return (typeof this.comment.body === 'string') ? this.comment.body : this.comment.body.value;
}

private createCommentEditor(editContainer: HTMLElement): void {
private async createCommentEditor(editContainer: HTMLElement): Promise<void> {
const container = dom.append(editContainer, dom.$('.edit-textarea'));
this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, container, SimpleCommentEditor.getEditorOptions(this.configurationService), this._contextKeyService, this.parentThread);
const resource = URI.parse(`comment:commentinput-${this.comment.uniqueIdInThread}-${Date.now()}.md`);
this._commentEditorModel = this.modelService.createModel('', this.languageService.createByFilepathOrFirstLine(resource), resource, false);

this._commentEditor.setModel(this._commentEditorModel);
const resource = URI.from({
scheme: Schemas.commentsInput,
path: `/commentinput-${this.comment.uniqueIdInThread}-${Date.now()}.md`
});
const modelRef = await this.textModelService.createModelReference(resource);
this._commentEditorModel = modelRef;

this._commentEditor.setModel(this._commentEditorModel.object.textEditorModel);
this._commentEditor.setValue(this.pendingEdit ?? this.commentBodyValue);
this.pendingEdit = undefined;
this._commentEditor.layout({ width: container.clientWidth - 14, height: this._editorHeight });
Expand All @@ -511,8 +513,8 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
this._commentEditor!.focus();
});

const lastLine = this._commentEditorModel.getLineCount();
const lastColumn = this._commentEditorModel.getLineLength(lastLine) + 1;
const lastLine = this._commentEditorModel.object.textEditorModel.getLineCount();
const lastColumn = this._commentEditorModel.object.textEditorModel.getLineLength(lastLine) + 1;
this._commentEditor.setSelection(new Selection(lastLine, lastColumn, lastLine, lastColumn));

const commentThread = this.commentThread;
Expand Down Expand Up @@ -547,7 +549,7 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {

this.calculateEditorHeight();

this._register((this._commentEditorModel.onDidChangeContent(() => {
this._register((this._commentEditorModel.object.textEditorModel.onDidChangeContent(() => {
if (this._commentEditor && this.calculateEditorHeight()) {
this._commentEditor.layout({ height: this._editorHeight, width: this._commentEditor.getLayoutInfo().width });
this._commentEditor.render(true);
Expand Down Expand Up @@ -604,15 +606,15 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
this._scrollableElement.setScrollDimensions({ width, scrollWidth, height, scrollHeight });
}

public switchToEditMode() {
public async switchToEditMode() {
alexr00 marked this conversation as resolved.
Show resolved Hide resolved
if (this.isEditing) {
return;
}

this.isEditing = true;
this._body.classList.add('hidden');
this._commentEditContainer = dom.append(this._commentDetailsContainer, dom.$('.edit-container'));
this.createCommentEditor(this._commentEditContainer);
await this.createCommentEditor(this._commentEditContainer);

const formActions = dom.append(this._commentEditContainer, dom.$('.form-actions'));
const otherActions = dom.append(formActions, dom.$('.other-actions'));
Expand Down Expand Up @@ -705,7 +707,7 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
}));
}

update(newComment: languages.Comment) {
async update(newComment: languages.Comment) {

if (newComment.body !== this.comment.body) {
this.updateCommentBody(newComment.body);
Expand All @@ -721,7 +723,7 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {

if (isChangingMode) {
if (newComment.mode === languages.CommentMode.Editing) {
this.switchToEditMode();
await this.switchToEditMode();
} else {
this.removeCommentEditor();
}
Expand Down
47 changes: 27 additions & 20 deletions src/vs/workbench/contrib/comments/browser/commentReply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from 'vs/base/browser/dom';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
import { IAction } from 'vs/base/common/actions';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { MarshalledId } from 'vs/base/common/marshallingIds';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IRange } from 'vs/editor/common/core/range';
import * as languages from 'vs/editor/common/languages';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/model';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions';
Expand All @@ -29,11 +31,8 @@ import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/comment
import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget';
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import { LayoutableEditor, MIN_EDITOR_HEIGHT, SimpleCommentEditor, calculateEditorHeight } from './simpleCommentEditor';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { IHoverService } from 'vs/platform/hover/browser/hover';

const COMMENT_SCHEME = 'comment';
let INMEM_MODEL_ID = 0;
export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration';

Expand All @@ -42,8 +41,8 @@ export class CommentReply<T extends IRange | ICellRange> extends Disposable {
form: HTMLElement;
commentEditorIsEmpty: IContextKey<boolean>;
private _error!: HTMLElement;
private _formActions: HTMLElement | null;
private _editorActions: HTMLElement | null;
private readonly _formActions: HTMLElement;
private readonly _editorActions: HTMLElement;
private _commentThreadDisposables: IDisposable[] = [];
private _commentFormActions!: CommentFormActions;
private _commentEditorActions!: CommentFormActions;
Expand All @@ -63,12 +62,11 @@ export class CommentReply<T extends IRange | ICellRange> extends Disposable {
private _parentThread: ICommentThreadWidget,
private _actionRunDelegate: (() => void) | null,
@ICommentService private commentService: ICommentService,
@ILanguageService private languageService: ILanguageService,
@IModelService private modelService: IModelService,
@IThemeService private themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@IKeybindingService private keybindingService: IKeybindingService,
@IHoverService private hoverService: IHoverService,
@ITextModelService private readonly textModelService: ITextModelService
) {
super();

Expand All @@ -77,32 +75,44 @@ export class CommentReply<T extends IRange | ICellRange> extends Disposable {
this.commentEditorIsEmpty = CommentContextKeys.commentIsEmpty.bindTo(this._contextKeyService);
this.commentEditorIsEmpty.set(!this._pendingComment);

const formActions = dom.append(this.form, dom.$('.form-actions'));
this._formActions = dom.append(formActions, dom.$('.other-actions'));
this._editorActions = dom.append(formActions, dom.$('.editor-actions'));
this.initialize();
}

async initialize() {
const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0;
const modeId = generateUuid() + '-' + (hasExistingComments ? this._commentThread.threadId : ++INMEM_MODEL_ID);
const params = JSON.stringify({
extensionId: this._commentThread.extensionId,
commentThreadId: this._commentThread.threadId
});

let resource = URI.parse(`${COMMENT_SCHEME}://${this._commentThread.extensionId}/commentinput-${modeId}.md?${params}`); // TODO. Remove params once extensions adopt authority.
const commentController = this.commentService.getCommentController(owner);
let resource = URI.from({
scheme: Schemas.commentsInput,
path: `/${this._commentThread.extensionId}/commentinput-${modeId}.md?${params}` // TODO. Remove params once extensions adopt authority.
});
const commentController = this.commentService.getCommentController(this.owner);
if (commentController) {
resource = resource.with({ authority: commentController.id });
}

const model = this.modelService.createModel(this._pendingComment || '', this.languageService.createByFilepathOrFirstLine(resource), resource, false);
const model = await this.textModelService.createModelReference(resource);
alexr00 marked this conversation as resolved.
Show resolved Hide resolved
model.object.textEditorModel.setValue(this._pendingComment || '');

this._register(model);
this.commentEditor.setModel(model);
this.commentEditor.setModel(model.object.textEditorModel);
this.calculateEditorHeight();

this._register((model.onDidChangeContent(() => {
this._register(model.object.textEditorModel.onDidChangeContent(() => {
this.setCommentEditorDecorations();
this.commentEditorIsEmpty?.set(!this.commentEditor.getValue());
if (this.calculateEditorHeight()) {
this.commentEditor.layout({ height: this._editorHeight, width: this.commentEditor.getLayoutInfo().width });
this.commentEditor.render(true);
}
})));
}));

this.createTextModelListener(this.commentEditor, this.form);

Expand All @@ -115,11 +125,8 @@ export class CommentReply<T extends IRange | ICellRange> extends Disposable {
this.expandReplyArea();
}
this._error = dom.append(this.form, dom.$('.validation-error.hidden'));
const formActions = dom.append(this.form, dom.$('.form-actions'));
this._formActions = dom.append(formActions, dom.$('.other-actions'));
this.createCommentWidgetFormActions(this._formActions, model);
this._editorActions = dom.append(formActions, dom.$('.editor-actions'));
this.createCommentWidgetEditorActions(this._editorActions, model);
this.createCommentWidgetFormActions(this._formActions, model.object.textEditorModel);
this.createCommentWidgetEditorActions(this._editorActions, model.object.textEditorModel);
}

private calculateEditorHeight(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/co
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { accessibilityHelpIsShown, accessibleViewCurrentProviderId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds';
import { registerWorkbenchContribution2, WorkbenchPhase } from 'vs/workbench/common/contributions';
import { CommentsInputContentProvider } from 'vs/workbench/contrib/comments/browser/commentsInputContentProvider';

registerEditorContribution(ID, CommentController, EditorContributionInstantiation.AfterFirstRender);
registerWorkbenchContribution2(CommentsInputContentProvider.ID, CommentsInputContentProvider, WorkbenchPhase.BlockRestore);

KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CommentCommandId.NextThread,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/model';
import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';

export class CommentsInputContentProvider extends Disposable implements ITextModelContentProvider, IEditorContribution {

public static readonly ID = 'comments.input.contentProvider';

constructor(
@ITextModelService textModelService: ITextModelService,
@IModelService private readonly _modelService: IModelService,
@ILanguageService private readonly _languageService: ILanguageService,
) {
super();
this._register(textModelService.registerTextModelContentProvider(Schemas.commentsInput, this));
}

async provideTextContent(resource: URI): Promise<ITextModel | null> {
const existing = this._modelService.getModel(resource);
return existing ?? this._modelService.createModel('', this._languageService.createById('markdown'), resource);
}
}
Loading
Loading