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
This commit is contained in:
Matt Bierner 2023-03-31 07:28:45 -07:00 committed by GitHub
parent f720dba429
commit f6e9a38c1d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 16 deletions

View file

@ -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

View file

@ -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, ISCMResourceGroup> | ISCMResource;
@ -205,7 +207,7 @@ class InputRenderer implements ICompressibleTreeRenderer<ISCMInput, FuzzyScore,
renderElement(node: ITreeNode<ISCMInput, FuzzyScore>, 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<IResolvedTextEditorModel> } | undefined;
private repositoryIdContextKey: IContextKey<string | undefined>;
private repositoryDisposables = new DisposableStore();
@ -1738,11 +1740,11 @@ class SCMInputWidget {
readonly onDidChangeContentHeight: Event<void>;
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<ITextModel | null> {
const existing = this._modelService.getModel(resource);
if (existing) {
return existing;
}
return this._modelService.createModel('', this._languageService.createById('scminput'), resource);
}
}