Merge branch 'master' into aeschli/ts-sem

This commit is contained in:
Martin Aeschlimann 2020-01-14 09:24:30 +01:00
commit a9b2eb97de
18 changed files with 206 additions and 119 deletions

View file

@ -6,16 +6,16 @@
import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel } from 'vs/editor/common/model';
import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
import * as modes from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types';
import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState';
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerType, filtersAction, mayIncludeActionsOfKind } from './types';
export const codeActionCommandId = 'editor.action.codeAction';
export const refactorCommandId = 'editor.action.refactor';
@ -24,14 +24,14 @@ export const organizeImportsCommandId = 'editor.action.organizeImports';
export const fixAllCommandId = 'editor.action.fixAll';
export interface CodeActionSet extends IDisposable {
readonly validActions: readonly CodeAction[];
readonly allActions: readonly CodeAction[];
readonly validActions: readonly modes.CodeAction[];
readonly allActions: readonly modes.CodeAction[];
readonly hasAutoFix: boolean;
}
class ManagedCodeActionSet extends Disposable implements CodeActionSet {
private static codeActionsComparator(a: CodeAction, b: CodeAction): number {
private static codeActionsComparator(a: modes.CodeAction, b: modes.CodeAction): number {
if (isNonEmptyArray(a.diagnostics)) {
if (isNonEmptyArray(b.diagnostics)) {
return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message);
@ -45,10 +45,10 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet {
}
}
public readonly validActions: readonly CodeAction[];
public readonly allActions: readonly CodeAction[];
public readonly validActions: readonly modes.CodeAction[];
public readonly allActions: readonly modes.CodeAction[];
public constructor(actions: readonly CodeAction[], disposables: DisposableStore) {
public constructor(actions: readonly modes.CodeAction[], disposables: DisposableStore) {
super();
this._register(disposables);
this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator);
@ -68,9 +68,9 @@ export function getCodeActions(
): Promise<CodeActionSet> {
const filter = trigger.filter || {};
const codeActionContext: CodeActionContext = {
const codeActionContext: modes.CodeActionContext = {
only: filter.include?.value,
trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic
trigger: trigger.type === CodeActionTriggerType.Manual ? modes.CodeActionTrigger.Manual : modes.CodeActionTrigger.Automatic
};
const cts = new TextModelCancellationTokenSource(model, token);
@ -94,8 +94,8 @@ export function getCodeActions(
}
});
const listener = CodeActionProviderRegistry.onDidChange(() => {
const newProviders = CodeActionProviderRegistry.all(model);
const listener = modes.CodeActionProviderRegistry.onDidChange(() => {
const newProviders = modes.CodeActionProviderRegistry.all(model);
if (!equals(newProviders, providers)) {
cts.cancel();
}
@ -114,7 +114,7 @@ function getCodeActionProviders(
model: ITextModel,
filter: CodeActionFilter
) {
return CodeActionProviderRegistry.all(model)
return modes.CodeActionProviderRegistry.all(model)
// Don't include providers that we know will not return code actions of interest
.filter(provider => {
if (!provider.providedCodeActionKinds) {
@ -125,7 +125,7 @@ function getCodeActionProviders(
});
}
registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise<ReadonlyArray<CodeAction>> {
registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise<ReadonlyArray<modes.CodeAction>> {
const { resource, rangeOrSelection, kind } = args;
if (!(resource instanceof URI)) {
throw illegalArgument();
@ -149,7 +149,7 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor,
const codeActionSet = await getCodeActions(
model,
validatedRangeOrSelection,
{ type: 'manual', filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } },
{ type: CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } },
CancellationToken.None);
setTimeout(() => codeActionSet.dispose(), 100);

View file

@ -16,21 +16,20 @@ import { IPosition } from 'vs/editor/common/core/position';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CodeAction } from 'vs/editor/common/modes';
import { CodeActionSet, refactorCommandId, sourceActionCommandId, codeActionCommandId, organizeImportsCommandId, fixAllCommandId } from 'vs/editor/contrib/codeAction/codeAction';
import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionUi } from 'vs/editor/contrib/codeAction/codeActionUi';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel';
import { CodeActionAutoApply, CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionCommandArgs } from './types';
import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerType } from './types';
function contextKeyForSupportedActions(kind: CodeActionKind) {
return ContextKeyExpr.regex(
@ -83,10 +82,6 @@ export class QuickFixController extends Disposable implements IEditorContributio
@IMarkerService markerService: IMarkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@IEditorProgressService progressService: IEditorProgressService,
@IContextMenuService contextMenuService: IContextMenuService,
@IKeybindingService keybindingService: IKeybindingService,
@ICommandService private readonly _commandService: ICommandService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super();
@ -102,7 +97,7 @@ export class QuickFixController extends Disposable implements IEditorContributio
await this._applyCodeAction(action);
} finally {
if (retrigger) {
this._trigger({ type: 'auto', filter: {} });
this._trigger({ type: CodeActionTriggerType.Auto, filter: {} });
}
}
}
@ -129,7 +124,7 @@ export class QuickFixController extends Disposable implements IEditorContributio
MessageController.get(this._editor).closeMessage();
const triggerPosition = this._editor.getPosition();
this._trigger({ type: 'manual', filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
this._trigger({ type: CodeActionTriggerType.Manual, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
}
private _trigger(trigger: CodeActionTrigger) {
@ -137,21 +132,41 @@ export class QuickFixController extends Disposable implements IEditorContributio
}
private _applyCodeAction(action: CodeAction): Promise<void> {
return this._instantiationService.invokeFunction(applyCodeAction, action, this._bulkEditService, this._commandService, this._editor);
return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor);
}
}
export async function applyCodeAction(
accessor: ServicesAccessor,
action: CodeAction,
bulkEditService: IBulkEditService,
commandService: ICommandService,
editor?: ICodeEditor,
): Promise<void> {
const bulkEditService = accessor.get(IBulkEditService);
const commandService = accessor.get(ICommandService);
const telemetryService = accessor.get(ITelemetryService);
const notificationService = accessor.get(INotificationService);
type ApplyCodeActionEvent = {
codeActionTitle: string;
codeActionKind: string | undefined;
codeActionIsPreferred: boolean;
};
type ApplyCodeEventClassification = {
codeActionTitle: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
codeActionKind: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
codeActionIsPreferred: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
telemetryService.publicLog2<ApplyCodeActionEvent, ApplyCodeEventClassification>('codeAction.applyCodeAction', {
codeActionTitle: action.title,
codeActionKind: action.kind,
codeActionIsPreferred: !!action.isPreferred,
});
if (action.edit) {
await bulkEditService.apply(action.edit, { editor });
}
if (action.command) {
try {
await commandService.executeCommand(action.command.id, ...(action.command.arguments || []));
@ -161,7 +176,6 @@ export async function applyCodeAction(
typeof message === 'string'
? message
: nls.localize('applyCodeActionFailed', "An unknown error occurred while applying the code action"));
}
}
}

View file

@ -16,7 +16,7 @@ import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/cont
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { getCodeActions, CodeActionSet } from './codeAction';
import { CodeActionTrigger } from './types';
import { CodeActionTrigger, CodeActionTriggerType } from './types';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { isEqual } from 'vs/base/common/resources';
@ -56,14 +56,14 @@ class CodeActionOracle extends Disposable {
if (resources.some(resource => isEqual(resource, model.uri))) {
this._autoTriggerTimer.cancelAndSet(() => {
this.trigger({ type: 'auto' });
this.trigger({ type: CodeActionTriggerType.Auto });
}, this._delay);
}
}
private _onCursorChange(): void {
this._autoTriggerTimer.cancelAndSet(() => {
this.trigger({ type: 'auto' });
this.trigger({ type: CodeActionTriggerType.Auto });
}, this._delay);
}
@ -88,7 +88,7 @@ class CodeActionOracle extends Disposable {
}
const model = this._editor.getModel();
const selection = this._editor.getSelection();
if (selection.isEmpty() && trigger.type === 'auto') {
if (selection.isEmpty() && trigger.type === CodeActionTriggerType.Auto) {
const { lineNumber, column } = selection.getPosition();
const line = model.getLineContent(lineNumber);
if (line.length === 0) {
@ -214,14 +214,14 @@ export class CodeActionModel extends Disposable {
}
const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token));
if (this._progressService && trigger.trigger.type === 'manual') {
if (this._progressService && trigger.trigger.type === CodeActionTriggerType.Manual) {
this._progressService.showWhile(actions, 250);
}
this.setState(new CodeActionsState.Triggered(trigger.trigger, trigger.selection, trigger.position, actions));
}, undefined);
this._codeActionOracle.value.trigger({ type: 'auto' });
this._codeActionOracle.value.trigger({ type: CodeActionTriggerType.Auto });
} else {
this._supportedCodeActions.reset();
}

View file

@ -16,7 +16,7 @@ import { MessageController } from 'vs/editor/contrib/message/messageController';
import { CodeActionsState } from './codeActionModel';
import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu';
import { LightBulbWidget } from './lightBulbWidget';
import { CodeActionAutoApply, CodeActionTrigger } from './types';
import { CodeActionAutoApply, CodeActionTrigger, CodeActionTriggerType } from './types';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export class CodeActionUi extends Disposable {
@ -67,7 +67,7 @@ export class CodeActionUi extends Disposable {
this._lightBulbWidget.getValue().update(actions, newState.position);
if (newState.trigger.type === 'manual') {
if (newState.trigger.type === CodeActionTriggerType.Manual) {
if (newState.trigger.filter?.include) { // Triggered for specific scope
// Check to see if we want to auto apply.

View file

@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import * as modes from 'vs/editor/common/modes';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { CancellationToken } from 'vs/base/common/cancellation';
@ -125,7 +125,7 @@ suite('CodeAction', () => {
testData.tsLint.abc
];
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Manual }, CancellationToken.None);
assert.equal(actions.length, 6);
assert.deepEqual(actions, expected);
});
@ -140,20 +140,20 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
assert.equal(actions.length, 2);
assert.strictEqual(actions[0].title, 'a');
assert.strictEqual(actions[1].title, 'a.b');
}
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a.b');
}
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None);
assert.equal(actions.length, 0);
}
});
@ -172,7 +172,7 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
});
@ -186,13 +186,13 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'b');
}
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
}
@ -209,7 +209,7 @@ suite('CodeAction', () => {
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
type: 'auto', filter: {
type: CodeActionTriggerType.Auto, filter: {
include: CodeActionKind.Source.append('test'),
excludes: [CodeActionKind.Source],
includeSourceActions: true,
@ -234,7 +234,7 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
type: 'auto',
type: CodeActionTriggerType.Auto,
filter: {
include: CodeActionKind.QuickFix
}

View file

@ -15,6 +15,7 @@ import { CodeActionModel, CodeActionsState } from 'vs/editor/contrib/codeAction/
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { MarkerService } from 'vs/platform/markers/common/markerService';
import { CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types';
const testProvider = {
provideCodeActions(): modes.CodeActionList {
@ -59,7 +60,7 @@ suite('CodeActionModel', () => {
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.strictEqual(e.trigger.type, 'auto');
assert.strictEqual(e.trigger.type, CodeActionTriggerType.Auto);
assert.ok(e.actions);
e.actions.then(fixes => {
@ -100,7 +101,7 @@ suite('CodeActionModel', () => {
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.equal(e.trigger.type, 'auto');
assert.equal(e.trigger.type, CodeActionTriggerType.Auto);
assert.ok(e.actions);
e.actions.then(fixes => {
model.dispose();
@ -138,7 +139,7 @@ suite('CodeActionModel', () => {
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.equal(e.trigger.type, 'auto');
assert.equal(e.trigger.type, CodeActionTriggerType.Auto);
const selection = <Selection>e.rangeOrSelection;
assert.deepEqual(selection.selectionStartLineNumber, 1);
assert.deepEqual(selection.selectionStartColumn, 1);
@ -163,7 +164,7 @@ suite('CodeActionModel', () => {
disposables.add(model.onDidChangeState((e: CodeActionsState.State) => {
assertType(e.type === CodeActionsState.Type.Triggered);
assert.equal(e.trigger.type, 'auto');
assert.equal(e.trigger.type, CodeActionTriggerType.Auto);
++triggerCount;
// give time for second trigger before completing test

View file

@ -102,8 +102,13 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo
return true;
}
export const enum CodeActionTriggerType {
Auto,
Manual
}
export interface CodeActionTrigger {
readonly type: 'auto' | 'manual';
readonly type: CodeActionTriggerType;
readonly filter?: CodeActionFilter;
readonly autoApply?: CodeActionAutoApply;
readonly context?: {

View file

@ -34,7 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
@ -592,7 +592,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
return getCodeActions(
this._editor.getModel()!,
new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn),
{ type: 'manual', filter: { include: CodeActionKind.QuickFix } },
{ type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } },
cancellationToken);
});
}

View file

@ -7,7 +7,6 @@ import { IdleValue } from 'vs/base/common/async';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import * as strings from 'vs/base/common/strings';
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { EditOperation } from 'vs/editor/common/core/editOperation';
@ -19,11 +18,11 @@ import { CodeAction } from 'vs/editor/common/modes';
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types';
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { localize } from 'vs/nls';
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
@ -247,8 +246,6 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
class CodeActionOnSaveParticipant implements ISaveParticipantParticipant {
constructor(
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@ICommandService private readonly _commandService: ICommandService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) { }
@ -308,13 +305,13 @@ class CodeActionOnSaveParticipant implements ISaveParticipantParticipant {
private async applyCodeActions(actionsToRun: readonly CodeAction[]) {
for (const action of actionsToRun) {
await this._instantiationService.invokeFunction(applyCodeAction, action, this._bulkEditService, this._commandService);
await this._instantiationService.invokeFunction(applyCodeAction, action);
}
}
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) {
return getCodeActions(model, model.getFullModelRange(), {
type: 'auto',
type: CodeActionTriggerType.Auto,
filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true },
}, token);
}

View file

@ -26,7 +26,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor';
@ -182,7 +182,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
options = EditorOptions.create(preservingOptions);
}
this.editorService.openEditor(binaryDiffInput, options, this.group);
// Replace this editor with the binary one
this.editorService.replaceEditors([{ editor: input, replacement: binaryDiffInput, options }], this.group || ACTIVE_GROUP);
return true;
}

View file

@ -32,6 +32,7 @@ export class DiffEditorInput extends SideBySideEditorInput {
if (!super.matches(otherInput)) {
return false;
}
return otherInput instanceof DiffEditorInput && otherInput.forceOpenAsBinary === this.forceOpenAsBinary;
}

View file

@ -57,7 +57,7 @@ export class CodeActionDocumentationContribution extends Disposable implements I
}
async provideCodeActions(_model: ITextModel, _range: Range | Selection, context: CodeActionContext, _token: CancellationToken): Promise<CodeActionList> {
if (!context.only || !CodeActionKind.Refactor.contains(new CodeActionKind(context.only))) {
if (CodeActionKind.Refactor.value !== context.only) {
return {
actions: [],
dispose: () => { }

View file

@ -347,9 +347,15 @@ export class CustomEditorContribution implements IWorkbenchContribution {
options: ITextEditorOptions | undefined,
group: IEditorGroup
): IOpenEditorOverride | undefined {
const userConfiguredEditors = this.customEditorService.getUserConfiguredCustomEditors(resource);
const contributedEditors = this.customEditorService.getContributedCustomEditors(resource);
if (!userConfiguredEditors.length && !contributedEditors.length) {
return;
}
// Check to see if there already an editor for the resource in the group.
// If there is, we want to open that instead of creating a new editor.
// This ensures that we preserve whatever state the editor was previously in
// This ensures that we preserve whatever type of editor was previously being used
// when the user switches back to it.
const existingEditorForResource = group.editors.find(editor => isEqual(resource, editor.getResource()));
if (existingEditorForResource) {
@ -358,14 +364,12 @@ export class CustomEditorContribution implements IWorkbenchContribution {
};
}
const userConfiguredEditors = this.customEditorService.getUserConfiguredCustomEditors(resource);
if (userConfiguredEditors.length) {
return {
override: this.customEditorService.openWith(resource, userConfiguredEditors.allEditors[0].id, options, group),
};
}
const contributedEditors = this.customEditorService.getContributedCustomEditors(resource);
if (!contributedEditors.length) {
return;
}

View file

@ -37,10 +37,8 @@ import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/com
import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types';
import { ITextModel } from 'vs/editor/common/model';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
@ -489,8 +487,6 @@ export class MarkerViewModel extends Disposable {
private readonly marker: Marker,
@IModelService private modelService: IModelService,
@IInstantiationService private instantiationService: IInstantiationService,
@IBulkEditService private readonly bulkEditService: IBulkEditService,
@ICommandService private readonly commandService: ICommandService,
@IEditorService private readonly editorService: IEditorService
) {
super();
@ -552,7 +548,7 @@ export class MarkerViewModel extends Disposable {
if (model) {
if (!this.codeActionsPromise) {
this.codeActionsPromise = createCancelablePromise(cancellationToken => {
return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { type: 'manual', filter: { include: CodeActionKind.QuickFix } }, cancellationToken).then(actions => {
return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } }, cancellationToken).then(actions => {
return this._register(actions);
});
});
@ -571,7 +567,7 @@ export class MarkerViewModel extends Disposable {
true,
() => {
return this.openFileAtMarker(this.marker)
.then(() => this.instantiationService.invokeFunction(applyCodeAction, codeAction, this.bulkEditService, this.commandService));
.then(() => this.instantiationService.invokeFunction(applyCodeAction, codeAction));
}));
}

View file

@ -57,7 +57,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { assertType } from 'vs/base/common/types';
import { SearchViewPaneContainer } from 'vs/workbench/contrib/search/browser/searchViewlet';
import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
import { SearchEditorInput, SearchEditorInputFactory } from 'vs/workbench/contrib/search/browser/searchEditorCommands';
import { SearchEditorInput, SearchEditorInputFactory, SearchEditorContribution } from 'vs/workbench/contrib/search/browser/searchEditorCommands';
import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
@ -909,3 +909,6 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(
SearchEditorInput.ID,
SearchEditorInputFactory);
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorContribution, LifecyclePhase.Starting);

View file

@ -158,6 +158,8 @@ export class SearchEditor extends BaseEditor {
}
}
});
this._register(this.searchResultEditor.onDidChangeModel(() => this.hideHeader()));
}
private async runSearch(instant = false) {
@ -225,14 +227,28 @@ export class SearchEditor extends BaseEditor {
(assertIsDefined(this._input) as SearchEditorInput).setConfig(config);
const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true });
const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false);
const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, true);
const textModel = assertIsDefined(this.searchResultEditor.getModel());
textModel.setValue(results.text.join(lineDelimiter));
this.hideHeader();
textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } })));
searchModel.dispose();
}
private hideHeader() {
const headerLines =
this.searchResultEditor
.getModel()
?.getValueInRange(new Range(1, 1, 6, 1))
.split('\n')
.filter(line => line.startsWith('#'))
.length
?? 0;
this.searchResultEditor.setHiddenAreas([new Range(1, 1, headerLines + 1, 1)]);
}
layout(dimension: DOM.Dimension) {
this.dimension = dimension;
this.reLayout();
@ -255,8 +271,7 @@ export class SearchEditor extends BaseEditor {
if (!(newInput instanceof SearchEditorInput)) { return; }
this.pauseSearching = true;
// TODO: Manage model lifecycle in SearchEditorInput
const model = this.modelService.getModel(newInput.getResource());
const model = this.modelService.getModel(newInput.resource);
this.searchResultEditor.setModel(model);
this.queryEditorWidget.setValue(newInput.config.query, true);
this.queryEditorWidget.searchInput.setCaseSensitive(newInput.config.caseSensitive);

View file

@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/searchEditor';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { EndOfLinePreference, TrackedRangeStickiness } from 'vs/editor/common/model';
import { EndOfLinePreference, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -25,16 +25,22 @@ import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/co
import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search';
import { EditorInput, IEditorInputFactory } from 'vs/workbench/common/editor';
import { IEditorInputFactory, GroupIdentifier, EditorInput } from 'vs/workbench/common/editor';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import type { IWorkbenchContribution } from 'vs/workbench/common/contributions';
// import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
export type SearchConfiguration = {
query: string,
includes: string,
excludes: string,
excludes: string
contextLines: number,
wholeWord: boolean,
caseSensitive: boolean,
@ -43,6 +49,27 @@ export type SearchConfiguration = {
showIncludesExcludes: boolean,
};
export class SearchEditorContribution implements IWorkbenchContribution {
// constructor(
// @IEditorService private readonly editorService: IEditorService,
// @ITextFileService protected readonly textFileService: ITextFileService,
// ) {
// this.editorService.overrideOpenEditor(async (editor, options, group) => {
// const resource = editor.getResource();
// if (!resource || !endsWith(resource.path, '.code-search') || !(editor instanceof FileEditorInput)) {
// return undefined;
// }
// console.log('hello agian');
// const contents = await this.textFileService.read(resource);
// contents.value;
// return undefined;
// });
// }
}
export class SearchEditorInputFactory implements IEditorInputFactory {
canSerialize() { return true; }
@ -52,7 +79,7 @@ export class SearchEditorInputFactory implements IEditorInputFactory {
}
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): SearchEditorInput | undefined {
return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput));
return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput), undefined);
}
}
@ -60,40 +87,56 @@ let searchEditorInputInstances = 0;
export class SearchEditorInput extends EditorInput {
static readonly ID: string = 'workbench.editorinputs.searchEditorInput';
public config: SearchConfiguration;
private instanceNumber: number = searchEditorInputInstances++;
private _config: SearchConfiguration;
public get config(): Readonly<SearchConfiguration> {
return this._config;
}
private model: ITextModel;
public readonly resource: URI;
constructor(
config: SearchConfiguration | undefined,
initialContents: string | undefined,
@IModelService private readonly modelService: IModelService,
@IModeService private readonly modeService: IModeService,
@IEditorService protected readonly editorService: IEditorService,
@IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService,
@ITextFileService protected readonly textFileService: ITextFileService,
@IUntitledTextEditorService protected readonly untitledTextEditorService: IUntitledTextEditorService,
) {
super();
this.resource = URI.from({ scheme: 'search-editor', fragment: `${searchEditorInputInstances++}` });
if (config === undefined) {
this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false };
this._config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false };
} else {
this.config = config;
this._config = config;
}
const searchResultMode = this.modeService.create('search-result');
this.modelService.createModel('', searchResultMode, this.getResource());
this.model = this.modelService.createModel(initialContents ?? '', searchResultMode, this.resource);
}
async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise<boolean> {
const path = (this.config.query.replace(/\W/g, '') || 'Untitled Search') + '.code-search';
const untitledResource = URI.from({ scheme: 'untitled', path, fragment: this.resource.fragment });
const a = this.untitledTextEditorService.createOrGet(untitledResource, 'search-result', this.model.getValue());
return a.save(group, options).finally(() => a.dispose());
}
getTypeId(): string {
return SearchEditorInput.ID;
}
getResource(): URI {
return URI.from({ scheme: 'search-editor', fragment: `${this.instanceNumber}` });
}
getName(): string {
return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search");
}
setConfig(config: SearchConfiguration) {
this.config = config;
this._config = config;
this._onDidChangeLabel.fire();
}
@ -102,9 +145,17 @@ export class SearchEditorInput extends EditorInput {
}
dispose() {
this.modelService.destroyModel(this.getResource());
this.modelService.destroyModel(this.resource);
super.dispose();
}
matches(other: unknown) {
if (this === other) { return true; }
if (other instanceof UntitledTextEditorInput) {
if (other.getResource().fragment === this.resource.fragment) { return true; }
}
return false;
}
}
// Using \r\n on Windows inserts an extra newline between results.
@ -313,7 +364,7 @@ export const serializeSearchResultForEditor = (searchResult: SearchResult, rawIn
.map(folderMatch => folderMatch.matches().sort(searchMatchComparer)
.map(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter)))));
return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text) };
return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text.length ? allResults.text : ['No Results']) };
};
export const refreshActiveEditorSearch =
@ -373,7 +424,7 @@ export const refreshActiveEditorSearch =
await searchModel.search(query);
const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true });
const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, false);
const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, true);
textModel.setValue(results.text.join(lineDelimiter));
textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } })));
@ -381,7 +432,7 @@ export const refreshActiveEditorSearch =
export const openNewSearchEditor =
async (editorService: IEditorService, instantiationService: IInstantiationService) => {
await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined), { pinned: true });
await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined, undefined), { pinned: true });
};
export const createEditorFromSearchResult =
@ -395,7 +446,7 @@ export const createEditorFromSearchResult =
const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true });
const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, false);
const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, true);
const contents = results.text.join(lineDelimiter);
let possible = {
contents,
@ -418,24 +469,22 @@ export const createEditorFromSearchResult =
existing = editorService.getOpened(possible);
}
const editor = await editorService.openEditor(
instantiationService.createInstance(
SearchEditorInput,
{
query: searchResult.query.contentPattern.pattern,
regexp: !!searchResult.query.contentPattern.isRegExp,
caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive,
wholeWord: !!searchResult.query.contentPattern.isWordMatch,
includes: rawIncludePattern,
excludes: rawExcludePattern,
contextLines: 0,
useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles,
showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles)
}),
{ pinned: true }) as SearchEditor;
const input = instantiationService.createInstance(
SearchEditorInput,
{
query: searchResult.query.contentPattern.pattern,
regexp: !!searchResult.query.contentPattern.isRegExp,
caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive,
wholeWord: !!searchResult.query.contentPattern.isWordMatch,
includes: rawIncludePattern,
excludes: rawExcludePattern,
contextLines: 0,
useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles,
showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles)
}, contents);
const editor = await editorService.openEditor(input, { pinned: true }) as SearchEditor;
const model = assertIsDefined(editor.getModel());
model.setValue(contents);
model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } })));
};

View file

@ -47,6 +47,7 @@ import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { dispose } from 'vs/base/common/lifecycle';
import { withNullAsUndefined } from 'vs/base/common/types';
import { CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types';
const defaultSelector = { scheme: 'far' };
const model: ITextModel = EditorModel.createFromString(
@ -589,7 +590,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None);
assert.equal(actions.length, 2);
const [first, second] = actions;
assert.equal(first.title, 'Testing1');
@ -613,7 +614,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None);
assert.equal(actions.length, 1);
const [first] = actions;
assert.equal(first.title, 'Testing1');
@ -636,7 +637,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None);
assert.equal(actions.length, 1);
});
@ -654,7 +655,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None);
assert.equal(actions.length, 1);
});