Merge from vscode 33a65245075e4d18908652865a79cf5489c30f40 (#9279)

* Merge from vscode 33a65245075e4d18908652865a79cf5489c30f40

* remove github
This commit is contained in:
Anthony Dresser
2020-02-21 23:42:19 -08:00
committed by GitHub
parent c446cea3a0
commit de5f1eb780
250 changed files with 3724 additions and 2756 deletions
+15 -2
View File
@@ -138,8 +138,12 @@ function configureCommandlineSwitchesSync(cliArgs) {
'disable-hardware-acceleration',
// provided by Electron
'disable-color-correct-rendering'
'disable-color-correct-rendering',
// override for the color profile to use
'force-color-profile'
];
if (process.platform === 'linux') {
SUPPORTED_ELECTRON_SWITCHES.push('force-renderer-accessibility');
}
@@ -154,7 +158,16 @@ function configureCommandlineSwitchesSync(cliArgs) {
}
const argvValue = argvConfig[argvKey];
if (argvValue === true || argvValue === 'true') {
// Color profile
if (argvKey === 'force-color-profile') {
if (argvValue) {
app.commandLine.appendSwitch(argvKey, argvValue);
}
}
// Others
else if (argvValue === true || argvValue === 'true') {
if (argvKey === 'disable-hardware-acceleration') {
app.disableHardwareAcceleration(); // needs to be called explicitly
} else {
@@ -37,6 +37,7 @@ import { attachModalDialogStyler, attachPanelStyler } from 'sql/workbench/common
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { ServiceOptionType } from 'sql/platform/connection/common/interfaces';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class CategoryView extends ViewPane {
@@ -51,9 +52,10 @@ export class CategoryView extends ViewPane {
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IOpenerService protected openerService: IOpenerService,
@IThemeService protected themeService: IThemeService
@IThemeService protected themeService: IThemeService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService);
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService, telemetryService);
}
// we want a fixed size, so when we render to will measure our content and set that to be our
@@ -50,6 +50,7 @@ import { NodeContextKey } from 'sql/workbench/browser/parts/views/nodeContext';
import { UserCancelledConnectionError } from 'sql/base/common/errors';
import { firstIndex } from 'vs/base/common/arrays';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class CustomTreeViewPanel extends ViewPane {
@@ -65,9 +66,10 @@ export class CustomTreeViewPanel extends ViewPane {
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IOpenerService protected openerService: IOpenerService,
@IThemeService protected themeService: IThemeService
@IThemeService protected themeService: IThemeService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
const { treeView } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
this.treeView = treeView as ITreeView;
this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this));
@@ -86,7 +86,7 @@ function createInstantiationService(addAccountFailureEmitter?: Emitter<string>):
.returns(() => undefined);
// Create a mock account dialog
let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!, undefined!, undefined!);
let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountDialog);
mockAccountDialog.setup(x => x.onAddAccountErrorEvent)
.returns(() => { return addAccountFailureEmitter ? addAccountFailureEmitter.event : mockEvent.event; });
@@ -22,7 +22,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestEditorService, TestDialogService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestEditorService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { URI } from 'vs/base/common/uri';
import { TestQueryModelService } from 'sql/workbench/services/query/test/common/testQueryModelService';
import { Event } from 'vs/base/common/event';
@@ -33,6 +33,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te
import { isUndefinedOrNull } from 'vs/base/common/types';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { FileQueryEditorInput } from 'sql/workbench/contrib/query/common/fileQueryEditorInput';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
class TestParsedArgs implements ParsedArgs {
[arg: string]: any;
@@ -24,6 +24,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ILogService } from 'vs/platform/log/common/log';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class ConnectionViewletPanel extends ViewPane {
@@ -46,9 +47,10 @@ export class ConnectionViewletPanel extends ViewPane {
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IOpenerService protected openerService: IOpenerService,
@IThemeService protected themeService: IThemeService,
@ILogService private readonly logService: ILogService
@ILogService private readonly logService: ILogService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService);
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService, telemetryService);
this._addServerAction = this.instantiationService.createInstance(AddServerAction,
AddServerAction.ID,
AddServerAction.LABEL);
@@ -47,6 +47,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { GridPanelState, GridTableState } from 'sql/workbench/common/editor/query/gridPanelState';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { SaveFormat } from 'sql/workbench/services/query/common/resultSerializer';
import { Progress } from 'vs/platform/progress/common/progress';
const ROW_HEIGHT = 29;
const HEADER_HEIGHT = 26;
@@ -580,7 +581,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
let content = value.displayValue;
const input = this.untitledEditorService.create({ mode: column.isXml ? 'xml' : 'json', initialValue: content });
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, input.textEditorModel, FormattingMode.Explicit, CancellationToken.None);
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, input.textEditorModel, FormattingMode.Explicit, Progress.None, CancellationToken.None);
return this.editorService.openEditor(input);
});
}
@@ -39,6 +39,7 @@ import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/vie
import { attachModalDialogStyler, attachPanelStyler } from 'sql/workbench/common/styler';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
class AccountPanel extends ViewPane {
public index: number;
@@ -54,8 +55,9 @@ class AccountPanel extends ViewPane {
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IOpenerService openerService: IOpenerService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
}
protected renderBody(container: HTMLElement): void {
@@ -133,7 +135,8 @@ export class AccountDialog extends Modal {
@ILogService logService: ILogService,
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
@IOpenerService protected readonly openerService: IOpenerService
@IOpenerService protected readonly openerService: IOpenerService,
@ITelemetryService private readonly vstelemetryService: ITelemetryService,
) {
super(
localize('linkedAccounts', "Linked accounts"),
@@ -305,7 +308,8 @@ export class AccountDialog extends Modal {
this.contextKeyService,
this._instantiationService,
this.viewDescriptorService,
this.openerService
this.openerService,
this.vstelemetryService
);
attachPanelStyler(providerView, this._themeService);
@@ -46,6 +46,7 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie
import { attachPanelStyler, attachModalDialogStyler } from 'sql/workbench/common/styler';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
const labelDisplay = nls.localize("insights.item", "Item");
const valueDisplay = nls.localize("insights.value", "Value");
@@ -70,8 +71,9 @@ class InsightTableView<T> extends ViewPane {
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IOpenerService openerService: IOpenerService,
@IThemeService themeService: IThemeService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService);
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
}
protected renderBody(container: HTMLElement): void {
+17
View File
@@ -283,3 +283,20 @@ export function isRootOrDriveLetter(path: string): boolean {
return pathNormalized === posix.sep;
}
export function indexOfPath(path: string, candidate: string, ignoreCase: boolean): number {
if (candidate.length > path.length) {
return -1;
}
if (path === candidate) {
return 0;
}
if (ignoreCase) {
path = path.toLowerCase();
candidate = candidate.toLowerCase();
}
return path.indexOf(candidate);
}
+20
View File
@@ -3,6 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI, UriComponents } from 'vs/base/common/uri';
const _typeof = {
number: 'number',
string: 'string',
@@ -262,3 +264,21 @@ export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, Firs
// Else: just leave as is
Target[K]
};
/**
* Mapped-type that replaces all occurrences of URI with UriComponents
*/
export type UriDto<T> = { [K in keyof T]: T[K] extends URI
? UriComponents
: UriDto<T[K]> };
/**
* Mapped-type that replaces all occurrences of URI with UriComponents and
* drops all functions.
* todo@joh use toJSON-results
*/
export type Dto<T> = { [K in keyof T]: T[K] extends URI
? UriComponents
: T[K] extends Function
? never
: UriDto<T[K]> };
@@ -575,10 +575,6 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
return this.visible ? this.ui.inputBox.hasFocus() : false;
}
public focusOnInput() {
this.ui.inputBox.setFocus();
}
onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;
@@ -209,8 +209,6 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
validationMessage: string | undefined;
inputHasFocus(): boolean;
focusOnInput(): void;
}
export interface IInputBox extends IQuickInput {
@@ -24,7 +24,7 @@ import { MenuId } from 'vs/platform/actions/common/actions';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
const CORE_WEIGHT = KeybindingWeight.EditorCore;
@@ -1530,6 +1530,26 @@ export namespace CoreNavigationCommands {
});
}
const columnSelectionCondition = ContextKeyExpr.and(
EditorContextKeys.textInputFocus,
EditorContextKeys.columnSelection
);
function registerColumnSelection(id: string, keybinding: number): void {
KeybindingsRegistry.registerKeybindingRule({
id: id,
primary: keybinding,
when: columnSelectionCondition,
weight: CORE_WEIGHT + 1
});
}
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectLeft.id, KeyMod.Shift | KeyCode.LeftArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectRight.id, KeyMod.Shift | KeyCode.RightArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectUp.id, KeyMod.Shift | KeyCode.UpArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageUp.id, KeyMod.Shift | KeyCode.PageUp);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectDown.id, KeyMod.Shift | KeyCode.DownArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageDown.id, KeyMod.Shift | KeyCode.PageDown);
/**
* A command that will:
* 1. invoke a command on the focused editor.
@@ -352,6 +352,7 @@ class MouseDownOperation extends Disposable {
if (!options.get(EditorOption.readOnly)
&& options.get(EditorOption.dragAndDrop)
&& !options.get(EditorOption.columnSelection)
&& !this._mouseState.altKey // we don't support multiple mouse
&& e.detail < 2 // only single click on a selection can work
&& !this._isActive // the mouse is not down yet
+8 -2
View File
@@ -133,7 +133,9 @@ export class ViewController {
}
public dispatchMouse(data: IMouseDispatchData): void {
const selectionClipboardIsOn = (platform.isLinux && this.configuration.options.get(EditorOption.selectionClipboard));
const options = this.configuration.options;
const selectionClipboardIsOn = (platform.isLinux && options.get(EditorOption.selectionClipboard));
const columnSelection = options.get(EditorOption.columnSelection);
if (data.middleButton && !selectionClipboardIsOn) {
this._columnSelect(data.position, data.mouseColumn, data.inSelectionMode);
} else if (data.startedOnLineNumbers) {
@@ -196,7 +198,11 @@ export class ViewController {
if (data.altKey) {
this._columnSelect(data.position, data.mouseColumn, true);
} else {
this._moveToSelect(data.position);
if (columnSelection) {
this._columnSelect(data.position, data.mouseColumn, true);
} else {
this._moveToSelect(data.position);
}
}
} else {
this.moveTo(data.position);
@@ -1666,6 +1666,7 @@ class EditorContextKeysManager extends Disposable {
private readonly _editorTextFocus: IContextKey<boolean>;
private readonly _editorTabMovesFocus: IContextKey<boolean>;
private readonly _editorReadonly: IContextKey<boolean>;
private readonly _editorColumnSelection: IContextKey<boolean>;
private readonly _hasMultipleSelections: IContextKey<boolean>;
private readonly _hasNonEmptySelection: IContextKey<boolean>;
private readonly _canUndo: IContextKey<boolean>;
@@ -1687,6 +1688,7 @@ class EditorContextKeysManager extends Disposable {
this._editorTextFocus = EditorContextKeys.editorTextFocus.bindTo(contextKeyService);
this._editorTabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService);
this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService);
this._editorColumnSelection = EditorContextKeys.columnSelection.bindTo(contextKeyService);
this._hasMultipleSelections = EditorContextKeys.hasMultipleSelections.bindTo(contextKeyService);
this._hasNonEmptySelection = EditorContextKeys.hasNonEmptySelection.bindTo(contextKeyService);
this._canUndo = EditorContextKeys.canUndo.bindTo(contextKeyService);
@@ -1714,6 +1716,7 @@ class EditorContextKeysManager extends Disposable {
this._editorTabMovesFocus.set(options.get(EditorOption.tabFocusMode));
this._editorReadonly.set(options.get(EditorOption.readOnly));
this._editorColumnSelection.set(options.get(EditorOption.columnSelection));
}
private _updateFromSelection(): void {
@@ -324,6 +324,11 @@ export interface IEditorOptions {
* Defaults to true.
*/
scrollPredominantAxis?: boolean;
/**
* Enable that the selection with the mouse and keys is doing column selection.
* Defaults to false.
*/
columnSelection?: boolean;
/**
* The modifier to be used to add multiple cursors with the mouse.
* Defaults to 'alt'
@@ -3302,6 +3307,7 @@ export const enum EditorOption {
autoSurround,
codeLens,
colorDecorators,
columnSelection,
comments,
contextmenu,
copyWithSyntaxHighlighting,
@@ -3526,6 +3532,10 @@ export const EditorOptions = {
EditorOption.colorDecorators, 'colorDecorators', true,
{ description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") }
)),
columnSelection: register(new EditorBooleanOption(
EditorOption.columnSelection, 'columnSelection', false,
{ description: nls.localize('columnSelection', "Enable that the selection with the mouse and keys is doing column selection.") }
)),
comments: register(new EditorComments()),
contextmenu: register(new EditorBooleanOption(
EditorOption.contextmenu, 'contextmenu', true,
@@ -23,6 +23,7 @@ export namespace EditorContextKeys {
export const textInputFocus = new RawContextKey<boolean>('textInputFocus', false);
export const readOnly = new RawContextKey<boolean>('editorReadonly', false);
export const columnSelection = new RawContextKey<boolean>('editorColumnSelection', false);
export const writable: ContextKeyExpr = readOnly.toNegated();
export const hasNonEmptySelection = new RawContextKey<boolean>('editorHasSelection', false);
export const hasOnlyEmptySelection: ContextKeyExpr = hasNonEmptySelection.toNegated();
+2 -7
View File
@@ -1077,7 +1077,7 @@ export interface ITextModel {
* @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed.
* @return The cursor state returned by the `cursorStateComputer`.
*/
pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null;
pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null;
/**
* Change the end of line sequence. This is the preferred way of
@@ -1093,11 +1093,6 @@ export interface ITextModel {
*/
applyEdits(operations: IIdentifiedSingleEditOperation[]): IValidEditOperation[];
/**
* @internal
*/
_applyEdits(edits: IValidEditOperations[], isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): IValidEditOperations[];
/**
* Change the end of line sequence without recording in the undo stack.
* This can have dire consequences on the undo stack! See @pushEOL for the preferred way.
@@ -1107,7 +1102,7 @@ export interface ITextModel {
/**
* @internal
*/
_setEOL(eol: EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void;
_applyUndoRedoEdits(edits: IValidEditOperations[], eol: EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): IValidEditOperations[];
/**
* Undo edit operations until the first previous stop point created by `pushStackElement`.
+115 -83
View File
@@ -8,41 +8,50 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { Selection } from 'vs/editor/common/core/selection';
import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation, ITextModel, IValidEditOperations } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IUndoRedoService, IUndoRedoElement, IUndoRedoContext } from 'vs/platform/undoRedo/common/undoRedo';
import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources';
class EditStackElement implements IUndoRedoElement {
export class EditStackElement implements IResourceUndoRedoElement {
public readonly type = UndoRedoElementType.Resource;
public readonly label: string;
private _isOpen: boolean;
private readonly _model: TextModel;
public readonly model: ITextModel;
private readonly _beforeVersionId: number;
private readonly _beforeCursorState: Selection[];
private readonly _beforeEOL: EndOfLineSequence;
private readonly _beforeCursorState: Selection[] | null;
private _afterVersionId: number;
private _afterEOL: EndOfLineSequence;
private _afterCursorState: Selection[] | null;
private _edits: IValidEditOperations[];
public get resources(): readonly URI[] {
return [this._model.uri];
public get resource(): URI {
return this.model.uri;
}
constructor(model: TextModel, beforeVersionId: number, beforeCursorState: Selection[], afterVersionId: number, afterCursorState: Selection[] | null, operations: IValidEditOperation[]) {
constructor(model: ITextModel, beforeCursorState: Selection[] | null) {
this.label = nls.localize('edit', "Typing");
this._isOpen = true;
this._model = model;
this._beforeVersionId = beforeVersionId;
this.model = model;
this._beforeVersionId = this.model.getAlternativeVersionId();
this._beforeEOL = getModelEOL(this.model);
this._beforeCursorState = beforeCursorState;
this._afterVersionId = afterVersionId;
this._afterCursorState = afterCursorState;
this._edits = [{ operations: operations }];
this._afterVersionId = this._beforeVersionId;
this._afterEOL = this._beforeEOL;
this._afterCursorState = this._beforeCursorState;
this._edits = [];
}
public isOpen(): boolean {
return this._isOpen;
public canAppend(model: ITextModel): boolean {
return (this._isOpen && this.model === model);
}
public append(operations: IValidEditOperation[], afterVersionId: number, afterCursorState: Selection[] | null): void {
this._edits.push({ operations: operations });
public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
if (operations.length > 0) {
this._edits.push({ operations: operations });
}
this._afterEOL = afterEOL;
this._afterVersionId = afterVersionId;
this._afterCursorState = afterCursorState;
}
@@ -51,20 +60,83 @@ class EditStackElement implements IUndoRedoElement {
this._isOpen = false;
}
undo(ctx: IUndoRedoContext): void {
public undo(): void {
this._isOpen = false;
this._edits.reverse();
this._edits = this._model._applyEdits(this._edits, true, false, this._beforeVersionId, this._beforeCursorState);
this._edits = this.model._applyUndoRedoEdits(this._edits, this._beforeEOL, true, false, this._beforeVersionId, this._beforeCursorState);
}
redo(ctx: IUndoRedoContext): void {
this._isOpen = false;
public redo(): void {
this._edits.reverse();
this._edits = this._model._applyEdits(this._edits, false, true, this._afterVersionId, this._afterCursorState);
this._edits = this.model._applyUndoRedoEdits(this._edits, this._afterEOL, false, true, this._afterVersionId, this._afterCursorState);
}
}
export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
public readonly type = UndoRedoElementType.Workspace;
public readonly label: string;
private _isOpen: boolean;
private readonly _editStackElementsArr: EditStackElement[];
private readonly _editStackElementsMap: Map<string, EditStackElement>;
public get resources(): readonly URI[] {
return this._editStackElementsArr.map(editStackElement => editStackElement.model.uri);
}
invalidate(resource: URI): void {
// nothing to do
constructor(
label: string,
editStackElements: EditStackElement[]
) {
this.label = label;
this._isOpen = true;
this._editStackElementsArr = editStackElements.slice(0);
this._editStackElementsMap = new Map<string, EditStackElement>();
for (const editStackElement of this._editStackElementsArr) {
const key = uriGetComparisonKey(editStackElement.model.uri);
this._editStackElementsMap.set(key, editStackElement);
}
}
public canAppend(model: ITextModel): boolean {
if (!this._isOpen) {
return false;
}
const key = uriGetComparisonKey(model.uri);
if (this._editStackElementsMap.has(key)) {
const editStackElement = this._editStackElementsMap.get(key)!;
return editStackElement.canAppend(model);
}
return false;
}
public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
const key = uriGetComparisonKey(model.uri);
const editStackElement = this._editStackElementsMap.get(key)!;
editStackElement.append(model, operations, afterEOL, afterVersionId, afterCursorState);
}
public close(): void {
this._isOpen = false;
}
public undo(): void {
this._isOpen = false;
for (const editStackElement of this._editStackElementsArr) {
editStackElement.undo();
}
}
public redo(): void {
for (const editStackElement of this._editStackElementsArr) {
editStackElement.redo();
}
}
public split(): IResourceUndoRedoElement[] {
return this._editStackElementsArr;
}
}
@@ -77,46 +149,11 @@ function getModelEOL(model: ITextModel): EndOfLineSequence {
}
}
class EOLStackElement implements IUndoRedoElement {
public readonly label: string;
private readonly _model: TextModel;
private readonly _beforeVersionId: number;
private readonly _afterVersionId: number;
private _eol: EndOfLineSequence;
public get resources(): readonly URI[] {
return [this._model.uri];
function isKnownStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement | MultiModelEditStackElement {
if (!element) {
return false;
}
constructor(model: TextModel, beforeVersionId: number, afterVersionId: number, eol: EndOfLineSequence) {
this.label = nls.localize('eol', "Change End Of Line Sequence");
this._model = model;
this._beforeVersionId = beforeVersionId;
this._afterVersionId = afterVersionId;
this._eol = eol;
}
undo(ctx: IUndoRedoContext): void {
const redoEOL = getModelEOL(this._model);
this._model._setEOL(this._eol, true, false, this._beforeVersionId, null);
this._eol = redoEOL;
}
redo(ctx: IUndoRedoContext): void {
const undoEOL = getModelEOL(this._model);
this._model._setEOL(this._eol, false, true, this._afterVersionId, null);
this._eol = undoEOL;
}
invalidate(resource: URI): void {
// nothing to do
}
}
export interface IUndoRedoResult {
selections: Selection[] | null;
recordedVersionId: number;
return ((element instanceof EditStackElement) || (element instanceof MultiModelEditStackElement));
}
export class EditStack {
@@ -131,7 +168,7 @@ export class EditStack {
public pushStackElement(): void {
const lastElement = this._undoRedoService.getLastElement(this._model.uri);
if (lastElement && lastElement instanceof EditStackElement) {
if (isKnownStackElement(lastElement)) {
lastElement.close();
}
}
@@ -140,32 +177,27 @@ export class EditStack {
this._undoRedoService.removeElements(this._model.uri);
}
public pushEOL(eol: EndOfLineSequence): void {
const beforeVersionId = this._model.getAlternativeVersionId();
const inverseEOL = getModelEOL(this._model);
this._model.setEOL(eol);
const afterVersionId = this._model.getAlternativeVersionId();
private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null): EditStackElement | MultiModelEditStackElement {
const lastElement = this._undoRedoService.getLastElement(this._model.uri);
if (lastElement && lastElement instanceof EditStackElement) {
lastElement.close();
if (isKnownStackElement(lastElement) && lastElement.canAppend(this._model)) {
return lastElement;
}
this._undoRedoService.pushElement(new EOLStackElement(this._model, inverseEOL, beforeVersionId, afterVersionId));
const newElement = new EditStackElement(this._model, beforeCursorState);
this._undoRedoService.pushElement(newElement);
return newElement;
}
public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null {
const beforeVersionId = this._model.getAlternativeVersionId();
public pushEOL(eol: EndOfLineSequence): void {
const editStackElement = this._getOrCreateEditStackElement(null);
this._model.setEOL(eol);
editStackElement.append(this._model, [], getModelEOL(this._model), this._model.getAlternativeVersionId(), null);
}
public pushEditOperation(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null {
const editStackElement = this._getOrCreateEditStackElement(beforeCursorState);
const inverseEditOperations = this._model.applyEdits(editOperations);
const afterVersionId = this._model.getAlternativeVersionId();
const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations);
const lastElement = this._undoRedoService.getLastElement(this._model.uri);
if (lastElement && lastElement instanceof EditStackElement && lastElement.isOpen()) {
lastElement.append(inverseEditOperations, afterVersionId, afterCursorState);
} else {
this._undoRedoService.pushElement(new EditStackElement(this._model, beforeVersionId, beforeCursorState, afterVersionId, afterCursorState, inverseEditOperations));
}
editStackElement.append(this._model, inverseEditOperations, getModelEOL(this._model), this._model.getAlternativeVersionId(), afterCursorState);
return afterCursorState;
}
+20 -36
View File
@@ -37,7 +37,6 @@ import { Color } from 'vs/base/common/color';
import { Constants } from 'vs/base/common/uint';
import { EditorTheme } from 'vs/editor/common/view/viewContext';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
function createTextBufferBuilder() {
return new PieceTreeTextBufferBuilder();
@@ -189,10 +188,6 @@ export class TextModel extends Disposable implements model.ITextModel {
largeFileOptimizations: EDITOR_MODEL_DEFAULTS.largeFileOptimizations,
};
public static createFromString(text: string, options: model.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier | null = null, uri: URI | null = null): TextModel {
return new TextModel(text, options, languageIdentifier, uri, new UndoRedoService());
}
public static resolveOptions(textBuffer: model.ITextBuffer, options: model.ITextModelCreationOptions): model.TextModelResolvedOptions {
if (options.detectIndentation) {
const guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces);
@@ -494,21 +489,6 @@ export class TextModel extends Disposable implements model.ITextModel {
);
}
_setEOL(eol: model.EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
this._isUndoing = isUndoing;
this._isRedoing = isRedoing;
this.setEOL(eol);
this._overwriteAlternativeVersionId(resultingAlternativeVersionId);
} finally {
this._isUndoing = false;
this._eventEmitter.endDeferredEmit(resultingSelection);
this._onDidChangeDecorations.endDeferredEmit();
}
}
private _onBeforeEOLChange(): void {
// Ensure all decorations get their `range` set.
const versionId = this.getVersionId();
@@ -1202,7 +1182,7 @@ export class TextModel extends Disposable implements model.ITextModel {
return result;
}
public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null {
public pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
@@ -1213,7 +1193,7 @@ export class TextModel extends Disposable implements model.ITextModel {
}
}
private _pushEditOperations(beforeCursorState: Selection[], editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null {
private _pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null {
if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) {
// Go through each saved line number and insert a trim whitespace edit
// if it is safe to do so (no conflicts with other edits).
@@ -1228,22 +1208,24 @@ export class TextModel extends Disposable implements model.ITextModel {
// Sometimes, auto-formatters change ranges automatically which can cause undesired auto whitespace trimming near the cursor
// We'll use the following heuristic: if the edits occur near the cursor, then it's ok to trim auto whitespace
let editsAreNearCursors = true;
for (let i = 0, len = beforeCursorState.length; i < len; i++) {
let sel = beforeCursorState[i];
let foundEditNearSel = false;
for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
let editRange = incomingEdits[j].range;
let selIsAbove = editRange.startLineNumber > sel.endLineNumber;
let selIsBelow = sel.startLineNumber > editRange.endLineNumber;
if (!selIsAbove && !selIsBelow) {
foundEditNearSel = true;
if (beforeCursorState) {
for (let i = 0, len = beforeCursorState.length; i < len; i++) {
let sel = beforeCursorState[i];
let foundEditNearSel = false;
for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
let editRange = incomingEdits[j].range;
let selIsAbove = editRange.startLineNumber > sel.endLineNumber;
let selIsBelow = sel.startLineNumber > editRange.endLineNumber;
if (!selIsAbove && !selIsBelow) {
foundEditNearSel = true;
break;
}
}
if (!foundEditNearSel) {
editsAreNearCursors = false;
break;
}
}
if (!foundEditNearSel) {
editsAreNearCursors = false;
break;
}
}
if (editsAreNearCursors) {
@@ -1298,7 +1280,7 @@ export class TextModel extends Disposable implements model.ITextModel {
return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer);
}
_applyEdits(edits: model.IValidEditOperations[], isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): model.IValidEditOperations[] {
_applyUndoRedoEdits(edits: model.IValidEditOperations[], eol: model.EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): model.IValidEditOperations[] {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
@@ -1308,10 +1290,12 @@ export class TextModel extends Disposable implements model.ITextModel {
for (let i = 0, len = edits.length; i < len; i++) {
reverseEdits[i] = { operations: this.applyEdits(edits[i].operations) };
}
this.setEOL(eol);
this._overwriteAlternativeVersionId(resultingAlternativeVersionId);
return reverseEdits;
} finally {
this._isUndoing = false;
this._isRedoing = false;
this._eventEmitter.endDeferredEmit(resultingSelection);
this._onDidChangeDecorations.endDeferredEmit();
}
+3
View File
@@ -635,6 +635,9 @@ export interface CodeActionList extends IDisposable {
* @internal
*/
export interface CodeActionProvider {
displayName?: string
/**
* Provide commands for the given document and range.
*/
@@ -178,105 +178,106 @@ export enum EditorOption {
autoSurround = 10,
codeLens = 11,
colorDecorators = 12,
comments = 13,
contextmenu = 14,
copyWithSyntaxHighlighting = 15,
cursorBlinking = 16,
cursorSmoothCaretAnimation = 17,
cursorStyle = 18,
cursorSurroundingLines = 19,
cursorSurroundingLinesStyle = 20,
cursorWidth = 21,
disableLayerHinting = 22,
disableMonospaceOptimizations = 23,
dragAndDrop = 24,
emptySelectionClipboard = 25,
extraEditorClassName = 26,
fastScrollSensitivity = 27,
find = 28,
fixedOverflowWidgets = 29,
folding = 30,
foldingStrategy = 31,
foldingHighlight = 32,
fontFamily = 33,
fontInfo = 34,
fontLigatures = 35,
fontSize = 36,
fontWeight = 37,
formatOnPaste = 38,
formatOnType = 39,
glyphMargin = 40,
gotoLocation = 41,
hideCursorInOverviewRuler = 42,
highlightActiveIndentGuide = 43,
hover = 44,
inDiffEditor = 45,
letterSpacing = 46,
lightbulb = 47,
lineDecorationsWidth = 48,
lineHeight = 49,
lineNumbers = 50,
lineNumbersMinChars = 51,
links = 52,
matchBrackets = 53,
minimap = 54,
mouseStyle = 55,
mouseWheelScrollSensitivity = 56,
mouseWheelZoom = 57,
multiCursorMergeOverlapping = 58,
multiCursorModifier = 59,
multiCursorPaste = 60,
occurrencesHighlight = 61,
overviewRulerBorder = 62,
overviewRulerLanes = 63,
padding = 64,
parameterHints = 65,
peekWidgetDefaultFocus = 66,
definitionLinkOpensInPeek = 67,
quickSuggestions = 68,
quickSuggestionsDelay = 69,
readOnly = 70,
renderControlCharacters = 71,
renderIndentGuides = 72,
renderFinalNewline = 73,
renderLineHighlight = 74,
renderValidationDecorations = 75,
renderWhitespace = 76,
revealHorizontalRightPadding = 77,
roundedSelection = 78,
rulers = 79,
scrollbar = 80,
scrollBeyondLastColumn = 81,
scrollBeyondLastLine = 82,
scrollPredominantAxis = 83,
selectionClipboard = 84,
selectionHighlight = 85,
selectOnLineNumbers = 86,
showFoldingControls = 87,
showUnused = 88,
snippetSuggestions = 89,
smoothScrolling = 90,
stopRenderingLineAfter = 91,
suggest = 92,
suggestFontSize = 93,
suggestLineHeight = 94,
suggestOnTriggerCharacters = 95,
suggestSelection = 96,
tabCompletion = 97,
useTabStops = 98,
wordSeparators = 99,
wordWrap = 100,
wordWrapBreakAfterCharacters = 101,
wordWrapBreakBeforeCharacters = 102,
wordWrapColumn = 103,
wordWrapMinified = 104,
wrappingIndent = 105,
wrappingStrategy = 106,
editorClassName = 107,
pixelRatio = 108,
tabFocusMode = 109,
layoutInfo = 110,
wrappingInfo = 111
columnSelection = 13,
comments = 14,
contextmenu = 15,
copyWithSyntaxHighlighting = 16,
cursorBlinking = 17,
cursorSmoothCaretAnimation = 18,
cursorStyle = 19,
cursorSurroundingLines = 20,
cursorSurroundingLinesStyle = 21,
cursorWidth = 22,
disableLayerHinting = 23,
disableMonospaceOptimizations = 24,
dragAndDrop = 25,
emptySelectionClipboard = 26,
extraEditorClassName = 27,
fastScrollSensitivity = 28,
find = 29,
fixedOverflowWidgets = 30,
folding = 31,
foldingStrategy = 32,
foldingHighlight = 33,
fontFamily = 34,
fontInfo = 35,
fontLigatures = 36,
fontSize = 37,
fontWeight = 38,
formatOnPaste = 39,
formatOnType = 40,
glyphMargin = 41,
gotoLocation = 42,
hideCursorInOverviewRuler = 43,
highlightActiveIndentGuide = 44,
hover = 45,
inDiffEditor = 46,
letterSpacing = 47,
lightbulb = 48,
lineDecorationsWidth = 49,
lineHeight = 50,
lineNumbers = 51,
lineNumbersMinChars = 52,
links = 53,
matchBrackets = 54,
minimap = 55,
mouseStyle = 56,
mouseWheelScrollSensitivity = 57,
mouseWheelZoom = 58,
multiCursorMergeOverlapping = 59,
multiCursorModifier = 60,
multiCursorPaste = 61,
occurrencesHighlight = 62,
overviewRulerBorder = 63,
overviewRulerLanes = 64,
padding = 65,
parameterHints = 66,
peekWidgetDefaultFocus = 67,
definitionLinkOpensInPeek = 68,
quickSuggestions = 69,
quickSuggestionsDelay = 70,
readOnly = 71,
renderControlCharacters = 72,
renderIndentGuides = 73,
renderFinalNewline = 74,
renderLineHighlight = 75,
renderValidationDecorations = 76,
renderWhitespace = 77,
revealHorizontalRightPadding = 78,
roundedSelection = 79,
rulers = 80,
scrollbar = 81,
scrollBeyondLastColumn = 82,
scrollBeyondLastLine = 83,
scrollPredominantAxis = 84,
selectionClipboard = 85,
selectionHighlight = 86,
selectOnLineNumbers = 87,
showFoldingControls = 88,
showUnused = 89,
snippetSuggestions = 90,
smoothScrolling = 91,
stopRenderingLineAfter = 92,
suggest = 93,
suggestFontSize = 94,
suggestLineHeight = 95,
suggestOnTriggerCharacters = 96,
suggestSelection = 97,
tabCompletion = 98,
useTabStops = 99,
wordSeparators = 100,
wordWrap = 101,
wordWrapBreakAfterCharacters = 102,
wordWrapBreakBeforeCharacters = 103,
wordWrapColumn = 104,
wordWrapMinified = 105,
wrappingIndent = 106,
wrappingStrategy = 107,
editorClassName = 108,
pixelRatio = 109,
tabFocusMode = 110,
layoutInfo = 111,
wrappingInfo = 112
}
/**
@@ -5,7 +5,7 @@
import * as assert from 'assert';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/bracketMatching';
@@ -31,7 +31,7 @@ suite('bracket matching', () => {
test('issue #183: jump to matching bracket position', () => {
let mode = new BracketMode();
let model = TextModel.createFromString('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier());
let model = createTextModel('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
@@ -63,7 +63,7 @@ suite('bracket matching', () => {
test('Jump to next bracket', () => {
let mode = new BracketMode();
let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
@@ -100,7 +100,7 @@ suite('bracket matching', () => {
test('Select to next bracket', () => {
let mode = new BracketMode();
let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
@@ -152,7 +152,7 @@ suite('bracket matching', () => {
'};',
].join('\n');
const mode = new BracketMode();
const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier());
const model = createTextModel(text, undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
const bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
@@ -177,7 +177,7 @@ suite('bracket matching', () => {
'};',
].join('\n');
const mode = new BracketMode();
const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier());
const model = createTextModel(text, undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
const bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
@@ -195,7 +195,7 @@ suite('bracket matching', () => {
test('issue #45369: Select to Bracket with multicursor', () => {
let mode = new BracketMode();
let model = TextModel.createFromString('{ } { } { }', undefined, mode.getLanguageIdentifier());
let model = createTextModel('{ } { } { }', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
@@ -16,6 +16,7 @@ import { ITextModel } from 'vs/editor/common/model';
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 { IProgress, Progress } from 'vs/platform/progress/common/progress';
export const codeActionCommandId = 'editor.action.codeAction';
export const refactorCommandId = 'editor.action.refactor';
@@ -70,7 +71,8 @@ export function getCodeActions(
model: ITextModel,
rangeOrSelection: Range | Selection,
trigger: CodeActionTrigger,
token: CancellationToken
progress: IProgress<modes.CodeActionProvider>,
token: CancellationToken,
): Promise<CodeActionSet> {
const filter = trigger.filter || {};
@@ -85,6 +87,7 @@ export function getCodeActions(
const disposables = new DisposableStore();
const promises = providers.map(async provider => {
try {
progress.report(provider);
const providedCodeActions = await provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token);
if (providedCodeActions) {
disposables.add(providedCodeActions);
@@ -210,6 +213,7 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor,
model,
validatedRangeOrSelection,
{ type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } },
Progress.None,
CancellationToken.None);
setTimeout(() => codeActionSet.dispose(), 100);
@@ -14,7 +14,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { CodeActionProviderRegistry, CodeActionTriggerType } from 'vs/editor/common/modes';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress';
import { getCodeActions, CodeActionSet } from './codeAction';
import { CodeActionTrigger } from './types';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
@@ -213,7 +213,7 @@ export class CodeActionModel extends Disposable {
return;
}
const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token));
const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token));
if (this._progressService && trigger.trigger.type === CodeActionTriggerType.Manual) {
this._progressService.showWhile(actions, 250);
}
@@ -12,6 +12,8 @@ import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Progress } from 'vs/platform/progress/common/progress';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
function staticCodeActionProvider(...actions: modes.CodeAction[]): modes.CodeActionProvider {
return new class implements modes.CodeActionProvider {
@@ -92,7 +94,7 @@ suite('CodeAction', () => {
setup(function () {
disposables.clear();
model = TextModel.createFromString('test1\ntest2\ntest3', undefined, langId, uri);
model = createTextModel('test1\ntest2\ntest3', undefined, langId, uri);
disposables.add(model);
});
@@ -125,7 +127,7 @@ suite('CodeAction', () => {
testData.tsLint.abc
];
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 6);
assert.deepEqual(actions, expected);
});
@@ -140,20 +142,20 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, 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: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, Progress.None, 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: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 0);
}
});
@@ -172,7 +174,7 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
});
@@ -186,13 +188,13 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, Progress.None, 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: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
}
@@ -214,7 +216,7 @@ suite('CodeAction', () => {
excludes: [CodeActionKind.Source],
includeSourceActions: true,
}
}, CancellationToken.None);
}, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'b');
}
@@ -250,7 +252,7 @@ suite('CodeAction', () => {
include: baseType,
excludes: [subType],
}
}, CancellationToken.None);
}, Progress.None, CancellationToken.None);
assert.strictEqual(didInvoke, false);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
@@ -275,7 +277,7 @@ suite('CodeAction', () => {
filter: {
include: CodeActionKind.QuickFix
}
}, CancellationToken.None);
}, Progress.None, CancellationToken.None);
assert.strictEqual(actions.length, 0);
assert.strictEqual(wasInvoked, false);
});
@@ -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 { createTextModel } from 'vs/editor/test/common/editorTestUtils';
const testProvider = {
provideCodeActions(): modes.CodeActionList {
@@ -38,7 +39,7 @@ suite('CodeActionModel', () => {
setup(() => {
disposables.clear();
markerService = new MarkerService();
model = TextModel.createFromString('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri);
model = createTextModel('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri);
editor = createTestCodeEditor({ model: model });
editor.setPosition({ lineNumber: 1, column: 1 });
});
+2 -2
View File
@@ -68,7 +68,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu
}
private onEditorKeyDown(e: IKeyboardEvent): void {
if (!this._editor.getOption(EditorOption.dragAndDrop)) {
if (!this._editor.getOption(EditorOption.dragAndDrop) || this._editor.getOption(EditorOption.columnSelection)) {
return;
}
@@ -84,7 +84,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu
}
private onEditorKeyUp(e: IKeyboardEvent): void {
if (!this._editor.getOption(EditorOption.dragAndDrop)) {
if (!this._editor.getOption(EditorOption.dragAndDrop) || this._editor.getOption(EditorOption.columnSelection)) {
return;
}
@@ -8,7 +8,7 @@ import { OutlineElement, OutlineGroup, OutlineModel } from '../outlineModel';
import { SymbolKind, DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { Range } from 'vs/editor/common/core/range';
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { URI } from 'vs/base/common/uri';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
@@ -16,7 +16,7 @@ suite('OutlineModel', function () {
test('OutlineModel#create, cached', async function () {
let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo'));
let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo'));
let count = 0;
let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, {
provideDocumentSymbols() {
@@ -42,7 +42,7 @@ suite('OutlineModel', function () {
test('OutlineModel#create, cached/cancel', async function () {
let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo'));
let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo'));
let isCancelled = false;
let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, {
@@ -16,6 +16,8 @@ import { FindModelBoundToEditorModel } from 'vs/editor/contrib/find/findModel';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
suite('FindModel', () => {
@@ -45,7 +47,7 @@ suite('FindModel', () => {
const factory = ptBuilder.finish();
withTestCodeEditor([],
{
model: new TextModel(factory, TextModel.DEFAULT_CREATION_OPTIONS, null, null, new UndoRedoService())
model: new TextModel(factory, TextModel.DEFAULT_CREATION_OPTIONS, null, null, new UndoRedoService(new TestDialogService(), new TestNotificationService()))
},
(editor, cursor) => callback(editor as unknown as IActiveCodeEditor, cursor)
);
@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel';
import { TextModel, ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { TrackedRangeStickiness, IModelDeltaDecoration, ITextModel, IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { EditOperation } from 'vs/editor/common/core/editOperation';
@@ -92,7 +93,7 @@ suite('Folding Model', () => {
/* 7*/ ' }',
/* 8*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -131,7 +132,7 @@ suite('Folding Model', () => {
/* 7*/ ' }',
/* 8*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -177,7 +178,7 @@ suite('Folding Model', () => {
/* 7*/ ' }',
/* 8*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -217,7 +218,7 @@ suite('Folding Model', () => {
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -254,7 +255,7 @@ suite('Folding Model', () => {
/* 7*/ ' }',
/* 8*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -295,7 +296,7 @@ suite('Folding Model', () => {
/* 11*/ ' }',
/* 12*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -346,7 +347,7 @@ suite('Folding Model', () => {
/* 10*/ '//#endregion',
/* 11*/ ''];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -392,7 +393,7 @@ suite('Folding Model', () => {
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -448,7 +449,7 @@ suite('Folding Model', () => {
/* 15*/ ' //#endregion',
/* 16*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -504,7 +505,7 @@ suite('Folding Model', () => {
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -556,7 +557,7 @@ suite('Folding Model', () => {
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -603,7 +604,7 @@ suite('Folding Model', () => {
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -648,7 +649,7 @@ suite('Folding Model', () => {
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
import { MAX_FOLDING_REGIONS } from 'vs/editor/contrib/folding/foldingRanges';
@@ -26,7 +26,7 @@ suite('FoldingRanges', () => {
for (let i = 0; i < nRegions; i++) {
lines.push('#endregion');
}
let model = TextModel.createFromString(lines.join('\n'));
let model = createTextModel(lines.join('\n'));
let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS);
assert.equal(actual.length, nRegions, 'len');
for (let i = 0; i < nRegions; i++) {
@@ -53,7 +53,7 @@ suite('FoldingRanges', () => {
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
try {
let actual = computeRanges(textModel, false, markers);
// let r0 = r(1, 2);
@@ -91,7 +91,7 @@ suite('FoldingRanges', () => {
for (let i = 0; i < nRegions; i++) {
lines.push('#endregion');
}
let model = TextModel.createFromString(lines.join('\n'));
let model = createTextModel(lines.join('\n'));
let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS);
assert.equal(actual.length, nRegions, 'len');
for (let i = 0; i < nRegions; i++) {
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { FoldingModel } from 'vs/editor/contrib/folding/foldingModel';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { TestDecorationProvider } from './foldingModel.test';
import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel';
@@ -38,7 +38,7 @@ suite('Hidden Range Model', () => {
/* 9*/ ' }',
/* 10*/ '}'];
let textModel = TextModel.createFromString(lines.join('\n'));
let textModel = createTextModel(lines.join('\n'));
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let hiddenRangeModel = new HiddenRangeModel(foldingModel);
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
interface IndentRange {
start: number;
@@ -47,7 +47,7 @@ suite('Indentation Folding', () => {
let r8 = r(13, 14);//4
let r9 = r(15, 16);//0
let model = TextModel.createFromString(lines.join('\n'));
let model = createTextModel(lines.join('\n'));
function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
let indentRanges = computeRanges(model, true, undefined, maxEntries);
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
@@ -15,7 +15,7 @@ interface ExpectedIndentRange {
}
function assertRanges(lines: string[], expected: ExpectedIndentRange[], offside: boolean, markers?: FoldingMarkers): void {
let model = TextModel.createFromString(lines.join('\n'));
let model = createTextModel(lines.join('\n'));
let actual = computeRanges(model, offside, markers);
let actualRanges: ExpectedIndentRange[] = [];
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { SyntaxRangeProvider } from 'vs/editor/contrib/folding/syntaxRangeProvider';
import { FoldingRangeProvider, FoldingRange, FoldingContext, ProviderResult } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
@@ -69,7 +69,7 @@ suite('Syntax folding', () => {
let r8 = r(14, 15); //6
let r9 = r(22, 23); //0
let model = TextModel.createFromString(lines.join('\n'));
let model = createTextModel(lines.join('\n'));
let ranges = [r1, r2, r3, r4, r5, r6, r7, r8, r9];
let providers = [new TestFoldingRangeProvider(model, ranges)];
+3
View File
@@ -27,6 +27,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { assertType } from 'vs/base/common/types';
import { IProgress } from 'vs/platform/progress/common/progress';
export function alertFormattingEdits(edits: ISingleEditOperation[]): void {
@@ -209,6 +210,7 @@ export async function formatDocumentWithSelectedProvider(
accessor: ServicesAccessor,
editorOrModel: ITextModel | IActiveCodeEditor,
mode: FormattingMode,
progress: IProgress<DocumentFormattingEditProvider>,
token: CancellationToken
): Promise<void> {
@@ -217,6 +219,7 @@ export async function formatDocumentWithSelectedProvider(
const provider = getRealAndSyntheticDocumentFormattersOrdered(model);
const selected = await FormattingConflicts.select(provider, model, mode);
if (selected) {
progress.report(selected);
await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, mode, token);
}
}
@@ -25,6 +25,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Progress } from 'vs/platform/progress/common/progress';
class FormatOnType implements IEditorContribution {
@@ -230,7 +231,7 @@ class FormatDocumentAction extends EditorAction {
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
if (editor.hasModel()) {
const instaService = accessor.get(IInstantiationService);
await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, CancellationToken.None);
await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None);
}
}
}
@@ -33,6 +33,7 @@ import * as peekView from 'vs/editor/contrib/peekView/peekView';
import { FileReferences, OneReference, ReferencesModel } from '../referencesModel';
import { FuzzyScore } from 'vs/base/common/filters';
import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
class DecorationsManager implements IDisposable {
@@ -215,7 +216,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
@ITextModelService private readonly _textModelResolverService: ITextModelService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@peekView.IPeekViewService private readonly _peekViewService: peekView.IPeekViewService,
@ILabelService private readonly _uriLabel: ILabelService
@ILabelService private readonly _uriLabel: ILabelService,
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
) {
super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true });
@@ -304,7 +306,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
};
this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, this._previewContainer, options, this.editor);
dom.hide(this._previewContainer);
this._previewNotAvailableMessage = TextModel.createFromString(nls.localize('missingPreviewMessage', "no preview available"));
this._previewNotAvailableMessage = new TextModel(nls.localize('missingPreviewMessage', "no preview available"), TextModel.DEFAULT_CREATION_OPTIONS, null, null, this._undoRedoService);
// tree
this._treeContainer = dom.append(containerElement, dom.$('div.ref-tree.inline'));
@@ -40,6 +40,7 @@ import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Constants } from 'vs/base/common/uint';
import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { Progress } from 'vs/platform/progress/common/progress';
const $ = dom.$;
@@ -626,6 +627,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._editor.getModel()!,
new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn),
markerCodeActionTrigger,
Progress.None,
cancellationToken);
});
}
@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { Position } from 'vs/editor/common/core/position';
import { Handler } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import * as modes from 'vs/editor/common/modes';
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -49,7 +49,7 @@ suite('ParameterHintsModel', () => {
});
function createMockEditor(fileContents: string) {
const textModel = TextModel.createFromString(fileContents, undefined, undefined, mockFile);
const textModel = createTextModel(fileContents, undefined, undefined, mockFile);
const editor = createTestCodeEditor({
model: textModel,
serviceCollection: new ServiceCollection(
@@ -20,6 +20,8 @@ import { TestTextResourcePropertiesService } from 'vs/editor/test/common/service
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
class MockJSMode extends MockMode {
@@ -48,7 +50,7 @@ suite('SmartSelect', () => {
setup(() => {
const configurationService = new TestConfigurationService();
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService());
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(new TestDialogService(), new TestNotificationService()));
mode = new MockJSMode();
});
@@ -12,6 +12,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { NullLogService } from 'vs/platform/log/common/log';
import { Handler } from 'vs/editor/common/editorCommon';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('SnippetController2', function () {
@@ -36,7 +37,7 @@ suite('SnippetController2', function () {
setup(function () {
contextKeys = new MockContextKeyService();
model = TextModel.createFromString('if\n $state\nfi');
model = createTextModel('if\n $state\nfi');
editor = createTestCodeEditor({ model: model });
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]);
assert.equal(model.getEOL(), '\n');
@@ -11,6 +11,7 @@ import { TextModel } from 'vs/editor/common/model/textModel';
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
import { SnippetSession } from 'vs/editor/contrib/snippet/snippetSession';
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('SnippetSession', function () {
@@ -26,7 +27,7 @@ suite('SnippetSession', function () {
}
setup(function () {
model = TextModel.createFromString('function foo() {\n console.log(a);\n}');
model = createTextModel('function foo() {\n console.log(a);\n}');
editor = createTestCodeEditor({ model: model }) as IActiveCodeEditor;
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]);
assert.equal(model.getEOL(), '\n');
@@ -12,6 +12,7 @@ import { TextModel } from 'vs/editor/common/model/textModel';
import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ILabelService } from 'vs/platform/label/common/label';
import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('Snippet Variables Resolver', function () {
@@ -25,7 +26,7 @@ suite('Snippet Variables Resolver', function () {
let resolver: VariableResolver;
setup(function () {
model = TextModel.createFromString([
model = createTextModel([
'this is line one',
'this is line two',
' this is line three'
@@ -67,7 +68,7 @@ suite('Snippet Variables Resolver', function () {
resolver = new ModelBasedVariableResolver(
labelService,
TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi'))
createTextModel('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi'))
);
assertVariableResolve(resolver, 'TM_FILENAME', 'ghi');
if (!isWindows) {
@@ -77,7 +78,7 @@ suite('Snippet Variables Resolver', function () {
resolver = new ModelBasedVariableResolver(
labelService,
TextModel.createFromString('', undefined, undefined, URI.parse('mem:fff.ts'))
createTextModel('', undefined, undefined, URI.parse('mem:fff.ts'))
);
assertVariableResolve(resolver, 'TM_DIRECTORY', '');
assertVariableResolve(resolver, 'TM_FILEPATH', 'fff.ts');
@@ -92,7 +93,7 @@ suite('Snippet Variables Resolver', function () {
}
};
const model = TextModel.createFromString([].join('\n'), undefined, undefined, URI.parse('foo:///foo/files/text.txt'));
const model = createTextModel([].join('\n'), undefined, undefined, URI.parse('foo:///foo/files/text.txt'));
const resolver = new CompositeSnippetVariableResolver([new ModelBasedVariableResolver(labelService, model)]);
@@ -144,19 +145,19 @@ suite('Snippet Variables Resolver', function () {
resolver = new ModelBasedVariableResolver(
labelService,
TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi'))
createTextModel('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi'))
);
assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'ghi');
resolver = new ModelBasedVariableResolver(
labelService,
TextModel.createFromString('', undefined, undefined, URI.parse('mem:.git'))
createTextModel('', undefined, undefined, URI.parse('mem:.git'))
);
assertVariableResolve(resolver, 'TM_FILENAME_BASE', '.git');
resolver = new ModelBasedVariableResolver(
labelService,
TextModel.createFromString('', undefined, undefined, URI.parse('mem:foo.'))
createTextModel('', undefined, undefined, URI.parse('mem:foo.'))
);
assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'foo');
});
@@ -10,6 +10,7 @@ import { provideSuggestionItems, SnippetSortOrder, CompletionOptions } from 'vs/
import { Position } from 'vs/editor/common/core/position';
import { TextModel } from 'vs/editor/common/model/textModel';
import { Range } from 'vs/editor/common/core/range';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('Suggest', function () {
@@ -19,7 +20,7 @@ suite('Suggest', function () {
setup(function () {
model = TextModel.createFromString('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path'));
model = createTextModel('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path'));
registration = CompletionProviderRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, {
provideCompletionItems(_doc, pos) {
return {
@@ -23,6 +23,7 @@ import { CompletionProviderRegistry, CompletionItemKind, CompletionItemInsertTex
import { Event } from 'vs/base/common/event';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { IMenuService, IMenu } from 'vs/platform/actions/common/actions';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('SuggestController', function () {
@@ -57,7 +58,7 @@ suite('SuggestController', function () {
}]
);
model = TextModel.createFromString('', undefined, undefined, URI.from({ scheme: 'test-ctrl', path: '/path.tst' }));
model = createTextModel('', undefined, undefined, URI.from({ scheme: 'test-ctrl', path: '/path.tst' }));
editor = createTestCodeEditor({
model,
serviceCollection,
@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { LRUMemory, NoMemory, PrefixMemory, Memory } from 'vs/editor/contrib/suggest/suggestMemory';
import { ITextModel } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { createSuggestItem } from 'vs/editor/contrib/suggest/test/completionModel.test';
import { IPosition } from 'vs/editor/common/core/position';
import { CompletionItem } from 'vs/editor/contrib/suggest/suggest';
@@ -19,7 +19,7 @@ suite('SuggestMemories', function () {
setup(function () {
pos = { lineNumber: 1, column: 1 };
buffer = TextModel.createFromString('This is some text.\nthis.\nfoo: ,');
buffer = createTextModel('This is some text.\nthis.\nfoo: ,');
items = [
createSuggestItem('foo', 0),
createSuggestItem('bar', 0)
@@ -32,6 +32,7 @@ import { ISuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory';
import { ITextModel } from 'vs/editor/common/model';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
export interface Ctor<T> {
new(): T;
@@ -124,7 +125,7 @@ suite('SuggestModel - Context', function () {
});
test('Context - shouldAutoTrigger', function () {
const model = TextModel.createFromString('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?');
const model = createTextModel('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?');
disposables.push(model);
assertAutoTrigger(model, 3, true, 'end of word, Das|');
@@ -138,7 +139,7 @@ suite('SuggestModel - Context', function () {
const innerMode = new InnerMode();
disposables.push(outerMode, innerMode);
const model = TextModel.createFromString('a<xx>a<x>', undefined, outerMode.getLanguageIdentifier());
const model = createTextModel('a<xx>a<x>', undefined, outerMode.getLanguageIdentifier());
disposables.push(model);
assertAutoTrigger(model, 1, true, 'a|<x — should trigger at end of word');
@@ -187,7 +188,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
setup(function () {
disposables = dispose(disposables);
model = TextModel.createFromString('abc def', undefined, undefined, URI.parse('test:somefile.ttt'));
model = createTextModel('abc def', undefined, undefined, URI.parse('test:somefile.ttt'));
disposables.push(model);
});
@@ -8,7 +8,7 @@ import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker
import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test';
import { EditorWorkerHost, EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
import { IModelService } from 'vs/editor/common/services/modelService';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { URI } from 'vs/base/common/uri';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { NullLogService } from 'vs/platform/log/common/log';
@@ -48,7 +48,7 @@ suite('suggest, word distance', function () {
disposables.clear();
let mode = new BracketMode();
let model = TextModel.createFromString('function abc(aa, ab){\na\n}', undefined, mode.getLanguageIdentifier(), URI.parse('test:///some.path'));
let model = createTextModel('function abc(aa, ab){\na\n}', undefined, mode.getLanguageIdentifier(), URI.parse('test:///some.path'));
let editor = createTestCodeEditor({ model: model });
editor.updateOptions({ suggest: { localityBonus: true } });
editor.setPosition({ lineNumber: 2, column: 2 });
@@ -152,7 +152,7 @@ export module StaticServices {
export const logService = define(ILogService, () => new NullLogService());
export const undoRedoService = define(IUndoRedoService, () => new UndoRedoService());
export const undoRedoService = define(IUndoRedoService, (o) => new UndoRedoService(dialogService.get(o), notificationService.get(o)));
export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o), logService.get(o), undoRedoService.get(o)));
@@ -10,7 +10,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
@@ -199,7 +199,7 @@ suite('SideEditing', () => {
];
function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void {
const model = TextModel.createFromString(LINES.join('\n'));
const model = createTextModel(LINES.join('\n'));
const config = new TestConfiguration({});
const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(config.options);
const viewModel = new ViewModel(0, config, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!);
@@ -5551,4 +5551,28 @@ suite('Undo stops', () => {
});
});
test('can undo typing and EOL change in one undo stop', () => {
let model = createTextModel(
[
'A line',
'Another line',
].join('\n')
);
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [new Selection(1, 3, 1, 3)]);
cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard');
assert.equal(model.getValue(), 'A first line\nAnother line');
assertCursor(cursor, new Selection(1, 8, 1, 8));
model.pushEOL(EndOfLineSequence.CRLF);
assert.equal(model.getValue(), 'A first line\r\nAnother line');
assertCursor(cursor, new Selection(1, 8, 1, 8));
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
assert.equal(model.getValue(), 'A line\nAnother line');
assertCursor(cursor, new Selection(1, 3, 1, 3));
});
});
});
@@ -14,6 +14,7 @@ import { TextModel } from 'vs/editor/common/model/textModel';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('Cursor move command test', () => {
@@ -31,7 +32,7 @@ suite('Cursor move command test', () => {
'1'
].join('\n');
thisModel = TextModel.createFromString(text);
thisModel = createTextModel(text);
thisConfiguration = new TestConfiguration({});
const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(thisConfiguration.options);
thisViewModel = new ViewModel(0, thisConfiguration, thisModel, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!);
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { ITextAreaWrapper, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
export class MockTextAreaWrapper extends Disposable implements ITextAreaWrapper {
@@ -506,7 +506,7 @@ suite('TextAreaState', () => {
suite('PagedScreenReaderStrategy', () => {
function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void {
const model = TextModel.createFromString(lines.join('\n'));
const model = createTextModel(lines.join('\n'));
const actual = PagedScreenReaderStrategy.fromEditorSelection(TextAreaState.EMPTY, model, selection, 10, true);
assert.ok(equalsTextAreaState(actual, expected));
model.dispose();
+3 -3
View File
@@ -11,7 +11,7 @@ import * as editorOptions from 'vs/editor/common/config/editorOptions';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { IConfiguration, IEditorContribution } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { TestCodeEditorService, TestCommandService } from 'vs/editor/test/browser/editorTestServices';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
@@ -77,9 +77,9 @@ export function withTestCodeEditor(text: string | string[] | null, options: Test
// create a model if necessary and remember it in order to dispose it.
if (!options.model) {
if (typeof text === 'string') {
options.model = TextModel.createFromString(text);
options.model = createTextModel(text);
} else if (text) {
options.model = TextModel.createFromString(text.join('\n'));
options.model = createTextModel(text.join('\n'));
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ import { IRange } from 'vs/editor/common/core/range';
import { Selection, ISelection } from 'vs/editor/common/core/selection';
import { ICommand, Handler, IEditOperationBuilder } from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
@@ -21,7 +21,7 @@ export function testCommand(
expectedSelection: Selection,
forceTokenization?: boolean
): void {
let model = TextModel.createFromString(lines.join('\n'), undefined, languageIdentifier);
let model = createTextModel(lines.join('\n'), undefined, languageIdentifier);
withTestCodeEditor('', { model: model }, (_editor, cursor) => {
if (!cursor) {
return;
+8 -2
View File
@@ -7,9 +7,12 @@ import { URI } from 'vs/base/common/uri';
import { DefaultEndOfLine, ITextModelCreationOptions } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
export function withEditorModel(text: string[], callback: (model: TextModel) => void): void {
let model = TextModel.createFromString(text.join('\n'));
let model = createTextModel(text.join('\n'));
callback(model);
model.dispose();
}
@@ -36,5 +39,8 @@ export function createTextModel(text: string, _options: IRelaxedTextModelCreatio
isForSimpleWidget: (typeof _options.isForSimpleWidget === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.isForSimpleWidget : _options.isForSimpleWidget),
largeFileOptimizations: (typeof _options.largeFileOptimizations === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.largeFileOptimizations : _options.largeFileOptimizations),
};
return TextModel.createFromString(text, options, languageIdentifier, uri);
const dialogService = new TestDialogService();
const notificationService = new TestNotificationService();
const undoRedoService = new UndoRedoService(dialogService, notificationService);
return new TextModel(text, options, languageIdentifier, uri, undoRedoService);
}
@@ -10,9 +10,10 @@ import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { assertSyncedModels, testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
function createEditableTextModelFromString(text: string): TextModel {
return TextModel.createFromString(text, TextModel.DEFAULT_CREATION_OPTIONS, null);
return createTextModel(text, TextModel.DEFAULT_CREATION_OPTIONS, null);
}
suite('EditorModel - EditableTextModel.applyEdits updates mightContainRTL', () => {
@@ -9,6 +9,7 @@ import { EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation
import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
export function testApplyEditsWithSyncedModels(original: string[], edits: IIdentifiedSingleEditOperation[], expected: string[], inputEditsAreInvalid: boolean = false): void {
let originalStr = original.join('\n');
@@ -88,7 +89,7 @@ function assertLineMapping(model: TextModel, msg: string): void {
export function assertSyncedModels(text: string, callback: (model: TextModel, assertMirrorModels: () => void) => void, setup: ((model: TextModel) => void) | null = null): void {
let model = TextModel.createFromString(text, TextModel.DEFAULT_CREATION_OPTIONS, null);
let model = createTextModel(text, TextModel.DEFAULT_CREATION_OPTIONS, null);
model.setEOL(EndOfLineSequence.LF);
assertLineMapping(model, 'model');
@@ -9,6 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes';
import { ViewLineToken, ViewLineTokenFactory } from 'vs/editor/test/common/core/viewLineToken';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
interface ILineEdit {
startColumn: number;
@@ -106,7 +107,7 @@ suite('ModelLinesTokens', () => {
function testApplyEdits(initial: IBufferLineState[], edits: IEdit[], expected: IBufferLineState[]): void {
const initialText = initial.map(el => el.text).join('\n');
const model = TextModel.createFromString(initialText, TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0));
const model = createTextModel(initialText, TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0));
for (let lineIndex = 0; lineIndex < initial.length; lineIndex++) {
const lineTokens = initial[lineIndex].tokens;
const lineTextLength = model.getLineMaxColumn(lineIndex + 1) - 1;
@@ -442,7 +443,7 @@ suite('ModelLinesTokens', () => {
}
test('insertion on empty line', () => {
const model = TextModel.createFromString('some text', TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0));
const model = createTextModel('some text', TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0));
const tokens = TestToken.toTokens([new TestToken(0, 1)]);
LineTokens.convertToEndOffset(tokens, model.getLineMaxColumn(1) - 1);
model.setLineTokens(1, tokens);
@@ -12,6 +12,7 @@ import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { TextModel } from 'vs/editor/common/model/textModel';
import * as modes from 'vs/editor/common/modes';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
// --------- utils
@@ -46,7 +47,7 @@ suite('Editor Model - Model Modes 1', () => {
const LANGUAGE_ID = 'modelModeTest1';
calledFor = [];
languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
thisModel = TextModel.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
thisModel = createTextModel(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
});
teardown(() => {
@@ -199,7 +200,7 @@ suite('Editor Model - Model Modes 2', () => {
'Line5';
const LANGUAGE_ID = 'modelModeTest2';
languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
thisModel = TextModel.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
thisModel = createTextModel(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
});
teardown(() => {
@@ -15,6 +15,7 @@ import { IState, LanguageIdentifier, MetadataConsts, TokenizationRegistry } from
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
// --------- utils
@@ -35,7 +36,7 @@ suite('Editor Model - Model', () => {
LINE3 + '\n' +
LINE4 + '\r\n' +
LINE5;
thisModel = TextModel.createFromString(text);
thisModel = createTextModel(text);
});
teardown(() => {
@@ -349,7 +350,7 @@ suite('Editor Model - Model Line Separators', () => {
LINE3 + '\u2028' +
LINE4 + '\r\n' +
LINE5;
thisModel = TextModel.createFromString(text);
thisModel = createTextModel(text);
});
teardown(() => {
@@ -365,7 +366,7 @@ suite('Editor Model - Model Line Separators', () => {
});
test('Bug 13333:Model should line break on lonely CR too', () => {
let model = TextModel.createFromString('Hello\rWorld!\r\nAnother line');
let model = createTextModel('Hello\rWorld!\r\nAnother line');
assert.equal(model.getLineCount(), 3);
assert.equal(model.getValue(), 'Hello\r\nWorld!\r\nAnother line');
model.dispose();
@@ -430,7 +431,7 @@ suite('Editor Model - Words', () => {
test('Get word at position', () => {
const text = ['This text has some words. '];
const thisModel = TextModel.createFromString(text.join('\n'));
const thisModel = createTextModel(text.join('\n'));
disposables.push(thisModel);
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: 'This', startColumn: 1, endColumn: 5 });
@@ -451,7 +452,7 @@ suite('Editor Model - Words', () => {
const innerMode = new InnerMode();
disposables.push(outerMode, innerMode);
const model = TextModel.createFromString('ab<xx>ab<x>', undefined, outerMode.getLanguageIdentifier());
const model = createTextModel('ab<xx>ab<x>', undefined, outerMode.getLanguageIdentifier());
disposables.push(model);
assert.deepEqual(model.getWordAtPosition(new Position(1, 1)), { word: 'ab', startColumn: 1, endColumn: 3 });
@@ -476,7 +477,7 @@ suite('Editor Model - Words', () => {
};
disposables.push(mode);
const thisModel = TextModel.createFromString('.🐷-a-b', undefined, MODE_ID);
const thisModel = createTextModel('.🐷-a-b', undefined, MODE_ID);
disposables.push(thisModel);
assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: '.', startColumn: 1, endColumn: 2 });
@@ -9,6 +9,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { EndOfLineSequence, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
// --------- utils
@@ -92,7 +93,7 @@ suite('Editor Model - Model Decorations', () => {
LINE3 + '\n' +
LINE4 + '\r\n' +
LINE5;
thisModel = TextModel.createFromString(text);
thisModel = createTextModel(text);
});
teardown(() => {
@@ -400,7 +401,7 @@ suite('Editor Model - Model Decorations', () => {
});
test('removeAllDecorationsWithOwnerId can be called after model dispose', () => {
let model = TextModel.createFromString('asd');
let model = createTextModel('asd');
model.dispose();
model.removeAllDecorationsWithOwnerId(1);
});
@@ -415,7 +416,7 @@ suite('Editor Model - Model Decorations', () => {
suite('Decorations and editing', () => {
function _runTest(decRange: Range, stickiness: TrackedRangeStickiness, editRange: Range, editText: string, editForceMoveMarkers: boolean, expectedDecRange: Range, msg: string): void {
let model = TextModel.createFromString([
let model = createTextModel([
'My First Line',
'My Second Line',
'Third Line'
@@ -1148,7 +1149,7 @@ suite('deltaDecorations', () => {
function testDeltaDecorations(text: string[], decorations: ILightWeightDecoration[], newDecorations: ILightWeightDecoration[]): void {
let model = TextModel.createFromString(text.join('\n'));
let model = createTextModel(text.join('\n'));
// Add initial decorations & assert they are added
let initialIds = model.deltaDecorations([], decorations.map(toModelDeltaDecoration));
@@ -1177,7 +1178,7 @@ suite('deltaDecorations', () => {
}
test('result respects input', () => {
let model = TextModel.createFromString([
let model = createTextModel([
'Hello world,',
'How are you?'
].join('\n'));
@@ -1265,7 +1266,7 @@ suite('deltaDecorations', () => {
test('issue #4317: editor.setDecorations doesn\'t update the hover message', () => {
let model = TextModel.createFromString('Hello world!');
let model = createTextModel('Hello world!');
let ids = model.deltaDecorations([], [{
range: {
@@ -1299,7 +1300,7 @@ suite('deltaDecorations', () => {
});
test('model doesn\'t get confused with individual tracked ranges', () => {
let model = TextModel.createFromString([
let model = createTextModel([
'Hello world,',
'How are you?'
].join('\n'));
@@ -1340,7 +1341,7 @@ suite('deltaDecorations', () => {
});
test('issue #16922: Clicking on link doesn\'t seem to do anything', () => {
let model = TextModel.createFromString([
let model = createTextModel([
'Hello world,',
'How are you?',
'Fine.',
@@ -1371,7 +1372,7 @@ suite('deltaDecorations', () => {
test('issue #41492: URL highlighting persists after pasting over url', () => {
let model = TextModel.createFromString([
let model = createTextModel([
'My First Line'
].join('\n'));
@@ -6,6 +6,7 @@ import * as assert from 'assert';
import { Range } from 'vs/editor/common/core/range';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('Editor Model - Model Edit Operation', () => {
const LINE1 = 'My First Line';
@@ -23,7 +24,7 @@ suite('Editor Model - Model Edit Operation', () => {
LINE3 + '\n' +
LINE4 + '\r\n' +
LINE5;
model = TextModel.createFromString(text);
model = createTextModel(text);
});
teardown(() => {
@@ -12,7 +12,7 @@ import { PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceT
import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer';
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase';
import { TextModel } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { SearchData } from 'vs/editor/common/model/textModelSearch';
const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n';
@@ -1761,7 +1761,7 @@ function getValueInSnapshot(snapshot: ITextSnapshot) {
}
suite('snapshot', () => {
test('bug #45564, piece tree pieces should be immutable', () => {
const model = TextModel.createFromString('\n');
const model = createTextModel('\n');
model.applyEdits([
{
range: new Range(2, 1, 2, 1),
@@ -1789,7 +1789,7 @@ suite('snapshot', () => {
});
test('immutable snapshot 1', () => {
const model = TextModel.createFromString('abc\ndef');
const model = createTextModel('abc\ndef');
const snapshot = model.createSnapshot();
model.applyEdits([
{
@@ -1809,7 +1809,7 @@ suite('snapshot', () => {
});
test('immutable snapshot 2', () => {
const model = TextModel.createFromString('abc\ndef');
const model = createTextModel('abc\ndef');
const snapshot = model.createSnapshot();
model.applyEdits([
{
@@ -1829,7 +1829,7 @@ suite('snapshot', () => {
});
test('immutable snapshot 3', () => {
const model = TextModel.createFromString('abc\ndef');
const model = createTextModel('abc\ndef');
model.applyEdits([
{
range: new Range(2, 4, 2, 4),
@@ -1896,4 +1896,4 @@ suite('chunk based search', () => {
assert.equal(ret.length, 1);
assert.deepEqual(ret[0].range, new Range(2, 2, 2, 3));
});
});
});
@@ -163,7 +163,7 @@ suite('Editor Model - TextModel', () => {
test('getValueLengthInRange', () => {
let m = TextModel.createFromString('My First Line\r\nMy Second Line\r\nMy Third Line');
let m = createTextModel('My First Line\r\nMy Second Line\r\nMy Third Line');
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length);
@@ -176,7 +176,7 @@ suite('Editor Model - TextModel', () => {
assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1000)), 'y First Line\r\nMy Second Line\r\nMy Third Line'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1000, 1000)), 'My First Line\r\nMy Second Line\r\nMy Third Line'.length);
m = TextModel.createFromString('My First Line\nMy Second Line\nMy Third Line');
m = createTextModel('My First Line\nMy Second Line\nMy Third Line');
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length);
assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length);
assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length);
@@ -662,7 +662,7 @@ suite('Editor Model - TextModel', () => {
test('validatePosition', () => {
let m = TextModel.createFromString('line one\nline two');
let m = createTextModel('line one\nline two');
assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1));
@@ -691,7 +691,7 @@ suite('Editor Model - TextModel', () => {
test('validatePosition around high-low surrogate pairs 1', () => {
let m = TextModel.createFromString('a📚b');
let m = createTextModel('a📚b');
assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1));
@@ -718,7 +718,7 @@ suite('Editor Model - TextModel', () => {
test('validatePosition around high-low surrogate pairs 2', () => {
let m = TextModel.createFromString('a📚📚b');
let m = createTextModel('a📚📚b');
assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2));
@@ -732,7 +732,7 @@ suite('Editor Model - TextModel', () => {
test('validatePosition handle NaN.', () => {
let m = TextModel.createFromString('line one\nline two');
let m = createTextModel('line one\nline two');
assert.deepEqual(m.validatePosition(new Position(NaN, 1)), new Position(1, 1));
assert.deepEqual(m.validatePosition(new Position(1, NaN)), new Position(1, 1));
@@ -743,7 +743,7 @@ suite('Editor Model - TextModel', () => {
});
test('issue #71480: validatePosition handle floats', () => {
let m = TextModel.createFromString('line one\nline two');
let m = createTextModel('line one\nline two');
assert.deepEqual(m.validatePosition(new Position(0.2, 1)), new Position(1, 1), 'a');
assert.deepEqual(m.validatePosition(new Position(1.2, 1)), new Position(1, 1), 'b');
@@ -756,7 +756,7 @@ suite('Editor Model - TextModel', () => {
});
test('issue #71480: validateRange handle floats', () => {
let m = TextModel.createFromString('line one\nline two');
let m = createTextModel('line one\nline two');
assert.deepEqual(m.validateRange(new Range(0.2, 1.5, 0.8, 2.5)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(1.2, 1.7, 1.8, 2.2)), new Range(1, 1, 1, 2));
@@ -764,7 +764,7 @@ suite('Editor Model - TextModel', () => {
test('validateRange around high-low surrogate pairs 1', () => {
let m = TextModel.createFromString('a📚b');
let m = createTextModel('a📚b');
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1));
@@ -792,7 +792,7 @@ suite('Editor Model - TextModel', () => {
test('validateRange around high-low surrogate pairs 2', () => {
let m = TextModel.createFromString('a📚📚b');
let m = createTextModel('a📚📚b');
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1));
assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1));
@@ -835,7 +835,7 @@ suite('Editor Model - TextModel', () => {
test('modifyPosition', () => {
let m = TextModel.createFromString('line one\nline two');
let m = createTextModel('line one\nline two');
assert.deepEqual(m.modifyPosition(new Position(1, 1), 0), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(0, 0), 0), new Position(1, 1));
assert.deepEqual(m.modifyPosition(new Position(30, 1), 0), new Position(2, 9));
@@ -913,7 +913,7 @@ suite('Editor Model - TextModel', () => {
});
test('getLineFirstNonWhitespaceColumn', () => {
let model = TextModel.createFromString([
let model = createTextModel([
'asd',
' asd',
'\tasd',
@@ -943,7 +943,7 @@ suite('Editor Model - TextModel', () => {
});
test('getLineLastNonWhitespaceColumn', () => {
let model = TextModel.createFromString([
let model = createTextModel([
'asd',
'asd ',
'asd\t',
@@ -973,7 +973,7 @@ suite('Editor Model - TextModel', () => {
});
test('#50471. getValueInRange with invalid range', () => {
let m = TextModel.createFromString('My First Line\r\nMy Second Line\r\nMy Third Line');
let m = createTextModel('My First Line\r\nMy Second Line\r\nMy Third Line');
assert.equal(m.getValueInRange(new Range(1, NaN, 1, 3)), 'My');
assert.equal(m.getValueInRange(new Range(NaN, NaN, NaN, NaN)), '');
});
@@ -982,24 +982,24 @@ suite('Editor Model - TextModel', () => {
suite('TextModel.mightContainRTL', () => {
test('nope', () => {
let model = TextModel.createFromString('hello world!');
let model = createTextModel('hello world!');
assert.equal(model.mightContainRTL(), false);
});
test('yes', () => {
let model = TextModel.createFromString('Hello,\nזוהי עובדה מבוססת שדעתו');
let model = createTextModel('Hello,\nזוהי עובדה מבוססת שדעתו');
assert.equal(model.mightContainRTL(), true);
});
test('setValue resets 1', () => {
let model = TextModel.createFromString('hello world!');
let model = createTextModel('hello world!');
assert.equal(model.mightContainRTL(), false);
model.setValue('Hello,\nזוהי עובדה מבוססת שדעתו');
assert.equal(model.mightContainRTL(), true);
});
test('setValue resets 2', () => {
let model = TextModel.createFromString('Hello,\nهناك حقيقة مثبتة منذ زمن طويل');
let model = createTextModel('Hello,\nهناك حقيقة مثبتة منذ زمن طويل');
assert.equal(model.mightContainRTL(), true);
model.setValue('hello world!');
assert.equal(model.mightContainRTL(), false);
@@ -1010,14 +1010,14 @@ suite('TextModel.mightContainRTL', () => {
suite('TextModel.createSnapshot', () => {
test('empty file', () => {
let model = TextModel.createFromString('');
let model = createTextModel('');
let snapshot = model.createSnapshot();
assert.equal(snapshot.read(), null);
model.dispose();
});
test('file with BOM', () => {
let model = TextModel.createFromString(UTF8_BOM_CHARACTER + 'Hello');
let model = createTextModel(UTF8_BOM_CHARACTER + 'Hello');
assert.equal(model.getLineContent(1), 'Hello');
let snapshot = model.createSnapshot(true);
assert.equal(snapshot.read(), UTF8_BOM_CHARACTER + 'Hello');
@@ -1026,7 +1026,7 @@ suite('TextModel.createSnapshot', () => {
});
test('regular file', () => {
let model = TextModel.createFromString('My First Line\n\t\tMy Second Line\n Third Line\n\n1');
let model = createTextModel('My First Line\n\t\tMy Second Line\n Third Line\n\n1');
let snapshot = model.createSnapshot();
assert.equal(snapshot.read(), 'My First Line\n\t\tMy Second Line\n Third Line\n\n1');
assert.equal(snapshot.read(), null);
@@ -1040,7 +1040,7 @@ suite('TextModel.createSnapshot', () => {
}
const text = lines.join('\n');
let model = TextModel.createFromString(text);
let model = createTextModel(text);
let snapshot = model.createSnapshot();
let actual = '';
@@ -11,6 +11,7 @@ import { EndOfLineSequence, FindMatch } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { SearchData, SearchParams, TextModelSearch, isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch';
import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
// --------- Find
suite('TextModelSearch', () => {
@@ -51,12 +52,12 @@ suite('TextModelSearch', () => {
let expectedMatches = expectedRanges.map(entry => new FindMatch(entry, null));
let searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
let model = TextModel.createFromString(text);
let model = createTextModel(text);
_assertFindMatches(model, searchParams, expectedMatches);
model.dispose();
let model2 = TextModel.createFromString(text);
let model2 = createTextModel(text);
model2.setEOL(EndOfLineSequence.CRLF);
_assertFindMatches(model2, searchParams, expectedMatches);
model2.dispose();
@@ -380,7 +381,7 @@ suite('TextModelSearch', () => {
});
test('findNextMatch without regex', () => {
let model = TextModel.createFromString('line line one\nline two\nthree');
let model = createTextModel('line line one\nline two\nthree');
let searchParams = new SearchParams('line', false, false, null);
@@ -403,7 +404,7 @@ suite('TextModelSearch', () => {
});
test('findNextMatch with beginning boundary regex', () => {
let model = TextModel.createFromString('line one\nline two\nthree');
let model = createTextModel('line one\nline two\nthree');
let searchParams = new SearchParams('^line', true, false, null);
@@ -423,7 +424,7 @@ suite('TextModelSearch', () => {
});
test('findNextMatch with beginning boundary regex and line has repetitive beginnings', () => {
let model = TextModel.createFromString('line line one\nline two\nthree');
let model = createTextModel('line line one\nline two\nthree');
let searchParams = new SearchParams('^line', true, false, null);
@@ -443,7 +444,7 @@ suite('TextModelSearch', () => {
});
test('findNextMatch with beginning boundary multiline regex and line has repetitive beginnings', () => {
let model = TextModel.createFromString('line line one\nline two\nline three\nline four');
let model = createTextModel('line line one\nline two\nline three\nline four');
let searchParams = new SearchParams('^line.*\\nline', true, false, null);
@@ -460,7 +461,7 @@ suite('TextModelSearch', () => {
});
test('findNextMatch with ending boundary regex', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let model = createTextModel('one line line\ntwo line\nthree');
let searchParams = new SearchParams('line$', true, false, null);
@@ -480,7 +481,7 @@ suite('TextModelSearch', () => {
});
test('findMatches with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let model = createTextModel('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)', true, false, null);
@@ -495,7 +496,7 @@ suite('TextModelSearch', () => {
});
test('findMatches multiline with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let model = createTextModel('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)\\n', true, false, null);
@@ -509,7 +510,7 @@ suite('TextModelSearch', () => {
});
test('findNextMatch with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let model = createTextModel('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)', true, false, null);
@@ -520,7 +521,7 @@ suite('TextModelSearch', () => {
});
test('findNextMatch multiline with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let model = createTextModel('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)\\n', true, false, null);
@@ -531,7 +532,7 @@ suite('TextModelSearch', () => {
});
test('findPreviousMatch with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let model = createTextModel('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)', true, false, null);
@@ -542,7 +543,7 @@ suite('TextModelSearch', () => {
});
test('findPreviousMatch multiline with capturing matches', () => {
let model = TextModel.createFromString('one line line\ntwo line\nthree');
let model = createTextModel('one line line\ntwo line\nthree');
let searchParams = new SearchParams('(l(in)e)\\n', true, false, null);
@@ -553,7 +554,7 @@ suite('TextModelSearch', () => {
});
test('\\n matches \\r\\n', () => {
let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni');
let model = createTextModel('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni');
assert.equal(model.getEOL(), '\r\n');
@@ -576,7 +577,7 @@ suite('TextModelSearch', () => {
});
test('\\r can never be found', () => {
let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni');
let model = createTextModel('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni');
assert.equal(model.getEOL(), '\r\n');
@@ -763,7 +764,7 @@ suite('TextModelSearch', () => {
});
test('issue #74715. \\d* finds empty string and stops searching.', () => {
let model = TextModel.createFromString('10.243.30.10');
let model = createTextModel('10.243.30.10');
let searchParams = new SearchParams('\\d*', true, false, null);
@@ -15,6 +15,7 @@ import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { ViewLineToken } from 'vs/editor/test/common/core/viewLineToken';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('TextModelWithTokens', () => {
@@ -72,7 +73,7 @@ suite('TextModelWithTokens', () => {
brackets: brackets
});
let model = TextModel.createFromString(
let model = createTextModel(
contents.join('\n'),
TextModel.DEFAULT_CREATION_OPTIONS,
languageIdentifier
@@ -178,7 +179,7 @@ suite('TextModelWithTokens - bracket matching', () => {
let text =
')]}{[(' + '\n' +
')]}{[(';
let model = TextModel.createFromString(text, undefined, languageIdentifier);
let model = createTextModel(text, undefined, languageIdentifier);
assertIsNotBracket(model, 1, 1);
assertIsNotBracket(model, 1, 2);
@@ -206,7 +207,7 @@ suite('TextModelWithTokens - bracket matching', () => {
'}, bar: {hallo: [{' + '\n' +
'}, {' + '\n' +
'}]}}';
let model = TextModel.createFromString(text, undefined, languageIdentifier);
let model = createTextModel(text, undefined, languageIdentifier);
let brackets: [Position, Range, Range][] = [
[new Position(1, 11), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)],
@@ -284,7 +285,7 @@ suite('TextModelWithTokens', () => {
'end;',
].join('\n');
const model = TextModel.createFromString(text, undefined, languageIdentifier);
const model = createTextModel(text, undefined, languageIdentifier);
// <if> ... <end ifa> is not matched
assertIsNotBracket(model, 10, 9);
@@ -322,7 +323,7 @@ suite('TextModelWithTokens', () => {
'endrecord',
].join('\n');
const model = TextModel.createFromString(text, undefined, languageIdentifier);
const model = createTextModel(text, undefined, languageIdentifier);
// <recordbegin> ... <endrecord> is matched
assertIsBracket(model, new Position(1, 1), [new Range(1, 1, 1, 12), new Range(4, 1, 4, 10)]);
@@ -388,7 +389,7 @@ suite('TextModelWithTokens', () => {
],
});
const model = TextModel.createFromString([
const model = createTextModel([
'function hello() {',
' console.log(`${100}`);',
'}'
@@ -459,7 +460,7 @@ suite('TextModelWithTokens regression tests', () => {
let registration1 = TokenizationRegistry.register(LANG_ID1, tokenizationSupport);
let registration2 = TokenizationRegistry.register(LANG_ID2, tokenizationSupport);
let model = TextModel.createFromString('A model with\ntwo lines');
let model = createTextModel('A model with\ntwo lines');
assertViewLineTokens(model, 1, true, [createViewLineToken(12, 1)]);
assertViewLineTokens(model, 2, true, [createViewLineToken(9, 1)]);
@@ -498,7 +499,7 @@ suite('TextModelWithTokens regression tests', () => {
]
});
let model = TextModel.createFromString([
let model = createTextModel([
'Imports System',
'Imports System.Collections.Generic',
'',
@@ -528,7 +529,7 @@ suite('TextModelWithTokens regression tests', () => {
]
});
let model = TextModel.createFromString([
let model = createTextModel([
'sequence "outer"',
' sequence "inner"',
' endsequence',
@@ -561,7 +562,7 @@ suite('TextModelWithTokens regression tests', () => {
let registration = TokenizationRegistry.register(outerMode.language, tokenizationSupport);
let model = TextModel.createFromString('A model with one line', undefined, outerMode);
let model = createTextModel('A model with one line', undefined, outerMode);
model.forceTokenization(1);
assert.equal(model.getLanguageIdAtPosition(1, 1), innerMode.id);
@@ -574,7 +575,7 @@ suite('TextModelWithTokens regression tests', () => {
suite('TextModel.getLineIndentGuide', () => {
function assertIndentGuides(lines: [number, number, number, number, string][], tabSize: number): void {
let text = lines.map(l => l[4]).join('\n');
let model = TextModel.createFromString(text);
let model = createTextModel(text);
model.updateOptions({ tabSize: tabSize });
let actualIndents = model.getLinesIndentGuides(1, model.getLineCount());
@@ -747,7 +748,7 @@ suite('TextModel.getLineIndentGuide', () => {
});
test('issue #49173', () => {
let model = TextModel.createFromString([
let model = createTextModel([
'class A {',
' public m1(): void {',
' }',
@@ -9,6 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { MetadataConsts, TokenMetadata } from 'vs/editor/common/modes';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('TokensStore', () => {
@@ -96,7 +97,7 @@ suite('TokensStore', () => {
function testTokensAdjustment(rawInitialState: string[], edits: IIdentifiedSingleEditOperation[], rawFinalState: string[]) {
const initialState = parseTokensState(rawInitialState);
const model = TextModel.createFromString(initialState.text);
const model = createTextModel(initialState.text);
model.setSemanticTokens([initialState.tokens]);
model.applyEdits(edits);
@@ -11,7 +11,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { createStringBuilder } from 'vs/editor/common/core/stringBuilder';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { createTextBuffer } from 'vs/editor/common/model/textModel';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -19,6 +19,9 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
const GENERATE_TESTS = false;
@@ -30,7 +33,7 @@ suite('ModelService', () => {
configService.setUserConfiguration('files', { 'eol': '\n' });
configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot'));
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService());
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService(new TestDialogService(), new TestNotificationService()));
});
teardown(() => {
@@ -49,7 +52,7 @@ suite('ModelService', () => {
test('_computeEdits no change', function () {
const model = TextModel.createFromString(
const model = createTextModel(
[
'This is line one', //16
'and this is line number two', //27
@@ -75,7 +78,7 @@ suite('ModelService', () => {
test('_computeEdits first line changed', function () {
const model = TextModel.createFromString(
const model = createTextModel(
[
'This is line one', //16
'and this is line number two', //27
@@ -103,7 +106,7 @@ suite('ModelService', () => {
test('_computeEdits EOL changed', function () {
const model = TextModel.createFromString(
const model = createTextModel(
[
'This is line one', //16
'and this is line number two', //27
@@ -129,7 +132,7 @@ suite('ModelService', () => {
test('_computeEdits EOL and other change 1', function () {
const model = TextModel.createFromString(
const model = createTextModel(
[
'This is line one', //16
'and this is line number two', //27
@@ -165,7 +168,7 @@ suite('ModelService', () => {
test('_computeEdits EOL and other change 2', function () {
const model = TextModel.createFromString(
const model = createTextModel(
[
'package main', // 1
'func foo() {', // 2
@@ -307,7 +310,7 @@ suite('ModelService', () => {
});
function assertComputeEdits(lines1: string[], lines2: string[]): void {
const model = TextModel.createFromString(lines1.join('\n'));
const model = createTextModel(lines1.join('\n'));
const textBuffer = createTextBuffer(lines2.join('\n'), DefaultEndOfLine.LF);
// compute required edits
@@ -18,6 +18,7 @@ import { LineBreakData, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs
import { ViewLineData } from 'vs/editor/common/viewModel/viewModel';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('Editor ViewModel - SplitLinesCollection', () => {
test('SplitLine', () => {
@@ -96,7 +97,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => {
const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters);
const model = TextModel.createFromString([
const model = createTextModel([
'int main() {',
'\tprintf("Hello world!");',
'}',
@@ -347,7 +348,7 @@ suite('SplitLinesCollection', () => {
};
const LANGUAGE_ID = 'modelModeTest1';
languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
model = TextModel.createFromString(_text.join('\n'), undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
model = createTextModel(_text.join('\n'), undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0));
// force tokenization
model.forceTokenization(model.getLineCount());
});
@@ -8,12 +8,13 @@ import { TextModel } from 'vs/editor/common/model/textModel';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
export function testViewModel(text: string[], options: IEditorOptions, callback: (viewModel: ViewModel, model: TextModel) => void): void {
const EDITOR_ID = 1;
const configuration = new TestConfiguration(options);
const model = TextModel.createFromString(text.join('\n'));
const model = createTextModel(text.join('\n'));
const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(configuration.options);
const viewModel = new ViewModel(EDITOR_ID, configuration, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!);
+107 -100
View File
@@ -1884,7 +1884,7 @@ declare namespace monaco.editor {
* @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed.
* @return The cursor state returned by the `cursorStateComputer`.
*/
pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null;
pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null;
/**
* Change the end of line sequence. This is the preferred way of
* changing the eol sequence. This will land on the undo stack.
@@ -2849,6 +2849,11 @@ declare namespace monaco.editor {
* Defaults to true.
*/
scrollPredominantAxis?: boolean;
/**
* Enable that the selection with the mouse and keys is doing column selection.
* Defaults to false.
*/
columnSelection?: boolean;
/**
* The modifier to be used to add multiple cursors with the mouse.
* Defaults to 'alt'
@@ -3798,105 +3803,106 @@ declare namespace monaco.editor {
autoSurround = 10,
codeLens = 11,
colorDecorators = 12,
comments = 13,
contextmenu = 14,
copyWithSyntaxHighlighting = 15,
cursorBlinking = 16,
cursorSmoothCaretAnimation = 17,
cursorStyle = 18,
cursorSurroundingLines = 19,
cursorSurroundingLinesStyle = 20,
cursorWidth = 21,
disableLayerHinting = 22,
disableMonospaceOptimizations = 23,
dragAndDrop = 24,
emptySelectionClipboard = 25,
extraEditorClassName = 26,
fastScrollSensitivity = 27,
find = 28,
fixedOverflowWidgets = 29,
folding = 30,
foldingStrategy = 31,
foldingHighlight = 32,
fontFamily = 33,
fontInfo = 34,
fontLigatures = 35,
fontSize = 36,
fontWeight = 37,
formatOnPaste = 38,
formatOnType = 39,
glyphMargin = 40,
gotoLocation = 41,
hideCursorInOverviewRuler = 42,
highlightActiveIndentGuide = 43,
hover = 44,
inDiffEditor = 45,
letterSpacing = 46,
lightbulb = 47,
lineDecorationsWidth = 48,
lineHeight = 49,
lineNumbers = 50,
lineNumbersMinChars = 51,
links = 52,
matchBrackets = 53,
minimap = 54,
mouseStyle = 55,
mouseWheelScrollSensitivity = 56,
mouseWheelZoom = 57,
multiCursorMergeOverlapping = 58,
multiCursorModifier = 59,
multiCursorPaste = 60,
occurrencesHighlight = 61,
overviewRulerBorder = 62,
overviewRulerLanes = 63,
padding = 64,
parameterHints = 65,
peekWidgetDefaultFocus = 66,
definitionLinkOpensInPeek = 67,
quickSuggestions = 68,
quickSuggestionsDelay = 69,
readOnly = 70,
renderControlCharacters = 71,
renderIndentGuides = 72,
renderFinalNewline = 73,
renderLineHighlight = 74,
renderValidationDecorations = 75,
renderWhitespace = 76,
revealHorizontalRightPadding = 77,
roundedSelection = 78,
rulers = 79,
scrollbar = 80,
scrollBeyondLastColumn = 81,
scrollBeyondLastLine = 82,
scrollPredominantAxis = 83,
selectionClipboard = 84,
selectionHighlight = 85,
selectOnLineNumbers = 86,
showFoldingControls = 87,
showUnused = 88,
snippetSuggestions = 89,
smoothScrolling = 90,
stopRenderingLineAfter = 91,
suggest = 92,
suggestFontSize = 93,
suggestLineHeight = 94,
suggestOnTriggerCharacters = 95,
suggestSelection = 96,
tabCompletion = 97,
useTabStops = 98,
wordSeparators = 99,
wordWrap = 100,
wordWrapBreakAfterCharacters = 101,
wordWrapBreakBeforeCharacters = 102,
wordWrapColumn = 103,
wordWrapMinified = 104,
wrappingIndent = 105,
wrappingStrategy = 106,
editorClassName = 107,
pixelRatio = 108,
tabFocusMode = 109,
layoutInfo = 110,
wrappingInfo = 111
columnSelection = 13,
comments = 14,
contextmenu = 15,
copyWithSyntaxHighlighting = 16,
cursorBlinking = 17,
cursorSmoothCaretAnimation = 18,
cursorStyle = 19,
cursorSurroundingLines = 20,
cursorSurroundingLinesStyle = 21,
cursorWidth = 22,
disableLayerHinting = 23,
disableMonospaceOptimizations = 24,
dragAndDrop = 25,
emptySelectionClipboard = 26,
extraEditorClassName = 27,
fastScrollSensitivity = 28,
find = 29,
fixedOverflowWidgets = 30,
folding = 31,
foldingStrategy = 32,
foldingHighlight = 33,
fontFamily = 34,
fontInfo = 35,
fontLigatures = 36,
fontSize = 37,
fontWeight = 38,
formatOnPaste = 39,
formatOnType = 40,
glyphMargin = 41,
gotoLocation = 42,
hideCursorInOverviewRuler = 43,
highlightActiveIndentGuide = 44,
hover = 45,
inDiffEditor = 46,
letterSpacing = 47,
lightbulb = 48,
lineDecorationsWidth = 49,
lineHeight = 50,
lineNumbers = 51,
lineNumbersMinChars = 52,
links = 53,
matchBrackets = 54,
minimap = 55,
mouseStyle = 56,
mouseWheelScrollSensitivity = 57,
mouseWheelZoom = 58,
multiCursorMergeOverlapping = 59,
multiCursorModifier = 60,
multiCursorPaste = 61,
occurrencesHighlight = 62,
overviewRulerBorder = 63,
overviewRulerLanes = 64,
padding = 65,
parameterHints = 66,
peekWidgetDefaultFocus = 67,
definitionLinkOpensInPeek = 68,
quickSuggestions = 69,
quickSuggestionsDelay = 70,
readOnly = 71,
renderControlCharacters = 72,
renderIndentGuides = 73,
renderFinalNewline = 74,
renderLineHighlight = 75,
renderValidationDecorations = 76,
renderWhitespace = 77,
revealHorizontalRightPadding = 78,
roundedSelection = 79,
rulers = 80,
scrollbar = 81,
scrollBeyondLastColumn = 82,
scrollBeyondLastLine = 83,
scrollPredominantAxis = 84,
selectionClipboard = 85,
selectionHighlight = 86,
selectOnLineNumbers = 87,
showFoldingControls = 88,
showUnused = 89,
snippetSuggestions = 90,
smoothScrolling = 91,
stopRenderingLineAfter = 92,
suggest = 93,
suggestFontSize = 94,
suggestLineHeight = 95,
suggestOnTriggerCharacters = 96,
suggestSelection = 97,
tabCompletion = 98,
useTabStops = 99,
wordSeparators = 100,
wordWrap = 101,
wordWrapBreakAfterCharacters = 102,
wordWrapBreakBeforeCharacters = 103,
wordWrapColumn = 104,
wordWrapMinified = 105,
wrappingIndent = 106,
wrappingStrategy = 107,
editorClassName = 108,
pixelRatio = 109,
tabFocusMode = 110,
layoutInfo = 111,
wrappingInfo = 112
}
export const EditorOptions: {
acceptSuggestionOnCommitCharacter: IEditorOption<EditorOption.acceptSuggestionOnCommitCharacter, boolean>;
@@ -3912,6 +3918,7 @@ declare namespace monaco.editor {
autoSurround: IEditorOption<EditorOption.autoSurround, EditorAutoSurroundStrategy>;
codeLens: IEditorOption<EditorOption.codeLens, boolean>;
colorDecorators: IEditorOption<EditorOption.colorDecorators, boolean>;
columnSelection: IEditorOption<EditorOption.columnSelection, boolean>;
comments: IEditorOption<EditorOption.comments, EditorCommentsOptions>;
contextmenu: IEditorOption<EditorOption.contextmenu, boolean>;
copyWithSyntaxHighlighting: IEditorOption<EditorOption.copyWithSyntaxHighlighting, boolean>;
+3 -4
View File
@@ -11,8 +11,9 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
import { ICommandService, CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { UriDto } from 'vs/base/common/types';
export interface ILocalizedString {
value: string;
@@ -28,9 +29,7 @@ export interface ICommandAction {
toggled?: ContextKeyExpr;
}
type Serialized<T> = { [K in keyof T]: T[K] extends URI ? UriComponents : Serialized<T[K]> };
export type ISerializableCommandAction = Serialized<ICommandAction>;
export type ISerializableCommandAction = UriDto<ICommandAction>;
export interface IMenuItem {
command: ICommandAction;
@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Severity from 'vs/base/common/severity';
import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs';
export class TestDialogService implements IDialogService {
_serviceBrand: undefined;
confirm(_confirmation: IConfirmation): Promise<IConfirmationResult> { return Promise.resolve({ confirmed: false }); }
show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise<IShowResult> { return Promise.resolve({ choice: 0 }); }
about(): Promise<void> { return Promise.resolve(); }
}
@@ -157,7 +157,7 @@ export class InstantiationService implements IInstantiationService {
graph.lookupOrInsertNode(item);
// a weak but working heuristic for cycle checks
if (cycleCount++ > 150) {
if (cycleCount++ > 200) {
throw new CyclicDependencyError(graph);
}
+2 -2
View File
@@ -86,8 +86,6 @@ export interface IProgressRunner {
done(): void;
}
export const emptyProgress: IProgress<IProgressStep> = { report: () => { } };
export const emptyProgressRunner: IProgressRunner = Object.freeze({
total() { },
worked() { },
@@ -100,6 +98,8 @@ export interface IProgress<T> {
export class Progress<T> implements IProgress<T> {
static readonly None: IProgress<any> = Object.freeze({ report() { } });
private _value?: T;
get value(): T | undefined { return this._value; }
+20 -36
View File
@@ -8,42 +8,26 @@ import { URI } from 'vs/base/common/uri';
export const IUndoRedoService = createDecorator<IUndoRedoService>('undoRedoService');
export interface IUndoRedoContext {
replaceCurrentElement(others: IUndoRedoElement[]): void;
export const enum UndoRedoElementType {
Resource,
Workspace
}
export interface IUndoRedoElement {
/**
* None, one or multiple resources that this undo/redo element impacts.
*/
readonly resources: readonly URI[];
/**
* The label of the undo/redo element.
*/
export interface IResourceUndoRedoElement {
readonly type: UndoRedoElementType.Resource;
readonly resource: URI;
readonly label: string;
undo(): Promise<void> | void;
redo(): Promise<void> | void;
}
/**
* Undo.
* Will always be called before `redo`.
* Can be called multiple times.
* e.g. `undo` -> `redo` -> `undo` -> `redo`
*/
undo(ctx: IUndoRedoContext): void;
/**
* Redo.
* Will always be called after `undo`.
* Can be called multiple times.
* e.g. `undo` -> `redo` -> `undo` -> `redo`
*/
redo(ctx: IUndoRedoContext): void;
/**
* Invalidate the edits concerning `resource`.
* i.e. the undo/redo stack for that particular resource has been destroyed.
*/
invalidate(resource: URI): void;
export interface IWorkspaceUndoRedoElement {
readonly type: UndoRedoElementType.Workspace;
readonly resources: readonly URI[];
readonly label: string;
undo(): Promise<void> | void;
redo(): Promise<void> | void;
split(): IResourceUndoRedoElement[];
}
export interface IUndoRedoService {
@@ -53,12 +37,12 @@ export interface IUndoRedoService {
* Add a new element to the `undo` stack.
* This will destroy the `redo` stack.
*/
pushElement(element: IUndoRedoElement): void;
pushElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement): void;
/**
* Get the last pushed element. If the last pushed element has been undone, returns null.
*/
getLastElement(resource: URI): IUndoRedoElement | null;
getLastElement(resource: URI): IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null;
/**
* Remove elements that target `resource`.
@@ -66,8 +50,8 @@ export interface IUndoRedoService {
removeElements(resource: URI): void;
canUndo(resource: URI): boolean;
undo(resource: URI): void;
undo(resource: URI): Promise<void> | void;
redo(resource: URI): void;
canRedo(resource: URI): boolean;
redo(resource: URI): Promise<void> | void;
}
+270 -110
View File
@@ -3,31 +3,88 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUndoRedoService, IUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import * as nls from 'vs/nls';
import { IUndoRedoService, IResourceUndoRedoElement, IWorkspaceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources';
import { onUnexpectedError } from 'vs/base/common/errors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { Schemas } from 'vs/base/common/network';
import { INotificationService } from 'vs/platform/notification/common/notification';
class StackElement {
public readonly actual: IUndoRedoElement;
class ResourceStackElement {
public readonly type = UndoRedoElementType.Resource;
public readonly actual: IResourceUndoRedoElement;
public readonly label: string;
public readonly resources: readonly URI[];
public readonly resource: URI;
public readonly strResource: string;
public readonly resources: URI[];
public readonly strResources: string[];
constructor(actual: IUndoRedoElement) {
constructor(actual: IResourceUndoRedoElement) {
this.actual = actual;
this.label = actual.label;
this.resources = actual.resources;
this.resource = actual.resource;
this.strResource = uriGetComparisonKey(this.resource);
this.resources = [this.resource];
this.strResources = [this.strResource];
}
}
const enum RemovedResourceReason {
ExternalRemoval = 0,
NoParallelUniverses = 1
}
class RemovedResources {
public readonly set: Set<string> = new Set<string>();
public readonly reason: [URI[], URI[]] = [[], []];
public createMessage(): string {
let messages: string[] = [];
if (this.reason[RemovedResourceReason.ExternalRemoval].length > 0) {
const paths = this.reason[RemovedResourceReason.ExternalRemoval].map(uri => uri.scheme === Schemas.file ? uri.fsPath : uri.path);
messages.push(nls.localize('externalRemoval', "The following files have been closed: {0}.", paths.join(', ')));
}
if (this.reason[RemovedResourceReason.NoParallelUniverses].length > 0) {
const paths = this.reason[RemovedResourceReason.NoParallelUniverses].map(uri => uri.scheme === Schemas.file ? uri.fsPath : uri.path);
messages.push(nls.localize('noParallelUniverses', "The following files have been modified in an incompatible way: {0}.", paths.join(', ')));
}
return messages.join('\n');
}
}
class WorkspaceStackElement {
public readonly type = UndoRedoElementType.Workspace;
public readonly actual: IWorkspaceUndoRedoElement;
public readonly label: string;
public readonly resources: URI[];
public readonly strResources: string[];
public removedResources: RemovedResources | null;
constructor(actual: IWorkspaceUndoRedoElement) {
this.actual = actual;
this.label = actual.label;
this.resources = actual.resources.slice(0);
this.strResources = this.resources.map(resource => uriGetComparisonKey(resource));
this.removedResources = null;
}
public invalidate(resource: URI): void {
if (this.resources.length > 1) {
this.actual.invalidate(resource);
public removeResource(resource: URI, strResource: string, reason: RemovedResourceReason): void {
if (!this.removedResources) {
this.removedResources = new RemovedResources();
}
if (!this.removedResources.set.has(strResource)) {
this.removedResources.set.add(strResource);
this.removedResources.reason[reason].push(resource);
}
}
}
type StackElement = ResourceStackElement | WorkspaceStackElement;
class ResourceEditStack {
public resource: URI;
@@ -46,12 +103,15 @@ export class UndoRedoService implements IUndoRedoService {
private readonly _editStacks: Map<string, ResourceEditStack>;
constructor() {
constructor(
@IDialogService private readonly _dialogService: IDialogService,
@INotificationService private readonly _notificationService: INotificationService,
) {
this._editStacks = new Map<string, ResourceEditStack>();
}
public pushElement(_element: IUndoRedoElement): void {
const element = new StackElement(_element);
public pushElement(_element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement): void {
const element: StackElement = (_element.type === UndoRedoElementType.Resource ? new ResourceStackElement(_element) : new WorkspaceStackElement(_element));
for (let i = 0, len = element.resources.length; i < len; i++) {
const resource = element.resources[i];
const strResource = element.strResources[i];
@@ -66,14 +126,16 @@ export class UndoRedoService implements IUndoRedoService {
// remove the future
for (const futureElement of editStack.future) {
futureElement.invalidate(resource);
if (futureElement.type === UndoRedoElementType.Workspace) {
futureElement.removeResource(resource, strResource, RemovedResourceReason.NoParallelUniverses);
}
}
editStack.future = [];
editStack.past.push(element);
}
}
public getLastElement(resource: URI): IUndoRedoElement | null {
public getLastElement(resource: URI): IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null {
const strResource = uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
@@ -88,15 +150,75 @@ export class UndoRedoService implements IUndoRedoService {
return null;
}
private _splitPastWorkspaceElement(toRemove: WorkspaceStackElement, ignoreResources: Set<string> | null): void {
const individualArr = toRemove.actual.split();
const individualMap = new Map<string, ResourceStackElement>();
for (const _element of individualArr) {
const element = new ResourceStackElement(_element);
individualMap.set(element.strResource, element);
}
for (const strResource of toRemove.strResources) {
if (ignoreResources && ignoreResources.has(strResource)) {
continue;
}
const editStack = this._editStacks.get(strResource)!;
for (let j = editStack.past.length - 1; j >= 0; j--) {
if (editStack.past[j] === toRemove) {
if (individualMap.has(strResource)) {
// gets replaced
editStack.past[j] = individualMap.get(strResource)!;
} else {
// gets deleted
editStack.past.splice(j, 1);
}
break;
}
}
}
}
private _splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, ignoreResources: Set<string> | null): void {
const individualArr = toRemove.actual.split();
const individualMap = new Map<string, ResourceStackElement>();
for (const _element of individualArr) {
const element = new ResourceStackElement(_element);
individualMap.set(element.strResource, element);
}
for (const strResource of toRemove.strResources) {
if (ignoreResources && ignoreResources.has(strResource)) {
continue;
}
const editStack = this._editStacks.get(strResource)!;
for (let j = editStack.future.length - 1; j >= 0; j--) {
if (editStack.future[j] === toRemove) {
if (individualMap.has(strResource)) {
// gets replaced
editStack.future[j] = individualMap.get(strResource)!;
} else {
// gets deleted
editStack.future.splice(j, 1);
}
break;
}
}
}
}
public removeElements(resource: URI): void {
const strResource = uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
for (const pastElement of editStack.past) {
pastElement.invalidate(resource);
for (const element of editStack.past) {
if (element.type === UndoRedoElementType.Workspace) {
element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval);
}
}
for (const futureElement of editStack.future) {
futureElement.invalidate(resource);
for (const element of editStack.future) {
if (element.type === UndoRedoElementType.Workspace) {
element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval);
}
}
this._editStacks.delete(strResource);
}
@@ -111,7 +233,85 @@ export class UndoRedoService implements IUndoRedoService {
return false;
}
public undo(resource: URI): void {
private _onError(err: Error, element: StackElement): void {
onUnexpectedError(err);
// An error occured while undoing or redoing => drop the undo/redo stack for all affected resources
for (const resource of element.resources) {
this.removeElements(resource);
}
this._notificationService.error(err);
}
private _safeInvoke(element: StackElement, invoke: () => Promise<void> | void): Promise<void> | void {
let result: Promise<void> | void;
try {
result = invoke();
} catch (err) {
return this._onError(err, element);
}
if (result) {
return result.then(undefined, (err) => this._onError(err, element));
}
}
private _workspaceUndo(resource: URI, element: WorkspaceStackElement): Promise<void> | void {
if (element.removedResources) {
this._splitPastWorkspaceElement(element, element.removedResources.set);
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message);
return;
}
// this must be the last past element in all the impacted resources!
let affectedEditStacks: ResourceEditStack[] = [];
for (const strResource of element.strResources) {
affectedEditStacks.push(this._editStacks.get(strResource)!);
}
let cannotUndoDueToResources: URI[] = [];
for (const editStack of affectedEditStacks) {
if (editStack.past.length === 0 || editStack.past[editStack.past.length - 1] !== element) {
cannotUndoDueToResources.push(editStack.resource);
}
}
if (cannotUndoDueToResources.length > 0) {
this._splitPastWorkspaceElement(element, null);
const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
this._notificationService.info(message);
return;
}
return this._dialogService.show(
Severity.Info,
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label),
[
nls.localize('ok', "Yes, change {0} files.", affectedEditStacks.length),
nls.localize('nok', "No, change only this file.")
]
).then((result) => {
if (result.choice === 0) {
for (const editStack of affectedEditStacks) {
editStack.past.pop();
editStack.future.push(element);
}
return this._safeInvoke(element, () => element.actual.undo());
} else {
this._splitPastWorkspaceElement(element, null);
return this.undo(resource);
}
});
}
private _resourceUndo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
editStack.past.pop();
editStack.future.push(element);
return this._safeInvoke(element, () => element.actual.undo());
}
public undo(resource: URI): Promise<void> | void {
const strResource = uriGetComparisonKey(resource);
if (!this._editStacks.has(strResource)) {
return;
@@ -123,51 +323,10 @@ export class UndoRedoService implements IUndoRedoService {
}
const element = editStack.past[editStack.past.length - 1];
let replaceCurrentElement: IUndoRedoElement[] | null = null as IUndoRedoElement[] | null;
try {
element.actual.undo({
replaceCurrentElement: (others: IUndoRedoElement[]): void => {
replaceCurrentElement = others;
}
});
} catch (e) {
onUnexpectedError(e);
editStack.past.pop();
editStack.future.push(element);
return;
}
if (replaceCurrentElement === null) {
// regular case
editStack.past.pop();
editStack.future.push(element);
return;
}
const replaceCurrentElementMap = new Map<string, StackElement>();
for (const _replace of replaceCurrentElement) {
const replace = new StackElement(_replace);
for (const strResource of replace.strResources) {
replaceCurrentElementMap.set(strResource, replace);
}
}
for (let i = 0, len = element.strResources.length; i < len; i++) {
const strResource = element.strResources[i];
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
for (let j = editStack.past.length - 1; j >= 0; j--) {
if (editStack.past[j] === element) {
if (replaceCurrentElementMap.has(strResource)) {
editStack.past[j] = replaceCurrentElementMap.get(strResource)!;
} else {
editStack.past.splice(j, 1);
}
break;
}
}
}
if (element.type === UndoRedoElementType.Workspace) {
return this._workspaceUndo(resource, element);
} else {
return this._resourceUndo(editStack, element);
}
}
@@ -180,7 +339,49 @@ export class UndoRedoService implements IUndoRedoService {
return false;
}
public redo(resource: URI): void {
private _workspaceRedo(resource: URI, element: WorkspaceStackElement): Promise<void> | void {
if (element.removedResources) {
this._splitFutureWorkspaceElement(element, element.removedResources.set);
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message);
return;
}
// this must be the last future element in all the impacted resources!
let affectedEditStacks: ResourceEditStack[] = [];
for (const strResource of element.strResources) {
affectedEditStacks.push(this._editStacks.get(strResource)!);
}
let cannotRedoDueToResources: URI[] = [];
for (const editStack of affectedEditStacks) {
if (editStack.future.length === 0 || editStack.future[editStack.future.length - 1] !== element) {
cannotRedoDueToResources.push(editStack.resource);
}
}
if (cannotRedoDueToResources.length > 0) {
this._splitFutureWorkspaceElement(element, null);
const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
this._notificationService.info(message);
return;
}
for (const editStack of affectedEditStacks) {
editStack.future.pop();
editStack.past.push(element);
}
return this._safeInvoke(element, () => element.actual.redo());
}
private _resourceRedo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
editStack.future.pop();
editStack.past.push(element);
return this._safeInvoke(element, () => element.actual.redo());
}
public redo(resource: URI): Promise<void> | void {
const strResource = uriGetComparisonKey(resource);
if (!this._editStacks.has(strResource)) {
return;
@@ -192,51 +393,10 @@ export class UndoRedoService implements IUndoRedoService {
}
const element = editStack.future[editStack.future.length - 1];
let replaceCurrentElement: IUndoRedoElement[] | null = null as IUndoRedoElement[] | null;
try {
element.actual.redo({
replaceCurrentElement: (others: IUndoRedoElement[]): void => {
replaceCurrentElement = others;
}
});
} catch (e) {
onUnexpectedError(e);
editStack.future.pop();
editStack.past.push(element);
return;
}
if (replaceCurrentElement === null) {
// regular case
editStack.future.pop();
editStack.past.push(element);
return;
}
const replaceCurrentElementMap = new Map<string, StackElement>();
for (const _replace of replaceCurrentElement) {
const replace = new StackElement(_replace);
for (const strResource of replace.strResources) {
replaceCurrentElementMap.set(strResource, replace);
}
}
for (let i = 0, len = element.strResources.length; i < len; i++) {
const strResource = element.strResources[i];
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
for (let j = editStack.future.length - 1; j >= 0; j--) {
if (editStack.future[j] === element) {
if (replaceCurrentElementMap.has(strResource)) {
editStack.future[j] = replaceCurrentElementMap.get(strResource)!;
} else {
editStack.future.splice(j, 1);
}
break;
}
}
}
if (element.type === UndoRedoElementType.Workspace) {
return this._workspaceRedo(resource, element);
} else {
return this._resourceRedo(editStack, element);
}
}
}
@@ -18,8 +18,7 @@ import { ParseError, parse } from 'vs/base/common/json';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { IStringDictionary } from 'vs/base/common/collections';
import { localize } from 'vs/nls';
const BACK_UP_MAX_AGE = 1000 * 60 * 60 * 24 * 30; /* 30 days */
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
type SyncSourceClassification = {
source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -65,6 +64,7 @@ export abstract class AbstractSynchroniser extends Disposable {
@IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
) {
super();
this.syncFolder = joinPath(environmentService.userDataSyncHome, source);
@@ -91,7 +91,7 @@ export abstract class AbstractSynchroniser extends Disposable {
protected get enabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resourceKey); }
async sync(ref?: string): Promise<void> {
async sync(ref?: string, donotUseLastSyncUserData?: boolean): Promise<void> {
if (!this.enabled) {
this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as it is disabled.`);
return;
@@ -108,7 +108,7 @@ export abstract class AbstractSynchroniser extends Disposable {
this.logService.trace(`${this.source}: Started synchronizing ${this.source.toLowerCase()}...`);
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
const lastSyncUserData = donotUseLastSyncUserData ? null : await this.getLastSyncUserData();
const remoteUserData = ref && lastSyncUserData && lastSyncUserData.ref === ref ? lastSyncUserData : await this.getRemoteUserData(lastSyncUserData);
if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) {
@@ -117,7 +117,19 @@ export abstract class AbstractSynchroniser extends Disposable {
throw new UserDataSyncError(localize('incompatible', "Cannot sync {0} as its version {1} is not compatible with cloud {2}", this.source, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.source);
}
return this.doSync(remoteUserData, lastSyncUserData);
try {
await this.doSync(remoteUserData, lastSyncUserData);
} catch (e) {
if (e instanceof UserDataSyncError) {
switch (e.code) {
case UserDataSyncErrorCode.RemotePreconditionFailed:
// Rejected as there is a new remote version. Syncing again,
this.logService.info(`${this.source}: Failed to synchronize as there is a new remote version available. Synchronizing again...`);
return this.sync(undefined, true);
}
}
throw e;
}
}
async hasPreviouslySynced(): Promise<boolean> {
@@ -191,15 +203,21 @@ export abstract class AbstractSynchroniser extends Disposable {
protected async backupLocal(content: VSBuffer): Promise<void> {
const resource = joinPath(this.syncFolder, toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''));
await this.fileService.writeFile(resource, content);
try {
await this.fileService.writeFile(resource, content);
} catch (e) {
this.logService.error(e);
}
this.cleanUpDelayer.trigger(() => this.cleanUpBackup());
}
private async cleanUpBackup(): Promise<void> {
const stat = await this.fileService.resolve(this.syncFolder);
if (stat.children) {
const toDelete = stat.children.filter(stat => {
if (stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)) {
try {
const stat = await this.fileService.resolve(this.syncFolder);
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort();
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
let toDelete = all.filter(stat => {
const ctime = stat.ctime || new Date(
parseInt(stat.name.substring(0, 4)),
parseInt(stat.name.substring(4, 6)) - 1,
@@ -208,14 +226,19 @@ export abstract class AbstractSynchroniser extends Disposable {
parseInt(stat.name.substring(11, 13)),
parseInt(stat.name.substring(13, 15))
).getTime();
return Date.now() - ctime > BACK_UP_MAX_AGE;
return Date.now() - ctime > backUpMaxAge;
});
const remaining = all.length - toDelete.length;
if (remaining < 10) {
toDelete = toDelete.slice(10 - remaining);
}
return false;
});
await Promise.all(toDelete.map(stat => {
this.logService.info('Deleting from backup', stat.resource.path);
this.fileService.del(stat.resource);
}));
await Promise.all(toDelete.map(stat => {
this.logService.info('Deleting from backup', stat.resource.path);
this.fileService.del(stat.resource);
}));
}
} catch (e) {
this.logService.error(e);
}
}
@@ -247,8 +270,9 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(dirname(file)));
this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e)));
}
@@ -346,8 +370,9 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni
@ITelemetryService telemetryService: ITelemetryService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IUserDataSyncUtilService protected readonly userDataSyncUtilService: IUserDataSyncUtilService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(file, source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
super(file, source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
}
protected hasErrors(content: string): boolean {
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
@@ -46,11 +46,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
@IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IConfigurationService configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(
Event.debounce(
Event.any<any>(
@@ -154,11 +154,6 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
await this.apply(previewResult);
} catch (e) {
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.RemotePreconditionFailed) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Extensions: Failed to synchronize extensions as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
}
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -15,6 +15,7 @@ import { merge } from 'vs/platform/userDataSync/common/globalStateMerge';
import { parse } from 'vs/base/common/json';
import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const argvProperties: string[] = ['locale'];
@@ -38,8 +39,9 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
}
@@ -129,11 +131,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
this.logService.trace('UI State: Finished synchronizing ui state.');
} catch (e) {
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.RemotePreconditionFailed) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('UI State: Failed to synchronize ui state as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
} finally {
this.setStatus(SyncStatus.Idle);
@@ -36,14 +36,14 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
constructor(
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IConfigurationService configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@IFileService fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService);
super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
}
async pull(): Promise<void> {
@@ -180,10 +180,6 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError) {
switch (e.code) {
case UserDataSyncErrorCode.RemotePreconditionFailed:
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new remote version available. Synchronizing again...');
return this.sync();
case UserDataSyncErrorCode.LocalPreconditionFailed:
// Rejected as there is a new local version. Syncing again.
this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...');
@@ -10,8 +10,8 @@ import { values } from 'vs/base/common/map';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions, Edit, getEOL } from 'vs/base/common/jsonFormatter';
import * as contentUtil from 'vs/platform/userDataSync/common/content';
import { IConflictSetting, CONFIGURATION_SYNC_STORE_KEY } from 'vs/platform/userDataSync/common/userDataSync';
import { firstIndex } from 'vs/base/common/arrays';
import { IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync';
import { firstIndex, distinct } from 'vs/base/common/arrays';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { startsWith } from 'vs/base/common/strings';
@@ -22,7 +22,7 @@ export interface IMergeResult {
conflictsSettings: IConflictSetting[];
}
export function getIgnoredSettings(configurationService: IConfigurationService, settingsContent?: string): string[] {
export function getIgnoredSettings(defaultIgnoredSettings: string[], configurationService: IConfigurationService, settingsContent?: string): string[] {
let value: string[] = [];
if (settingsContent) {
const setting = parse(settingsContent);
@@ -42,7 +42,7 @@ export function getIgnoredSettings(configurationService: IConfigurationService,
}
}
}
return [CONFIGURATION_SYNC_STORE_KEY, ...added].filter(setting => removed.indexOf(setting) === -1);
return distinct([...defaultIgnoredSettings, ...added,].filter(setting => removed.indexOf(setting) === -1));
}
@@ -21,6 +21,7 @@ import { edit } from 'vs/platform/userDataSync/common/content';
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { URI } from 'vs/base/common/uri';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
interface ISettingsSyncContent {
settings: string;
@@ -51,11 +52,12 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IConfigurationService configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
) {
super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService);
super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
}
protected setStatus(status: SyncStatus): void {
@@ -94,7 +96,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
const fileContent = await this.getLocalFileContent();
const formatUtils = await this.getFormattingOptions();
// Update ignored settings from local file content
const content = updateIgnoredSettings(remoteSettingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils);
const ignoredSettings = await this.getIgnoredSettings();
const content = updateIgnoredSettings(remoteSettingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', ignoredSettings, formatUtils);
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<IFileSyncPreviewResult>({
fileContent,
remoteUserData,
@@ -136,7 +139,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
if (fileContent !== null) {
const formatUtils = await this.getFormattingOptions();
// Remove ignored settings
const content = updateIgnoredSettings(fileContent.value.toString(), '{}', getIgnoredSettings(this.configurationService), formatUtils);
const ignoredSettings = await this.getIgnoredSettings();
const content = updateIgnoredSettings(fileContent.value.toString(), '{}', ignoredSettings, formatUtils);
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
@@ -192,7 +196,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
if (preview && content !== null) {
const formatUtils = await this.getFormattingOptions();
// remove ignored settings from the remote content for preview
content = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formatUtils);
const ignoredSettings = await this.getIgnoredSettings();
content = updateIgnoredSettings(content, '{}', ignoredSettings, formatUtils);
}
return content;
}
@@ -203,7 +208,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
this.cancel();
const formatUtils = await this.getFormattingOptions();
// Add ignored settings from local file content
content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils);
const ignoredSettings = await this.getIgnoredSettings();
content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', ignoredSettings, formatUtils);
this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content }));
await this.apply(true);
this.setStatus(SyncStatus.Idle);
@@ -237,10 +243,6 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError) {
switch (e.code) {
case UserDataSyncErrorCode.RemotePreconditionFailed:
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Settings: Failed to synchronize settings as there is a new remote version available. Synchronizing again...');
return this.sync();
case UserDataSyncErrorCode.LocalPreconditionFailed:
// Rejected as there is a new local version. Syncing again.
this.logService.info('Settings: Failed to synchronize settings as there is a new local version available. Synchronizing again...');
@@ -271,7 +273,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
const formatUtils = await this.getFormattingOptions();
// Update ignored settings from remote
const remoteSettingsSyncContent = this.getSettingsSyncContent(remoteUserData);
content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', getIgnoredSettings(this.configurationService, content), formatUtils);
const ignoredSettings = await this.getIgnoredSettings(content);
content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', ignoredSettings, formatUtils);
this.logService.trace('Settings: Updating remote settings...');
remoteUserData = await this.updateRemoteUserData(JSON.stringify(<ISettingsSyncContent>{ settings: content }), forcePush ? null : remoteUserData.ref);
this.logService.info('Settings: Updated remote settings');
@@ -317,7 +320,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
const localContent: string = fileContent ? fileContent.value.toString() : '{}';
this.validateContent(localContent);
this.logService.trace('Settings: Merging remote settings with local settings...');
const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, getIgnoredSettings(this.configurationService), resolvedConflicts, formattingOptions);
const ignoredSettings = await this.getIgnoredSettings();
const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, ignoredSettings, resolvedConflicts, formattingOptions);
content = result.localContent || result.remoteContent;
hasLocalChanged = result.localContent !== null;
hasRemoteChanged = result.remoteContent !== null;
@@ -334,7 +338,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
if (content && !token.isCancellationRequested) {
// Remove the ignored settings from the preview.
const previewContent = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formattingOptions);
const ignoredSettings = await this.getIgnoredSettings();
const previewContent = updateIgnoredSettings(content, '{}', ignoredSettings, formattingOptions);
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(previewContent));
}
@@ -356,6 +361,21 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
return null;
}
private _defaultIgnoredSettings: Promise<string[]> | undefined = undefined;
protected async getIgnoredSettings(content?: string): Promise<string[]> {
if (!this._defaultIgnoredSettings) {
this._defaultIgnoredSettings = this.userDataSyncUtilService.resolveDefaultIgnoredSettings();
const disposable = Event.any<any>(
Event.filter(this.extensionManagementService.onDidInstallExtension, (e => !!e.gallery)),
Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)))(() => {
disposable.dispose();
this._defaultIgnoredSettings = undefined;
});
}
const defaultIgnoredSettings = await this._defaultIgnoredSettings;
return getIgnoredSettings(defaultIgnoredSettings, this.configurationService, content);
}
private validateContent(content: string): void {
if (this.hasErrors(content)) {
throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
@@ -36,6 +36,12 @@ export interface ISyncConfiguration {
}
}
export function getDefaultIgnoredSettings(): string[] {
const allSettings = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
const machineSettings = Object.keys(allSettings).filter(setting => allSettings[setting].scope === ConfigurationScope.MACHINE || allSettings[setting].scope === ConfigurationScope.MACHINE_OVERRIDABLE);
return [CONFIGURATION_SYNC_STORE_KEY, ...machineSettings];
}
export function registerConfiguration(): IDisposable {
const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings';
const ignoredExtensionsSchemaId = 'vscode://schemas/ignoredExtensions';
@@ -105,11 +111,12 @@ export function registerConfiguration(): IDisposable {
});
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
const registerIgnoredSettingsSchema = () => {
const defaultIgnoreSettings = getDefaultIgnoredSettings().filter(s => s !== CONFIGURATION_SYNC_STORE_KEY);
const ignoredSettingsSchema: IJSONSchema = {
items: {
type: 'string',
enum: Object.keys(allSettings.properties)
}
enum: [...Object.keys(allSettings.properties).filter(setting => defaultIgnoreSettings.indexOf(setting) === -1), ...defaultIgnoreSettings.map(setting => `-${setting}`)]
},
};
jsonRegistry.registerSchema(ignoredSettingsSchemaId, ignoredSettingsSchema);
};
@@ -180,6 +187,7 @@ export enum UserDataSyncErrorCode {
// Local Errors
LocalPreconditionFailed = 'LocalPreconditionFailed',
LocalInvalidContent = 'LocalInvalidContent',
LocalError = 'LocalError',
Incompatible = 'Incompatible',
Unknown = 'Unknown',
@@ -286,6 +294,7 @@ export interface IUserDataSyncService {
readonly onDidChangeConflicts: Event<SyncSource[]>;
readonly onDidChangeLocal: Event<void>;
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>;
readonly lastSyncTime: number | undefined;
readonly onDidChangeLastSyncTime: Event<number>;
@@ -313,6 +322,7 @@ export interface IUserDataSyncUtilService {
_serviceBrand: undefined;
resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>>;
resolveFormattingOptions(resource: URI): Promise<FormattingOptions>;
resolveDefaultIgnoredSettings(): Promise<string[]>;
}
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');
@@ -20,6 +20,7 @@ export class UserDataSyncChannel implements IServerChannel {
case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
case 'onDidChangeLastSyncTime': return this.service.onDidChangeLastSyncTime;
case 'onSyncErrors': return this.service.onSyncErrors;
}
throw new Error(`Event not found: ${event}`);
}
@@ -101,6 +102,7 @@ export class UserDataSycnUtilServiceChannel implements IServerChannel {
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'resolveDefaultIgnoredSettings': return this.service.resolveDefaultIgnoredSettings();
case 'resolveUserKeybindings': return this.service.resolveUserBindings(args[0]);
case 'resolveFormattingOptions': return this.service.resolveFormattingOptions(URI.revive(args[0]));
}
@@ -115,6 +117,10 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService {
constructor(private readonly channel: IChannel) {
}
async resolveDefaultIgnoredSettings(): Promise<string[]> {
return this.channel.call('resolveDefaultIgnoredSettings');
}
async resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>> {
return this.channel.call('resolveUserKeybindings', [userbindings]);
}
@@ -41,6 +41,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
private _onDidChangeConflicts: Emitter<SyncSource[]> = this._register(new Emitter<SyncSource[]>());
readonly onDidChangeConflicts: Event<SyncSource[]> = this._onDidChangeConflicts.event;
private _syncErrors: [SyncSource, UserDataSyncError][] = [];
private _onSyncErrors: Emitter<[SyncSource, UserDataSyncError][]> = this._register(new Emitter<[SyncSource, UserDataSyncError][]>());
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]> = this._onSyncErrors.event;
private _lastSyncTime: number | undefined = undefined;
get lastSyncTime(): number | undefined { return this._lastSyncTime; }
private _onDidChangeLastSyncTime: Emitter<number> = this._register(new Emitter<number>());
@@ -82,6 +86,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.handleSyncError(e, synchroniser.source);
}
}
this.updateLastSyncTime();
}
async push(): Promise<void> {
@@ -93,12 +98,14 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.handleSyncError(e, synchroniser.source);
}
}
this.updateLastSyncTime();
}
async sync(): Promise<void> {
await this.checkEnablement();
const startTime = new Date().getTime();
this._syncErrors = [];
try {
this.logService.trace('Sync started.');
if (this.status !== SyncStatus.HasConflicts) {
@@ -124,6 +131,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined);
} catch (e) {
this.handleSyncError(e, synchroniser.source);
this._syncErrors.push([synchroniser.source, UserDataSyncError.toUserDataSyncError(e)]);
}
}
@@ -138,9 +146,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
this.updateLastSyncTime();
} finally {
this.updateStatus();
this._onSyncErrors.fire(this._syncErrors);
}
}
@@ -239,8 +249,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
if (this._status !== status) {
this._status = status;
this._onDidChangeStatus.fire(status);
if (oldStatus !== SyncStatus.Uninitialized && this.status === SyncStatus.Idle) {
this.updateLastSyncTime(new Date().getTime());
if (oldStatus === SyncStatus.HasConflicts) {
this.updateLastSyncTime();
}
}
}
@@ -268,11 +278,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return SyncStatus.Idle;
}
private updateLastSyncTime(lastSyncTime: number): void {
if (this._lastSyncTime !== lastSyncTime) {
this._lastSyncTime = lastSyncTime;
this.storageService.store(LAST_SYNC_TIME_KEY, lastSyncTime, StorageScope.GLOBAL);
this._onDidChangeLastSyncTime.fire(lastSyncTime);
private updateLastSyncTime(): void {
if (this.status === SyncStatus.Idle) {
this._lastSyncTime = new Date().getTime();
this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.GLOBAL);
this._onDidChangeLastSyncTime.fire(this._lastSyncTime);
}
}
@@ -6,7 +6,7 @@
import { IRequestService } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService, getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync';
import { bufferToStream, VSBuffer } from 'vs/base/common/buffer';
import { generateUuid } from 'vs/base/common/uuid';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
@@ -231,6 +231,10 @@ export class TestUserDataSyncUtilService implements IUserDataSyncUtilService {
_serviceBrand: any;
async resolveDefaultIgnoredSettings(): Promise<string[]> {
return getDefaultIgnoredSettings();
}
async resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>> {
const keys: IStringDictionary<string> = {};
for (const keybinding of userbindings) {
+149 -35
View File
@@ -1187,65 +1187,77 @@ declare module 'vscode' {
//#region Custom editors: https://github.com/microsoft/vscode/issues/77131
// TODO:
// - Naming!
// - Think about where a rename would live.
// - Think about handling go to line?
// - Should we expose edits?
// - More properties from `TextDocument`?
/**
* Defines the editing functionality of a webview editor. This allows the webview editor to hook into standard
* Defines the capabilities of a custom webview editor.
*/
interface WebviewCustomEditorCapabilities {
/**
* Defines the editing capability of a custom webview document.
*
* When not provided, the document is considered readonly.
*/
readonly editing?: WebviewCustomEditorEditingCapability;
}
/**
* Defines the editing capability of a custom webview editor. This allows the webview editor to hook into standard
* editor events such as `undo` or `save`.
*
* @param EditType Type of edits.
*/
interface WebviewCustomEditorEditingDelegate<EditType> {
interface WebviewCustomEditorEditingCapability<EditType = unknown> {
/**
* Save a resource.
*
* @param resource Resource being saved.
* Save the resource.
*
* @return Thenable signaling that the save has completed.
*/
save(resource: Uri): Thenable<void>;
save(): Thenable<void>;
/**
* Save an existing resource at a new path.
* Save the existing resource at a new path.
*
* @param resource Resource being saved.
* @param targetResource Location to save to.
*
* @return Thenable signaling that the save has completed.
*/
saveAs(resource: Uri, targetResource: Uri): Thenable<void>;
saveAs(targetResource: Uri): Thenable<void>;
/**
* Event triggered by extensions to signal to VS Code that an edit has occurred.
*/
// TODO@matt
// eslint-disable-next-line vscode-dts-event-naming
readonly onEdit: Event<{ readonly resource: Uri, readonly edit: EditType }>;
readonly onDidEdit: Event<EditType>;
/**
* Apply a set of edits.
*
* Note that is not invoked when `onEdit` is called as `onEdit` implies also updating the view to reflect the edit.
* Note that is not invoked when `onDidEdit` is called because `onDidEdit` implies also updating the view to reflect the edit.
*
* @param resource Resource being edited.
* @param edit Array of edits. Sorted from oldest to most recent.
*
* @return Thenable signaling that the change has completed.
*/
applyEdits(resource: Uri, edits: readonly EditType[]): Thenable<void>;
applyEdits(edits: readonly EditType[]): Thenable<void>;
/**
* Undo a set of edits.
*
* This is triggered when a user undoes an edit or when revert is called on a file.
*
* @param resource Resource being edited.
* @param edit Array of edits. Sorted from most recent to oldest.
*
* @return Thenable signaling that the change has completed.
*/
undoEdits(resource: Uri, edits: readonly EditType[]): Thenable<void>;
undoEdits(edits: readonly EditType[]): Thenable<void>;
/**
* Back up `resource` in its current state.
* Back up the resource in its current state.
*
* Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in
* its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in
@@ -1257,57 +1269,159 @@ declare module 'vscode' {
* made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when
* `auto save` is enabled (since auto save already persists resource ).
*
* @param resource The resource to back up.
* @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your
* extension to decided how to respond to cancellation. If for example your extension is backing up a large file
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
* than cancelling it to ensure that VS Code has some valid backup.
*/
backup?(resource: Uri, cancellation: CancellationToken): Thenable<boolean>;
backup(cancellation: CancellationToken): Thenable<boolean>;
}
/**
* Represents a custom document for a custom webview editor.
*
* Custom documents are only used within a given `WebviewCustomEditorProvider`. The lifecycle of a
* `WebviewEditorCustomDocument` is managed by VS Code. When more more references remain to a given `WebviewEditorCustomDocument`
* then it is disposed of.
*
* @param UserDataType Type of custom object that extensions can store on the document.
*/
interface WebviewEditorCustomDocument<UserDataType = unknown> {
/**
* The associated viewType for this document.
*/
readonly viewType: string;
/**
* The associated uri for this document.
*/
readonly uri: Uri;
/**
* Event fired when there are no more references to the `WebviewEditorCustomDocument`.
*/
readonly onDidDispose: Event<void>;
/**
* Custom data that an extension can store on the document.
*/
readonly userData: UserDataType;
// TODO: Should we expose edits here?
// This could be helpful for tracking the life cycle of edits
}
/**
* Provider for webview editors that use a custom data model.
*
* Custom webview editors use [`WebviewEditorCustomDocument`](#WebviewEditorCustomDocument) as their data model.
* This gives extensions full control over actions such as edit, save, and backup.
*
* You should use custom text based editors when dealing with binary files or more complex scenarios. For simple text
* based documents, use [`WebviewTextEditorProvider`](#WebviewTextEditorProvider) instead.
*/
export interface WebviewCustomEditorProvider {
/**
* Create the model for a given
*
* @param document Resource being resolved.
*/
provideWebviewCustomEditorDocument(resource: Uri): Thenable<WebviewEditorCustomDocument>;
/**
* Resolve a webview editor for a given resource.
*
* To resolve a webview editor, a provider must fill in its initial html content and hook up all
* the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
*
* @param resource Resource being resolved.
* @param document Document for resource being resolved.
* @param webview Webview being resolved. The provider should take ownership of this webview.
*
* @return Thenable indicating that the webview editor has been resolved.
*/
resolveWebviewEditor(
resource: Uri,
webview: WebviewPanel,
): Thenable<void>;
resolveWebviewCustomEditor(document: WebviewEditorCustomDocument, webview: WebviewPanel): Thenable<void>;
}
/**
* Provider for text based webview editors.
*
* Text based webview editors use a [`TextDocument`](#TextDocument) as their data model. This considerably simplifies
* implementing a webview editor as it allows VS Code to handle many common operations such as
* undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`.
*
* You should use text based webview editors when dealing with text based file formats, such as `xml` or `json`.
* For binary files or more specialized use cases, see [WebviewCustomEditorProvider](#WebviewCustomEditorProvider).
*/
export interface WebviewTextEditorProvider {
/**
* Controls the editing functionality of a webview editor. This allows the webview editor to hook into standard
* editor events such as `undo` or `save`.
* Resolve a webview editor for a given resource.
*
* WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact
* with readonly editors, but these editors will not integrate with VS Code's standard editor functionality.
* To resolve a webview editor, the provider must fill in its initial html content and hook up all
* the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
*
* @param document Resource being resolved.
* @param webview Webview being resolved. The provider should take ownership of this webview.
*
* @return Thenable indicating that the webview editor has been resolved.
*/
readonly editingDelegate?: WebviewCustomEditorEditingDelegate<unknown>;
resolveWebviewTextEditor(document: TextDocument, webview: WebviewPanel): Thenable<void>;
}
namespace window {
/**
* Register a new provider for webview editors of a given type.
* Register a new provider for text based webview editors.
*
* @param viewType Type of the webview editor provider.
* @param provider Resolves webview editors.
* @param options Content settings for a webview panels the provider is given.
* @param viewType Type of the webview editor provider. This should match the `viewType` from the
* `package.json` contributions
* @param provider Provider that resolves webview editors.
* @param webviewOptions Content settings for the webview panels that provider is given.
*
* @return Disposable that unregisters the `WebviewTextEditorProvider`.
*/
export function registerWebviewTextEditorProvider(
viewType: string,
provider: WebviewTextEditorProvider,
webviewOptions?: WebviewPanelOptions,
): Disposable;
/**
* Register a new provider for custom webview editors.
*
* @param viewType Type of the webview editor provider. This should match the `viewType` from the
* `package.json` contributions.
* @param provider Provider that resolves webview editors.
* @param webviewOptions Content settings for the webview panels that provider is given.
*
* @return Disposable that unregisters the `WebviewCustomEditorProvider`.
*/
export function registerWebviewCustomEditorProvider(
viewType: string,
provider: WebviewCustomEditorProvider,
options?: WebviewPanelOptions,
webviewOptions?: WebviewPanelOptions,
): Disposable;
/**
* Create a new `WebviewEditorCustomDocument`.
*
* Note that this method only creates a custom document object. To have it be registered with VS Code, you
* must return the document from `WebviewCustomEditorProvider.provideWebviewCustomEditorDocument`.
*
* @param viewType Type of the webview editor provider. This should match the `viewType` from the
* `package.json` contributions.
* @param uri The document's resource.
* @param userData Custom data attached to the document.
* @param capabilities Controls the editing functionality of a webview editor. This allows the webview
* editor to hook into standard editor events such as `undo` or `save`.
*
* WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact
* with readonly editors, but these editors will not integrate with VS Code's standard editor functionality.
*/
export function createWebviewEditorCustomDocument<UserDataType>(
viewType: string,
uri: Uri,
userData: UserDataType,
capabilities: WebviewCustomEditorCapabilities
): WebviewEditorCustomDocument<UserDataType>;
}
//#endregion
@@ -19,7 +19,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { Selection } from 'vs/editor/common/core/selection';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import * as callh from 'vs/workbench/contrib/callHierarchy/browser/callHierarchy';
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { mixin } from 'vs/base/common/objects';
import { decodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokens';
@@ -273,7 +273,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- quick fix
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void {
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string): void {
this._registrations.set(handle, modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
provideCodeActions: async (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise<modes.CodeActionList | undefined> => {
const listDto = await this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token);
@@ -290,7 +290,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
},
providedCodeActionKinds: metadata.providedKinds,
documentation: metadata.documentation
documentation: metadata.documentation,
displayName
}));
}
@@ -512,7 +512,7 @@ export class MainThreadTask implements MainThreadTaskShape {
public $executeTask(value: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO> {
return new Promise<TaskExecutionDTO>((resolve, reject) => {
if (TaskHandleDTO.is(value)) {
const workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
if (workspaceFolder) {
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task | undefined) => {
if (!task) {
@@ -21,7 +21,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { IEditorInput } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { CustomEditorInput, ModelType } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
@@ -97,7 +97,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
private readonly _webviewInputs = new WebviewInputStore();
private readonly _revivers = new Map<string, IDisposable>();
private readonly _editorProviders = new Map<string, IDisposable>();
private readonly _customEditorModels = new Map<ICustomEditorModel, { referenceCount: number }>();
private readonly _customEditorModels = new Map<string, { referenceCount: number }>();
constructor(
context: extHostProtocol.IExtHostContext,
@@ -121,7 +121,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
// This should trigger the real reviver to be registered from the extension host side.
this._register(_webviewWorkbenchService.registerResolver({
canResolve: (webview: WebviewInput) => {
if (webview instanceof CustomFileEditorInput) {
if (webview instanceof CustomEditorInput) {
extensionService.activateByEvent(`onWebviewEditor:${webview.viewType}`);
return false;
}
@@ -256,7 +256,20 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._revivers.delete(viewType);
}
public $registerEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: readonly extHostProtocol.WebviewEditorCapabilities[]): void {
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void {
return this.registerEditorProvider(ModelType.Text, extensionData, viewType, options);
}
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void {
return this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options);
}
public registerEditorProvider(
modelType: ModelType,
extensionData: extHostProtocol.WebviewExtensionDescription,
viewType: string,
options: modes.IWebviewPanelOptions,
): void {
if (this._editorProviders.has(viewType)) {
throw new Error(`Provider for ${viewType} already registered`);
}
@@ -265,21 +278,25 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._editorProviders.set(viewType, this._webviewWorkbenchService.registerResolver({
canResolve: (webviewInput) => {
return webviewInput instanceof CustomFileEditorInput && webviewInput.viewType === viewType;
return webviewInput instanceof CustomEditorInput && webviewInput.viewType === viewType;
},
resolveWebview: async (webviewInput: CustomFileEditorInput) => {
resolveWebview: async (webviewInput: CustomEditorInput) => {
const handle = webviewInput.id;
this._webviewInputs.add(handle, webviewInput);
this.hookupWebviewEventDelegate(handle, webviewInput);
webviewInput.webview.options = options;
webviewInput.webview.extension = extension;
webviewInput.modelType = modelType;
const resource = webviewInput.resource;
const model = await this.retainCustomEditorModel(webviewInput, resource, viewType, capabilities);
webviewInput.onDisposeWebview(() => {
this.releaseCustomEditorModel(model);
});
if (modelType === ModelType.Custom) {
const model = await this.retainCustomEditorModel(webviewInput, resource, viewType);
webviewInput.onDisposeWebview(() => {
this.releaseCustomEditorModel(model);
});
}
try {
await this._proxy.$resolveWebviewEditor(
@@ -311,33 +328,26 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._customEditorService.models.disposeAllModelsForView(viewType);
}
private async retainCustomEditorModel(webviewInput: WebviewInput, resource: URI, viewType: string, capabilities: readonly extHostProtocol.WebviewEditorCapabilities[]) {
private async retainCustomEditorModel(webviewInput: WebviewInput, resource: URI, viewType: string) {
const model = await this._customEditorService.models.resolve(webviewInput.resource, webviewInput.viewType);
const existingEntry = this._customEditorModels.get(model);
const key = viewType + resource.toString();
const existingEntry = this._customEditorModels.get(key);
if (existingEntry) {
++existingEntry.referenceCount;
// no need to hook up listeners again
return model;
}
this._customEditorModels.set(key, { referenceCount: 1 });
const { editable } = await this._proxy.$createWebviewCustomEditorDocument(resource, viewType);
this._customEditorModels.set(model, { referenceCount: 1 });
const capabilitiesSet = new Set(capabilities);
const isEditable = capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.Editable);
if (isEditable) {
model.onUndo(e => {
this._proxy.$undoEdits(resource, viewType, e.edits);
if (editable) {
model.onUndo(() => {
this._proxy.$undo(resource, viewType);
});
model.onDisposeEdits(e => {
this._proxy.$disposeEdits(e.edits);
});
model.onApplyEdit(e => {
if (e.trigger !== model) {
this._proxy.$applyEdits(resource, viewType, e.edits);
}
model.onRedo(() => {
this._proxy.$redo(resource, viewType);
});
model.onWillSave(e => {
@@ -347,7 +357,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
// Save as should always be implemented even if the model is readonly
model.onWillSaveAs(e => {
if (isEditable) {
if (editable) {
e.waitUntil(this._proxy.$onSaveAs(e.resource.toJSON(), viewType, e.targetResource.toJSON()));
} else {
// Since the editor is readonly, just copy the file over
@@ -355,36 +365,37 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
}
});
if (capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.SupportsHotExit)) {
model.onBackup(() => {
return createCancelablePromise(token =>
this._proxy.$backup(model.resource.toJSON(), viewType, token));
});
}
model.onBackup(() => {
return createCancelablePromise(token =>
this._proxy.$backup(model.resource.toJSON(), viewType, token));
});
return model;
}
private async releaseCustomEditorModel(model: ICustomEditorModel) {
const entry = this._customEditorModels.get(model);
const key = model.viewType + model.resource;
const entry = this._customEditorModels.get(key);
if (!entry) {
return;
throw new Error('Model not found');
}
--entry.referenceCount;
if (entry.referenceCount <= 0) {
this._proxy.$disposeWebviewCustomEditorDocument(model.resource, model.viewType);
this._customEditorService.models.disposeModel(model);
this._customEditorModels.delete(model);
this._customEditorModels.delete(key);
}
}
public $onEdit(resource: UriComponents, viewType: string, editId: number): void {
public $onDidChangeCustomDocumentState(resource: UriComponents, viewType: string, state: { dirty: boolean }) {
const model = this._customEditorService.models.get(URI.revive(resource), viewType);
if (!model) {
throw new Error('Could not find model for webview editor');
}
model.pushEdit(editId, model);
model.setDirty(state.dirty);
}
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) {
@@ -114,7 +114,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, accessor.get(IExtHostOutputService));
// manually create and register addressable instances
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService));
const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
@@ -135,6 +134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands));
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments));
// Check that no named customers are missing
// {{SQL CARBON EDIT}} filter out the services we don't expose
@@ -564,10 +564,18 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
},
registerWebviewTextEditorProvider: (viewType: string, provider: vscode.WebviewTextEditorProvider, options?: vscode.WebviewPanelOptions) => {
checkProposedApiEnabled(extension);
return extHostWebviews.registerWebviewTextEditorProvider(extension, viewType, provider, options);
},
registerWebviewCustomEditorProvider: (viewType: string, provider: vscode.WebviewCustomEditorProvider, options?: vscode.WebviewPanelOptions) => {
checkProposedApiEnabled(extension);
return extHostWebviews.registerWebviewCustomEditorProvider(extension, viewType, provider, options);
},
createWebviewEditorCustomDocument: <UserDataType>(viewType: string, resource: vscode.Uri, userData: UserDataType, capabilities: vscode.WebviewCustomEditorCapabilities) => {
checkProposedApiEnabled(extension);
return extHostWebviews.createWebviewEditorCustomDocument<UserDataType>(viewType, resource, userData, capabilities);
},
registerDecorationProvider(provider: vscode.DecorationProvider) {
checkProposedApiEnabled(extension);
return extHostDecorations.registerDecorationProvider(provider, extension.identifier);
+13 -22
View File
@@ -51,6 +51,8 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { Timeline, TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
// {{SQL CARBON EDIT}}
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
@@ -363,7 +365,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void;
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string): void;
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
@@ -574,11 +576,6 @@ export interface WebviewExtensionDescription {
readonly location: UriComponents;
}
export enum WebviewEditorCapabilities {
Editable,
SupportsHotExit,
}
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void;
$disposeWebview(handle: WebviewPanelHandle): void;
@@ -594,10 +591,11 @@ export interface MainThreadWebviewsShape extends IDisposable {
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
$registerEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: readonly WebviewEditorCapabilities[]): void;
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
$unregisterEditorProvider(viewType: string): void;
$onEdit(resource: UriComponents, viewType: string, editId: number): void;
$onDidChangeCustomDocumentState(resource: UriComponents, viewType: string, state: { dirty: boolean }): void;
}
export interface WebviewPanelViewStateData {
@@ -615,12 +613,14 @@ export interface ExtHostWebviewsShape {
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$createWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise<{ editable: boolean }>;
$disposeWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise<void>;
$undoEdits(resource: UriComponents, viewType: string, editIds: readonly number[]): void;
$applyEdits(resource: UriComponents, viewType: string, editIds: readonly number[]): void;
$disposeEdits(editIds: readonly number[]): void;
$undo(resource: UriComponents, viewType: string): void;
$redo(resource: UriComponents, viewType: string): void;
$revert(resource: UriComponents, viewType: string): void;
$onSave(resource: UriComponents, viewType: string): Promise<void>;
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
@@ -1194,16 +1194,7 @@ export interface ICodeLensDto {
command?: ICommandDto;
}
export interface ICallHierarchyItemDto {
_sessionId: string;
_itemId: string;
kind: modes.SymbolKind;
name: string;
detail?: string;
uri: UriComponents;
range: IRange;
selectionRange: IRange;
}
export type ICallHierarchyItemDto = Dto<CallHierarchyItem>;
export interface IIncomingCallDto {
from: ICallHierarchyItemDto;
@@ -1617,7 +1617,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
kind: x.kind.value,
command: this._commands.converter.toInternal(x.command, store),
}))
});
}, ExtHostLanguageFeatures._extLabel(extension));
store.add(this._createDisposable(handle));
return store;
}
+4 -1
View File
@@ -26,6 +26,7 @@ import { Schemas } from 'vs/base/common/network';
import * as Platform from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService';
export interface IExtHostTask extends ExtHostTaskShape {
@@ -192,9 +193,11 @@ export namespace CustomExecutionDTO {
export namespace TaskHandleDTO {
export function from(value: types.Task): tasks.TaskHandleDTO {
let folder: UriComponents | undefined;
let folder: UriComponents | string;
if (value.scope !== undefined && typeof value.scope !== 'number') {
folder = value.scope.uri;
} else if (value.scope !== undefined && typeof value.scope === 'number') {
folder = USER_TASKS_GROUP_KEY;
}
return {
id: value._id!,

Some files were not shown because too many files have changed in this diff Show More