From f6e9a38c1dddfc939e871cbaf0a4a7ac88062f19 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 31 Mar 2023 07:28:45 -0700 Subject: [PATCH] Enable code actions and workspace edits for the scm input (#176699) Enable quick fixes in the scm input Fixes #159940 This is a replacement of #159941 with proper support for using workspace edits on the scm input box --- .../codeAction/browser/codeActionCommands.ts | 8 +-- .../contrib/scm/browser/scmViewPane.ts | 60 +++++++++++++++---- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts index d23a95b304b..caf47d6dcf6 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts @@ -72,7 +72,7 @@ export class QuickFixAction extends EditorAction { alias: 'Quick Fix...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), kbOpts: { - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Period, weight: KeybindingWeight.EditorContrib } @@ -129,7 +129,7 @@ export class RefactorAction extends EditorAction { alias: 'Refactor...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), kbOpts: { - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyR, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KeyR @@ -226,7 +226,7 @@ export class OrganizeImportsAction extends EditorAction { EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.SourceOrganizeImports)), kbOpts: { - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KeyO, weight: KeybindingWeight.EditorContrib }, @@ -273,7 +273,7 @@ export class AutoFixAction extends EditorAction { EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.QuickFix)), kbOpts: { - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyMod.Shift | KeyCode.Period, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Period diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 37146f55d6b..12cc5ea9be1 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/scm'; import { Event, Emitter } from 'vs/base/common/event'; import { basename, dirname } from 'vs/base/common/resources'; -import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose, toDisposable, MutableDisposable, IReference } from 'vs/base/common/lifecycle'; import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { append, $, Dimension, asCSSUrl, trackFocus, clearNode } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; @@ -90,6 +90,8 @@ import { MessageController } from 'vs/editor/contrib/message/browser/messageCont import { contrastBorder, registerColor } from 'vs/platform/theme/common/colorRegistry'; import { defaultButtonStyles, defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; import { GhostTextController } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextController'; +import { CodeActionController } from 'vs/editor/contrib/codeAction/browser/codeActionController'; +import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; type TreeElement = ISCMRepository | ISCMInput | ISCMActionButton | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -205,7 +207,7 @@ class InputRenderer implements ICompressibleTreeRenderer, index: number, templateData: InputTemplate): void { const input = node.element; - templateData.inputWidget.input = input; + templateData.inputWidget.setInput(input); // Remember widget this.inputWidgets.set(input, templateData.inputWidget); @@ -1722,7 +1724,7 @@ class SCMInputWidget { private inputEditor: CodeEditorWidget; private disposables = new DisposableStore(); - private model: { readonly input: ISCMInput; readonly textModel: ITextModel } | undefined; + private model: { readonly input: ISCMInput; textModelRef?: IReference } | undefined; private repositoryIdContextKey: IContextKey; private repositoryDisposables = new DisposableStore(); @@ -1738,11 +1740,11 @@ class SCMInputWidget { readonly onDidChangeContentHeight: Event; - get input(): ISCMInput | undefined { + private get input(): ISCMInput | undefined { return this.model?.input; } - set input(input: ISCMInput | undefined) { + public async setInput(input: ISCMInput | undefined) { if (input === this.input) { return; } @@ -1755,7 +1757,7 @@ class SCMInputWidget { this.repositoryIdContextKey.set(input?.repository.id); if (!input) { - this.model?.textModel.dispose(); + this.model?.textModelRef?.dispose(); this.inputEditor.setModel(undefined); this.model = undefined; return; @@ -1777,7 +1779,21 @@ class SCMInputWidget { this.configurationService.updateValue('editor.wordBasedSuggestions', false, { resource: uri }, ConfigurationTarget.MEMORY); } - const textModel = this.modelService.getModel(uri) ?? this.modelService.createModel('', this.languageService.createById('scminput'), uri); + const modelValue: typeof this.model = { input, textModelRef: undefined }; + + // Save model + this.model = modelValue; + + const modelRef = await this.textModelService.createModelReference(uri); + // Model has been changed in the meantime + if (this.model !== modelValue) { + modelRef.dispose(); + return; + } + + modelValue.textModelRef = modelRef; + + const textModel = modelRef.object.textEditorModel; this.inputEditor.setModel(textModel); // Validation @@ -1866,9 +1882,6 @@ class SCMInputWidget { }; this.repositoryDisposables.add(input.onDidChangeEnablement(enabled => updateEnablement(enabled))); updateEnablement(input.enabled); - - // Save model - this.model = { input, textModel }; } get selections(): Selection[] | null { @@ -1904,7 +1917,7 @@ class SCMInputWidget { overflowWidgetsDomNode: HTMLElement, @IContextKeyService contextKeyService: IContextKeyService, @IModelService private modelService: IModelService, - @ILanguageService private languageService: ILanguageService, + @ITextModelService private textModelService: ITextModelService, @IKeybindingService private keybindingService: IKeybindingService, @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -1963,6 +1976,7 @@ class SCMInputWidget { SnippetController2.ID, SuggestController.ID, GhostTextController.ID, + CodeActionController.ID, ]) }; @@ -2181,7 +2195,7 @@ class SCMInputWidget { } dispose(): void { - this.input = undefined; + this.setInput(undefined); this.repositoryDisposables.dispose(); this.clearValidation(); this.disposables.dispose(); @@ -2228,6 +2242,8 @@ export class SCMViewPane extends ViewPane { onDidChange: this._onDidLayout.event }; + this._register(this.instantiationService.createInstance(ScmInputContentProvider)); + this._register(Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire())); } @@ -2583,3 +2599,23 @@ export class SCMActionButton implements IDisposable { } } } + +class ScmInputContentProvider extends Disposable implements ITextModelContentProvider { + + constructor( + @ITextModelService textModelService: ITextModelService, + @IModelService private readonly _modelService: IModelService, + @ILanguageService private readonly _languageService: ILanguageService, + ) { + super(); + this._register(textModelService.registerTextModelContentProvider(Schemas.vscodeSourceControl, this)); + } + + async provideTextContent(resource: URI): Promise { + const existing = this._modelService.getModel(resource); + if (existing) { + return existing; + } + return this._modelService.createModel('', this._languageService.createById('scminput'), resource); + } +}