Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 (#8911)

* Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2

* update distro

* fix layering

* update distro

* fix tests
This commit is contained in:
Anthony Dresser
2020-01-22 13:42:37 -08:00
committed by GitHub
parent 977111eb21
commit bd7aac8ee0
895 changed files with 24651 additions and 14520 deletions

View File

@@ -5,11 +5,11 @@
import { Event, Emitter } from 'vs/base/common/event';
import { assign } from 'vs/base/common/objects';
import { isUndefinedOrNull, withNullAsUndefined, assertIsDefined } from 'vs/base/common/types';
import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor';
import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext, ITextEditorSelection } from 'vs/platform/editor/common/editor';
import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -24,6 +24,7 @@ import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/te
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { isEqual } from 'vs/base/common/resources';
import { IPanel } from 'vs/workbench/common/panel';
import { IRange } from 'vs/editor/common/core/range';
export const DirtyWorkingCopiesContext = new RawContextKey<boolean>('dirtyWorkingCopies', false);
export const ActiveEditorContext = new RawContextKey<string | null>('activeEditor', null);
@@ -220,7 +221,9 @@ export interface IUntitledTextResourceInput extends IBaseResourceInput {
/**
* Optional resource. If the resource is not provided a new untitled file is created (e.g. Untitled-1).
* Otherwise the untitled text editor will have an associated path and use that when saving.
* If the used scheme for the resource is not `untitled://`, `forceUntitled: true` must be configured to
* force use the provided resource as associated path. As such, the resource will be used when saving
* the untitled editor.
*/
resource?: URI;
@@ -317,7 +320,7 @@ export interface ISaveOptions {
context?: SaveContext;
/**
* Forces to load the contents of the working copy
* Forces to save the contents of the working copy
* again even if the working copy is not dirty.
*/
force?: boolean;
@@ -403,6 +406,14 @@ export interface IEditorInput extends IDisposable {
*/
isDirty(): boolean;
/**
* Returns if this input is currently being saved or soon to be
* saved. Based on this assumption the editor may for example
* decide to not signal the dirty state to the user assuming that
* the save is scheduled to happen anyway.
*/
isSaving(): boolean;
/**
* Saves the editor. The provided groupId helps
* implementors to e.g. preserve view state of the editor
@@ -508,16 +519,20 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
return false;
}
save(groupId: GroupIdentifier, options?: ISaveOptions): Promise<boolean> {
return Promise.resolve(true);
isSaving(): boolean {
return false;
}
saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise<boolean> {
return Promise.resolve(true);
async save(groupId: GroupIdentifier, options?: ISaveOptions): Promise<boolean> {
return true;
}
revert(options?: IRevertOptions): Promise<boolean> {
return Promise.resolve(true);
async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise<boolean> {
return true;
}
async revert(options?: IRevertOptions): Promise<boolean> {
return true;
}
/**
@@ -559,10 +574,6 @@ export abstract class TextEditorInput extends EditorInput {
}
async save(groupId: GroupIdentifier, options?: ITextFileSaveOptions): Promise<boolean> {
if (this.isReadonly()) {
return false; // return early if editor is readonly
}
return this.textFileService.save(this.resource, options);
}
@@ -701,6 +712,10 @@ export class SideBySideEditorInput extends EditorInput {
return this.master.isDirty();
}
isSaving(): boolean {
return this.master.isSaving();
}
save(groupId: GroupIdentifier, options?: ISaveOptions): Promise<boolean> {
return this.master.save(groupId, options);
}
@@ -741,8 +756,8 @@ export class SideBySideEditorInput extends EditorInput {
this._register(this.master.onDidChangeLabel(() => this._onDidChangeLabel.fire()));
}
resolve(): Promise<EditorModel | null> {
return Promise.resolve(null);
async resolve(): Promise<EditorModel | null> {
return null;
}
getTypeId(): string {
@@ -791,8 +806,8 @@ export class EditorModel extends Disposable implements IEditorModel {
/**
* Causes this model to load returning a promise when loading is completed.
*/
load(): Promise<IEditorModel> {
return Promise.resolve(this);
async load(): Promise<IEditorModel> {
return this;
}
/**
@@ -971,14 +986,22 @@ export class EditorOptions implements IEditorOptions {
/**
* Base Text Editor Options.
*/
export class TextEditorOptions extends EditorOptions {
private startLineNumber: number | undefined;
private startColumn: number | undefined;
private endLineNumber: number | undefined;
private endColumn: number | undefined;
export class TextEditorOptions extends EditorOptions implements ITextEditorOptions {
private revealInCenterIfOutsideViewport: boolean | undefined;
private editorViewState: IEditorViewState | undefined;
/**
* Text editor selection.
*/
selection: ITextEditorSelection | undefined;
/**
* Text editor view state.
*/
editorViewState: IEditorViewState | undefined;
/**
* Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport.
*/
revealInCenterIfOutsideViewport: boolean | undefined;
static from(input?: IBaseResourceInput): TextEditorOptions | undefined {
if (!input || !input.options) {
@@ -1005,8 +1028,12 @@ export class TextEditorOptions extends EditorOptions {
super.overwrite(options);
if (options.selection) {
const selection = options.selection;
this.selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn);
this.selection = {
startLineNumber: options.selection.startLineNumber,
startColumn: options.selection.startColumn,
endLineNumber: options.selection.endLineNumber ?? options.selection.startLineNumber,
endColumn: options.selection.endColumn ?? options.selection.startColumn
};
}
if (options.viewState) {
@@ -1024,19 +1051,7 @@ export class TextEditorOptions extends EditorOptions {
* Returns if this options object has objects defined for the editor.
*/
hasOptionsDefined(): boolean {
return !!this.editorViewState || (!isUndefinedOrNull(this.startLineNumber) && !isUndefinedOrNull(this.startColumn));
}
/**
* Tells the editor to set show the given selection when the editor is being opened.
*/
selection(startLineNumber: number, startColumn: number, endLineNumber: number = startLineNumber, endColumn: number = startColumn): EditorOptions {
this.startLineNumber = startLineNumber;
this.startColumn = startColumn;
this.endLineNumber = endLineNumber;
this.endColumn = endColumn;
return this;
return !!this.editorViewState || !!this.revealInCenterIfOutsideViewport || !!this.selection;
}
/**
@@ -1057,12 +1072,6 @@ export class TextEditorOptions extends EditorOptions {
* @return if something was applied
*/
apply(editor: ICodeEditor, scrollType: ScrollType): boolean {
// View state
return this.applyViewState(editor, scrollType);
}
private applyViewState(editor: ICodeEditor, scrollType: ScrollType): boolean {
let gotApplied = false;
// First try viewstate
@@ -1072,36 +1081,20 @@ export class TextEditorOptions extends EditorOptions {
}
// Otherwise check for selection
else if (!isUndefinedOrNull(this.startLineNumber) && !isUndefinedOrNull(this.startColumn)) {
else if (this.selection) {
const range: IRange = {
startLineNumber: this.selection.startLineNumber,
startColumn: this.selection.startColumn,
endLineNumber: this.selection.endLineNumber ?? this.selection.startLineNumber,
endColumn: this.selection.endColumn ?? this.selection.startColumn
};
// Select
if (!isUndefinedOrNull(this.endLineNumber) && !isUndefinedOrNull(this.endColumn)) {
const range = {
startLineNumber: this.startLineNumber,
startColumn: this.startColumn,
endLineNumber: this.endLineNumber,
endColumn: this.endColumn
};
editor.setSelection(range);
if (this.revealInCenterIfOutsideViewport) {
editor.revealRangeInCenterIfOutsideViewport(range, scrollType);
} else {
editor.revealRangeInCenter(range, scrollType);
}
}
editor.setSelection(range);
// Reveal
else {
const pos = {
lineNumber: this.startLineNumber,
column: this.startColumn
};
editor.setPosition(pos);
if (this.revealInCenterIfOutsideViewport) {
editor.revealPositionInCenterIfOutsideViewport(pos, scrollType);
} else {
editor.revealPositionInCenter(pos, scrollType);
}
if (this.revealInCenterIfOutsideViewport) {
editor.revealRangeInCenterIfOutsideViewport(range, scrollType);
} else {
editor.revealRangeInCenter(range, scrollType);
}
gotApplied = true;
@@ -1307,13 +1300,13 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService
const exists = (typeof path.exists === 'boolean') ? path.exists : await fileService.exists(resource);
const options: ITextEditorOptions = { pinned: true };
if (exists && typeof path.lineNumber === 'number') {
options.selection = {
const options: ITextEditorOptions = (exists && typeof path.lineNumber === 'number') ? {
selection: {
startLineNumber: path.lineNumber,
startColumn: path.columnNumber || 1
};
}
},
pinned: true
} : { pinned: true };
let input: IResourceInput | IUntitledTextResourceInput;
if (!exists) {

View File

@@ -28,6 +28,14 @@ export class DiffEditorInput extends SideBySideEditorInput {
super(name, description, original, modified);
}
matches(otherInput: unknown): boolean {
if (!super.matches(otherInput)) {
return false;
}
return otherInput instanceof DiffEditorInput && otherInput.forceOpenAsBinary === this.forceOpenAsBinary;
}
getTypeId(): string {
return DiffEditorInput.ID;
}

View File

@@ -3,12 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorInput, ITextEditorModel, IModeSupport } from 'vs/workbench/common/editor';
import { EditorInput, ITextEditorModel, IModeSupport, GroupIdentifier, isTextEditor } from 'vs/workbench/common/editor';
import { URI } from 'vs/base/common/uri';
import { IReference } from 'vs/base/common/lifecycle';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import { basename } from 'vs/base/common/resources';
import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
/**
* A read-only text editor input whos contents are made of the provided resource that points to an existing
@@ -26,7 +29,9 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport {
private description: string | undefined,
private readonly resource: URI,
private preferredMode: string | undefined,
@ITextModelService private readonly textModelResolverService: ITextModelService
@ITextModelService private readonly textModelResolverService: ITextModelService,
@ITextFileService private readonly textFileService: ITextFileService,
@IEditorService private readonly editorService: IEditorService
) {
super();
@@ -104,6 +109,28 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport {
return model;
}
async saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise<boolean> {
// Preserve view state by opening the editor first. In addition
// this allows the user to review the contents of the editor.
let viewState: IEditorViewState | undefined = undefined;
const editor = await this.editorService.openEditor(this, undefined, group);
if (isTextEditor(editor)) {
viewState = editor.getViewState();
}
// Save as
const target = await this.textFileService.saveAs(this.resource, undefined, options);
if (!target) {
return false; // save cancelled
}
// Open the target
await this.editorService.openEditor({ resource: target, options: { viewState, pinned: true } }, group);
return true;
}
matches(otherInput: unknown): boolean {
if (super.matches(otherInput) === true) {
return true;

View File

@@ -28,19 +28,18 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
private static readonly MEMOIZER = createMemoizer();
private readonly _onDidModelChangeEncoding = this._register(new Emitter<void>());
readonly onDidModelChangeEncoding = this._onDidModelChangeEncoding.event;
private cachedModel: UntitledTextEditorModel | null = null;
private modelResolve: Promise<UntitledTextEditorModel & IResolvedTextEditorModel> | null = null;
private readonly _onDidModelChangeContent = this._register(new Emitter<void>());
readonly onDidModelChangeContent = this._onDidModelChangeContent.event;
private readonly _onDidModelChangeEncoding = this._register(new Emitter<void>());
readonly onDidModelChangeEncoding = this._onDidModelChangeEncoding.event;
private preferredMode: string | undefined;
constructor(
resource: URI,
private readonly _hasAssociatedFilePath: boolean,
private preferredMode: string | undefined,
preferredMode: string | undefined,
private readonly initialValue: string | undefined,
private preferredEncoding: string | undefined,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@@ -52,6 +51,10 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
) {
super(resource, editorService, editorGroupService, textFileService);
if (preferredMode) {
this.setMode(preferredMode);
}
this.registerListeners();
}
@@ -141,6 +144,8 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
}
isDirty(): boolean {
// Always trust the model first if existing
if (this.cachedModel) {
return this.cachedModel.isDirty();
}
@@ -150,7 +155,13 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
return false;
}
// untitled files with an associated path or associated resource
// A input with initial value is always dirty
if (this.initialValue && this.initialValue.length > 0) {
return true;
}
// A input with associated path is always dirty because it is the intent
// of the user to create a new file at that location through saving
return this.hasAssociatedFilePath;
}
@@ -225,10 +236,20 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
}
setMode(mode: string): void {
this.preferredMode = mode;
let actualMode: string | undefined = undefined;
if (mode === '${activeEditorLanguage}') {
// support the special '${activeEditorLanguage}' mode by
// looking up the language mode from the currently
// active text editor if any
actualMode = this.editorService.activeTextEditorMode;
} else {
actualMode = mode;
}
if (this.cachedModel) {
this.cachedModel.setMode(mode);
this.preferredMode = actualMode;
if (this.preferredMode && this.cachedModel) {
this.cachedModel.setMode(this.preferredMode);
}
}
@@ -258,7 +279,6 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin
const model = this._register(this.instantiationService.createInstance(UntitledTextEditorModel, this.preferredMode, this.resource, this.hasAssociatedFilePath, this.initialValue, this.preferredEncoding));
// re-emit some events from the model
this._register(model.onDidChangeContent(() => this._onDidModelChangeContent.fire()));
this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
this._register(model.onDidChangeEncoding(() => this._onDidModelChangeEncoding.fire()));

View File

@@ -3,41 +3,38 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEncodingSupport, ISaveOptions } from 'vs/workbench/common/editor';
import { IEncodingSupport, ISaveOptions, IModeSupport } from 'vs/workbench/common/editor';
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
import { URI } from 'vs/base/common/uri';
import { CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Event, Emitter } from 'vs/base/common/event';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Emitter } from 'vs/base/common/event';
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { IResolvedTextEditorModel, ITextEditorModel } from 'vs/editor/common/services/resolverService';
import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
export class UntitledTextEditorModel extends BaseTextEditorModel implements IEncodingSupport, IWorkingCopy {
export interface IUntitledTextEditorModel extends ITextEditorModel, IModeSupport, IEncodingSupport, IWorkingCopy { }
static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY;
export class UntitledTextEditorModel extends BaseTextEditorModel implements IUntitledTextEditorModel {
private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent = this._onDidChangeContent.event;
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeDirty: Event<void> = this._onDidChangeDirty.event;
private readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
private readonly _onDidChangeEncoding: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeEncoding: Event<void> = this._onDidChangeEncoding.event;
private readonly _onDidChangeEncoding = this._register(new Emitter<void>());
readonly onDidChangeEncoding = this._onDidChangeEncoding.event;
readonly capabilities = 0;
readonly capabilities = WorkingCopyCapabilities.Untitled;
private dirty = false;
private versionId = 0;
private readonly contentChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidChangeContent.fire(), UntitledTextEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY));
private configuredEncoding?: string;
private configuredEncoding: string | undefined;
constructor(
private readonly preferredMode: string | undefined,
@@ -126,18 +123,13 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IEnc
async revert(): Promise<boolean> {
this.setDirty(false);
// Handle content change event buffered
this.contentChangeEventScheduler.schedule();
return true;
}
backup(): Promise<void> {
async backup(): Promise<void> {
if (this.isResolved()) {
return this.backupFileService.backupResource(this.resource, this.createSnapshot(), this.versionId);
}
return Promise.resolve();
}
hasBackup(): boolean {
@@ -206,8 +198,8 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IEnc
this.setDirty(true);
}
// Handle content change event buffered
this.contentChangeEventScheduler.schedule();
// Emit as event
this._onDidChangeContent.fire();
}
isReadonly(): boolean {

View File

@@ -19,6 +19,7 @@ import { IAction } from 'vs/base/common/actions';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { flatten } from 'vs/base/common/arrays';
import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';
export const FocusedViewContext = new RawContextKey<string>('focusedView', '');
@@ -39,7 +40,7 @@ export interface IViewContainerDescriptor {
readonly name: string;
readonly ctorDescriptor: { ctor: new (...args: any[]) => IViewPaneContainer, arguments?: any[] };
readonly ctorDescriptor: SyncDescriptor<IViewPaneContainer>;
readonly icon?: string | URI;
@@ -99,6 +100,11 @@ export interface IViewContainersRegistry {
* Returns all view containers in the given location
*/
getViewContainers(location: ViewContainerLocation): ViewContainer[];
/**
* Returns the view container location
*/
getViewContainerLocation(container: ViewContainer): ViewContainerLocation | undefined;
}
interface ViewOrderDelegate {
@@ -156,6 +162,10 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe
getViewContainers(location: ViewContainerLocation): ViewContainer[] {
return [...(this.viewContainers.get(location) || [])];
}
getViewContainerLocation(container: ViewContainer): ViewContainerLocation | undefined {
return keys(this.viewContainers).filter(location => this.getViewContainers(location).filter(viewContainer => viewContainer.id === container.id).length > 0)[0];
}
}
Registry.add(Extensions.ViewContainersRegistry, new ViewContainersRegistryImpl());
@@ -166,7 +176,7 @@ export interface IViewDescriptor {
readonly name: string;
readonly ctorDescriptor: { ctor: any, arguments?: any[] };
readonly ctorDescriptor: SyncDescriptor<IView>;
readonly when?: ContextKeyExpr;
@@ -192,6 +202,7 @@ export interface IViewDescriptor {
}
export interface IViewDescriptorCollection extends IDisposable {
readonly onDidChangeViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>;
readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>;
readonly activeViewDescriptors: IViewDescriptor[];
readonly allViewDescriptors: IViewDescriptor[];
@@ -335,14 +346,26 @@ export interface IViewsViewlet extends IViewlet {
}
export const IViewDescriptorService = createDecorator<IViewDescriptorService>('viewDescriptorService');
export const IViewsService = createDecorator<IViewsService>('viewsService');
export interface IViewsService {
_serviceBrand: undefined;
openView(id: string, focus?: boolean): Promise<IView | null>;
}
getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null;
export interface IViewDescriptorService {
_serviceBrand: undefined;
moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void;
getViewDescriptors(container: ViewContainer): IViewDescriptorCollection;
getViewContainer(viewId: string): ViewContainer | null;
}
// Custom views