From baf257dafb1fd1f55dd97dd5ffb5ca48dbbcc759 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 22 Dec 2022 14:36:40 +0100 Subject: [PATCH] Expose comment editor to extension API and add new menu (#169831) * Expose comment editor to extension API and add new menu Fixes #169737 * Also add in comment node --- src/vs/platform/actions/common/actions.ts | 1 + .../contrib/comments/browser/commentMenus.ts | 4 ++ .../contrib/comments/browser/commentNode.ts | 39 ++++++++++++++++++- .../contrib/comments/browser/commentReply.ts | 35 +++++++++++++++-- .../comments/browser/simpleCommentEditor.ts | 1 - .../actions/common/menusExtensionPoint.ts | 6 +++ .../common/extensionsApiProposals.ts | 1 + ...posed.contribCommentEditorActionsMenu.d.ts | 6 +++ 8 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index dd4f4dd7713..db8b0d04551 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -126,6 +126,7 @@ export class MenuId { static readonly ViewContainerTitleContext = new MenuId('ViewContainerTitleContext'); static readonly ViewTitle = new MenuId('ViewTitle'); static readonly ViewTitleContext = new MenuId('ViewTitleContext'); + static readonly CommentEditorActions = new MenuId('CommentEditorActions'); static readonly CommentThreadTitle = new MenuId('CommentThreadTitle'); static readonly CommentThreadActions = new MenuId('CommentThreadActions'); static readonly CommentThreadAdditionalActions = new MenuId('CommentThreadAdditionalActions'); diff --git a/src/vs/workbench/contrib/comments/browser/commentMenus.ts b/src/vs/workbench/contrib/comments/browser/commentMenus.ts index 479e5156b12..c53852bb8cc 100644 --- a/src/vs/workbench/contrib/comments/browser/commentMenus.ts +++ b/src/vs/workbench/contrib/comments/browser/commentMenus.ts @@ -23,6 +23,10 @@ export class CommentMenus implements IDisposable { return this.getMenu(MenuId.CommentThreadActions, contextKeyService); } + getCommentEditorActions(contextKeyService: IContextKeyService): IMenu { + return this.getMenu(MenuId.CommentEditorActions, contextKeyService); + } + getCommentThreadAdditionalActions(contextKeyService: IContextKeyService): IMenu { return this.getMenu(MenuId.CommentThreadAdditionalActions, contextKeyService); } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index e55ce947175..a791cb800fe 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -67,6 +67,7 @@ export class CommentNode extends Disposable { protected actionRunner?: IActionRunner; protected toolbar: ToolBar | undefined; private _commentFormActions: CommentFormActions | null = null; + private _commentEditorActions: CommentFormActions | null = null; private readonly _onDidClick = new Emitter>(); @@ -469,8 +470,16 @@ export class CommentNode extends Disposable { this._body.classList.add('hidden'); this._commentEditContainer = dom.append(this._commentDetailsContainer, dom.$('.edit-container')); this.createCommentEditor(this._commentEditContainer); - const formActions = dom.append(this._commentEditContainer, dom.$('.form-actions')); + const formActions = dom.append(this._commentEditContainer, dom.$('.form-actions')); + const otherActions = dom.append(formActions, dom.$('.other-actions')); + this.createCommentWidgetFormActions(otherActions); + const editorActions = dom.append(formActions, dom.$('.editor-actions')); + this.createCommentWidgetEditorActions(editorActions); + + } + + private createCommentWidgetFormActions(container: HTMLElement) { const menus = this.commentService.getCommentMenus(this.owner); const menu = menus.getCommentActions(this.comment, this._contextKeyService); @@ -479,7 +488,7 @@ export class CommentNode extends Disposable { this._commentFormActions?.setActions(menu); })); - this._commentFormActions = new CommentFormActions(formActions, (action: IAction): void => { + this._commentFormActions = new CommentFormActions(container, (action: IAction): void => { const text = this._commentEditor!.getValue(); action.run({ @@ -496,6 +505,32 @@ export class CommentNode extends Disposable { this._commentFormActions.setActions(menu); } + private createCommentWidgetEditorActions(container: HTMLElement) { + const menus = this.commentService.getCommentMenus(this.owner); + const menu = menus.getCommentEditorActions(this._contextKeyService); + + this._register(menu); + this._register(menu.onDidChange(() => { + this._commentEditorActions?.setActions(menu); + })); + + this._commentEditorActions = new CommentFormActions(container, (action: IAction): void => { + const text = this._commentEditor!.getValue(); + + action.run({ + thread: this.commentThread, + commentUniqueId: this.comment.uniqueIdInThread, + text: text, + $mid: MarshalledId.CommentThreadNode + }); + + this._commentEditor?.focus(); + }); + + this._register(this._commentEditorActions); + this._commentEditorActions.setActions(menu, true); + } + setFocus(focused: boolean, visible: boolean = false) { if (focused) { this._domNode.focus(); diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index 3efe67224b9..81f80a1a8e0 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -40,8 +40,10 @@ export class CommentReply extends Disposable { commentEditorIsEmpty: IContextKey; private _error!: HTMLElement; private _formActions: HTMLElement | null; + private _editorActions: HTMLElement | null; private _commentThreadDisposables: IDisposable[] = []; private _commentFormActions!: CommentFormActions; + private _commentEditorActions!: CommentFormActions; private _reviewThreadReplyButton!: HTMLElement; constructor( @@ -103,9 +105,11 @@ export class CommentReply extends Disposable { } } this._error = dom.append(this.form, dom.$('.validation-error.hidden')); - - this._formActions = dom.append(this.form, dom.$('.form-actions')); - this.createCommentWidgetActions(this._formActions, model); + 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); } public updateCommentThread(commentThread: languages.CommentThread) { @@ -241,7 +245,7 @@ export class CommentReply extends Disposable { /** * Command based actions. */ - private createCommentWidgetActions(container: HTMLElement, model: ITextModel) { + private createCommentWidgetFormActions(container: HTMLElement, model: ITextModel) { const menu = this._commentMenus.getCommentThreadActions(this._contextKeyService); this._register(menu); @@ -265,6 +269,29 @@ export class CommentReply extends Disposable { this._commentFormActions.setActions(menu); } + private createCommentWidgetEditorActions(container: HTMLElement, model: ITextModel) { + const editorMenu = this._commentMenus.getCommentEditorActions(this._contextKeyService); + this._register(editorMenu); + this._register(editorMenu.onDidChange(() => { + this._commentEditorActions.setActions(editorMenu); + })); + + this._commentEditorActions = new CommentFormActions(container, async (action: IAction) => { + this._actionRunDelegate?.(); + + action.run({ + thread: this._commentThread, + text: this.commentEditor.getValue(), + $mid: MarshalledId.CommentThreadReply + }); + + this.focusCommentEditor(); + }); + + this._register(this._commentEditorActions); + this._commentEditorActions.setActions(editorMenu, true); + } + private get isReplyExpanded(): boolean { return this.form.classList.contains('expand'); } diff --git a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts index b1baca4d50a..708ac4e0be2 100644 --- a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts +++ b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts @@ -49,7 +49,6 @@ export class SimpleCommentEditor extends CodeEditorWidget { @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { - isSimpleWidget: true, contributions: [ { id: MenuPreventer.ID, ctor: MenuPreventer, instantiation: EditorContributionInstantiation.BeforeFirstInteraction }, { id: ContextMenuController.ID, ctor: ContextMenuController, instantiation: EditorContributionInstantiation.BeforeFirstInteraction }, diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index fe81a6da6bc..629d38b20eb 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -148,6 +148,12 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.ViewItemContext, description: localize('view.itemContext', "The contributed view item context menu") }, + { + key: 'comments/comment/editorActions', + id: MenuId.CommentEditorActions, + description: localize('commentThread.editorActions', "The contributed comment editor actions"), + proposed: 'contribCommentEditorActionsMenu' + }, { key: 'comments/commentThread/title', id: MenuId.CommentThreadTitle, diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index d8a6dd45156..f0f0b916875 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -9,6 +9,7 @@ export const allApiProposals = Object.freeze({ authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts', commentsResolvedState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsResolvedState.d.ts', + contribCommentEditorActionsMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts', contribCommentPeekContext: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentPeekContext.d.ts', contribCommentThreadAdditionalMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentThreadAdditionalMenu.d.ts', contribEditSessions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribEditSessions.d.ts', diff --git a/src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts b/src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts new file mode 100644 index 00000000000..9b24bcc32ea --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `comments/comment/editorActions` menu