From c35421b22405d099831846b18072ae4e203af413 Mon Sep 17 00:00:00 2001 From: Preet Mutneja Date: Sun, 28 Apr 2019 22:50:46 +0100 Subject: [PATCH 01/99] update breadcrumb on case-only file rename --- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 8412408f4bd..5cd799b070a 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -70,7 +70,7 @@ class Item extends BreadcrumbsItem { return false; } if (this.element instanceof FileElement && other.element instanceof FileElement) { - return isEqual(this.element.uri, other.element.uri); + return isEqual(this.element.uri, other.element.uri, false); } if (this.element instanceof TreeElement && other.element instanceof TreeElement) { return this.element.id === other.element.id; From 1b12c1e4edd1fe03f6a873214b939884fbd305a9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 29 Apr 2019 14:51:03 +0200 Subject: [PATCH 02/99] hot exit - simplify model creation from backup --- src/vs/workbench/browser/dnd.ts | 2 +- .../browser/nodeless.simpleservices.ts | 4 +- .../common/editor/untitledEditorModel.ts | 4 +- .../services/backup/common/backup.ts | 2 +- .../services/backup/node/backupFileService.ts | 6 +- .../backupFileService.test.ts | 4 +- .../textfile/common/textFileEditorModel.ts | 213 ++++++++---------- 7 files changed, 100 insertions(+), 135 deletions(-) diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 5c4a13a5587..3181415f1b4 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -240,7 +240,7 @@ export class ResourcesDropHandler { return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource!).then(content => { // Set the contents of to the resource to the target - return this.backupFileService.backupResource(droppedDirtyEditor.resource, content!.create(this.getDefaultEOL()).createSnapshot(true)); + return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.create(this.getDefaultEOL()).createSnapshot(true)); }).then(() => false, () => false /* ignore any error */); } diff --git a/src/vs/workbench/browser/nodeless.simpleservices.ts b/src/vs/workbench/browser/nodeless.simpleservices.ts index 474cd6f53e5..70bec5c8377 100644 --- a/src/vs/workbench/browser/nodeless.simpleservices.ts +++ b/src/vs/workbench/browser/nodeless.simpleservices.ts @@ -93,13 +93,13 @@ export class SimpleBackupFileService implements IBackupFileService { return Promise.resolve(); } - resolveBackupContent(backupResource: URI): Promise { + resolveBackupContent(backupResource: URI): Promise { const snapshot = this.backups.get(backupResource.toString()); if (snapshot) { return Promise.resolve(createTextBufferFactoryFromSnapshot(snapshot)); } - return Promise.resolve(undefined); + return Promise.reject('Unexpected backup resource to resolve'); } getWorkspaceFileBackups(): Promise { diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 4f8901e3001..cc7df4eed37 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -137,12 +137,12 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin load(): Promise { // Check for backups first - return this.backupFileService.loadBackupResource(this.resource).then((backupResource) => { + return this.backupFileService.loadBackupResource(this.resource).then(backupResource => { if (backupResource) { return this.backupFileService.resolveBackupContent(backupResource); } - return undefined; + return Promise.resolve(undefined); }).then(backupTextBufferFactory => { const hasBackup = !!backupTextBufferFactory; diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index b1f30ade0a3..1c93e78a5f7 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -58,7 +58,7 @@ export interface IBackupFileService { * @param value The contents from a backup resource as stream. * @return The backup file's backed up content as text buffer factory. */ - resolveBackupContent(backup: URI): Promise; + resolveBackupContent(backup: URI): Promise; /** * Discards the backup associated with a resource if it exists.. diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index 21d8187ef26..d8f312e2a56 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -136,7 +136,7 @@ export class BackupFileService implements IBackupFileService { return this.impl.getWorkspaceFileBackups(); } - resolveBackupContent(backup: Uri): Promise { + resolveBackupContent(backup: Uri): Promise { return this.impl.resolveBackupContent(backup); } @@ -306,13 +306,13 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(); } - resolveBackupContent(backupResource: Uri): Promise { + resolveBackupContent(backupResource: Uri): Promise { const snapshot = this.backups.get(backupResource.toString()); if (snapshot) { return Promise.resolve(createTextBufferFactoryFromSnapshot(snapshot)); } - return Promise.resolve(undefined); + return Promise.reject('Unexpected backup resource to resolve'); } getWorkspaceFileBackups(): Promise { diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index fff74b2b701..40f50d6eb49 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -302,7 +302,7 @@ suite('BackupFileService', () => { const contents = 'test\nand more stuff'; service.backupResource(untitledFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => { - assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); }); }); }); @@ -317,7 +317,7 @@ suite('BackupFileService', () => { service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => { - assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); }); }); }); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index ecb2e2bab4a..c0f46503577 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; -import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types'; +import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -24,7 +24,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceScheduler, timeout } from 'vs/base/common/async'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { hash } from 'vs/base/common/hash'; -import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isLinux } from 'vs/base/common/platform'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -64,8 +63,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private bufferSavedVersionId: number; private blockModelContentChange: boolean; - private createTextEditorModelPromise: Promise | null; - private lastResolvedDiskStat: IFileStatWithMetadata; private autoSaveAfterMillies?: number; @@ -245,7 +242,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - load(options?: ILoadOptions): Promise { + async load(options?: ILoadOptions): Promise { this.logService.trace('load() - enter', this.resource); // It is very important to not reload the model when the model is dirty. @@ -254,44 +251,51 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.dirty || this.saveSequentializer.hasPendingSave()) { this.logService.trace('load() - exit - without loading because model is dirty or being saved', this.resource); - return Promise.resolve(this); + return this; } // Only for new models we support to load from backup - if (!this.textEditorModel && !this.createTextEditorModelPromise) { - return this.loadFromBackup(options); + if (!this.textEditorModel) { + const backup = await this.backupFileService.loadBackupResource(this.resource); + + if (this.textEditorModel) { + return this; // Make sure meanwhile someone else did not suceed in loading + } + + if (backup) { + try { + await this.loadFromBackup(backup, options); + + return this; + } catch (error) { + // ignore error and continue to load as file below + } + } } // Otherwise load from file resource return this.loadFromFile(options); } - private async loadFromBackup(options?: ILoadOptions): Promise { - const backup = await this.backupFileService.loadBackupResource(this.resource); + private async loadFromBackup(backup: URI, options?: ILoadOptions): Promise { - // Make sure meanwhile someone else did not suceed or start loading - if (this.createTextEditorModelPromise || this.textEditorModel) { - return this.createTextEditorModelPromise || this; + // Resolve actual backup contents + const backupContent = await this.backupFileService.resolveBackupContent(backup); + + if (this.textEditorModel) { + return this; // Make sure meanwhile someone else did not suceed in loading } - // If we have a backup, continue loading with it - if (!!backup) { - const content: ITextFileStreamContent = { - resource: this.resource, - name: basename(this.resource), - mtime: Date.now(), - size: 0, - etag: ETAG_DISABLED, // always allow to save content restored from a backup (see https://github.com/Microsoft/vscode/issues/72343) - value: createTextBufferFactory(''), // will be filled later from backup - encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, - isReadonly: false - }; - - return this.loadWithContent(content, options, backup); - } - - // Otherwise load from file - return this.loadFromFile(options); + return this.loadFromContent({ + resource: this.resource, + name: basename(this.resource), + mtime: Date.now(), + size: 0, + etag: ETAG_DISABLED, // always allow to save content restored from a backup (see https://github.com/Microsoft/vscode/issues/72343) + value: backupContent, + encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, + isReadonly: false + }, options, true /* from backup */); } private async loadFromFile(options?: ILoadOptions): Promise { @@ -321,12 +325,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Clear orphaned state when loading was successful this.setOrphaned(false); - // Guard against the model having changed in the meantime - if (currentVersionId === this.versionId) { - return this.loadWithContent(content, options); + if (currentVersionId !== this.versionId) { + return this; // Make sure meanwhile someone else did not suceed loading } - return this; + return this.loadFromContent(content, options); } catch (error) { const result = error.fileOperationResult; @@ -356,33 +359,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private async loadWithContent(content: ITextFileStreamContent, options?: ILoadOptions, backup?: URI): Promise { - const model = await this.doLoadWithContent(content, backup); - - // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype - const settingsType = this.getTypeIfSettings(); - if (settingsType) { - /* __GDPR__ - "settingsRead" : { - "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data - } else { - /* __GDPR__ - "fileGet" : { - "${include}": [ - "${FileTelemetryData}" - ] - } - */ - this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER)); - } - - return model; - } - - private doLoadWithContent(content: ITextFileStreamContent, backup?: URI): Promise { + private loadFromContent(content: ITextFileStreamContent, options?: ILoadOptions, fromBackup?: boolean): TextFileEditorModel { this.logService.trace('load() - resolved content', this.resource); // Update our resolved disk stat model @@ -411,19 +388,59 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Update Existing Model if (this.textEditorModel) { this.doUpdateTextModel(content.value); - - return Promise.resolve(this); - } - - // Join an existing request to create the editor model to avoid race conditions - else if (this.createTextEditorModelPromise) { - this.logService.trace('load() - join existing text editor model promise', this.resource); - - return this.createTextEditorModelPromise; } // Create New Model - return this.doCreateTextModel(content.resource, content.value, backup); + else { + this.doCreateTextModel(content.resource, content.value, !!fromBackup); + } + + // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype + const settingsType = this.getTypeIfSettings(); + if (settingsType) { + /* __GDPR__ + "settingsRead" : { + "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data + } else { + /* __GDPR__ + "fileGet" : { + "${include}": [ + "${FileTelemetryData}" + ] + } + */ + this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER)); + } + + return this; + } + + private doCreateTextModel(resource: URI, value: ITextBufferFactory, fromBackup: boolean): void { + this.logService.trace('load() - created text editor model', this.resource); + + // Create model + this.createTextEditorModel(value, resource); + + // We restored a backup so we have to set the model as being dirty + // We also want to trigger auto save if it is enabled to simulate the exact same behaviour + // you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977) + if (fromBackup) { + this.makeDirty(); + if (this.autoSaveAfterMilliesEnabled) { + this.doAutoSave(this.versionId); + } + } + + // Ensure we are not tracking a stale state + else { + this.setDirty(false); + } + + // Model Listeners + this.installModelListeners(); } private doUpdateTextModel(value: ITextBufferFactory): void { @@ -444,44 +461,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateSavedVersionId(); } - private doCreateTextModel(resource: URI, value: ITextBufferFactory, backup: URI | undefined): Promise { - this.logService.trace('load() - created text editor model', this.resource); - - this.createTextEditorModelPromise = this.doLoadBackup(backup).then(backupContent => { - this.createTextEditorModelPromise = null; - - // Create model - const hasBackupContent = !!backupContent; - this.createTextEditorModel(backupContent ? backupContent : value, resource); - - // We restored a backup so we have to set the model as being dirty - // We also want to trigger auto save if it is enabled to simulate the exact same behaviour - // you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977) - if (hasBackupContent) { - this.makeDirty(); - if (this.autoSaveAfterMilliesEnabled) { - this.doAutoSave(this.versionId); - } - } - - // Ensure we are not tracking a stale state - else { - this.setDirty(false); - } - - // Model Listeners - this.installModelListeners(); - - return this; - }, error => { - this.createTextEditorModelPromise = null; - - return Promise.reject(error); - }); - - return this.createTextEditorModelPromise; - } - private installModelListeners(): void { // See https://github.com/Microsoft/vscode/issues/30189 @@ -494,18 +473,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private async doLoadBackup(backup: URI | undefined): Promise { - if (!backup) { - return null; - } - - try { - return withUndefinedAsNull(await this.backupFileService.resolveBackupContent(backup)); - } catch (error) { - return null; // ignore errors - } - } - protected getOrCreateMode(modeService: IModeService, preferredModeIds: string | undefined, firstLineText?: string): ILanguageSelection { return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); } @@ -1046,8 +1013,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.inOrphanMode = false; this.inErrorMode = false; - this.createTextEditorModelPromise = null; - this.cancelPendingAutoSave(); super.dispose(); From 94bb53909276e98cb97393135939bd2e88e03e8e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 30 Apr 2019 07:59:07 +0200 Subject: [PATCH 03/99] hot exit - convert to async/await --- .../services/backup/node/backupFileService.ts | 233 ++++++++-------- .../backupFileService.test.ts | 255 ++++++++---------- 2 files changed, 231 insertions(+), 257 deletions(-) diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index d8f312e2a56..8faafbe47ac 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -3,17 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'vs/base/common/path'; -import * as crypto from 'crypto'; -import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri } from 'vs/base/common/uri'; +import { join } from 'vs/base/common/path'; +import { createHash } from 'crypto'; +import { readdir, readDirsInDir, rimraf, RimRafMode } from 'vs/base/node/pfs'; +import { URI } from 'vs/base/common/uri'; +import { coalesce } from 'vs/base/common/arrays'; import { ResourceQueue } from 'vs/base/common/async'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IFileService } from 'vs/platform/files/common/files'; import { readToMatchingString } from 'vs/base/node/stream'; import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { keys } from 'vs/base/common/map'; +import { keys, ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -23,47 +24,47 @@ import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/text export interface IBackupFilesModel { resolve(backupRoot: string): Promise; - add(resource: Uri, versionId?: number): void; - has(resource: Uri, versionId?: number): boolean; - get(): Uri[]; - remove(resource: Uri): void; + add(resource: URI, versionId?: number): void; + has(resource: URI, versionId?: number): boolean; + get(): URI[]; + remove(resource: URI): void; count(): number; clear(): void; } export class BackupFilesModel implements IBackupFilesModel { - private cache: { [resource: string]: number /* version ID */ } = Object.create(null); + private cache: ResourceMap = new ResourceMap(); - resolve(backupRoot: string): Promise { - return pfs.readDirsInDir(backupRoot).then(backupSchemas => { + async resolve(backupRoot: string): Promise { + try { + const backupSchemas = await readDirsInDir(backupRoot); - // For all supported schemas - return Promise.all(backupSchemas.map(backupSchema => { + await Promise.all(backupSchemas.map(async backupSchema => { // Read backup directory for backups - const backupSchemaPath = path.join(backupRoot, backupSchema); - return pfs.readdir(backupSchemaPath).then(backupHashes => { + const backupSchemaPath = join(backupRoot, backupSchema); + const backupHashes = await readdir(backupSchemaPath); - // Remember known backups in our caches - backupHashes.forEach(backupHash => { - const backupResource = Uri.file(path.join(backupSchemaPath, backupHash)); - this.add(backupResource); - }); - }); + // Remember known backups in our caches + backupHashes.forEach(backupHash => this.add(URI.file(join(backupSchemaPath, backupHash)))); })); - }).then(() => this, error => this); + } catch (error) { + // ignore any errors + } + + return this; } - add(resource: Uri, versionId = 0): void { - this.cache[resource.toString()] = versionId; + add(resource: URI, versionId = 0): void { + this.cache.set(resource, versionId); } count(): number { - return Object.keys(this.cache).length; + return this.cache.size; } - has(resource: Uri, versionId?: number): boolean { - const cachedVersionId = this.cache[resource.toString()]; + has(resource: URI, versionId?: number): boolean { + const cachedVersionId = this.cache.get(resource); if (typeof cachedVersionId !== 'number') { return false; // unknown resource } @@ -75,16 +76,16 @@ export class BackupFilesModel implements IBackupFilesModel { return true; } - get(): Uri[] { - return Object.keys(this.cache).map(k => Uri.parse(k)); + get(): URI[] { + return this.cache.keys(); } - remove(resource: Uri): void { - delete this.cache[resource.toString()]; + remove(resource: URI): void { + this.cache.delete(resource); } clear(): void { - this.cache = Object.create(null); + this.cache.clear(); } } @@ -116,15 +117,15 @@ export class BackupFileService implements IBackupFileService { return this.impl.hasBackups(); } - loadBackupResource(resource: Uri): Promise { + loadBackupResource(resource: URI): Promise { return this.impl.loadBackupResource(resource); } - backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { + backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise { return this.impl.backupResource(resource, content, versionId); } - discardResourceBackup(resource: Uri): Promise { + discardResourceBackup(resource: URI): Promise { return this.impl.discardResourceBackup(resource); } @@ -132,15 +133,15 @@ export class BackupFileService implements IBackupFileService { return this.impl.discardAllWorkspaceBackups(); } - getWorkspaceFileBackups(): Promise { + getWorkspaceFileBackups(): Promise { return this.impl.getWorkspaceFileBackups(); } - resolveBackupContent(backup: Uri): Promise { + resolveBackupContent(backup: URI): Promise { return this.impl.resolveBackupContent(backup); } - toBackupResource(resource: Uri): Uri { + toBackupResource(resource: URI): URI { return this.impl.toBackupResource(resource); } } @@ -179,104 +180,110 @@ class BackupFileServiceImpl implements IBackupFileService { return model.resolve(this.backupWorkspacePath); } - hasBackups(): Promise { - return this.ready.then(model => { - return model.count() > 0; - }); + async hasBackups(): Promise { + const model = await this.ready; + + return model.count() > 0; } - loadBackupResource(resource: Uri): Promise { - return this.ready.then(model => { + async loadBackupResource(resource: URI): Promise { + const model = await this.ready; - // Return directly if we have a known backup with that resource - const backupResource = this.toBackupResource(resource); - if (model.has(backupResource)) { - return backupResource; - } + // Return directly if we have a known backup with that resource + const backupResource = this.toBackupResource(resource); + if (model.has(backupResource)) { + return backupResource; + } - return undefined; - }); + return undefined; } - backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { + async backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise { if (this.isShuttingDown) { return Promise.resolve(); } - return this.ready.then(model => { - const backupResource = this.toBackupResource(resource); - if (model.has(backupResource, versionId)) { - return undefined; // return early if backup version id matches requested one - } + const model = await this.ready; - return this.ioOperationQueues.queueFor(backupResource).queue(() => { - const preamble = `${resource.toString()}${BackupFileServiceImpl.META_MARKER}`; + const backupResource = this.toBackupResource(resource); + if (model.has(backupResource, versionId)) { + return undefined; // return early if backup version id matches requested one + } - // Update content with value - return this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble)).then(() => model.add(backupResource, versionId)); - }); + return this.ioOperationQueues.queueFor(backupResource).queue(async () => { + const preamble = `${resource.toString()}${BackupFileServiceImpl.META_MARKER}`; + + // Update content with value + await this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble)); + + // Update model + model.add(backupResource, versionId); }); } - discardResourceBackup(resource: Uri): Promise { - return this.ready.then(model => { - const backupResource = this.toBackupResource(resource); + async discardResourceBackup(resource: URI): Promise { + const model = await this.ready; + const backupResource = this.toBackupResource(resource); - return this.ioOperationQueues.queueFor(backupResource).queue(() => { - return pfs.rimraf(backupResource.fsPath, pfs.RimRafMode.MOVE).then(() => model.remove(backupResource)); - }); + return this.ioOperationQueues.queueFor(backupResource).queue(async () => { + await rimraf(backupResource.fsPath, RimRafMode.MOVE); + + model.remove(backupResource); }); } - discardAllWorkspaceBackups(): Promise { + async discardAllWorkspaceBackups(): Promise { this.isShuttingDown = true; - return this.ready.then(model => { - return pfs.rimraf(this.backupWorkspacePath, pfs.RimRafMode.MOVE).then(() => model.clear()); - }); + const model = await this.ready; + + await rimraf(this.backupWorkspacePath, RimRafMode.MOVE); + + model.clear(); } - getWorkspaceFileBackups(): Promise { - return this.ready.then(model => { - const readPromises: Promise[] = []; + async getWorkspaceFileBackups(): Promise { + const model = await this.ready; - model.get().forEach(fileBackup => { - readPromises.push( - readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.META_MARKER, 2000, 10000).then(Uri.parse) - ); - }); + const backups = await Promise.all(model.get().map(async fileBackup => { + const backup = await readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.META_MARKER, 2000, 10000); + if (!backup) { + return undefined; + } - return Promise.all(readPromises); - }); + return URI.parse(backup); + })); + + return coalesce(backups); } - resolveBackupContent(backup: Uri): Promise { - return this.fileService.readFileStream(backup).then(content => { + async resolveBackupContent(backup: URI): Promise { + const content = await this.fileService.readFileStream(backup); - // Add a filter method to filter out everything until the meta marker - let metaFound = false; - const metaPreambleFilter = (chunk: VSBuffer) => { - const chunkString = chunk.toString(); + // Add a filter method to filter out everything until the meta marker + let metaFound = false; + const metaPreambleFilter = (chunk: VSBuffer) => { + const chunkString = chunk.toString(); - if (!metaFound && chunk) { - const metaIndex = chunkString.indexOf(BackupFileServiceImpl.META_MARKER); - if (metaIndex === -1) { - return VSBuffer.fromString(''); // meta not yet found, return empty string - } - - metaFound = true; - return VSBuffer.fromString(chunkString.substr(metaIndex + 1)); // meta found, return everything after + if (!metaFound && chunk) { + const metaIndex = chunkString.indexOf(BackupFileServiceImpl.META_MARKER); + if (metaIndex === -1) { + return VSBuffer.fromString(''); // meta not yet found, return empty string } - return chunk; - }; + metaFound = true; - return createTextBufferFactoryFromStream(content.value, metaPreambleFilter); - }); + return VSBuffer.fromString(chunkString.substr(metaIndex + 1)); // meta found, return everything after + } + + return chunk; + }; + + return createTextBufferFactoryFromStream(content.value, metaPreambleFilter); } - toBackupResource(resource: Uri): Uri { - return Uri.file(path.join(this.backupWorkspacePath, resource.scheme, hashPath(resource))); + toBackupResource(resource: URI): URI { + return URI.file(join(this.backupWorkspacePath, resource.scheme, hashPath(resource))); } } @@ -290,7 +297,7 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(this.backups.size > 0); } - loadBackupResource(resource: Uri): Promise { + loadBackupResource(resource: URI): Promise { const backupResource = this.toBackupResource(resource); if (this.backups.has(backupResource.toString())) { return Promise.resolve(backupResource); @@ -299,14 +306,14 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(undefined); } - backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { + backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise { const backupResource = this.toBackupResource(resource); this.backups.set(backupResource.toString(), content); return Promise.resolve(); } - resolveBackupContent(backupResource: Uri): Promise { + resolveBackupContent(backupResource: URI): Promise { const snapshot = this.backups.get(backupResource.toString()); if (snapshot) { return Promise.resolve(createTextBufferFactoryFromSnapshot(snapshot)); @@ -315,11 +322,11 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.reject('Unexpected backup resource to resolve'); } - getWorkspaceFileBackups(): Promise { - return Promise.resolve(keys(this.backups).map(key => Uri.parse(key))); + getWorkspaceFileBackups(): Promise { + return Promise.resolve(keys(this.backups).map(key => URI.parse(key))); } - discardResourceBackup(resource: Uri): Promise { + discardResourceBackup(resource: URI): Promise { this.backups.delete(this.toBackupResource(resource).toString()); return Promise.resolve(); @@ -331,17 +338,17 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(); } - toBackupResource(resource: Uri): Uri { - return Uri.file(path.join(resource.scheme, hashPath(resource))); + toBackupResource(resource: URI): URI { + return URI.file(join(resource.scheme, hashPath(resource))); } } /* * Exported only for testing */ -export function hashPath(resource: Uri): string { +export function hashPath(resource: URI): string { const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); - return crypto.createHash('md5').update(str).digest('hex'); + return createHash('md5').update(str).digest('hex'); } registerSingleton(IBackupFileService, BackupFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 40f50d6eb49..b1a62ca4910 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -70,15 +70,14 @@ class TestBackupFileService extends BackupFileService { suite('BackupFileService', () => { let service: TestBackupFileService; - setup(() => { + setup(async () => { service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); // Delete any existing backups completely and then re-create it. - return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE).then(() => { - return pfs.mkdirp(backupHome).then(() => { - return pfs.writeFile(workspacesJsonPath, ''); - }); - }); + await pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + await pfs.mkdirp(backupHome); + + return pfs.writeFile(workspacesJsonPath, ''); }); teardown(() => { @@ -131,183 +130,155 @@ suite('BackupFileService', () => { }); suite('loadBackupResource', () => { - test('should return whether a backup resource exists', () => { - return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { - fs.writeFileSync(fooBackupPath, 'foo'); - service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); - return service.loadBackupResource(fooFile).then(resource => { - assert.ok(resource); - assert.equal(path.basename(resource!.fsPath), path.basename(fooBackupPath)); - return service.hasBackups().then(hasBackups => { - assert.ok(hasBackups); - }); - }); - }); + test('should return whether a backup resource exists', async () => { + await pfs.mkdirp(path.dirname(fooBackupPath)); + fs.writeFileSync(fooBackupPath, 'foo'); + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + const resource = await service.loadBackupResource(fooFile); + assert.ok(resource); + assert.equal(path.basename(resource!.fsPath), path.basename(fooBackupPath)); + const hasBackups = await service.hasBackups(); + assert.ok(hasBackups); }); }); suite('backupResource', () => { - test('text file', function () { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); - }); + test('text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); }); - test('untitled file', function () { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - assert.equal(fs.existsSync(untitledBackupPath), true); - assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); - }); + test('untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + assert.equal(fs.existsSync(untitledBackupPath), true); + assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); }); - test('text file (ITextSnapshot)', function () { + test('text file (ITextSnapshot)', async () => { const model = TextModel.createFromString('test'); - return service.backupResource(fooFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); - model.dispose(); - }); + await service.backupResource(fooFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); + model.dispose(); }); - test('untitled file (ITextSnapshot)', function () { + test('untitled file (ITextSnapshot)', async () => { const model = TextModel.createFromString('test'); - return service.backupResource(untitledFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - assert.equal(fs.existsSync(untitledBackupPath), true); - assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); - model.dispose(); - }); + await service.backupResource(untitledFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + assert.equal(fs.existsSync(untitledBackupPath), true); + assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); + model.dispose(); }); - test('text file (large file, ITextSnapshot)', function () { + test('text file (large file, ITextSnapshot)', async () => { const largeString = (new Array(10 * 1024)).join('Large String\n'); const model = TextModel.createFromString(largeString); - return service.backupResource(fooFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\n${largeString}`); - model.dispose(); - }); + await service.backupResource(fooFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\n${largeString}`); + model.dispose(); }); - test('untitled file (large file, ITextSnapshot)', function () { + test('untitled file (large file, ITextSnapshot)', async () => { const largeString = (new Array(10 * 1024)).join('Large String\n'); const model = TextModel.createFromString(largeString); - return service.backupResource(untitledFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - assert.equal(fs.existsSync(untitledBackupPath), true); - assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\n${largeString}`); - model.dispose(); - }); + await service.backupResource(untitledFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + assert.equal(fs.existsSync(untitledBackupPath), true); + assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\n${largeString}`); + model.dispose(); }); }); suite('discardResourceBackup', () => { - test('text file', function () { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - return service.discardResourceBackup(fooFile).then(() => { - assert.equal(fs.existsSync(fooBackupPath), false); - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0); - }); - }); + test('text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + await service.discardResourceBackup(fooFile); + assert.equal(fs.existsSync(fooBackupPath), false); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0); }); - test('untitled file', function () { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - return service.discardResourceBackup(untitledFile).then(() => { - assert.equal(fs.existsSync(untitledBackupPath), false); - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0); - }); - }); + test('untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + await service.discardResourceBackup(untitledFile); + assert.equal(fs.existsSync(untitledBackupPath), false); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0); }); }); suite('discardAllWorkspaceBackups', () => { - test('text file', function () { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2); - return service.discardAllWorkspaceBackups().then(() => { - assert.equal(fs.existsSync(fooBackupPath), false); - assert.equal(fs.existsSync(barBackupPath), false); - assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false); - }); - }); - }); + test('text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + await service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2); + await service.discardAllWorkspaceBackups(); + assert.equal(fs.existsSync(fooBackupPath), false); + assert.equal(fs.existsSync(barBackupPath), false); + assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false); }); - test('untitled file', function () { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - return service.discardAllWorkspaceBackups().then(() => { - assert.equal(fs.existsSync(untitledBackupPath), false); - assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false); - }); - }); + test('untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + await service.discardAllWorkspaceBackups(); + assert.equal(fs.existsSync(untitledBackupPath), false); + assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false); }); - test('should disable further backups', function () { - return service.discardAllWorkspaceBackups().then(() => { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.existsSync(workspaceBackupPath), false); - }); - }); + test('should disable further backups', async () => { + await service.discardAllWorkspaceBackups(); + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.existsSync(workspaceBackupPath), false); }); }); suite('getWorkspaceFileBackups', () => { - test('("file") - text file', () => { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]); - return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]); - }); - }); - }); - }); + test('("file") - text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]); + await service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles_1 = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles_1.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]); }); - test('("file") - untitled file', () => { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]); - }); - }); + test('("file") - untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]); }); - test('("untitled") - untitled file', () => { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']); - }); - }); + test('("untitled") - untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']); }); }); test('resolveBackupContent', () => { - test('should restore the original contents (untitled file)', () => { + test('should restore the original contents (untitled file)', async () => { const contents = 'test\nand more stuff'; - service.backupResource(untitledFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => { - assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); - }); - }); + + await service.backupResource(untitledFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)); + + const factory = await service.resolveBackupContent(service.toBackupResource(untitledFile)); + assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); }); - test('should restore the original contents (text file)', () => { + test('should restore the original contents (text file)', async () => { const contents = [ 'Lorem ipsum ', 'dolor öäü sit amet ', @@ -315,11 +286,10 @@ suite('BackupFileService', () => { 'adipiscing ßß elit', ].join(''); - service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => { - assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); - }); - }); + await service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)); + + const factory = await service.resolveBackupContent(service.toBackupResource(untitledFile)); + assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); }); }); }); @@ -372,16 +342,13 @@ suite('BackupFilesModel', () => { assert.equal(model.has(resource4), true); }); - test('resolve', () => { - return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { - fs.writeFileSync(fooBackupPath, 'foo'); + test('resolve', async () => { + await pfs.mkdirp(path.dirname(fooBackupPath)); + fs.writeFileSync(fooBackupPath, 'foo'); + const model = new BackupFilesModel(); - const model = new BackupFilesModel(); - - return model.resolve(workspaceBackupPath).then(model => { - assert.equal(model.has(Uri.file(fooBackupPath)), true); - }); - }); + const resolvedModel = await model.resolve(workspaceBackupPath); + assert.equal(resolvedModel.has(Uri.file(fooBackupPath)), true); }); test('get', () => { From fc9958771ae2ab2e08f9a03ec018fadb072304f1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 30 Apr 2019 07:59:44 +0200 Subject: [PATCH 04/99] hot exit - move tests into node layer --- .../test/{electron-browser => node}/backupFileService.test.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/vs/workbench/services/backup/test/{electron-browser => node}/backupFileService.test.ts (100%) diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts similarity index 100% rename from src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts rename to src/vs/workbench/services/backup/test/node/backupFileService.test.ts From 22b2bd49834de66d07aa610441b4db668017e960 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 30 Apr 2019 16:56:48 +0200 Subject: [PATCH 05/99] hot exit - implement support for metadata --- src/vs/workbench/browser/dnd.ts | 2 +- .../browser/nodeless.simpleservices.ts | 10 +- .../common/editor/untitledEditorModel.ts | 17 +- .../backup/common/backupModelTracker.ts | 12 +- .../services/backup/common/backup.ts | 15 +- .../services/backup/node/backupFileService.ts | 166 +++++++++----- .../test/node/backupFileService.test.ts | 217 ++++++++++++++++-- .../textfile/common/textFileEditorModel.ts | 55 ++++- .../textfile/common/textFileService.ts | 25 +- .../services/textfile/common/textfiles.ts | 2 + .../workbench/test/workbenchTestServices.ts | 6 +- 11 files changed, 407 insertions(+), 120 deletions(-) diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 3181415f1b4..3751e3bf4b2 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -240,7 +240,7 @@ export class ResourcesDropHandler { return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource!).then(content => { // Set the contents of to the resource to the target - return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.create(this.getDefaultEOL()).createSnapshot(true)); + return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true)); }).then(() => false, () => false /* ignore any error */); } diff --git a/src/vs/workbench/browser/nodeless.simpleservices.ts b/src/vs/workbench/browser/nodeless.simpleservices.ts index 70bec5c8377..7814e0986ba 100644 --- a/src/vs/workbench/browser/nodeless.simpleservices.ts +++ b/src/vs/workbench/browser/nodeless.simpleservices.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; +import { ITextSnapshot } from 'vs/editor/common/model'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { keys, ResourceMap } from 'vs/base/common/map'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -86,17 +86,17 @@ export class SimpleBackupFileService implements IBackupFileService { return Promise.resolve(undefined); } - backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise { + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { const backupResource = this.toBackupResource(resource); this.backups.set(backupResource.toString(), content); return Promise.resolve(); } - resolveBackupContent(backupResource: URI): Promise { + resolveBackupContent(backupResource: URI): Promise> { const snapshot = this.backups.get(backupResource.toString()); if (snapshot) { - return Promise.resolve(createTextBufferFactoryFromSnapshot(snapshot)); + return Promise.resolve({ value: createTextBufferFactoryFromSnapshot(snapshot) }); } return Promise.reject('Unexpected backup resource to resolve'); diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index cc7df4eed37..3488d2e67e5 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -134,6 +134,15 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin this.contentChangeEventScheduler.schedule(); } + backup(): Promise { + const snapshot = this.createSnapshot(); + if (!snapshot) { + return Promise.resolve(); // should not happen + } + + return this.backupFileService.backupResource(this.resource, snapshot, this.versionId); + } + load(): Promise { // Check for backups first @@ -143,15 +152,15 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin } return Promise.resolve(undefined); - }).then(backupTextBufferFactory => { - const hasBackup = !!backupTextBufferFactory; + }).then(backup => { + const hasBackup = !!backup; // untitled associated to file path are dirty right away as well as untitled with content this.setDirty(this._hasAssociatedFilePath || hasBackup); let untitledContents: ITextBufferFactory; - if (backupTextBufferFactory) { - untitledContents = backupTextBufferFactory; + if (backup) { + untitledContents = backup.value; } else { untitledContents = createTextBufferFactory(this.initialValue || ''); } diff --git a/src/vs/workbench/contrib/backup/common/backupModelTracker.ts b/src/vs/workbench/contrib/backup/common/backupModelTracker.ts index 2b245986c9b..aa16c733036 100644 --- a/src/vs/workbench/contrib/backup/common/backupModelTracker.ts +++ b/src/vs/workbench/contrib/backup/common/backupModelTracker.ts @@ -66,10 +66,7 @@ export class BackupModelTracker extends Disposable implements IWorkbenchContribu if (!this.configuredAutoSaveAfterDelay) { const model = this.textFileService.models.get(event.resource); if (model) { - const snapshot = model.createSnapshot(); - if (snapshot) { - this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId()); - } + model.backup(); } } } @@ -77,12 +74,7 @@ export class BackupModelTracker extends Disposable implements IWorkbenchContribu private onUntitledModelChanged(resource: Uri): void { if (this.untitledEditorService.isDirty(resource)) { - this.untitledEditorService.loadOrCreate({ resource }).then(model => { - const snapshot = model.createSnapshot(); - if (snapshot) { - this.backupFileService.backupResource(resource, snapshot, model.getVersionId()); - } - }); + this.untitledEditorService.loadOrCreate({ resource }).then(model => model.backup()); } else { this.discardBackup(resource); } diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index 1c93e78a5f7..9d1b0208434 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -9,6 +9,11 @@ import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; export const IBackupFileService = createDecorator('backupFileService'); +export interface IResolvedBackup { + value: ITextBufferFactory; + meta?: T; +} + /** * A service that handles any I/O and state associated with the backup system. */ @@ -42,8 +47,10 @@ export interface IBackupFileService { * @param resource The resource to back up. * @param content The content of the resource as snapshot. * @param versionId The version id of the resource to backup. + * @param meta The (optional) meta data of the resource to backup. This information + * can be restored later when loading the backup again. */ - backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise; + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise; /** * Gets a list of file backups for the current workspace. @@ -55,10 +62,10 @@ export interface IBackupFileService { /** * Resolves the backup for the given resource. * - * @param value The contents from a backup resource as stream. - * @return The backup file's backed up content as text buffer factory. + * @param resource The resource to get the backup for. + * @return The backup file's backed up content and metadata if available. */ - resolveBackupContent(backup: URI): Promise; + resolveBackupContent(resource: URI): Promise>; /** * Discards the backup associated with a resource if it exists.. diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index 8faafbe47ac..2361e120711 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -5,14 +5,14 @@ import { join } from 'vs/base/common/path'; import { createHash } from 'crypto'; -import { readdir, readDirsInDir, rimraf, RimRafMode } from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { coalesce } from 'vs/base/common/arrays'; +import { equals, deepClone } from 'vs/base/common/objects'; import { ResourceQueue } from 'vs/base/common/async'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { IFileService } from 'vs/platform/files/common/files'; import { readToMatchingString } from 'vs/base/node/stream'; -import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; +import { ITextSnapshot } from 'vs/editor/common/model'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { keys, ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; @@ -20,34 +20,46 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { VSBuffer } from 'vs/base/common/buffer'; import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/textfiles'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export interface IBackupFilesModel { resolve(backupRoot: string): Promise; - add(resource: URI, versionId?: number): void; - has(resource: URI, versionId?: number): boolean; + add(resource: URI, versionId?: number, meta?: object): void; + has(resource: URI, versionId?: number, meta?: object): boolean; get(): URI[]; remove(resource: URI): void; count(): number; clear(): void; } +interface IBackupCacheEntry { + versionId?: number; + meta?: object; +} + export class BackupFilesModel implements IBackupFilesModel { - private cache: ResourceMap = new ResourceMap(); + private cache: ResourceMap = new ResourceMap(); + + constructor(private fileService: IFileService) { } async resolve(backupRoot: string): Promise { try { - const backupSchemas = await readDirsInDir(backupRoot); + const backupRootStat = await this.fileService.resolve(URI.file(backupRoot)); + if (backupRootStat.children) { + await Promise.all(backupRootStat.children + .filter(child => child.isDirectory). + map(async backupSchema => { - await Promise.all(backupSchemas.map(async backupSchema => { + // Read backup directory for backups + const backupSchemaStat = await this.fileService.resolve(backupSchema.resource); - // Read backup directory for backups - const backupSchemaPath = join(backupRoot, backupSchema); - const backupHashes = await readdir(backupSchemaPath); - - // Remember known backups in our caches - backupHashes.forEach(backupHash => this.add(URI.file(join(backupSchemaPath, backupHash)))); - })); + // Remember known backups in our caches + if (backupSchemaStat.children) { + backupSchemaStat.children.forEach(backupHash => this.add(backupHash.resource)); + } + })); + } } catch (error) { // ignore any errors } @@ -55,22 +67,26 @@ export class BackupFilesModel implements IBackupFilesModel { return this; } - add(resource: URI, versionId = 0): void { - this.cache.set(resource, versionId); + add(resource: URI, versionId = 0, meta?: object): void { + this.cache.set(resource, { versionId, meta: deepClone(meta) }); // make sure to not store original meta in our cache... } count(): number { return this.cache.size; } - has(resource: URI, versionId?: number): boolean { - const cachedVersionId = this.cache.get(resource); - if (typeof cachedVersionId !== 'number') { + has(resource: URI, versionId?: number, meta?: object): boolean { + const entry = this.cache.get(resource); + if (!entry) { return false; // unknown resource } - if (typeof versionId === 'number') { - return versionId === cachedVersionId; // if we are asked with a specific version ID, make sure to test for it + if (typeof versionId === 'number' && typeof entry.versionId === 'number' && entry.versionId !== versionId) { + return false; // different versionId + } + + if (meta && entry.meta && !equals(meta, entry.meta)) { + return false; // different metadata } return true; @@ -91,7 +107,7 @@ export class BackupFilesModel implements IBackupFilesModel { export class BackupFileService implements IBackupFileService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; private impl: IBackupFileService; @@ -121,8 +137,8 @@ export class BackupFileService implements IBackupFileService { return this.impl.loadBackupResource(resource); } - backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise { - return this.impl.backupResource(resource, content, versionId); + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { + return this.impl.backupResource(resource, content, versionId, meta); } discardResourceBackup(resource: URI): Promise { @@ -137,7 +153,7 @@ export class BackupFileService implements IBackupFileService { return this.impl.getWorkspaceFileBackups(); } - resolveBackupContent(backup: URI): Promise { + resolveBackupContent(backup: URI): Promise> { return this.impl.resolveBackupContent(backup); } @@ -148,7 +164,9 @@ export class BackupFileService implements IBackupFileService { class BackupFileServiceImpl implements IBackupFileService { - private static readonly META_MARKER = '\n'; + private static readonly PREAMBLE_END_MARKER = '\n'; + private static readonly PREAMBLE_META_START_MARKER = ' '; + private static readonly PREAMBLE_MAX_LENGTH = 10000; _serviceBrand: any; @@ -175,7 +193,7 @@ class BackupFileServiceImpl implements IBackupFileService { } private init(): Promise { - const model = new BackupFilesModel(); + const model = new BackupFilesModel(this.fileService); return model.resolve(this.backupWorkspacePath); } @@ -198,26 +216,39 @@ class BackupFileServiceImpl implements IBackupFileService { return undefined; } - async backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise { + async backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { if (this.isShuttingDown) { - return Promise.resolve(); + return; } const model = await this.ready; const backupResource = this.toBackupResource(resource); - if (model.has(backupResource, versionId)) { - return undefined; // return early if backup version id matches requested one + if (model.has(backupResource, versionId, meta)) { + return; // return early if backup version id matches requested one } return this.ioOperationQueues.queueFor(backupResource).queue(async () => { - const preamble = `${resource.toString()}${BackupFileServiceImpl.META_MARKER}`; + let preamble: string | undefined = undefined; + + // With Metadata: URI + META-START + Meta + END + if (meta) { + const preambleWithMeta = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_META_START_MARKER}${JSON.stringify(meta)}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; + if (preambleWithMeta.length < BackupFileServiceImpl.PREAMBLE_MAX_LENGTH) { + preamble = preambleWithMeta; + } + } + + // Without Metadata: URI + END + if (!preamble) { + preamble = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; + } // Update content with value await this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble)); // Update model - model.add(backupResource, versionId); + model.add(backupResource, versionId, meta); }); } @@ -226,7 +257,7 @@ class BackupFileServiceImpl implements IBackupFileService { const backupResource = this.toBackupResource(resource); return this.ioOperationQueues.queueFor(backupResource).queue(async () => { - await rimraf(backupResource.fsPath, RimRafMode.MOVE); + await this.fileService.del(backupResource, { recursive: true }); model.remove(backupResource); }); @@ -237,7 +268,7 @@ class BackupFileServiceImpl implements IBackupFileService { const model = await this.ready; - await rimraf(this.backupWorkspacePath, RimRafMode.MOVE); + await this.fileService.del(URI.file(this.backupWorkspacePath), { recursive: true }); model.clear(); } @@ -246,40 +277,71 @@ class BackupFileServiceImpl implements IBackupFileService { const model = await this.ready; const backups = await Promise.all(model.get().map(async fileBackup => { - const backup = await readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.META_MARKER, 2000, 10000); - if (!backup) { + const backupPreamble = await readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH / 5, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH); + if (!backupPreamble) { return undefined; } - return URI.parse(backup); + // Preamble with metadata: URI + META-START + Meta + END + const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_START_MARKER); + if (metaStartIndex > 0) { + return URI.parse(backupPreamble.substring(0, metaStartIndex)); + } + + // Preamble without metadata: URI + END + else { + return URI.parse(backupPreamble); + } })); return coalesce(backups); } - async resolveBackupContent(backup: URI): Promise { - const content = await this.fileService.readFileStream(backup); + async resolveBackupContent(backup: URI): Promise> { - // Add a filter method to filter out everything until the meta marker - let metaFound = false; + // Metadata extraction + let metaRaw: string = ''; + let metaEndFound = false; + + // Add a filter method to filter out everything until the meta end marker const metaPreambleFilter = (chunk: VSBuffer) => { const chunkString = chunk.toString(); - if (!metaFound && chunk) { - const metaIndex = chunkString.indexOf(BackupFileServiceImpl.META_MARKER); - if (metaIndex === -1) { + if (!metaEndFound) { + const metaEndIndex = chunkString.indexOf(BackupFileServiceImpl.PREAMBLE_END_MARKER); + if (metaEndIndex === -1) { + metaRaw += chunkString; + return VSBuffer.fromString(''); // meta not yet found, return empty string } - metaFound = true; + metaEndFound = true; + metaRaw += chunkString.substring(0, metaEndIndex); // ensure to get last chunk from metadata - return VSBuffer.fromString(chunkString.substr(metaIndex + 1)); // meta found, return everything after + return VSBuffer.fromString(chunkString.substr(metaEndIndex + 1)); // meta found, return everything after } return chunk; }; - return createTextBufferFactoryFromStream(content.value, metaPreambleFilter); + // Read backup into factory + const content = await this.fileService.readFileStream(backup); + const factory = await createTextBufferFactoryFromStream(content.value, metaPreambleFilter); + + // Trigger read for meta data extraction from the filter above + factory.getFirstLineText(1); + + let meta: T | undefined; + const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_START_MARKER); + if (metaStartIndex !== -1) { + try { + meta = JSON.parse(metaRaw.substr(metaStartIndex)); + } catch (error) { + // ignore JSON parse errors + } + } + + return { value: factory, meta }; } toBackupResource(resource: URI): URI { @@ -306,17 +368,17 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(undefined); } - backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise { + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { const backupResource = this.toBackupResource(resource); this.backups.set(backupResource.toString(), content); return Promise.resolve(); } - resolveBackupContent(backupResource: URI): Promise { + resolveBackupContent(backupResource: URI): Promise> { const snapshot = this.backups.get(backupResource.toString()); if (snapshot) { - return Promise.resolve(createTextBufferFactoryFromSnapshot(snapshot)); + return Promise.resolve({ value: createTextBufferFactoryFromSnapshot(snapshot) }); } return Promise.reject('Unexpected backup resource to resolve'); diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts index b1a62ca4910..e9d2eb116ad 100644 --- a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -23,6 +23,7 @@ import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFil import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService } from 'vs/platform/files/common/files'; const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); const backupHome = path.join(parentDir, 'Backups'); @@ -31,7 +32,9 @@ const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); const workspaceResource = Uri.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource)); const fooFile = Uri.file(platform.isWindows ? 'c:\\Foo' : '/Foo'); +const customFile = Uri.parse('customScheme://some/path'); const barFile = Uri.file(platform.isWindows ? 'c:\\Bar' : '/Bar'); +const fooBarFile = Uri.file(platform.isWindows ? 'c:\\Foo Bar' : '/Foo Bar'); const untitledFile = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); const fooBackupPath = path.join(workspaceBackupPath, 'file', hashPath(fooFile)); const barBackupPath = path.join(workspaceBackupPath, 'file', hashPath(barFile)); @@ -54,15 +57,20 @@ class TestBackupEnvironmentService extends WorkbenchEnvironmentService { } class TestBackupFileService extends BackupFileService { + + readonly fileService: IFileService; + constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const environmentService = new TestBackupEnvironmentService(workspaceBackupPath); super(environmentService, fileService); + + this.fileService = fileService; } - public toBackupResource(resource: Uri): Uri { + toBackupResource(resource: Uri): Uri { return super.toBackupResource(resource); } } @@ -150,6 +158,13 @@ suite('BackupFileService', () => { assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); }); + test('text file (with meta)', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), undefined, { etag: '678', orphaned: true }); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`); + }); + test('untitled file', async () => { await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); @@ -268,14 +283,32 @@ suite('BackupFileService', () => { }); }); - test('resolveBackupContent', () => { + suite('resolveBackupContent', () => { + + interface IBackupTestMetaData { + mtime?: number; + size?: number; + etag?: string; + orphaned?: boolean; + } + test('should restore the original contents (untitled file)', async () => { const contents = 'test\nand more stuff'; - await service.backupResource(untitledFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)); + await testResolveBackup(untitledFile, contents); + }); - const factory = await service.resolveBackupContent(service.toBackupResource(untitledFile)); - assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + test('should restore the original contents (untitled file with metadata)', async () => { + const contents = 'test\nand more stuff'; + + const meta = { + etag: 'the Etag', + size: 666, + mtime: Date.now(), + orphaned: true + }; + + await testResolveBackup(untitledFile, contents, meta); }); test('should restore the original contents (text file)', async () => { @@ -283,20 +316,172 @@ suite('BackupFileService', () => { 'Lorem ipsum ', 'dolor öäü sit amet ', 'consectetur ', - 'adipiscing ßß elit', + 'adipiscing ßß elit' ].join(''); - await service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)); - - const factory = await service.resolveBackupContent(service.toBackupResource(untitledFile)); - assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + await testResolveBackup(fooFile, contents); }); + + test('should restore the original contents (text file - custom scheme)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'consectetur ', + 'adipiscing ßß elit' + ].join(''); + + await testResolveBackup(customFile, contents); + }); + + test('should restore the original contents (text file with metadata)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooFile, contents, meta); + }); + + test('should restore the original contents (text file with metadata changed once)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooFile, contents, meta); + + // Change meta and test again + meta.size = 999; + await testResolveBackup(fooFile, contents, meta); + }); + + test('should restore the original contents (text file with broken metadata)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta); + + const fileContents = fs.readFileSync(fooBackupPath).toString(); + assert.equal(fileContents.indexOf(fooFile.toString()), 0); + + const metaIndex = fileContents.indexOf('{'); + const newFileContents = fileContents.substring(0, metaIndex) + '{{' + fileContents.substr(metaIndex); + fs.writeFileSync(fooBackupPath, newFileContents); + + const backup = await service.resolveBackupContent(service.toBackupResource(fooFile)); + assert.equal(contents, snapshotToString(backup.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + assert.ok(!backup.meta); + }); + + test('should restore the original contents (text file with space in name with metadata)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooBarFile, contents, meta); + }); + + test('should restore the original contents (text file with too large metadata to persist)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: (new Array(100 * 1024)).join('Large String'), + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooBarFile, contents, meta, null); + }); + + async function testResolveBackup(resource: Uri, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) { + if (typeof expectedMeta === 'undefined') { + expectedMeta = meta; + } + + await service.backupResource(resource, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta); + + assert.ok(service.loadBackupResource(resource)); + + const backup = await service.resolveBackupContent(service.toBackupResource(resource)); + assert.equal(contents, snapshotToString(backup.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + + if (expectedMeta) { + assert.equal(backup.meta!.etag, expectedMeta.etag); + assert.equal(backup.meta!.size, expectedMeta.size); + assert.equal(backup.meta!.mtime, expectedMeta.mtime); + assert.equal(backup.meta!.orphaned, expectedMeta.orphaned); + } else { + assert.ok(!backup.meta); + } + } }); }); suite('BackupFilesModel', () => { + + let service: TestBackupFileService; + + setup(async () => { + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + + // Delete any existing backups completely and then re-create it. + await pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + await pfs.mkdirp(backupHome); + + return pfs.writeFile(workspacesJsonPath, ''); + }); + + teardown(() => { + return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + }); + test('simple', () => { - const model = new BackupFilesModel(); + const model = new BackupFilesModel(service.fileService); const resource1 = Uri.file('test.html'); @@ -307,6 +492,7 @@ suite('BackupFilesModel', () => { assert.equal(model.has(resource1), true); assert.equal(model.has(resource1, 0), true); assert.equal(model.has(resource1, 1), false); + assert.equal(model.has(resource1, 1, { foo: 'bar' }), false); model.remove(resource1); @@ -334,25 +520,28 @@ suite('BackupFilesModel', () => { model.add(resource2); model.add(resource3); - model.add(resource4); + model.add(resource4, undefined, { foo: 'bar' }); assert.equal(model.has(resource1), true); assert.equal(model.has(resource2), true); assert.equal(model.has(resource3), true); + assert.equal(model.has(resource4), true); + assert.equal(model.has(resource4, undefined, { foo: 'bar' }), true); + assert.equal(model.has(resource4, undefined, { bar: 'foo' }), false); }); test('resolve', async () => { await pfs.mkdirp(path.dirname(fooBackupPath)); fs.writeFileSync(fooBackupPath, 'foo'); - const model = new BackupFilesModel(); + const model = new BackupFilesModel(service.fileService); const resolvedModel = await model.resolve(workspaceBackupPath); assert.equal(resolvedModel.has(Uri.file(fooBackupPath)), true); }); test('get', () => { - const model = new BackupFilesModel(); + const model = new BackupFilesModel(service.fileService); assert.deepEqual(model.get(), []); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index c0f46503577..e0d3749daa4 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -32,6 +32,13 @@ import { isEqual, isEqualOrParent, extname, basename } from 'vs/base/common/reso import { onUnexpectedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; +export interface IBackupMetaData { + mtime?: number; + size?: number; + etag?: string; + orphaned?: boolean; +} + /** * The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk. */ @@ -210,6 +217,26 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.versionId; } + async backup(target = this.resource): Promise { + const snapshot = this.createSnapshot(); + if (!snapshot) { + return Promise.resolve(); // should not happen + } + + // Only fill in model metadata if resource matches + let meta: IBackupMetaData | undefined = undefined; + if (isEqual(target, this.resource) && this.lastResolvedDiskStat) { + meta = { + mtime: this.lastResolvedDiskStat.mtime, + size: this.lastResolvedDiskStat.size, + etag: this.lastResolvedDiskStat.etag, + orphaned: this.inOrphanMode + }; + } + + return this.backupFileService.backupResource(target, snapshot, this.versionId, meta); + } + async revert(soft?: boolean): Promise { if (!this.isResolved()) { return Promise.resolve(undefined); @@ -280,22 +307,38 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private async loadFromBackup(backup: URI, options?: ILoadOptions): Promise { // Resolve actual backup contents - const backupContent = await this.backupFileService.resolveBackupContent(backup); + const resolvedBackup = await this.backupFileService.resolveBackupContent(backup); if (this.textEditorModel) { return this; // Make sure meanwhile someone else did not suceed in loading } - return this.loadFromContent({ + // Resolve backup metadata from before + const backupMetadata: Required = { + mtime: resolvedBackup.meta && typeof resolvedBackup.meta.mtime === 'number' ? resolvedBackup.meta.mtime : Date.now(), + size: resolvedBackup.meta && typeof resolvedBackup.meta.size === 'number' ? resolvedBackup.meta.size : 0, + etag: resolvedBackup.meta && typeof resolvedBackup.meta.etag === 'string' ? resolvedBackup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! + orphaned: resolvedBackup.meta && resolvedBackup.meta.orphaned ? true : false + }; + + // Load with backup + this.loadFromContent({ resource: this.resource, name: basename(this.resource), - mtime: Date.now(), - size: 0, - etag: ETAG_DISABLED, // always allow to save content restored from a backup (see https://github.com/Microsoft/vscode/issues/72343) - value: backupContent, + mtime: backupMetadata.mtime, + size: backupMetadata.size, + etag: backupMetadata.etag, + value: resolvedBackup.value, encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, isReadonly: false }, options, true /* from backup */); + + // Restore orphaned flag based on state + if (backupMetadata.orphaned) { + this.setOrphaned(true); + } + + return this; } private async loadFromFile(options?: ILoadOptions): Promise { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 1c513e1bca5..59692243f38 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -238,26 +238,12 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private async doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise { // Handle file resources first - await Promise.all(dirtyFileModels.map(async model => { - const snapshot = model.createSnapshot(); - if (snapshot) { - await this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId()); - } - })); + await Promise.all(dirtyFileModels.map(async model => await model.backup())); // Handle untitled resources - const untitledModelPromises = untitledResources + await Promise.all(untitledResources .filter(untitled => this.untitledEditorService.exists(untitled)) - .map(untitled => this.untitledEditorService.loadOrCreate({ resource: untitled })); - - const untitledModels = await Promise.all(untitledModelPromises); - - await Promise.all(untitledModels.map(async model => { - const snapshot = model.createSnapshot(); - if (snapshot) { - await this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId()); - } - })); + .map(async untitled => (await this.untitledEditorService.loadOrCreate({ resource: untitled })).backup())); } private confirmBeforeShutdown(): boolean | Promise { @@ -503,10 +489,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer dirtyTargetModelUris.push(targetModelResource); // Backup dirty source model to the target resource it will become later - const snapshot = sourceModel.createSnapshot(); - if (snapshot) { - await this.backupFileService.backupResource(targetModelResource, snapshot, sourceModel.getVersionId()); - } + await sourceModel.backup(targetModelResource); })); } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 422ea39499b..1c24bbbf139 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -464,6 +464,8 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport revert(soft?: boolean): Promise; + backup(target?: URI): Promise; + createSnapshot(): ITextSnapshot | null; isDirty(): boolean; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 9cd91b50fa6..411f524fd5e 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -15,7 +15,7 @@ import { ConfirmResult, IEditorInputWithOptions, CloseDirection, IEditorIdentifi import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { Event, Emitter } from 'vs/base/common/event'; import Severity from 'vs/base/common/severity'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; @@ -1084,7 +1084,7 @@ export class TestBackupFileService implements IBackupFileService { throw new Error('not implemented'); } - public backupResource(_resource: URI, _content: ITextSnapshot): Promise { + public backupResource(_resource: URI, _content: ITextSnapshot, versionId?: number, meta?: T): Promise { return Promise.resolve(); } @@ -1099,7 +1099,7 @@ export class TestBackupFileService implements IBackupFileService { return textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); } - public resolveBackupContent(_backup: URI): Promise { + public resolveBackupContent(_backup: URI): Promise> { throw new Error('not implemented'); } From aad3a227877501dea24897f12c03c9592aa532e3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 1 May 2019 09:21:48 +0200 Subject: [PATCH 06/99] hot exit - polish --- .../services/backup/node/backupFileService.ts | 21 ++++---- .../test/node/backupFileService.test.ts | 48 +++++++++---------- .../textfile/common/textFileEditorModel.ts | 28 ++++------- .../textfile/common/textFileService.ts | 2 +- 4 files changed, 45 insertions(+), 54 deletions(-) diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index 2361e120711..47c85db30d7 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { join } from 'vs/base/common/path'; +import { joinPath } from 'vs/base/common/resources'; import { createHash } from 'crypto'; import { URI } from 'vs/base/common/uri'; import { coalesce } from 'vs/base/common/arrays'; @@ -23,7 +24,7 @@ import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/text import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export interface IBackupFilesModel { - resolve(backupRoot: string): Promise; + resolve(backupRoot: URI): Promise; add(resource: URI, versionId?: number, meta?: object): void; has(resource: URI, versionId?: number, meta?: object): boolean; @@ -43,13 +44,13 @@ export class BackupFilesModel implements IBackupFilesModel { constructor(private fileService: IFileService) { } - async resolve(backupRoot: string): Promise { + async resolve(backupRoot: URI): Promise { try { - const backupRootStat = await this.fileService.resolve(URI.file(backupRoot)); + const backupRootStat = await this.fileService.resolve(backupRoot); if (backupRootStat.children) { await Promise.all(backupRootStat.children - .filter(child => child.isDirectory). - map(async backupSchema => { + .filter(child => child.isDirectory) + .map(async backupSchema => { // Read backup directory for backups const backupSchemaStat = await this.fileService.resolve(backupSchema.resource); @@ -170,7 +171,7 @@ class BackupFileServiceImpl implements IBackupFileService { _serviceBrand: any; - private backupWorkspacePath: string; + private backupWorkspacePath: URI; private isShuttingDown: boolean; private ready: Promise; @@ -187,7 +188,7 @@ class BackupFileServiceImpl implements IBackupFileService { } initialize(backupWorkspacePath: string): void { - this.backupWorkspacePath = backupWorkspacePath; + this.backupWorkspacePath = URI.file(backupWorkspacePath); this.ready = this.init(); } @@ -268,7 +269,7 @@ class BackupFileServiceImpl implements IBackupFileService { const model = await this.ready; - await this.fileService.del(URI.file(this.backupWorkspacePath), { recursive: true }); + await this.fileService.del(this.backupWorkspacePath, { recursive: true }); model.clear(); } @@ -300,7 +301,7 @@ class BackupFileServiceImpl implements IBackupFileService { async resolveBackupContent(backup: URI): Promise> { // Metadata extraction - let metaRaw: string = ''; + let metaRaw = ''; let metaEndFound = false; // Add a filter method to filter out everything until the meta end marker @@ -345,7 +346,7 @@ class BackupFileServiceImpl implements IBackupFileService { } toBackupResource(resource: URI): URI { - return URI.file(join(this.backupWorkspacePath, resource.scheme, hashPath(resource))); + return joinPath(this.backupWorkspacePath, resource.scheme, hashPath(resource)); } } diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts index e9d2eb116ad..26123ee56e5 100644 --- a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -10,7 +10,7 @@ import * as os from 'os'; import * as fs from 'fs'; import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService'; import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; @@ -29,13 +29,13 @@ const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice' const backupHome = path.join(parentDir, 'Backups'); const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); -const workspaceResource = Uri.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); +const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource)); -const fooFile = Uri.file(platform.isWindows ? 'c:\\Foo' : '/Foo'); -const customFile = Uri.parse('customScheme://some/path'); -const barFile = Uri.file(platform.isWindows ? 'c:\\Bar' : '/Bar'); -const fooBarFile = Uri.file(platform.isWindows ? 'c:\\Foo Bar' : '/Foo Bar'); -const untitledFile = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); +const fooFile = URI.file(platform.isWindows ? 'c:\\Foo' : '/Foo'); +const customFile = URI.parse('customScheme://some/path'); +const barFile = URI.file(platform.isWindows ? 'c:\\Bar' : '/Bar'); +const fooBarFile = URI.file(platform.isWindows ? 'c:\\Foo Bar' : '/Foo Bar'); +const untitledFile = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); const fooBackupPath = path.join(workspaceBackupPath, 'file', hashPath(fooFile)); const barBackupPath = path.join(workspaceBackupPath, 'file', hashPath(barFile)); const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(untitledFile)); @@ -60,7 +60,7 @@ class TestBackupFileService extends BackupFileService { readonly fileService: IFileService; - constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { + constructor(workspace: URI, backupHome: string, workspacesJsonPath: string) { const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const environmentService = new TestBackupEnvironmentService(workspaceBackupPath); @@ -70,7 +70,7 @@ class TestBackupFileService extends BackupFileService { this.fileService = fileService; } - toBackupResource(resource: Uri): Uri { + toBackupResource(resource: URI): URI { return super.toBackupResource(resource); } } @@ -94,7 +94,7 @@ suite('BackupFileService', () => { suite('hashPath', () => { test('should correctly hash the path for untitled scheme URIs', () => { - const uri = Uri.from({ + const uri = URI.from({ scheme: 'untitled', path: 'Untitled-1' }); @@ -105,7 +105,7 @@ suite('BackupFileService', () => { }); test('should correctly hash the path for file scheme URIs', () => { - const uri = Uri.file('/foo'); + const uri = URI.file('/foo'); const actual = hashPath(uri); // If these hashes change people will lose their backed up files! if (platform.isWindows) { @@ -123,16 +123,16 @@ suite('BackupFileService', () => { const backupResource = fooFile; const workspaceHash = hashPath(workspaceResource); const filePathHash = hashPath(backupResource); - const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath; + const expectedPath = URI.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath; assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); }); test('should get the correct backup path for untitled files', () => { // Format should be: /// - const backupResource = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); + const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); const workspaceHash = hashPath(workspaceResource); const filePathHash = hashPath(backupResource); - const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath; + const expectedPath = URI.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath; assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); }); }); @@ -438,7 +438,7 @@ suite('BackupFileService', () => { await testResolveBackup(fooBarFile, contents, meta, null); }); - async function testResolveBackup(resource: Uri, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) { + async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) { if (typeof expectedMeta === 'undefined') { expectedMeta = meta; } @@ -483,7 +483,7 @@ suite('BackupFilesModel', () => { test('simple', () => { const model = new BackupFilesModel(service.fileService); - const resource1 = Uri.file('test.html'); + const resource1 = URI.file('test.html'); assert.equal(model.has(resource1), false); @@ -514,9 +514,9 @@ suite('BackupFilesModel', () => { assert.equal(model.has(resource1, 0), false); assert.equal(model.has(resource1, 1), true); - const resource2 = Uri.file('test1.html'); - const resource3 = Uri.file('test2.html'); - const resource4 = Uri.file('test3.html'); + const resource2 = URI.file('test1.html'); + const resource3 = URI.file('test2.html'); + const resource4 = URI.file('test3.html'); model.add(resource2); model.add(resource3); @@ -536,8 +536,8 @@ suite('BackupFilesModel', () => { fs.writeFileSync(fooBackupPath, 'foo'); const model = new BackupFilesModel(service.fileService); - const resolvedModel = await model.resolve(workspaceBackupPath); - assert.equal(resolvedModel.has(Uri.file(fooBackupPath)), true); + const resolvedModel = await model.resolve(URI.file(workspaceBackupPath)); + assert.equal(resolvedModel.has(URI.file(fooBackupPath)), true); }); test('get', () => { @@ -545,9 +545,9 @@ suite('BackupFilesModel', () => { assert.deepEqual(model.get(), []); - const file1 = Uri.file('/root/file/foo.html'); - const file2 = Uri.file('/root/file/bar.html'); - const untitled = Uri.file('/root/untitled/bar.html'); + const file1 = URI.file('/root/file/foo.html'); + const file2 = URI.file('/root/file/bar.html'); + const untitled = URI.file('/root/untitled/bar.html'); model.add(file1); model.add(file2); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index e0d3749daa4..bd366488118 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -33,10 +33,10 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; export interface IBackupMetaData { - mtime?: number; - size?: number; - etag?: string; - orphaned?: boolean; + mtime: number; + size: number; + etag: string; + orphaned: boolean; } /** @@ -291,9 +291,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (backup) { try { - await this.loadFromBackup(backup, options); - - return this; + return await this.loadFromBackup(backup, options); } catch (error) { // ignore error and continue to load as file below } @@ -313,28 +311,20 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this; // Make sure meanwhile someone else did not suceed in loading } - // Resolve backup metadata from before - const backupMetadata: Required = { - mtime: resolvedBackup.meta && typeof resolvedBackup.meta.mtime === 'number' ? resolvedBackup.meta.mtime : Date.now(), - size: resolvedBackup.meta && typeof resolvedBackup.meta.size === 'number' ? resolvedBackup.meta.size : 0, - etag: resolvedBackup.meta && typeof resolvedBackup.meta.etag === 'string' ? resolvedBackup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! - orphaned: resolvedBackup.meta && resolvedBackup.meta.orphaned ? true : false - }; - // Load with backup this.loadFromContent({ resource: this.resource, name: basename(this.resource), - mtime: backupMetadata.mtime, - size: backupMetadata.size, - etag: backupMetadata.etag, + mtime: resolvedBackup.meta ? resolvedBackup.meta.mtime : Date.now(), + size: resolvedBackup.meta ? resolvedBackup.meta.size : 0, + etag: resolvedBackup.meta ? resolvedBackup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! value: resolvedBackup.value, encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, isReadonly: false }, options, true /* from backup */); // Restore orphaned flag based on state - if (backupMetadata.orphaned) { + if (resolvedBackup.meta && resolvedBackup.meta.orphaned) { this.setOrphaned(true); } diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 59692243f38..ee9ca6449b4 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -238,7 +238,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private async doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise { // Handle file resources first - await Promise.all(dirtyFileModels.map(async model => await model.backup())); + await Promise.all(dirtyFileModels.map(model => model.backup())); // Handle untitled resources await Promise.all(untitledResources From e3fae473d231c359f87942258a2dc53e38c90ebc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 1 May 2019 11:40:32 +0200 Subject: [PATCH 07/99] hot exit - use # as separator for better forwards compatibilty --- .../workbench/services/backup/node/backupFileService.ts | 8 ++++---- .../services/backup/test/node/backupFileService.test.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index 47c85db30d7..19451cd1d8e 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -82,11 +82,11 @@ export class BackupFilesModel implements IBackupFilesModel { return false; // unknown resource } - if (typeof versionId === 'number' && typeof entry.versionId === 'number' && entry.versionId !== versionId) { + if (typeof versionId === 'number' && versionId !== entry.versionId) { return false; // different versionId } - if (meta && entry.meta && !equals(meta, entry.meta)) { + if (meta && !equals(meta, entry.meta)) { return false; // different metadata } @@ -166,7 +166,7 @@ export class BackupFileService implements IBackupFileService { class BackupFileServiceImpl implements IBackupFileService { private static readonly PREAMBLE_END_MARKER = '\n'; - private static readonly PREAMBLE_META_START_MARKER = ' '; + private static readonly PREAMBLE_META_START_MARKER = '#'; // using a character that is know to be escaped in a URI as separator private static readonly PREAMBLE_MAX_LENGTH = 10000; _serviceBrand: any; @@ -336,7 +336,7 @@ class BackupFileServiceImpl implements IBackupFileService { const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_START_MARKER); if (metaStartIndex !== -1) { try { - meta = JSON.parse(metaRaw.substr(metaStartIndex)); + meta = JSON.parse(metaRaw.substr(metaStartIndex + 1)); } catch (error) { // ignore JSON parse errors } diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts index 26123ee56e5..8f0d44186f7 100644 --- a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -162,7 +162,7 @@ suite('BackupFileService', () => { await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), undefined, { etag: '678', orphaned: true }); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`); + assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()}#{"etag":"678","orphaned":true}\ntest`); }); test('untitled file', async () => { From 84620b1fea553b2f201ff2da5db54288bf8a7a43 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 1 May 2019 12:03:01 +0200 Subject: [PATCH 08/99] hot exit - back to ' ' as separator --- .../services/backup/node/backupFileService.ts | 8 +++--- .../test/node/backupFileService.test.ts | 25 +++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index 19451cd1d8e..850fe4b103a 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -166,7 +166,7 @@ export class BackupFileService implements IBackupFileService { class BackupFileServiceImpl implements IBackupFileService { private static readonly PREAMBLE_END_MARKER = '\n'; - private static readonly PREAMBLE_META_START_MARKER = '#'; // using a character that is know to be escaped in a URI as separator + private static readonly PREAMBLE_META_SEPARATOR = ' '; // using a character that is know to be escaped in a URI as separator private static readonly PREAMBLE_MAX_LENGTH = 10000; _serviceBrand: any; @@ -234,7 +234,7 @@ class BackupFileServiceImpl implements IBackupFileService { // With Metadata: URI + META-START + Meta + END if (meta) { - const preambleWithMeta = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_META_START_MARKER}${JSON.stringify(meta)}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; + const preambleWithMeta = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_META_SEPARATOR}${JSON.stringify(meta)}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; if (preambleWithMeta.length < BackupFileServiceImpl.PREAMBLE_MAX_LENGTH) { preamble = preambleWithMeta; } @@ -284,7 +284,7 @@ class BackupFileServiceImpl implements IBackupFileService { } // Preamble with metadata: URI + META-START + Meta + END - const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_START_MARKER); + const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); if (metaStartIndex > 0) { return URI.parse(backupPreamble.substring(0, metaStartIndex)); } @@ -333,7 +333,7 @@ class BackupFileServiceImpl implements IBackupFileService { factory.getFirstLineText(1); let meta: T | undefined; - const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_START_MARKER); + const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); if (metaStartIndex !== -1) { try { meta = JSON.parse(metaRaw.substr(metaStartIndex + 1)); diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts index 8f0d44186f7..52a5bc95d1c 100644 --- a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -33,6 +33,7 @@ const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/work const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource)); const fooFile = URI.file(platform.isWindows ? 'c:\\Foo' : '/Foo'); const customFile = URI.parse('customScheme://some/path'); +const customFileWithFragment = URI.parse('customScheme2://some/path#fragment'); const barFile = URI.file(platform.isWindows ? 'c:\\Bar' : '/Bar'); const fooBarFile = URI.file(platform.isWindows ? 'c:\\Foo Bar' : '/Foo Bar'); const untitledFile = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); @@ -162,7 +163,7 @@ suite('BackupFileService', () => { await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), undefined, { etag: '678', orphaned: true }); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()}#{"etag":"678","orphaned":true}\ntest`); + assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`); }); test('untitled file', async () => { @@ -390,6 +391,8 @@ suite('BackupFileService', () => { await service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta); + assert.ok(await service.loadBackupResource(fooFile)); + const fileContents = fs.readFileSync(fooBackupPath).toString(); assert.equal(fileContents.indexOf(fooFile.toString()), 0); @@ -402,6 +405,24 @@ suite('BackupFileService', () => { assert.ok(!backup.meta); }); + test('should restore the original contents (text file with metadata and fragment URI)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(customFileWithFragment, contents, meta); + }); + test('should restore the original contents (text file with space in name with metadata)', async () => { const contents = [ 'Lorem ipsum ', @@ -445,7 +466,7 @@ suite('BackupFileService', () => { await service.backupResource(resource, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta); - assert.ok(service.loadBackupResource(resource)); + assert.ok(await service.loadBackupResource(resource)); const backup = await service.resolveBackupContent(service.toBackupResource(resource)); assert.equal(contents, snapshotToString(backup.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); From abd57bb06599bb487f2ffe41218ffbe4292d8f51 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 3 May 2019 09:48:11 +0200 Subject: [PATCH 09/99] text model - improve null handling (fixes #70260) --- .../editor/common/services/resolverService.ts | 8 +- .../standalone/browser/simpleServices.ts | 6 +- .../common/editor/textEditorModel.ts | 7 +- .../common/editor/untitledEditorModel.ts | 7 +- .../textfile/common/textFileEditorModel.ts | 80 +++++++++---------- .../textfile/common/textFileService.ts | 17 ++-- .../services/textfile/common/textfiles.ts | 17 ++-- 7 files changed, 70 insertions(+), 72 deletions(-) diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index 442813334d5..958469e16f1 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -5,7 +5,7 @@ import { IDisposable, IReference } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { ITextModel } from 'vs/editor/common/model'; +import { ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -46,6 +46,12 @@ export interface ITextEditorModel extends IEditorModel { */ readonly textEditorModel: ITextModel | null; + /** + * Creates a snapshot of the model's contents. + */ + createSnapshot(this: IResolvedTextEditorModel): ITextSnapshot; + createSnapshot(this: ITextEditorModel): ITextSnapshot | null; + isReadonly(): boolean; } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index b6468cddfc1..349f1403657 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -19,7 +19,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position as Pos } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; +import { ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { TextEdit, WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -67,6 +67,10 @@ export class SimpleModel implements IResolvedTextEditorModel { return this.model; } + public createSnapshot(): ITextSnapshot { + return this.model.createSnapshot(); + } + public isReadonly(): boolean { return false; } diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 0d7f807b4d1..9f01a364979 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -125,10 +125,11 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd this.modelService.updateModel(this.textEditorModel, newValue); } + createSnapshot(this: IResolvedTextEditorModel): ITextSnapshot; + createSnapshot(this: ITextEditorModel): ITextSnapshot | null; createSnapshot(): ITextSnapshot | null { - const model = this.textEditorModel; - if (model) { - return model.createSnapshot(true /* Preserve BOM */); + if (this.isResolved()) { + return this.textEditorModel.createSnapshot(true /* preserve BOM */); } return null; diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 3488d2e67e5..9d757f2279e 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -135,12 +135,11 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin } backup(): Promise { - const snapshot = this.createSnapshot(); - if (!snapshot) { - return Promise.resolve(); // should not happen + if (this.isResolved()) { + return this.backupFileService.backupResource(this.resource, this.createSnapshot(), this.versionId); } - return this.backupFileService.backupResource(this.resource, snapshot, this.versionId); + return Promise.resolve(); } load(): Promise { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index bd366488118..599ab2c3687 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -203,7 +203,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private onFilesAssociationChange(): void { - if (!this.textEditorModel) { + if (!this.isResolved()) { return; } @@ -213,28 +213,24 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.modelService.setMode(this.textEditorModel, languageSelection); } - getVersionId(): number { - return this.versionId; - } - async backup(target = this.resource): Promise { - const snapshot = this.createSnapshot(); - if (!snapshot) { - return Promise.resolve(); // should not happen + if (this.isResolved()) { + + // Only fill in model metadata if resource matches + let meta: IBackupMetaData | undefined = undefined; + if (isEqual(target, this.resource) && this.lastResolvedDiskStat) { + meta = { + mtime: this.lastResolvedDiskStat.mtime, + size: this.lastResolvedDiskStat.size, + etag: this.lastResolvedDiskStat.etag, + orphaned: this.inOrphanMode + }; + } + + return this.backupFileService.backupResource(target, this.createSnapshot(), this.versionId, meta); } - // Only fill in model metadata if resource matches - let meta: IBackupMetaData | undefined = undefined; - if (isEqual(target, this.resource) && this.lastResolvedDiskStat) { - meta = { - mtime: this.lastResolvedDiskStat.mtime, - size: this.lastResolvedDiskStat.size, - etag: this.lastResolvedDiskStat.etag, - orphaned: this.inOrphanMode - }; - } - - return this.backupFileService.backupResource(target, snapshot, this.versionId, meta); + return Promise.resolve(); } async revert(soft?: boolean): Promise { @@ -282,10 +278,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Only for new models we support to load from backup - if (!this.textEditorModel) { + if (!this.isResolved()) { const backup = await this.backupFileService.loadBackupResource(this.resource); - if (this.textEditorModel) { + if (this.isResolved()) { return this; // Make sure meanwhile someone else did not suceed in loading } @@ -307,7 +303,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Resolve actual backup contents const resolvedBackup = await this.backupFileService.resolveBackupContent(backup); - if (this.textEditorModel) { + if (this.isResolved()) { return this; // Make sure meanwhile someone else did not suceed in loading } @@ -419,7 +415,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Update Existing Model - if (this.textEditorModel) { + if (this.isResolved()) { this.doUpdateTextModel(content.value); } @@ -501,7 +497,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // where `value` was captured in the content change listener closure scope. // Content Change - if (this.textEditorModel) { + if (this.isResolved()) { this._register(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); } } @@ -526,7 +522,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // In this case we clear the dirty flag and emit a SAVED event to indicate this state. // Note: we currently only do this check when auto-save is turned off because there you see // a dirty indicator that you want to get rid of when undoing to the saved version. - if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { + if (!this.autoSaveAfterMilliesEnabled && this.isResolved() && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource); // Clear flags @@ -657,7 +653,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Push all edit operations to the undo stack so that the user has a chance to // Ctrl+Z back to the saved version. We only do this when auto-save is turned off - if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel) { + if (!this.autoSaveAfterMilliesEnabled && this.isResolved()) { this.textEditorModel.pushStackElement(); } @@ -687,7 +683,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // saving contents to disk that are stale (see https://github.com/Microsoft/vscode/issues/50942). // To fix this issue, we will not store the contents to disk when we got disposed. if (this.disposed) { - return undefined; + return; + } + + // We require a resolved model from this point on, since we are about to write data to disk. + if (!this.isResolved()) { + return; } // Under certain conditions we do a short-cut of flushing contents to disk when we can assume that @@ -713,11 +714,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) this.logService.trace(`doSave(${versionId}) - before write()`, this.resource); - const snapshot = this.createSnapshot(); - if (!snapshot) { - throw new Error('Invalid snapshot'); - } - return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, { + return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedDiskStat.resource, this.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, overwriteEncoding: options.overwriteEncoding, mtime: this.lastResolvedDiskStat.mtime, @@ -850,12 +847,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doTouch(versionId: number): Promise { - const snapshot = this.createSnapshot(); - if (!snapshot) { - throw new Error('invalid snapshot'); + if (!this.isResolved()) { + return Promise.resolve(); } - return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, { + return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedDiskStat.resource, this.createSnapshot(), { mtime: this.lastResolvedDiskStat.mtime, encoding: this.getEncoding(), etag: this.lastResolvedDiskStat.etag @@ -896,7 +892,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // in order to find out if the model changed back to a saved version (e.g. // when undoing long enough to reach to a version that is saved and then to // clear the dirty flag) - if (this.textEditorModel) { + if (this.isResolved()) { this.bufferSavedVersionId = this.textEditorModel.getAlternativeVersionId(); } } @@ -935,10 +931,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.lastSaveAttemptTime; } - getETag(): string | null { - return this.lastResolvedDiskStat ? this.lastResolvedDiskStat.etag || null : null; - } - hasState(state: ModelState): boolean { switch (state) { case ModelState.CONFLICT: @@ -1020,8 +1012,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return true; } - isResolved(): boolean { - return !isUndefinedOrNull(this.lastResolvedDiskStat); + isResolved(): this is IResolvedTextFileEditorModel { + return !!this.textEditorModel; } isReadonly(): boolean { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index ee9ca6449b4..56008392a74 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -11,7 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; +import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, isResolvedTextEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -855,17 +855,12 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // take over encoding, mode and model value from source model targetModel.updatePreferredEncoding(sourceModel.getEncoding()); - if (targetModel.textEditorModel) { - const snapshot = sourceModel.createSnapshot(); - if (snapshot) { - this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(snapshot)); - } + if (isResolvedTextEditorModel(sourceModel) && isResolvedTextEditorModel(targetModel)) { + this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot())); - if (sourceModel.textEditorModel) { - const language = sourceModel.textEditorModel.getLanguageIdentifier(); - if (language.id > 1) { - targetModel.textEditorModel.setMode(language); // only use if more specific than plain/text - } + const language = sourceModel.textEditorModel.getLanguageIdentifier(); + if (language.id > 1) { + targetModel.textEditorModel.setMode(language); // only use if more specific than plain/text } } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 1c24bbbf139..3b09be969dc 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { ITextBufferFactory, ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; @@ -448,14 +448,10 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport readonly onDidContentChange: Event; readonly onDidStateChange: Event; - getVersionId(): number; - getResource(): URI; hasState(state: ModelState): boolean; - getETag(): string | null; - updatePreferredEncoding(encoding: string): void; save(options?: ISaveOptions): Promise; @@ -466,21 +462,26 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport backup(target?: URI): Promise; - createSnapshot(): ITextSnapshot | null; - isDirty(): boolean; - isResolved(): boolean; + isResolved(): this is IResolvedTextFileEditorModel; isDisposed(): boolean; } export interface IResolvedTextFileEditorModel extends ITextFileEditorModel { + readonly textEditorModel: ITextModel; createSnapshot(): ITextSnapshot; } +export function isResolvedTextEditorModel(model: ITextEditorModel): model is IResolvedTextEditorModel; +export function isResolvedTextEditorModel(model: ITextFileEditorModel): model is IResolvedTextFileEditorModel; +export function isResolvedTextEditorModel(model: ITextEditorModel | ITextFileEditorModel): model is IResolvedTextEditorModel | IResolvedTextFileEditorModel { + return !!model.textEditorModel; +} + export interface IWillMoveEvent { oldResource: URI; newResource: URI; From 4658b3638b8580e61a8d27797590d41455a4f5b9 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 4 May 2019 15:06:11 +0800 Subject: [PATCH 10/99] Fix SVG links in Webview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the links in SVGs in Webviews doesn’t work, because the value of `href` property might be a `SVGAnimatedString`. --- src/vs/workbench/contrib/webview/browser/pre/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 133b3d28edd..2afb9ea3125 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -153,7 +153,7 @@ module.exports = function createWebviewManager(host) { scrollTarget.scrollIntoView(); } } else { - host.postMessage('did-click-link', node.href); + host.postMessage('did-click-link', node.href.baseVal || node.href); } event.preventDefault(); break; From 32dfe5dd55bc69542f3548b45daeb2ab5ccc3423 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 6 May 2019 11:15:22 +0200 Subject: [PATCH 11/99] fix #66338 --- .../textfile/common/textFileEditorModel.ts | 4 ++ .../textfile/common/textFileService.ts | 45 +++++++++---------- .../node/textResourcePropertiesService.ts | 2 +- .../textfile/test/textFileEditorModel.test.ts | 30 ++++++++++++- .../textfile/test/textFileService.test.ts | 6 +-- 5 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 599ab2c3687..2b34169a796 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -859,6 +859,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Updated resolved stat with updated stat since touching it might have changed mtime this.updateLastResolvedDiskStat(stat); + + // Emit File Saved Event + this._onDidStateChange.fire(StateChange.SAVED); + }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 56008392a74..ebd718f913d 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -246,37 +246,36 @@ export abstract class TextFileService extends Disposable implements ITextFileSer .map(async untitled => (await this.untitledEditorService.loadOrCreate({ resource: untitled })).backup())); } - private confirmBeforeShutdown(): boolean | Promise { - return this.confirmSave().then(confirm => { + private async confirmBeforeShutdown(): Promise { + const confirm = await this.confirmSave(); - // Save - if (confirm === ConfirmResult.SAVE) { - return this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }).then(result => { - if (result.results.some(r => !r.success)) { - return true; // veto if some saves failed - } + // Save + if (confirm === ConfirmResult.SAVE) { + const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }); - return this.noVeto({ cleanUpBackups: true }); - }); + if (result.results.some(r => !r.success)) { + return true; // veto if some saves failed } - // Don't Save - else if (confirm === ConfirmResult.DONT_SAVE) { + return this.noVeto({ cleanUpBackups: true }); + } - // Make sure to revert untitled so that they do not restore - // see https://github.com/Microsoft/vscode/issues/29572 - this.untitledEditorService.revertAll(); + // Don't Save + else if (confirm === ConfirmResult.DONT_SAVE) { - return this.noVeto({ cleanUpBackups: true }); - } + // Make sure to revert untitled so that they do not restore + // see https://github.com/Microsoft/vscode/issues/29572 + this.untitledEditorService.revertAll(); - // Cancel - else if (confirm === ConfirmResult.CANCEL) { - return true; // veto - } + return this.noVeto({ cleanUpBackups: true }); + } - return false; - }); + // Cancel + else if (confirm === ConfirmResult.CANCEL) { + return true; // veto + } + + return false; } private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise { diff --git a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts index e9a71315bea..ce097d6fdf5 100644 --- a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts @@ -17,7 +17,7 @@ import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiatio export class TextResourcePropertiesService implements ITextResourcePropertiesService { - _serviceBrand: ServiceIdentifier; + _serviceBrand: ServiceIdentifier; private remoteEnvironment: IRemoteAgentEnvironment | null = null; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index ac6f0370672..f496e66234a 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -44,7 +44,7 @@ suite('Files - TextFileEditorModel', () => { accessor.fileService.setContent(content); }); - test('Save', async function () { + test('save', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); await model.load(); @@ -52,10 +52,38 @@ suite('Files - TextFileEditorModel', () => { model.textEditorModel!.setValue('bar'); assert.ok(getLastModifiedTime(model) <= Date.now()); + let savedEvent = false; + model.onDidStateChange(e => { + if (e === StateChange.SAVED) { + savedEvent = true; + } + }); + await model.save(); assert.ok(model.getLastSaveAttemptTime() <= Date.now()); assert.ok(!model.isDirty()); + assert.ok(savedEvent); + + model.dispose(); + assert.ok(!accessor.modelService.getModel(model.getResource())); + }); + + test('save - touching also emits saved event', async function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + + await model.load(); + + let savedEvent = false; + model.onDidStateChange(e => { + if (e === StateChange.SAVED) { + savedEvent = true; + } + }); + + await model.save({ force: true }); + + assert.ok(savedEvent); model.dispose(); assert.ok(!accessor.modelService.getModel(model.getResource())); diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index 72049db9f11..67fce66d3a0 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -65,7 +65,7 @@ suite('Files - TextFileService', () => { accessor.untitledEditorService.revertAll(); }); - test('confirm onWillShutdown - no veto', function () { + test('confirm onWillShutdown - no veto', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); (accessor.textFileService.models).add(model.getResource(), model); @@ -76,9 +76,7 @@ suite('Files - TextFileService', () => { if (typeof veto === 'boolean') { assert.ok(!veto); } else { - veto.then(veto => { - assert.ok(!veto); - }); + assert.ok(!(await veto)); } }); From 6ce6d6d1c5ecd92fd34a3f5e9d791531f914d81a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 6 May 2019 11:21:06 +0200 Subject: [PATCH 12/99] add integration test for #66338 --- .../src/singlefolder-tests/workspace.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index b3b91a036f5..dc700abdf1b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -287,6 +287,30 @@ suite('workspace-namespace', () => { }); }); + test('events: onDidSaveTextDocument fires even for non dirty file when saved', () => { + return createRandomFile().then(file => { + let disposables: vscode.Disposable[] = []; + + let onDidSaveTextDocument = false; + disposables.push(vscode.workspace.onDidSaveTextDocument(e => { + assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); + onDidSaveTextDocument = true; + })); + + return vscode.workspace.openTextDocument(file).then(doc => { + return vscode.window.showTextDocument(doc).then(() => { + return vscode.commands.executeCommand('workbench.action.files.save').then(() => { + assert.ok(onDidSaveTextDocument); + + disposeAll(disposables); + + return deleteFile(file); + }); + }); + }); + }); + }); + test('openTextDocument, with selection', function () { return createRandomFile('foo\nbar\nbar').then(file => { return vscode.workspace.openTextDocument(file).then(doc => { From 0e02825e42e466f2673c3020ec9ceabf31955c46 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 6 May 2019 15:27:21 +0200 Subject: [PATCH 13/99] fix #31524 --- .../browser/parts/editor/editorStatus.ts | 124 +++++++++++------- src/vs/workbench/common/editor.ts | 14 +- .../common/editor/resourceEditorInput.ts | 19 ++- .../common/editor/textEditorModel.ts | 14 +- .../common/editor/untitledEditorInput.ts | 27 ++-- .../files/common/editors/fileEditorInput.ts | 8 ++ .../test/browser/editorGroupsService.test.ts | 2 + .../editor/test/browser/editorService.test.ts | 2 + .../textfile/common/textFileEditorModel.ts | 12 ++ .../services/textfile/common/textfiles.ts | 4 +- .../test/common/editor/editorGroups.test.ts | 24 +--- 11 files changed, 166 insertions(+), 84 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index b6c197e5b66..805c4314765 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -14,11 +14,11 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { Action } from 'vs/base/common/actions'; import { Language } from 'vs/base/common/platform'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; -import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor'; +import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor, IModeSupport } from 'vs/workbench/common/editor'; import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorAction } from 'vs/editor/common/editorCommon'; -import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; +import { EndOfLineSequence } from 'vs/editor/common/model'; import { IModelLanguageChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/linesOperations'; import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/indentation'; @@ -59,7 +59,15 @@ class SideBySideEditorEncodingSupport implements IEncodingSupport { } setEncoding(encoding: string, mode: EncodingMode): void { - [this.master, this.details].forEach(s => s.setEncoding(encoding, mode)); + [this.master, this.details].forEach(editor => editor.setEncoding(encoding, mode)); + } +} + +class SideBySideEditorModeSupport implements IModeSupport { + constructor(private master: IModeSupport, private details: IModeSupport) { } + + setMode(mode: ILanguageSelection): void { + [this.master, this.details].forEach(editor => editor.setMode(mode)); } } @@ -83,7 +91,7 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu } // File or Resource Editor - let encodingSupport = input as IFileEditorInput; + const encodingSupport = input as IFileEditorInput; if (areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) { return encodingSupport; } @@ -92,14 +100,41 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu return null; } +function toEditorWithModeSupport(input: IEditorInput): IModeSupport | null { + + // Untitled Editor + if (input instanceof UntitledEditorInput) { + return input; + } + + // Side by Side (diff) Editor + if (input instanceof SideBySideEditorInput) { + const masterModeSupport = toEditorWithModeSupport(input.master); + const detailsModeSupport = toEditorWithModeSupport(input.details); + + if (masterModeSupport && detailsModeSupport) { + return new SideBySideEditorModeSupport(masterModeSupport, detailsModeSupport); + } + + return masterModeSupport; + } + + // File or Resource Editor + const modeSupport = input as IFileEditorInput; + if (typeof modeSupport.setMode === 'function') { + return modeSupport; + } + + // Unsupported for any other editor + return null; +} + interface IEditorSelectionStatus { selections?: Selection[]; charactersSelected?: number; } class StateChange { - _stateChangeBrand: void; - indentation: boolean = false; selectionStatus: boolean = false; mode: boolean = false; @@ -120,7 +155,7 @@ class StateChange { this.metadata = this.metadata || other.metadata; } - public hasChanges(): boolean { + hasChanges(): boolean { return this.indentation || this.selectionStatus || this.mode @@ -179,42 +214,49 @@ class State { change.selectionStatus = true; } } + if ('indentation' in update) { if (this._indentation !== update.indentation) { this._indentation = update.indentation; change.indentation = true; } } + if ('mode' in update) { if (this._mode !== update.mode) { this._mode = update.mode; change.mode = true; } } + if ('encoding' in update) { if (this._encoding !== update.encoding) { this._encoding = update.encoding; change.encoding = true; } } + if ('EOL' in update) { if (this._EOL !== update.EOL) { this._EOL = update.EOL; change.EOL = true; } } + if ('tabFocusMode' in update) { if (this._tabFocusMode !== update.tabFocusMode) { this._tabFocusMode = update.tabFocusMode; change.tabFocusMode = true; } } + if ('screenReaderMode' in update) { if (this._screenReaderMode !== update.screenReaderMode) { this._screenReaderMode = update.screenReaderMode; change.screenReaderMode = true; } } + if ('metadata' in update) { if (this._metadata !== update.metadata) { this._metadata = update.metadata; @@ -236,7 +278,6 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus"); const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized"); const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."); - class StatusBarItem { private _showing = true; @@ -248,15 +289,15 @@ class StatusBarItem { this.element.title = title; } - public set textContent(value: string) { + set textContent(value: string) { this.element.textContent = value; } - public set onclick(value: () => void) { + set onclick(value: () => void) { this.element.onclick = value; } - public setVisible(shouldShow: boolean): void { + setVisible(shouldShow: boolean): void { if (shouldShow !== this._showing) { this._showing = shouldShow; this.element.style.display = shouldShow ? '' : 'none'; @@ -264,7 +305,6 @@ class StatusBarItem { } } - export class EditorStatus implements IStatusbarItem { private state: State; private element: HTMLElement; @@ -661,7 +701,7 @@ export class EditorStatus implements IStatusbarItem { this.updateState(update); } - private _promptedScreenReader: boolean = false; + private promptedScreenReader: boolean = false; private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void { let screenReaderMode = false; @@ -673,8 +713,8 @@ export class EditorStatus implements IStatusbarItem { const screenReaderConfiguration = this.configurationService.getValue('editor').accessibilitySupport; if (screenReaderConfiguration === 'auto') { // show explanation - if (!this._promptedScreenReader) { - this._promptedScreenReader = true; + if (!this.promptedScreenReader) { + this.promptedScreenReader = true; setTimeout(() => { this.onScreenReaderModeClick(); }, 100); @@ -948,42 +988,27 @@ export class ChangeModeAction extends Action { // Change mode for active editor const activeEditor = this.editorService.activeEditor; - const activeTextEditorWidget = this.editorService.activeTextEditorWidget; - const models: ITextModel[] = []; - if (isCodeEditor(activeTextEditorWidget)) { - const codeEditorModel = activeTextEditorWidget.getModel(); - if (codeEditorModel) { - models.push(codeEditorModel); - } - } else if (isDiffEditor(activeTextEditorWidget)) { - const diffEditorModel = activeTextEditorWidget.getModel(); - if (diffEditorModel) { - if (diffEditorModel.original) { - models.push(diffEditorModel.original); - } - if (diffEditorModel.modified) { - models.push(diffEditorModel.modified); - } - } - } + if (activeEditor) { + const modeSupport = toEditorWithModeSupport(activeEditor); + if (modeSupport) { - // Find mode - let languageSelection: ILanguageSelection | undefined; - if (pick === autoDetectMode) { - if (textModel) { - const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); - if (resource) { - languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1)); + // Find mode + let languageSelection: ILanguageSelection | undefined; + if (pick === autoDetectMode) { + if (textModel) { + const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + if (resource) { + languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1)); + } + } + } else { + languageSelection = this.modeService.createByLanguageName(pick.label); } - } - } else { - languageSelection = this.modeService.createByLanguageName(pick.label); - } - // Change mode - if (typeof languageSelection !== 'undefined') { - for (const textModel of models) { - this.modelService.setMode(textModel, languageSelection); + // Change mode + if (typeof languageSelection !== 'undefined') { + modeSupport.setMode(languageSelection); + } } } }); @@ -1159,6 +1184,7 @@ export class ChangeEncodingAction extends Action { if (!activeControl) { return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } + const encodingSupport: IEncodingSupport | null = toEditorWithEncodingSupport(activeControl.input); if (!encodingSupport) { return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]); @@ -1249,10 +1275,12 @@ export class ChangeEncodingAction extends Action { if (!encoding) { return; } + const activeControl = this.editorService.activeControl; if (!activeControl) { return; } + const encodingSupport = toEditorWithEncodingSupport(activeControl.input); if (typeof encoding.id !== 'undefined' && encodingSupport && encodingSupport.getEncoding() !== encoding.id) { encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 80915b758e2..bed6606f63b 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -9,6 +9,7 @@ import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -505,12 +506,23 @@ export interface IEncodingSupport { setEncoding(encoding: string, mode: EncodingMode): void; } +export interface IModeSupport { + + /** + * Sets the language mode of the input. + */ + setMode(languageSelection: ILanguageSelection): void; +} + /** * This is a tagging interface to declare an editor input being capable of dealing with files. It is only used in the editor registry * to register this kind of input to the platform. */ -export interface IFileEditorInput extends IEditorInput, IEncodingSupport { +export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeSupport { + /** + * Gets the resource this editor is about. + */ getResource(): URI; /** diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 7fed290b43b..c9edfc1b656 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -3,17 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorInput, ITextEditorModel } from 'vs/workbench/common/editor'; +import { EditorInput, ITextEditorModel, IModeSupport } 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 { ILanguageSelection } from 'vs/editor/common/services/modeService'; /** * A read-only text editor input whos contents are made of the provided resource that points to an existing * code editor model. */ -export class ResourceEditorInput extends EditorInput { +export class ResourceEditorInput extends EditorInput implements IModeSupport { static readonly ID: string = 'workbench.editors.resourceEditorInput'; @@ -62,6 +63,18 @@ export class ResourceEditorInput extends EditorInput { } } + setMode(mode: ILanguageSelection): void { + if (!this.modelReference) { + return; + } + + this.modelReference.then(ref => { + if (ref.object instanceof ResourceEditorModel) { + ref.object.setMode(mode); + } + }); + } + resolve(): Promise { if (!this.modelReference) { this.modelReference = this.textModelResolverService.createModelReference(this.resource); @@ -87,7 +100,7 @@ export class ResourceEditorInput extends EditorInput { } if (otherInput instanceof ResourceEditorInput) { - let otherResourceEditorInput = otherInput; + const otherResourceEditorInput = otherInput; // Compare by properties return otherResourceEditorInput.resource.toString() === this.resource.toString(); diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 9f01a364979..0df0c1046c9 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITextModel, ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; -import { EditorModel } from 'vs/workbench/common/editor'; +import { EditorModel, IModeSupport } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; @@ -14,7 +14,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; /** * The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated. */ -export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel { +export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel, IModeSupport { protected createdEditorModel: boolean; @@ -64,6 +64,14 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd abstract isReadonly(): boolean; + setMode(languageSelection: ILanguageSelection): void { + if (!this.isResolved()) { + return; + } + + this.modelService.setMode(this.textEditorModel, languageSelection); + } + /** * Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL. */ @@ -118,7 +126,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd * Updates the text editor model with the provided value. If the value is the same as the model has, this is a no-op. */ protected updateTextEditorModel(newValue: ITextBufferFactory): void { - if (!this.textEditorModel) { + if (!this.isResolved()) { return; } diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index dea43a4c322..f67e89dd526 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -9,18 +9,19 @@ import { memoize } from 'vs/base/common/decorators'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { basename } from 'vs/base/common/path'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; -import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity } from 'vs/workbench/common/editor'; +import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity, IModeSupport } from 'vs/workbench/common/editor'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILabelService } from 'vs/platform/label/common/label'; import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; /** * An editor input to be used for untitled text buffers. */ -export class UntitledEditorInput extends EditorInput implements IEncodingSupport { +export class UntitledEditorInput extends EditorInput implements IEncodingSupport, IModeSupport { static readonly ID: string = 'workbench.editors.untitledEditorInput'; @@ -58,14 +59,6 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport return this.resource; } - getModeId(): string | null { - if (this.cachedModel) { - return this.cachedModel.getModeId(); - } - - return this.modeId; - } - getName(): string { return this.hasAssociatedFilePath ? basenameOrAuthority(this.resource) : this.resource.path; } @@ -194,6 +187,20 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport } } + setMode(mode: ILanguageSelection): void { + if (this.cachedModel) { + this.cachedModel.setMode(mode); + } + } + + getModeId(): string | null { + if (this.cachedModel) { + return this.cachedModel.getModeId(); + } + + return this.modeId; + } + resolve(): Promise { // Join a model resolve if we have had one before diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 2c5d1068a20..d5b5d459b16 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -18,6 +18,7 @@ import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { FILE_EDITOR_INPUT_ID, TEXT_FILE_EDITOR_ID, BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; /** * A file editor input is the input type for the file editor of file system resources. @@ -97,6 +98,13 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } } + setMode(mode: ILanguageSelection): void { + const textModel = this.textFileService.models.get(this.resource); + if (textModel) { + textModel.setMode(mode); + } + } + setPreferredEncoding(encoding: string): void { this.preferredEncoding = encoding; this.forceOpenAsText = true; // encoding is a good hint to open the file as text diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 0fe83803a0c..96db7068f60 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -19,6 +19,7 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; export class TestEditorControl extends BaseEditor { @@ -45,6 +46,7 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } + setMode(mode: ILanguageSelection) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } } diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index a670ff43ae3..c7a7ed45062 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -29,6 +29,7 @@ import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; export class TestEditorControl extends BaseEditor { @@ -56,6 +57,7 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } + setMode(mode: ILanguageSelection) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } setFailToOpen(): void { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 2b34169a796..6abd61eb3c6 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -66,6 +66,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private contentEncoding: string; // encoding as reported from disk private preferredEncoding: string; // encoding as chosen by the user + private preferredMode: ILanguageSelection; // mode as chosen by the user + private versionId: number; private bufferSavedVersionId: number; private blockModelContentChange: boolean; @@ -207,12 +209,22 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return; } + if (this.preferredMode) { + return; // do not override user choice + } + const firstLineText = this.getFirstLineText(this.textEditorModel); const languageSelection = this.getOrCreateMode(this.modeService, undefined, firstLineText); this.modelService.setMode(this.textEditorModel, languageSelection); } + setMode(languageSelection: ILanguageSelection): void { + super.setMode(languageSelection); + + this.preferredMode = languageSelection; + } + async backup(target = this.resource): Promise { if (this.isResolved()) { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 3b09be969dc..40aa181153b 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; +import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; @@ -443,7 +443,7 @@ export interface ILoadOptions { reason?: LoadReason; } -export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport { +export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport { readonly onDidContentChange: Event; readonly onDidStateChange: Event; diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index fde39c097be..9775b42c9ef 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -20,6 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; function inst(): IInstantiationService { let inst = new TestInstantiationService(); @@ -111,27 +112,16 @@ class TestFileEditorInput extends EditorInput implements IFileEditorInput { } getTypeId() { return 'testFileEditorInputForGroups'; } resolve(): Promise { return Promise.resolve(null!); } + setEncoding(encoding: string) { } + getEncoding(): string { return null!; } + setPreferredEncoding(encoding: string) { } + getResource(): URI { return this.resource; } + setForceOpenAsBinary(): void { } + setMode(mode: ILanguageSelection) { } matches(other: TestFileEditorInput): boolean { return other && this.id === other.id && other instanceof TestFileEditorInput; } - - setEncoding(encoding: string) { - } - - getEncoding(): string { - return null!; - } - - setPreferredEncoding(encoding: string) { - } - - getResource(): URI { - return this.resource; - } - - setForceOpenAsBinary(): void { - } } function input(id = String(index++), nonSerializable?: boolean, resource?: URI): EditorInput { From 8deb75622216be51d088cc9b86c01a10fdef728b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 6 May 2019 18:37:10 +0200 Subject: [PATCH 14/99] Revert "fix #31524" This reverts commit 0e02825e42e466f2673c3020ec9ceabf31955c46. --- .../browser/parts/editor/editorStatus.ts | 126 +++++++----------- src/vs/workbench/common/editor.ts | 14 +- .../common/editor/resourceEditorInput.ts | 19 +-- .../common/editor/textEditorModel.ts | 14 +- .../common/editor/untitledEditorInput.ts | 27 ++-- .../files/common/editors/fileEditorInput.ts | 8 -- .../test/browser/editorGroupsService.test.ts | 2 - .../editor/test/browser/editorService.test.ts | 2 - .../textfile/common/textFileEditorModel.ts | 12 -- .../services/textfile/common/textfiles.ts | 4 +- .../test/common/editor/editorGroups.test.ts | 24 +++- 11 files changed, 85 insertions(+), 167 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 805c4314765..b6c197e5b66 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -14,11 +14,11 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { Action } from 'vs/base/common/actions'; import { Language } from 'vs/base/common/platform'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; -import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor, IModeSupport } from 'vs/workbench/common/editor'; +import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor'; import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorAction } from 'vs/editor/common/editorCommon'; -import { EndOfLineSequence } from 'vs/editor/common/model'; +import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { IModelLanguageChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/linesOperations'; import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/indentation'; @@ -59,15 +59,7 @@ class SideBySideEditorEncodingSupport implements IEncodingSupport { } setEncoding(encoding: string, mode: EncodingMode): void { - [this.master, this.details].forEach(editor => editor.setEncoding(encoding, mode)); - } -} - -class SideBySideEditorModeSupport implements IModeSupport { - constructor(private master: IModeSupport, private details: IModeSupport) { } - - setMode(mode: ILanguageSelection): void { - [this.master, this.details].forEach(editor => editor.setMode(mode)); + [this.master, this.details].forEach(s => s.setEncoding(encoding, mode)); } } @@ -91,7 +83,7 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu } // File or Resource Editor - const encodingSupport = input as IFileEditorInput; + let encodingSupport = input as IFileEditorInput; if (areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) { return encodingSupport; } @@ -100,41 +92,14 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu return null; } -function toEditorWithModeSupport(input: IEditorInput): IModeSupport | null { - - // Untitled Editor - if (input instanceof UntitledEditorInput) { - return input; - } - - // Side by Side (diff) Editor - if (input instanceof SideBySideEditorInput) { - const masterModeSupport = toEditorWithModeSupport(input.master); - const detailsModeSupport = toEditorWithModeSupport(input.details); - - if (masterModeSupport && detailsModeSupport) { - return new SideBySideEditorModeSupport(masterModeSupport, detailsModeSupport); - } - - return masterModeSupport; - } - - // File or Resource Editor - const modeSupport = input as IFileEditorInput; - if (typeof modeSupport.setMode === 'function') { - return modeSupport; - } - - // Unsupported for any other editor - return null; -} - interface IEditorSelectionStatus { selections?: Selection[]; charactersSelected?: number; } class StateChange { + _stateChangeBrand: void; + indentation: boolean = false; selectionStatus: boolean = false; mode: boolean = false; @@ -155,7 +120,7 @@ class StateChange { this.metadata = this.metadata || other.metadata; } - hasChanges(): boolean { + public hasChanges(): boolean { return this.indentation || this.selectionStatus || this.mode @@ -214,49 +179,42 @@ class State { change.selectionStatus = true; } } - if ('indentation' in update) { if (this._indentation !== update.indentation) { this._indentation = update.indentation; change.indentation = true; } } - if ('mode' in update) { if (this._mode !== update.mode) { this._mode = update.mode; change.mode = true; } } - if ('encoding' in update) { if (this._encoding !== update.encoding) { this._encoding = update.encoding; change.encoding = true; } } - if ('EOL' in update) { if (this._EOL !== update.EOL) { this._EOL = update.EOL; change.EOL = true; } } - if ('tabFocusMode' in update) { if (this._tabFocusMode !== update.tabFocusMode) { this._tabFocusMode = update.tabFocusMode; change.tabFocusMode = true; } } - if ('screenReaderMode' in update) { if (this._screenReaderMode !== update.screenReaderMode) { this._screenReaderMode = update.screenReaderMode; change.screenReaderMode = true; } } - if ('metadata' in update) { if (this._metadata !== update.metadata) { this._metadata = update.metadata; @@ -278,6 +236,7 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus"); const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized"); const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."); + class StatusBarItem { private _showing = true; @@ -289,15 +248,15 @@ class StatusBarItem { this.element.title = title; } - set textContent(value: string) { + public set textContent(value: string) { this.element.textContent = value; } - set onclick(value: () => void) { + public set onclick(value: () => void) { this.element.onclick = value; } - setVisible(shouldShow: boolean): void { + public setVisible(shouldShow: boolean): void { if (shouldShow !== this._showing) { this._showing = shouldShow; this.element.style.display = shouldShow ? '' : 'none'; @@ -305,6 +264,7 @@ class StatusBarItem { } } + export class EditorStatus implements IStatusbarItem { private state: State; private element: HTMLElement; @@ -701,7 +661,7 @@ export class EditorStatus implements IStatusbarItem { this.updateState(update); } - private promptedScreenReader: boolean = false; + private _promptedScreenReader: boolean = false; private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void { let screenReaderMode = false; @@ -713,8 +673,8 @@ export class EditorStatus implements IStatusbarItem { const screenReaderConfiguration = this.configurationService.getValue('editor').accessibilitySupport; if (screenReaderConfiguration === 'auto') { // show explanation - if (!this.promptedScreenReader) { - this.promptedScreenReader = true; + if (!this._promptedScreenReader) { + this._promptedScreenReader = true; setTimeout(() => { this.onScreenReaderModeClick(); }, 100); @@ -988,29 +948,44 @@ export class ChangeModeAction extends Action { // Change mode for active editor const activeEditor = this.editorService.activeEditor; - if (activeEditor) { - const modeSupport = toEditorWithModeSupport(activeEditor); - if (modeSupport) { - - // Find mode - let languageSelection: ILanguageSelection | undefined; - if (pick === autoDetectMode) { - if (textModel) { - const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); - if (resource) { - languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1)); - } - } - } else { - languageSelection = this.modeService.createByLanguageName(pick.label); + const activeTextEditorWidget = this.editorService.activeTextEditorWidget; + const models: ITextModel[] = []; + if (isCodeEditor(activeTextEditorWidget)) { + const codeEditorModel = activeTextEditorWidget.getModel(); + if (codeEditorModel) { + models.push(codeEditorModel); + } + } else if (isDiffEditor(activeTextEditorWidget)) { + const diffEditorModel = activeTextEditorWidget.getModel(); + if (diffEditorModel) { + if (diffEditorModel.original) { + models.push(diffEditorModel.original); } - - // Change mode - if (typeof languageSelection !== 'undefined') { - modeSupport.setMode(languageSelection); + if (diffEditorModel.modified) { + models.push(diffEditorModel.modified); } } } + + // Find mode + let languageSelection: ILanguageSelection | undefined; + if (pick === autoDetectMode) { + if (textModel) { + const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + if (resource) { + languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1)); + } + } + } else { + languageSelection = this.modeService.createByLanguageName(pick.label); + } + + // Change mode + if (typeof languageSelection !== 'undefined') { + for (const textModel of models) { + this.modelService.setMode(textModel, languageSelection); + } + } }); } @@ -1184,7 +1159,6 @@ export class ChangeEncodingAction extends Action { if (!activeControl) { return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } - const encodingSupport: IEncodingSupport | null = toEditorWithEncodingSupport(activeControl.input); if (!encodingSupport) { return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]); @@ -1275,12 +1249,10 @@ export class ChangeEncodingAction extends Action { if (!encoding) { return; } - const activeControl = this.editorService.activeControl; if (!activeControl) { return; } - const encodingSupport = toEditorWithEncodingSupport(activeControl.input); if (typeof encoding.id !== 'undefined' && encodingSupport && encodingSupport.getEncoding() !== encoding.id) { encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index bed6606f63b..80915b758e2 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -9,7 +9,6 @@ import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; -import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -506,23 +505,12 @@ export interface IEncodingSupport { setEncoding(encoding: string, mode: EncodingMode): void; } -export interface IModeSupport { - - /** - * Sets the language mode of the input. - */ - setMode(languageSelection: ILanguageSelection): void; -} - /** * This is a tagging interface to declare an editor input being capable of dealing with files. It is only used in the editor registry * to register this kind of input to the platform. */ -export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeSupport { +export interface IFileEditorInput extends IEditorInput, IEncodingSupport { - /** - * Gets the resource this editor is about. - */ getResource(): URI; /** diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index c9edfc1b656..7fed290b43b 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -3,18 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorInput, ITextEditorModel, IModeSupport } from 'vs/workbench/common/editor'; +import { EditorInput, ITextEditorModel } 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 { ILanguageSelection } from 'vs/editor/common/services/modeService'; /** * A read-only text editor input whos contents are made of the provided resource that points to an existing * code editor model. */ -export class ResourceEditorInput extends EditorInput implements IModeSupport { +export class ResourceEditorInput extends EditorInput { static readonly ID: string = 'workbench.editors.resourceEditorInput'; @@ -63,18 +62,6 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { } } - setMode(mode: ILanguageSelection): void { - if (!this.modelReference) { - return; - } - - this.modelReference.then(ref => { - if (ref.object instanceof ResourceEditorModel) { - ref.object.setMode(mode); - } - }); - } - resolve(): Promise { if (!this.modelReference) { this.modelReference = this.textModelResolverService.createModelReference(this.resource); @@ -100,7 +87,7 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { } if (otherInput instanceof ResourceEditorInput) { - const otherResourceEditorInput = otherInput; + let otherResourceEditorInput = otherInput; // Compare by properties return otherResourceEditorInput.resource.toString() === this.resource.toString(); diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 0df0c1046c9..9f01a364979 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITextModel, ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; -import { EditorModel, IModeSupport } from 'vs/workbench/common/editor'; +import { EditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; @@ -14,7 +14,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; /** * The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated. */ -export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel, IModeSupport { +export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel { protected createdEditorModel: boolean; @@ -64,14 +64,6 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd abstract isReadonly(): boolean; - setMode(languageSelection: ILanguageSelection): void { - if (!this.isResolved()) { - return; - } - - this.modelService.setMode(this.textEditorModel, languageSelection); - } - /** * Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL. */ @@ -126,7 +118,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd * Updates the text editor model with the provided value. If the value is the same as the model has, this is a no-op. */ protected updateTextEditorModel(newValue: ITextBufferFactory): void { - if (!this.isResolved()) { + if (!this.textEditorModel) { return; } diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index f67e89dd526..dea43a4c322 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -9,19 +9,18 @@ import { memoize } from 'vs/base/common/decorators'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { basename } from 'vs/base/common/path'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; -import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity, IModeSupport } from 'vs/workbench/common/editor'; +import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity } from 'vs/workbench/common/editor'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILabelService } from 'vs/platform/label/common/label'; import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; -import { ILanguageSelection } from 'vs/editor/common/services/modeService'; /** * An editor input to be used for untitled text buffers. */ -export class UntitledEditorInput extends EditorInput implements IEncodingSupport, IModeSupport { +export class UntitledEditorInput extends EditorInput implements IEncodingSupport { static readonly ID: string = 'workbench.editors.untitledEditorInput'; @@ -59,6 +58,14 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport return this.resource; } + getModeId(): string | null { + if (this.cachedModel) { + return this.cachedModel.getModeId(); + } + + return this.modeId; + } + getName(): string { return this.hasAssociatedFilePath ? basenameOrAuthority(this.resource) : this.resource.path; } @@ -187,20 +194,6 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport } } - setMode(mode: ILanguageSelection): void { - if (this.cachedModel) { - this.cachedModel.setMode(mode); - } - } - - getModeId(): string | null { - if (this.cachedModel) { - return this.cachedModel.getModeId(); - } - - return this.modeId; - } - resolve(): Promise { // Join a model resolve if we have had one before diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index d5b5d459b16..2c5d1068a20 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -18,7 +18,6 @@ import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { FILE_EDITOR_INPUT_ID, TEXT_FILE_EDITOR_ID, BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; -import { ILanguageSelection } from 'vs/editor/common/services/modeService'; /** * A file editor input is the input type for the file editor of file system resources. @@ -98,13 +97,6 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } } - setMode(mode: ILanguageSelection): void { - const textModel = this.textFileService.models.get(this.resource); - if (textModel) { - textModel.setMode(mode); - } - } - setPreferredEncoding(encoding: string): void { this.preferredEncoding = encoding; this.forceOpenAsText = true; // encoding is a good hint to open the file as text diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 96db7068f60..0fe83803a0c 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -19,7 +19,6 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ILanguageSelection } from 'vs/editor/common/services/modeService'; export class TestEditorControl extends BaseEditor { @@ -46,7 +45,6 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } - setMode(mode: ILanguageSelection) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } } diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index c7a7ed45062..a670ff43ae3 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -29,7 +29,6 @@ import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ILanguageSelection } from 'vs/editor/common/services/modeService'; export class TestEditorControl extends BaseEditor { @@ -57,7 +56,6 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } - setMode(mode: ILanguageSelection) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } setFailToOpen(): void { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 6abd61eb3c6..2b34169a796 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -66,8 +66,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private contentEncoding: string; // encoding as reported from disk private preferredEncoding: string; // encoding as chosen by the user - private preferredMode: ILanguageSelection; // mode as chosen by the user - private versionId: number; private bufferSavedVersionId: number; private blockModelContentChange: boolean; @@ -209,22 +207,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return; } - if (this.preferredMode) { - return; // do not override user choice - } - const firstLineText = this.getFirstLineText(this.textEditorModel); const languageSelection = this.getOrCreateMode(this.modeService, undefined, firstLineText); this.modelService.setMode(this.textEditorModel, languageSelection); } - setMode(languageSelection: ILanguageSelection): void { - super.setMode(languageSelection); - - this.preferredMode = languageSelection; - } - async backup(target = this.resource): Promise { if (this.isResolved()) { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 40aa181153b..3b09be969dc 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor'; +import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; @@ -443,7 +443,7 @@ export interface ILoadOptions { reason?: LoadReason; } -export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport { +export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport { readonly onDidContentChange: Event; readonly onDidStateChange: Event; diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index 9775b42c9ef..fde39c097be 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -20,7 +20,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { ILanguageSelection } from 'vs/editor/common/services/modeService'; function inst(): IInstantiationService { let inst = new TestInstantiationService(); @@ -112,16 +111,27 @@ class TestFileEditorInput extends EditorInput implements IFileEditorInput { } getTypeId() { return 'testFileEditorInputForGroups'; } resolve(): Promise { return Promise.resolve(null!); } - setEncoding(encoding: string) { } - getEncoding(): string { return null!; } - setPreferredEncoding(encoding: string) { } - getResource(): URI { return this.resource; } - setForceOpenAsBinary(): void { } - setMode(mode: ILanguageSelection) { } matches(other: TestFileEditorInput): boolean { return other && this.id === other.id && other instanceof TestFileEditorInput; } + + setEncoding(encoding: string) { + } + + getEncoding(): string { + return null!; + } + + setPreferredEncoding(encoding: string) { + } + + getResource(): URI { + return this.resource; + } + + setForceOpenAsBinary(): void { + } } function input(id = String(index++), nonSerializable?: boolean, resource?: URI): EditorInput { From dac56c1816e948edf6b317f04db878198aff9848 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 7 May 2019 15:05:54 +0200 Subject: [PATCH 15/99] allow to provide a language mode when opening text editors --- src/vs/base/common/mime.ts | 5 +- src/vs/platform/editor/common/editor.ts | 8 +- .../api/browser/mainThreadDocuments.ts | 4 +- .../parts/editor/editor.contribution.ts | 8 +- .../browser/parts/editor/editorStatus.ts | 124 +++++--- src/vs/workbench/common/editor.ts | 24 +- .../common/editor/resourceEditorInput.ts | 30 +- .../common/editor/textEditorModel.ts | 48 ++- .../common/editor/untitledEditorInput.ts | 45 +-- .../common/editor/untitledEditorModel.ts | 21 +- .../files/browser/files.contribution.ts | 19 +- .../files/common/editors/fileEditorInput.ts | 29 +- .../test/browser/fileEditorInput.test.ts | 164 +++++----- .../contrib/output/browser/logViewer.ts | 2 +- .../contrib/output/browser/outputServices.ts | 2 +- .../electron-browser/perfviewEditor.ts | 1 + .../themes/browser/themes.contribution.ts | 2 +- .../services/editor/browser/editorService.ts | 23 +- .../test/browser/editorGroupsService.test.ts | 292 ++++++++---------- .../editor/test/browser/editorService.test.ts | 201 ++++++------ .../common/preferencesEditorInput.ts | 2 +- .../textfile/common/textFileEditorModel.ts | 26 +- .../common/textFileEditorModelManager.ts | 7 +- .../services/textfile/common/textfiles.ts | 9 +- .../textfile/test/textFileEditorModel.test.ts | 47 ++- .../test/textFileEditorModelManager.test.ts | 33 +- .../textfile/test/textFileService.test.ts | 30 +- .../test/textModelResolverService.test.ts | 113 ++++--- .../untitled/common/untitledEditorService.ts | 20 +- .../browser/parts/editor/baseEditor.test.ts | 41 ++- .../common/editor/editorDiffModel.test.ts | 33 +- .../test/common/editor/editorGroups.test.ts | 24 +- .../test/common/editor/editorModel.test.ts | 32 +- .../common/editor/resourceEditorInput.test.ts | 36 ++- .../test/common/editor/untitledEditor.test.ts | 226 +++++++------- .../api/mainThreadSaveParticipant.test.ts | 8 +- .../workbench/test/workbenchTestServices.ts | 2 +- 37 files changed, 961 insertions(+), 780 deletions(-) diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index de19c7c03f8..81bdd5e9416 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -230,10 +230,11 @@ export function isUnspecific(mime: string[] | string): boolean { * 2. Otherwise, if there are other extensions, suggest the first one. * 3. Otherwise, suggest the prefix. */ -export function suggestFilename(langId: string | null, prefix: string): string { +export function suggestFilename(mode: string | undefined, prefix: string): string { const extensions = registeredAssociations - .filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === langId) + .filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === mode) .map(assoc => assoc.extension); + const extensionsWithDotFirst = coalesce(extensions) .filter(assoc => startsWith(assoc, '.')); diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 203bd80327b..160ea244b45 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -63,7 +63,7 @@ export interface IBaseResourceInput { export interface IResourceInput extends IBaseResourceInput { /** - * The resource URL of the resource to open. + * The resource URI of the resource to open. */ resource: URI; @@ -71,6 +71,12 @@ export interface IResourceInput extends IBaseResourceInput { * The encoding of the text input if known. */ readonly encoding?: string; + + /** + * The identifier of the language mode of the text input + * if known to use when displaying the contents. + */ + readonly mode?: string; } export interface IEditorOptions { diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index c18e4fc1363..af99b8a7720 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -228,10 +228,10 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { }); } - private _doCreateUntitled(resource?: URI, modeId?: string, initialValue?: string): Promise { + private _doCreateUntitled(resource?: URI, mode?: string, initialValue?: string): Promise { return this._untitledEditorService.loadOrCreate({ resource, - modeId, + mode, initialValue, useResourcePath: Boolean(resource && resource.path) }).then(model => { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 414b807ea70..a8abe040f01 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -104,7 +104,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( interface ISerializedUntitledEditorInput { resource: string; resourceJSON: object; - modeId: string | null; + modeId: string | undefined; encoding: string; } @@ -131,7 +131,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory { const serialized: ISerializedUntitledEditorInput = { resource: resource.toString(), // Keep for backwards compatibility resourceJSON: resource.toJSON(), - modeId: untitledEditorInput.getModeId(), + modeId: untitledEditorInput.getMode(), encoding: untitledEditorInput.getEncoding() }; @@ -142,10 +142,10 @@ class UntitledEditorInputFactory implements IEditorInputFactory { return instantiationService.invokeFunction(accessor => { const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput); const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource); - const language = deserialized.modeId; + const mode = deserialized.modeId; const encoding = deserialized.encoding; - return accessor.get(IEditorService).createInput({ resource, language, encoding, forceUntitled: true }) as UntitledEditorInput; + return accessor.get(IEditorService).createInput({ resource, mode, encoding, forceUntitled: true }) as UntitledEditorInput; }); } } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index b6c197e5b66..5c6df3b90ae 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -14,11 +14,11 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { Action } from 'vs/base/common/actions'; import { Language } from 'vs/base/common/platform'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; -import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor'; +import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor, IModeSupport } from 'vs/workbench/common/editor'; import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorAction } from 'vs/editor/common/editorCommon'; -import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; +import { EndOfLineSequence } from 'vs/editor/common/model'; import { IModelLanguageChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/linesOperations'; import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/indentation'; @@ -59,7 +59,15 @@ class SideBySideEditorEncodingSupport implements IEncodingSupport { } setEncoding(encoding: string, mode: EncodingMode): void { - [this.master, this.details].forEach(s => s.setEncoding(encoding, mode)); + [this.master, this.details].forEach(editor => editor.setEncoding(encoding, mode)); + } +} + +class SideBySideEditorModeSupport implements IModeSupport { + constructor(private master: IModeSupport, private details: IModeSupport) { } + + setMode(mode: string): void { + [this.master, this.details].forEach(editor => editor.setMode(mode)); } } @@ -83,7 +91,7 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu } // File or Resource Editor - let encodingSupport = input as IFileEditorInput; + const encodingSupport = input as IFileEditorInput; if (areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) { return encodingSupport; } @@ -92,14 +100,41 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu return null; } +function toEditorWithModeSupport(input: IEditorInput): IModeSupport | null { + + // Untitled Editor + if (input instanceof UntitledEditorInput) { + return input; + } + + // Side by Side (diff) Editor + if (input instanceof SideBySideEditorInput) { + const masterModeSupport = toEditorWithModeSupport(input.master); + const detailsModeSupport = toEditorWithModeSupport(input.details); + + if (masterModeSupport && detailsModeSupport) { + return new SideBySideEditorModeSupport(masterModeSupport, detailsModeSupport); + } + + return masterModeSupport; + } + + // File or Resource Editor + const modeSupport = input as IFileEditorInput; + if (typeof modeSupport.setMode === 'function') { + return modeSupport; + } + + // Unsupported for any other editor + return null; +} + interface IEditorSelectionStatus { selections?: Selection[]; charactersSelected?: number; } class StateChange { - _stateChangeBrand: void; - indentation: boolean = false; selectionStatus: boolean = false; mode: boolean = false; @@ -120,7 +155,7 @@ class StateChange { this.metadata = this.metadata || other.metadata; } - public hasChanges(): boolean { + hasChanges(): boolean { return this.indentation || this.selectionStatus || this.mode @@ -179,42 +214,49 @@ class State { change.selectionStatus = true; } } + if ('indentation' in update) { if (this._indentation !== update.indentation) { this._indentation = update.indentation; change.indentation = true; } } + if ('mode' in update) { if (this._mode !== update.mode) { this._mode = update.mode; change.mode = true; } } + if ('encoding' in update) { if (this._encoding !== update.encoding) { this._encoding = update.encoding; change.encoding = true; } } + if ('EOL' in update) { if (this._EOL !== update.EOL) { this._EOL = update.EOL; change.EOL = true; } } + if ('tabFocusMode' in update) { if (this._tabFocusMode !== update.tabFocusMode) { this._tabFocusMode = update.tabFocusMode; change.tabFocusMode = true; } } + if ('screenReaderMode' in update) { if (this._screenReaderMode !== update.screenReaderMode) { this._screenReaderMode = update.screenReaderMode; change.screenReaderMode = true; } } + if ('metadata' in update) { if (this._metadata !== update.metadata) { this._metadata = update.metadata; @@ -236,7 +278,6 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus"); const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized"); const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."); - class StatusBarItem { private _showing = true; @@ -248,15 +289,15 @@ class StatusBarItem { this.element.title = title; } - public set textContent(value: string) { + set textContent(value: string) { this.element.textContent = value; } - public set onclick(value: () => void) { + set onclick(value: () => void) { this.element.onclick = value; } - public setVisible(shouldShow: boolean): void { + setVisible(shouldShow: boolean): void { if (shouldShow !== this._showing) { this._showing = shouldShow; this.element.style.display = shouldShow ? '' : 'none'; @@ -264,7 +305,6 @@ class StatusBarItem { } } - export class EditorStatus implements IStatusbarItem { private state: State; private element: HTMLElement; @@ -661,7 +701,7 @@ export class EditorStatus implements IStatusbarItem { this.updateState(update); } - private _promptedScreenReader: boolean = false; + private promptedScreenReader: boolean = false; private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void { let screenReaderMode = false; @@ -673,8 +713,8 @@ export class EditorStatus implements IStatusbarItem { const screenReaderConfiguration = this.configurationService.getValue('editor').accessibilitySupport; if (screenReaderConfiguration === 'auto') { // show explanation - if (!this._promptedScreenReader) { - this._promptedScreenReader = true; + if (!this.promptedScreenReader) { + this.promptedScreenReader = true; setTimeout(() => { this.onScreenReaderModeClick(); }, 100); @@ -948,42 +988,27 @@ export class ChangeModeAction extends Action { // Change mode for active editor const activeEditor = this.editorService.activeEditor; - const activeTextEditorWidget = this.editorService.activeTextEditorWidget; - const models: ITextModel[] = []; - if (isCodeEditor(activeTextEditorWidget)) { - const codeEditorModel = activeTextEditorWidget.getModel(); - if (codeEditorModel) { - models.push(codeEditorModel); - } - } else if (isDiffEditor(activeTextEditorWidget)) { - const diffEditorModel = activeTextEditorWidget.getModel(); - if (diffEditorModel) { - if (diffEditorModel.original) { - models.push(diffEditorModel.original); - } - if (diffEditorModel.modified) { - models.push(diffEditorModel.modified); - } - } - } + if (activeEditor) { + const modeSupport = toEditorWithModeSupport(activeEditor); + if (modeSupport) { - // Find mode - let languageSelection: ILanguageSelection | undefined; - if (pick === autoDetectMode) { - if (textModel) { - const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); - if (resource) { - languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1)); + // Find mode + let languageSelection: ILanguageSelection | undefined; + if (pick === autoDetectMode) { + if (textModel) { + const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + if (resource) { + languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1)); + } + } + } else { + languageSelection = this.modeService.createByLanguageName(pick.label); } - } - } else { - languageSelection = this.modeService.createByLanguageName(pick.label); - } - // Change mode - if (typeof languageSelection !== 'undefined') { - for (const textModel of models) { - this.modelService.setMode(textModel, languageSelection); + // Change mode + if (typeof languageSelection !== 'undefined') { + modeSupport.setMode(languageSelection.languageIdentifier.language); + } } } }); @@ -1159,6 +1184,7 @@ export class ChangeEncodingAction extends Action { if (!activeControl) { return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } + const encodingSupport: IEncodingSupport | null = toEditorWithEncodingSupport(activeControl.input); if (!encodingSupport) { return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]); @@ -1249,10 +1275,12 @@ export class ChangeEncodingAction extends Action { if (!encoding) { return; } + const activeControl = this.editorService.activeControl; if (!activeControl) { return; } + const encodingSupport = toEditorWithEncodingSupport(activeControl.input); if (typeof encoding.id !== 'undefined' && encodingSupport && encodingSupport.getEncoding() !== encoding.id) { encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 80915b758e2..37bd1a1eee6 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -144,7 +144,7 @@ export interface IEditorControl extends ICompositeControl { } export interface IFileInputFactory { - createFileInput(resource: URI, encoding: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; + createFileInput(resource: URI, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; isFileInput(obj: any): obj is IFileEditorInput; } @@ -209,7 +209,7 @@ export interface IUntitledResourceInput extends IBaseResourceInput { /** * Optional language of the untitled resource. */ - language?: string; + mode?: string; /** * Optional contents of the untitled resource. @@ -505,19 +505,35 @@ export interface IEncodingSupport { setEncoding(encoding: string, mode: EncodingMode): void; } +export interface IModeSupport { + + /** + * Sets the language mode of the input. + */ + setMode(mode: string): void; +} + /** * This is a tagging interface to declare an editor input being capable of dealing with files. It is only used in the editor registry * to register this kind of input to the platform. */ -export interface IFileEditorInput extends IEditorInput, IEncodingSupport { +export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeSupport { + /** + * Gets the resource this editor is about. + */ getResource(): URI; /** - * Sets the preferred encodingt to use for this input. + * Sets the preferred encoding to use for this input. */ setPreferredEncoding(encoding: string): void; + /** + * Sets the preferred language mode to use for this input. + */ + setPreferredMode(mode: string): void; + /** * Forces this file input to open as binary instead of text. */ diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 7fed290b43b..aad672b42e5 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorInput, ITextEditorModel } from 'vs/workbench/common/editor'; +import { EditorInput, ITextEditorModel, IModeSupport } 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'; @@ -13,16 +13,18 @@ import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorMo * A read-only text editor input whos contents are made of the provided resource that points to an existing * code editor model. */ -export class ResourceEditorInput extends EditorInput { +export class ResourceEditorInput extends EditorInput implements IModeSupport { static readonly ID: string = 'workbench.editors.resourceEditorInput'; + private cachedModel: ResourceEditorModel | null; private modelReference: Promise> | null; constructor( private name: string, private description: string | null, private readonly resource: URI, + private preferredMode: string | undefined, @ITextModelService private readonly textModelResolverService: ITextModelService ) { super(); @@ -62,6 +64,18 @@ export class ResourceEditorInput extends EditorInput { } } + setMode(mode: string): void { + this.setPreferredMode(mode); + + if (this.cachedModel) { + this.cachedModel.setMode(mode); + } + } + + setPreferredMode(mode: string): void { + this.preferredMode = mode; + } + resolve(): Promise { if (!this.modelReference) { this.modelReference = this.textModelResolverService.createModelReference(this.resource); @@ -70,6 +84,7 @@ export class ResourceEditorInput extends EditorInput { return this.modelReference.then(ref => { const model = ref.object; + // Ensure the resolved model is of expected type if (!(model instanceof ResourceEditorModel)) { ref.dispose(); this.modelReference = null; @@ -77,6 +92,13 @@ export class ResourceEditorInput extends EditorInput { return Promise.reject(new Error(`Unexpected model for ResourceInput: ${this.resource}`)); } + this.cachedModel = model; + + // Set mode if we have a preferred mode configured + if (this.preferredMode) { + model.setMode(this.preferredMode); + } + return model; }); } @@ -87,7 +109,7 @@ export class ResourceEditorInput extends EditorInput { } if (otherInput instanceof ResourceEditorInput) { - let otherResourceEditorInput = otherInput; + const otherResourceEditorInput = otherInput; // Compare by properties return otherResourceEditorInput.resource.toString() === this.resource.toString(); @@ -102,6 +124,8 @@ export class ResourceEditorInput extends EditorInput { this.modelReference = null; } + this.cachedModel = null; + super.dispose(); } } diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 9f01a364979..61ac9e224db 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -4,17 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { ITextModel, ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; -import { EditorModel } from 'vs/workbench/common/editor'; +import { EditorModel, IModeSupport } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; /** * The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated. */ -export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel { +export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel, IModeSupport { protected createdEditorModel: boolean; @@ -64,12 +65,25 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd abstract isReadonly(): boolean; + setMode(mode: string): void { + if (!this.isResolved()) { + return; + } + + if (!mode || mode === this.textEditorModel.getModeId()) { + return; + } + + this.modelService.setMode(this.textEditorModel, this.modeService.create(mode)); + } + /** - * Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL. + * Creates the text editor model with the provided value, optional preferred mode + * (can be comma separated for multiple values) and optional resource URL. */ - protected createTextEditorModel(value: ITextBufferFactory, resource: URI | undefined, modeId?: string): EditorModel { + protected createTextEditorModel(value: ITextBufferFactory, resource: URI | undefined, preferredMode?: string): EditorModel { const firstLineText = this.getFirstLineText(value); - const languageSelection = this.getOrCreateMode(this.modeService, modeId, firstLineText); + const languageSelection = this.getOrCreateMode(resource, this.modeService, preferredMode, firstLineText); return this.doCreateTextEditorModel(value, languageSelection, resource); } @@ -83,8 +97,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd // Make sure we clean up when this model gets disposed this.registerModelDisposeListener(model); } else { - this.modelService.updateModel(model, value); - this.modelService.setMode(model, languageSelection); + this.updateTextEditorModel(value, languageSelection.languageIdentifier.language); } this.textEditorModelHandle = model.uri; @@ -110,19 +123,32 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd * * @param firstLineText optional first line of the text buffer to set the mode on. This can be used to guess a mode from content. */ - protected getOrCreateMode(modeService: IModeService, modeId: string | undefined, firstLineText?: string): ILanguageSelection { - return modeService.create(modeId); + protected getOrCreateMode(resource: URI | undefined, modeService: IModeService, preferredMode: string | undefined, firstLineText?: string): ILanguageSelection { + + // lookup mode via resource path if the provided mode is unspecific + if (!preferredMode || preferredMode === PLAINTEXT_MODE_ID) { + return modeService.createByFilepathOrFirstLine(resource ? resource.fsPath : null, firstLineText); + } + + // otherwise take the preferred mode for granted + return modeService.create(preferredMode); } /** * Updates the text editor model with the provided value. If the value is the same as the model has, this is a no-op. */ - protected updateTextEditorModel(newValue: ITextBufferFactory): void { - if (!this.textEditorModel) { + protected updateTextEditorModel(newValue: ITextBufferFactory, preferredMode?: string): void { + if (!this.isResolved()) { return; } + // contents this.modelService.updateModel(this.textEditorModel, newValue); + + // mode (only if specific and changed) + if (preferredMode && preferredMode !== PLAINTEXT_MODE_ID && this.textEditorModel.getModeId() !== preferredMode) { + this.modelService.setMode(this.textEditorModel, this.modeService.create(preferredMode)); + } } createSnapshot(this: IResolvedTextEditorModel): ITextSnapshot; diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index dea43a4c322..37bd6eab9af 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -9,7 +9,7 @@ import { memoize } from 'vs/base/common/decorators'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { basename } from 'vs/base/common/path'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; -import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity } from 'vs/workbench/common/editor'; +import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity, IModeSupport } from 'vs/workbench/common/editor'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; @@ -20,12 +20,12 @@ import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverServ /** * An editor input to be used for untitled text buffers. */ -export class UntitledEditorInput extends EditorInput implements IEncodingSupport { +export class UntitledEditorInput extends EditorInput implements IEncodingSupport, IModeSupport { static readonly ID: string = 'workbench.editors.untitledEditorInput'; - private cachedModel: UntitledEditorModel; - private modelResolve?: Promise; + private cachedModel: UntitledEditorModel | null; + private modelResolve: Promise | null; private readonly _onDidModelChangeContent: Emitter = this._register(new Emitter()); get onDidModelChangeContent(): Event { return this._onDidModelChangeContent.event; } @@ -36,7 +36,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport constructor( private readonly resource: URI, private readonly _hasAssociatedFilePath: boolean, - private readonly modeId: string, + private preferredMode: string, private readonly initialValue: string, private preferredEncoding: string, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -58,14 +58,6 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport return this.resource; } - getModeId(): string | null { - if (this.cachedModel) { - return this.cachedModel.getModeId(); - } - - return this.modeId; - } - getName(): string { return this.hasAssociatedFilePath ? basenameOrAuthority(this.resource) : this.resource.path; } @@ -168,9 +160,9 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport suggestFileName(): string { if (!this.hasAssociatedFilePath) { if (this.cachedModel) { - const modeId = this.cachedModel.getModeId(); - if (modeId !== PLAINTEXT_MODE_ID) { // do not suggest when the mode ID is simple plain text - return suggestFilename(modeId, this.getName()); + const mode = this.cachedModel.getMode(); + if (mode !== PLAINTEXT_MODE_ID) { // do not suggest when the mode ID is simple plain text + return suggestFilename(mode, this.getName()); } } } @@ -194,6 +186,22 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport } } + setMode(mode: string): void { + this.preferredMode = mode; + + if (this.cachedModel) { + this.cachedModel.setMode(mode); + } + } + + getMode(): string | undefined { + if (this.cachedModel) { + return this.cachedModel.getMode(); + } + + return this.preferredMode; + } + resolve(): Promise { // Join a model resolve if we have had one before @@ -209,7 +217,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport } private createModel(): UntitledEditorModel { - const model = this._register(this.instantiationService.createInstance(UntitledEditorModel, this.modeId, this.resource, this.hasAssociatedFilePath, this.initialValue, this.preferredEncoding)); + const model = this._register(this.instantiationService.createInstance(UntitledEditorModel, this.preferredMode, this.resource, this.hasAssociatedFilePath, this.initialValue, this.preferredEncoding)); // re-emit some events from the model this._register(model.onDidChangeContent(() => this._onDidModelChangeContent.fire())); @@ -235,7 +243,8 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport } dispose(): void { - this.modelResolve = undefined; + this.cachedModel = null; + this.modelResolve = null; super.dispose(); } diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 9d757f2279e..7716742e212 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -6,9 +6,8 @@ import { IEncodingSupport } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { URI } from 'vs/base/common/uri'; -import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files'; -import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; +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'; @@ -37,7 +36,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin private configuredEncoding: string; constructor( - private readonly modeId: string, + private readonly preferredMode: string, private readonly resource: URI, private _hasAssociatedFilePath: boolean, private readonly initialValue: string, @@ -58,14 +57,6 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin return this._hasAssociatedFilePath; } - protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): ILanguageSelection { - if (!modeId || modeId === PLAINTEXT_MODE_ID) { - return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided modeId is unspecific - } - - return super.getOrCreateMode(modeService, modeId, firstLineText); - } - private registerListeners(): void { // Config Changes @@ -88,12 +79,12 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin return this.versionId; } - getModeId(): string | null { + getMode(): string | undefined { if (this.textEditorModel) { return this.textEditorModel.getLanguageIdentifier().language; } - return this.modeId; + return this.preferredMode; } getEncoding(): string { @@ -166,12 +157,12 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin // Create text editor model if not yet done if (!this.textEditorModel) { - this.createTextEditorModel(untitledContents, this.resource, this.modeId); + this.createTextEditorModel(untitledContents, this.resource, this.preferredMode); } // Otherwise update else { - this.updateTextEditorModel(untitledContents); + this.updateTextEditorModel(untitledContents, this.preferredMode); } // Encoding diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index e090fa12cc2..ef84bbdaf89 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -42,8 +42,8 @@ import { Schemas } from 'vs/base/common/network'; // Viewlet Action export class OpenExplorerViewletAction extends ShowViewletAction { - public static readonly ID = VIEWLET_ID; - public static readonly LABEL = nls.localize('showExplorerViewlet', "Show Explorer"); + static readonly ID = VIEWLET_ID; + static readonly LABEL = nls.localize('showExplorerViewlet', "Show Explorer"); constructor( id: string, @@ -124,8 +124,8 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register default file input factory Registry.as(EditorInputExtensions.EditorInputFactories).registerFileInputFactory({ - createFileInput: (resource, encoding, instantiationService): IFileEditorInput => { - return instantiationService.createInstance(FileEditorInput, resource, encoding); + createFileInput: (resource, encoding, mode, instantiationService): IFileEditorInput => { + return instantiationService.createInstance(FileEditorInput, resource, encoding, mode); }, isFileInput: (obj): obj is IFileEditorInput => { @@ -137,6 +137,7 @@ interface ISerializedFileInput { resource: string; resourceJSON: object; encoding?: string; + modeId?: string; } // Register Editor Input Factory @@ -144,25 +145,27 @@ class FileEditorInputFactory implements IEditorInputFactory { constructor() { } - public serialize(editorInput: EditorInput): string { + serialize(editorInput: EditorInput): string { const fileEditorInput = editorInput; const resource = fileEditorInput.getResource(); const fileInput: ISerializedFileInput = { resource: resource.toString(), // Keep for backwards compatibility resourceJSON: resource.toJSON(), - encoding: fileEditorInput.getEncoding() + encoding: fileEditorInput.getEncoding(), + modeId: fileEditorInput.getPreferredMode() // only using the preferred user associated mode here if available to not store redundant data }; return JSON.stringify(fileInput); } - public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileEditorInput { + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileEditorInput { return instantiationService.invokeFunction(accessor => { const fileInput: ISerializedFileInput = JSON.parse(serializedEditorInput); const resource = !!fileInput.resourceJSON ? URI.revive(fileInput.resourceJSON) : URI.parse(fileInput.resource); const encoding = fileInput.encoding; + const mode = fileInput.modeId; - return accessor.get(IEditorService).createInput({ resource, encoding, forceFile: true }) as FileEditorInput; + return accessor.get(IEditorService).createInput({ resource, encoding, mode, forceFile: true }) as FileEditorInput; }); } } diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 2c5d1068a20..02eab17958b 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -24,8 +24,11 @@ import { ILabelService } from 'vs/platform/label/common/label'; */ export class FileEditorInput extends EditorInput implements IFileEditorInput { private preferredEncoding: string; + private preferredMode: string; + private forceOpenAsBinary: boolean; private forceOpenAsText: boolean; + private textModelReference: Promise> | null; private name: string; @@ -35,6 +38,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { constructor( private resource: URI, preferredEncoding: string | undefined, + preferredMode: string | undefined, @IInstantiationService private readonly instantiationService: IInstantiationService, @ITextFileService private readonly textFileService: ITextFileService, @ITextModelService private readonly textModelResolverService: ITextModelService, @@ -46,6 +50,10 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { this.setPreferredEncoding(preferredEncoding); } + if (preferredMode) { + this.setPreferredMode(preferredMode); + } + this.registerListeners(); } @@ -89,7 +97,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } setEncoding(encoding: string, mode: EncodingMode): void { - this.preferredEncoding = encoding; + this.setPreferredEncoding(encoding); const textModel = this.textFileService.models.get(this.resource); if (textModel) { @@ -102,6 +110,24 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { this.forceOpenAsText = true; // encoding is a good hint to open the file as text } + getPreferredMode(): string | undefined { + return this.preferredMode; + } + + setMode(mode: string): void { + this.setPreferredMode(mode); + + const textModel = this.textFileService.models.get(this.resource); + if (textModel) { + textModel.setMode(mode); + } + } + + setPreferredMode(mode: string): void { + this.preferredMode = mode; + this.forceOpenAsText = true; // mode is a good hint to open the file as text + } + setForceOpenAsText(): void { this.forceOpenAsText = true; this.forceOpenAsBinary = false; @@ -251,6 +277,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { // Resolve as text return this.textFileService.models.loadOrCreate(this.resource, { + mode: this.preferredMode, encoding: this.preferredEncoding, reload: { async: true }, // trigger a reload of the model if it exists already but do not wait to show the model allowBinary: this.forceOpenAsText, diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index a134f8f6ab2..674d1f2a1e4 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -16,6 +16,7 @@ import { FileOperationResult, FileOperationError } from 'vs/platform/files/commo import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; +import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; class ServiceAccessor { constructor( @@ -36,10 +37,10 @@ suite('Files - FileEditorInput', () => { accessor = instantiationService.createInstance(ServiceAccessor); }); - test('Basics', function () { - let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined); - const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), undefined); - const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), undefined); + test('Basics', async function () { + let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); + const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), undefined, undefined); + const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), undefined, undefined); assert(input.matches(input)); assert(input.matches(otherInputSame)); @@ -54,52 +55,65 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(toResource.call(this, '/foo/bar/file.js').fsPath, input.getResource().fsPath); assert(input.getResource() instanceof URI); - input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), undefined); + input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), undefined, undefined); - const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined); - const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined); + const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); + const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); - return inputToResolve.resolve().then(resolved => { - assert.ok(inputToResolve.isResolved()); + let resolved = await inputToResolve.resolve(); + assert.ok(inputToResolve.isResolved()); - const resolvedModelA = resolved; - return inputToResolve.resolve().then(resolved => { - assert(resolvedModelA === resolved); // OK: Resolved Model cached globally per input + const resolvedModelA = resolved; + resolved = await inputToResolve.resolve(); + assert(resolvedModelA === resolved); // OK: Resolved Model cached globally per input - return sameOtherInput.resolve().then(otherResolved => { - assert(otherResolved === resolvedModelA); // OK: Resolved Model cached globally per input + const otherResolved = await sameOtherInput.resolve(); + assert(otherResolved === resolvedModelA); // OK: Resolved Model cached globally per input + inputToResolve.dispose(); - inputToResolve.dispose(); + resolved = await inputToResolve.resolve(); + assert(resolvedModelA === resolved); // Model is still the same because we had 2 clients + inputToResolve.dispose(); + sameOtherInput.dispose(); + resolvedModelA.dispose(); - return inputToResolve.resolve().then(resolved => { - assert(resolvedModelA === resolved); // Model is still the same because we had 2 clients + resolved = await inputToResolve.resolve(); + assert(resolvedModelA !== resolved); // Different instance, because input got disposed - inputToResolve.dispose(); - sameOtherInput.dispose(); + const stat = (resolved as TextFileEditorModel).getStat(); + resolved = await inputToResolve.resolve(); + await timeout(0); + assert(stat !== (resolved as TextFileEditorModel).getStat()); // Different stat, because resolve always goes to the server for refresh + }); - resolvedModelA.dispose(); - - return inputToResolve.resolve().then(resolved => { - assert(resolvedModelA !== resolved); // Different instance, because input got disposed - - let stat = (resolved as TextFileEditorModel).getStat(); - return inputToResolve.resolve().then(resolved => { - return timeout(0).then(() => { // due to file editor input using `reload: { async: true }` - assert(stat !== (resolved as TextFileEditorModel).getStat()); // Different stat, because resolve always goes to the server for refresh - }); - }); - }); - }); - }); - }); + test('preferred mode', async function () { + const mode = 'file-input-test'; + ModesRegistry.registerLanguage({ + id: mode, }); + + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, mode); + assert.equal(input.getPreferredMode(), mode); + + const model = await input.resolve() as TextFileEditorModel; + assert.equal(model.textEditorModel!.getModeId(), mode); + + input.setMode('text'); + assert.equal(input.getPreferredMode(), 'text'); + assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID); + + const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); + input2.setPreferredMode(mode); + + const model2 = await input2.resolve() as TextFileEditorModel; + assert.equal(model2.textEditorModel!.getModeId(), mode); }); test('matches', function () { - const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); - const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); - const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), undefined); - const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined); + const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), undefined, undefined); + const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined, undefined); assert.strictEqual(input1.matches(null), false); assert.strictEqual(input1.matches(input1), true); @@ -109,70 +123,58 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(input1.matches(input2Upper), false); }); - test('getEncoding/setEncoding', function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); + test('getEncoding/setEncoding', async function () { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); input.setEncoding('utf16', EncodingMode.Encode); assert.equal(input.getEncoding(), 'utf16'); - return input.resolve().then((resolved: TextFileEditorModel) => { - assert.equal(input.getEncoding(), resolved.getEncoding()); - - resolved.dispose(); - }); + const resolved = await input.resolve() as TextFileEditorModel; + assert.equal(input.getEncoding(), resolved.getEncoding()); + resolved.dispose(); }); - test('save', function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); + test('save', async function () { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); - return input.resolve().then((resolved: TextFileEditorModel) => { - resolved.textEditorModel!.setValue('changed'); - assert.ok(input.isDirty()); + const resolved = await input.resolve() as TextFileEditorModel; + resolved.textEditorModel!.setValue('changed'); + assert.ok(input.isDirty()); - return input.save().then(() => { - assert.ok(!input.isDirty()); - - resolved.dispose(); - }); - }); + await input.save(); + assert.ok(!input.isDirty()); + resolved.dispose(); }); - test('revert', function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); + test('revert', async function () { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); - return input.resolve().then((resolved: TextFileEditorModel) => { - resolved.textEditorModel!.setValue('changed'); - assert.ok(input.isDirty()); + const resolved = await input.resolve() as TextFileEditorModel; + resolved.textEditorModel!.setValue('changed'); + assert.ok(input.isDirty()); - return input.revert().then(() => { - assert.ok(!input.isDirty()); - - resolved.dispose(); - }); - }); + await input.revert(); + assert.ok(!input.isDirty()); + resolved.dispose(); }); - test('resolve handles binary files', function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); + test('resolve handles binary files', async function () { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); accessor.textFileService.setResolveTextContentErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY)); - return input.resolve().then(resolved => { - assert.ok(resolved); - - resolved.dispose(); - }); + const resolved = await input.resolve(); + assert.ok(resolved); + resolved.dispose(); }); - test('resolve handles too large files', function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); + test('resolve handles too large files', async function () { + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_TOO_LARGE)); - return input.resolve().then(resolved => { - assert.ok(resolved); - - resolved.dispose(); - }); + const resolved = await input.resolve(); + assert.ok(resolved); + resolved.dispose(); }); }); diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index c33c399e893..c1bbbd2f7c0 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -28,7 +28,7 @@ export class LogViewerInput extends ResourceEditorInput { constructor(private outputChannelDescriptor: IFileOutputChannelDescriptor, @ITextModelService textModelResolverService: ITextModelService ) { - super(basename(outputChannelDescriptor.file.path), dirname(outputChannelDescriptor.file.path), URI.from({ scheme: LOG_SCHEME, path: outputChannelDescriptor.id }), textModelResolverService); + super(basename(outputChannelDescriptor.file.path), dirname(outputChannelDescriptor.file.path), URI.from({ scheme: LOG_SCHEME, path: outputChannelDescriptor.id }), undefined, textModelResolverService); } public getTypeId(): string { diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index cb5a3e64108..eb8ef512efd 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -244,7 +244,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private createInput(channel: IOutputChannel): ResourceEditorInput { const resource = URI.from({ scheme: OUTPUT_SCHEME, path: channel.id }); - return this.instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), resource); + return this.instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), resource, undefined); } private saveState(): void { diff --git a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts index a6267a9a2d4..7152a8c871b 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts @@ -52,6 +52,7 @@ export class PerfviewInput extends ResourceEditorInput { localize('name', "Startup Performance"), null, PerfviewInput.Uri, + undefined, textModelResolverService ); } diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 756bfc7733a..7c65d7b8956 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -236,7 +236,7 @@ class GenerateColorThemeAction extends Action { }, null, '\t'); contents = contents.replace(/\"__/g, '//"'); - return this.editorService.openEditor({ contents, language: 'jsonc' }); + return this.editorService.openEditor({ contents, mode: 'jsonc' }); } } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 6969fe75956..9b93b9b3975 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -528,7 +528,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Untitled file support const untitledInput = input; if (untitledInput.forceUntitled || !untitledInput.resource || (untitledInput.resource && untitledInput.resource.scheme === Schemas.untitled)) { - return this.untitledEditorService.createOrGet(untitledInput.resource, untitledInput.language, untitledInput.contents, untitledInput.encoding); + return this.untitledEditorService.createOrGet(untitledInput.resource, untitledInput.mode, untitledInput.contents, untitledInput.encoding); } // Resource Editor Support @@ -539,13 +539,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { label = basename(resourceInput.resource); // derive the label from the path (but not for data URIs) } - return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, resourceInput.forceFile) as EditorInput; + return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, resourceInput.mode, resourceInput.forceFile) as EditorInput; } throw new Error('Unknown input type'); } - private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string | undefined, description: string | undefined, encoding: string | undefined, forceFile: boolean | undefined): ICachedEditorInput { + private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string | undefined, description: string | undefined, encoding: string | undefined, mode: string | undefined, forceFile: boolean | undefined): ICachedEditorInput { if (EditorService.CACHE.has(resource)) { const input = EditorService.CACHE.get(resource)!; if (input instanceof ResourceEditorInput) { @@ -556,10 +556,18 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (description) { input.setDescription(description); } + + if (mode) { + input.setPreferredMode(mode); + } } else if (!(input instanceof DataUriEditorInput)) { if (encoding) { input.setPreferredEncoding(encoding); } + + if (mode) { + input.setPreferredMode(mode); + } } return input; @@ -569,7 +577,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // File if (forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(resource)) { - input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService); + input = this.fileInputFactory.createFileInput(resource, encoding, mode, instantiationService); } // Data URI @@ -579,13 +587,12 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Resource else { - input = instantiationService.createInstance(ResourceEditorInput, label, description, resource); + input = instantiationService.createInstance(ResourceEditorInput, label, description, resource, mode); } + // Add to cache and remove when input gets disposed EditorService.CACHE.set(resource, input); - Event.once(input.onDispose)(() => { - EditorService.CACHE.delete(resource); - }); + Event.once(input.onDispose)(() => EditorService.CACHE.delete(resource)); return input; } diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 0fe83803a0c..28720257840 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -24,10 +24,10 @@ export class TestEditorControl extends BaseEditor { constructor(@ITelemetryService telemetryService: ITelemetryService) { super('MyFileEditorForEditorGroupService', NullTelemetryService, new TestThemeService(), new TestStorageService()); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { super.setInput(input, options, token); - return input.resolve().then(() => undefined); + await input.resolve(); } getId(): string { return 'MyFileEditorForEditorGroupService'; } @@ -45,11 +45,13 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } + setMode(mode: string) { } + setPreferredMode(mode: string) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } } -suite('Editor groups service', () => { +suite('EditorGroupsService', () => { function registerTestEditorInput(): void { @@ -291,7 +293,7 @@ suite('Editor groups service', () => { part.dispose(); }); - test('copy/merge groups', function () { + test('copy/merge groups', async () => { const part = createPart(); let groupAddedCounter = 0; @@ -312,40 +314,32 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); - return rootGroup.openEditor(input, EditorOptions.create({ pinned: true })).then(() => { - const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT, { activate: true }); - const downGroup = part.copyGroup(rootGroup, rightGroup, GroupDirection.DOWN); - - assert.equal(groupAddedCounter, 2); - assert.equal(downGroup.count, 1); - assert.ok(downGroup.activeEditor instanceof TestEditorInput); - - part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.COPY_EDITORS }); - assert.equal(rightGroup.count, 1); - assert.ok(rightGroup.activeEditor instanceof TestEditorInput); - - part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.MOVE_EDITORS }); - assert.equal(rootGroup.count, 0); - - part.mergeGroup(rootGroup, downGroup); - assert.equal(groupRemovedCounter, 1); - assert.equal(rootGroupDisposed, true); - - groupAddedListener.dispose(); - groupRemovedListener.dispose(); - disposeListener.dispose(); - - part.dispose(); - }); + await rootGroup.openEditor(input, EditorOptions.create({ pinned: true })); + const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT, { activate: true }); + const downGroup = part.copyGroup(rootGroup, rightGroup, GroupDirection.DOWN); + assert.equal(groupAddedCounter, 2); + assert.equal(downGroup.count, 1); + assert.ok(downGroup.activeEditor instanceof TestEditorInput); + part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.COPY_EDITORS }); + assert.equal(rightGroup.count, 1); + assert.ok(rightGroup.activeEditor instanceof TestEditorInput); + part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.MOVE_EDITORS }); + assert.equal(rootGroup.count, 0); + part.mergeGroup(rootGroup, downGroup); + assert.equal(groupRemovedCounter, 1); + assert.equal(rootGroupDisposed, true); + groupAddedListener.dispose(); + groupRemovedListener.dispose(); + disposeListener.dispose(); + part.dispose(); }); - test('whenRestored', () => { + test('whenRestored', async () => { const part = createPart(); - return part.whenRestored.then(() => { - assert.ok(true); - part.dispose(); - }); + await part.whenRestored; + assert.ok(true); + part.dispose(); }); test('options', () => { @@ -467,7 +461,7 @@ suite('Editor groups service', () => { part.dispose(); }); - test('openEditors / closeEditors', function () { + test('openEditors / closeEditors', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -475,20 +469,17 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); - return group.closeEditors([input, inputInactive]).then(() => { - assert.equal(group.isEmpty(), true); - - part.dispose(); - }); - }); + await group.closeEditors([input, inputInactive]); + assert.equal(group.isEmpty(), true); + part.dispose(); }); - test('closeEditors (except one)', function () { + test('closeEditors (except one)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -497,22 +488,19 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ except: input2 }).then(() => { - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input2); - - part.dispose(); - }); - }); + await group.closeEditors({ except: input2 }); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), input2); + part.dispose(); }); - test('closeEditors (saved only)', function () { + test('closeEditors (saved only)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -521,21 +509,18 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ savedOnly: true }).then(() => { - assert.equal(group.count, 0); - - part.dispose(); - }); - }); + await group.closeEditors({ savedOnly: true }); + assert.equal(group.count, 0); + part.dispose(); }); - test('closeEditors (direction: right)', function () { + test('closeEditors (direction: right)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -544,23 +529,20 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ direction: CloseDirection.RIGHT, except: input2 }).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - - part.dispose(); - }); - }); + await group.closeEditors({ direction: CloseDirection.RIGHT, except: input2 }); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + part.dispose(); }); - test('closeEditors (direction: left)', function () { + test('closeEditors (direction: left)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -569,23 +551,20 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ direction: CloseDirection.LEFT, except: input2 }).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input2); - assert.equal(group.getEditor(1), input3); - - part.dispose(); - }); - }); + await group.closeEditors({ direction: CloseDirection.LEFT, except: input2 }); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input2); + assert.equal(group.getEditor(1), input3); + part.dispose(); }); - test('closeAllEditors', () => { + test('closeAllEditors', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -593,20 +572,17 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); - return group.closeAllEditors().then(() => { - assert.equal(group.isEmpty(), true); - - part.dispose(); - }); - }); + await group.closeAllEditors(); + assert.equal(group.isEmpty(), true); + part.dispose(); }); - test('moveEditor (same group)', function () { + test('moveEditor (same group)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -622,22 +598,19 @@ suite('Editor groups service', () => { } }); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - group.moveEditor(inputInactive, group, { index: 0 }); - assert.equal(editorMoveCounter, 1); - assert.equal(group.getEditor(0), inputInactive); - assert.equal(group.getEditor(1), input); - - editorGroupChangeListener.dispose(); - part.dispose(); - }); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + group.moveEditor(inputInactive, group, { index: 0 }); + assert.equal(editorMoveCounter, 1); + assert.equal(group.getEditor(0), inputInactive); + assert.equal(group.getEditor(1), input); + editorGroupChangeListener.dispose(); + part.dispose(); }); - test('moveEditor (across groups)', function () { + test('moveEditor (across groups)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -647,23 +620,19 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - group.moveEditor(inputInactive, rightGroup, { index: 0 }); - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input); - - assert.equal(rightGroup.count, 1); - assert.equal(rightGroup.getEditor(0), inputInactive); - - part.dispose(); - }); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + group.moveEditor(inputInactive, rightGroup, { index: 0 }); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), input); + assert.equal(rightGroup.count, 1); + assert.equal(rightGroup.getEditor(0), inputInactive); + part.dispose(); }); - test('copyEditor (across groups)', function () { + test('copyEditor (across groups)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -673,24 +642,20 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - group.copyEditor(inputInactive, rightGroup, { index: 0 }); - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - assert.equal(rightGroup.count, 1); - assert.equal(rightGroup.getEditor(0), inputInactive); - - part.dispose(); - }); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + group.copyEditor(inputInactive, rightGroup, { index: 0 }); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + assert.equal(rightGroup.count, 1); + assert.equal(rightGroup.getEditor(0), inputInactive); + part.dispose(); }); - test('replaceEditors', () => { + test('replaceEditors', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -698,17 +663,14 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditor(input).then(() => { - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input); + await group.openEditor(input); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), input); - return group.replaceEditors([{ editor: input, replacement: inputInactive }]).then(() => { - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), inputInactive); - - part.dispose(); - }); - }); + await group.replaceEditors([{ editor: input, replacement: inputInactive }]); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), inputInactive); + part.dispose(); }); test('find neighbour group (left/right)', function () { diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index a670ff43ae3..658a4a84e77 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -29,15 +29,17 @@ import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; export class TestEditorControl extends BaseEditor { constructor(@ITelemetryService telemetryService: ITelemetryService) { super('MyTestEditorForEditorService', NullTelemetryService, new TestThemeService(), new TestStorageService()); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { super.setInput(input, options, token); - return input.resolve().then(() => undefined); + await input.resolve(); } getId(): string { return 'MyTestEditorForEditorService'; } @@ -56,6 +58,8 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } + setMode(mode: string) { } + setPreferredMode(mode: string) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } setFailToOpen(): void { @@ -75,7 +79,7 @@ class FileServiceProvider extends Disposable { } } -suite('Editor service', () => { +suite('EditorService', () => { function registerTestEditorInput(): void { Registry.as(Extensions.Editors).registerEditor(new EditorDescriptor(TestEditorControl, 'MyTestEditorForEditorService', 'My Test Editor For Next Editor Service'), new SyncDescriptor(TestEditorInput)); @@ -83,7 +87,7 @@ suite('Editor service', () => { registerTestEditorInput(); - test('basics', function () { + test('basics', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -112,51 +116,49 @@ suite('Editor service', () => { didCloseEditorListenerCounter++; }); - return part.whenRestored.then(() => { + await part.whenRestored; - // Open input - return service.openEditor(input, { pinned: true }).then(editor => { - assert.ok(editor instanceof TestEditorControl); - assert.equal(editor, service.activeControl); - assert.equal(input, service.activeEditor); - assert.equal(service.visibleControls.length, 1); - assert.equal(service.visibleControls[0], editor); - assert.ok(!service.activeTextEditorWidget); - assert.equal(service.visibleTextEditorWidgets.length, 0); - assert.equal(service.isOpen(input), true); - assert.equal(service.getOpened({ resource: input.getResource() }), input); - assert.equal(service.isOpen(input, part.activeGroup), true); - assert.equal(activeEditorChangeEventCounter, 1); - assert.equal(visibleEditorChangeEventCounter, 1); + // Open input + let editor = await service.openEditor(input, { pinned: true }); - // Close input - return editor!.group!.closeEditor(input).then(() => { - assert.equal(didCloseEditorListenerCounter, 1); - assert.equal(activeEditorChangeEventCounter, 2); - assert.equal(visibleEditorChangeEventCounter, 2); - assert.ok(input.gotDisposed); + assert.ok(editor instanceof TestEditorControl); + assert.equal(editor, service.activeControl); + assert.equal(input, service.activeEditor); + assert.equal(service.visibleControls.length, 1); + assert.equal(service.visibleControls[0], editor); + assert.ok(!service.activeTextEditorWidget); + assert.equal(service.visibleTextEditorWidgets.length, 0); + assert.equal(service.isOpen(input), true); + assert.equal(service.getOpened({ resource: input.getResource() }), input); + assert.equal(service.isOpen(input, part.activeGroup), true); + assert.equal(activeEditorChangeEventCounter, 1); + assert.equal(visibleEditorChangeEventCounter, 1); - // Open again 2 inputs - return service.openEditor(input, { pinned: true }).then(editor => { - return service.openEditor(otherInput, { pinned: true }).then(editor => { - assert.equal(service.visibleControls.length, 1); - assert.equal(service.isOpen(input), true); - assert.equal(service.isOpen(otherInput), true); + // Close input + await editor!.group!.closeEditor(input); - assert.equal(activeEditorChangeEventCounter, 4); - assert.equal(visibleEditorChangeEventCounter, 4); + assert.equal(didCloseEditorListenerCounter, 1); + assert.equal(activeEditorChangeEventCounter, 2); + assert.equal(visibleEditorChangeEventCounter, 2); + assert.ok(input.gotDisposed); - activeEditorChangeListener.dispose(); - visibleEditorChangeListener.dispose(); - didCloseEditorListener.dispose(); - }); - }); - }); - }); - }); + // Open again 2 inputs + await service.openEditor(input, { pinned: true }); + editor = await service.openEditor(otherInput, { pinned: true }); + + assert.equal(service.visibleControls.length, 1); + assert.equal(service.isOpen(input), true); + assert.equal(service.isOpen(otherInput), true); + + assert.equal(activeEditorChangeEventCounter, 4); + assert.equal(visibleEditorChangeEventCounter, 4); + + activeEditorChangeListener.dispose(); + visibleEditorChangeListener.dispose(); + didCloseEditorListener.dispose(); }); - test('openEditors() / replaceEditors()', function () { + test('openEditors() / replaceEditors()', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -171,18 +173,16 @@ suite('Editor service', () => { const otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openEditors')); const replaceInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource3-openEditors')); - return part.whenRestored.then(() => { + await part.whenRestored; - // Open editors - return service.openEditors([{ editor: input }, { editor: otherInput }]).then(() => { - assert.equal(part.activeGroup.count, 2); + // Open editors + await service.openEditors([{ editor: input }, { editor: otherInput }]); + assert.equal(part.activeGroup.count, 2); - return service.replaceEditors([{ editor: input, replacement: replaceInput }], part.activeGroup).then(() => { - assert.equal(part.activeGroup.count, 2); - assert.equal(part.activeGroup.getIndexOfEditor(replaceInput), 0); - }); - }); - }); + // Replace editors + await service.replaceEditors([{ editor: input, replacement: replaceInput }], part.activeGroup); + assert.equal(part.activeGroup.count, 2); + assert.equal(part.activeGroup.getIndexOfEditor(replaceInput), 0); }); test('caching', function () { @@ -234,10 +234,15 @@ suite('Editor service', () => { assert.ok(!input1AgainAndAgain!.isDisposed()); }); - test('createInput', function () { + test('createInput', async function () { const instantiationService = workbenchInstantiationService(); const service: EditorService = instantiationService.createInstance(EditorService); + const mode = 'create-input-test'; + ModesRegistry.registerLanguage({ + id: mode, + }); + // Untyped Input (file) let input = service.createInput({ resource: toResource.call(this, '/index.html'), options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof FileEditorInput); @@ -250,6 +255,18 @@ suite('Editor service', () => { contentInput = input; assert.equal(contentInput.getPreferredEncoding(), 'utf16le'); + // Untyped Input (file, mode) + input = service.createInput({ resource: toResource.call(this, '/index.html'), mode }); + assert(input instanceof FileEditorInput); + contentInput = input; + assert.equal(contentInput.getPreferredMode(), mode); + + // Untyped Input (file, different mode) + input = service.createInput({ resource: toResource.call(this, '/index.html'), mode: 'text' }); + assert(input instanceof FileEditorInput); + contentInput = input; + assert.equal(contentInput.getPreferredMode(), 'text'); + // Untyped Input (untitled) input = service.createInput({ options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof UntitledEditorInput); @@ -257,6 +274,14 @@ suite('Editor service', () => { // Untyped Input (untitled with contents) input = service.createInput({ contents: 'Hello Untitled', options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof UntitledEditorInput); + let model = await input.resolve() as UntitledEditorModel; + assert.equal(model.textEditorModel!.getValue(), 'Hello Untitled'); + + // Untyped Input (untitled with mode) + input = service.createInput({ mode, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + assert(input instanceof UntitledEditorInput); + model = await input.resolve() as UntitledEditorModel; + assert.equal(model.getMode(), mode); // Untyped Input (untitled with file path) input = service.createInput({ resource: URI.file('/some/path.txt'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); @@ -276,6 +301,10 @@ suite('Editor service', () => { assert.ok((input as UntitledEditorInput).hasAssociatedFilePath); provider.dispose(); + + // Untyped Input (resource) + input = service.createInput({ resource: URI.parse('custom:resource') }); + assert(input instanceof ResourceEditorInput); }); test('delegate', function (done) { @@ -298,7 +327,7 @@ suite('Editor service', () => { const ed = instantiationService.createInstance(MyEditor, 'my.editor'); - const inp = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.parse('my://resource-delegate')); + const inp = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.parse('my://resource-delegate'), undefined); const delegate = instantiationService.createInstance(DelegatingEditorService); delegate.setEditorOpenHandler((group: IEditorGroup, input: IEditorInput, options?: EditorOptions) => { assert.strictEqual(input, inp); @@ -311,7 +340,7 @@ suite('Editor service', () => { delegate.openEditor(inp); }); - test('close editor does not dispose when editor opened in other group', function () { + test('close editor does not dispose when editor opened in other group', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -327,30 +356,26 @@ suite('Editor service', () => { const rootGroup = part.activeGroup; const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); - return part.whenRestored.then(() => { + await part.whenRestored; - // Open input - return service.openEditor(input, { pinned: true }).then(editor => { - return service.openEditor(input, { pinned: true }, rightGroup).then(editor => { - const editors = service.editors; - assert.equal(editors.length, 2); - assert.equal(editors[0], input); - assert.equal(editors[1], input); + // Open input + await service.openEditor(input, { pinned: true }); + await service.openEditor(input, { pinned: true }, rightGroup); - // Close input - return rootGroup.closeEditor(input).then(() => { - assert.equal(input.isDisposed(), false); + const editors = service.editors; + assert.equal(editors.length, 2); + assert.equal(editors[0], input); + assert.equal(editors[1], input); - return rightGroup.closeEditor(input).then(() => { - assert.equal(input.isDisposed(), true); - }); - }); - }); - }); - }); + // Close input + await rootGroup.closeEditor(input); + assert.equal(input.isDisposed(), false); + + await rightGroup.closeEditor(input); + assert.equal(input.isDisposed(), true); }); - test('open to the side', function () { + test('open to the side', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -366,22 +391,20 @@ suite('Editor service', () => { const rootGroup = part.activeGroup; - return part.whenRestored.then(() => { - return service.openEditor(input1, { pinned: true }, rootGroup).then(editor => { - return service.openEditor(input1, { pinned: true, preserveFocus: true }, SIDE_GROUP).then(editor => { - assert.equal(part.activeGroup, rootGroup); - assert.equal(part.count, 2); - assert.equal(editor!.group, part.groups[1]); + await part.whenRestored; - // Open to the side uses existing neighbour group if any - return service.openEditor(input2, { pinned: true, preserveFocus: true }, SIDE_GROUP).then(editor => { - assert.equal(part.activeGroup, rootGroup); - assert.equal(part.count, 2); - assert.equal(editor!.group, part.groups[1]); - }); - }); - }); - }); + await service.openEditor(input1, { pinned: true }, rootGroup); + let editor = await service.openEditor(input1, { pinned: true, preserveFocus: true }, SIDE_GROUP); + + assert.equal(part.activeGroup, rootGroup); + assert.equal(part.count, 2); + assert.equal(editor!.group, part.groups[1]); + + // Open to the side uses existing neighbour group if any + editor = await service.openEditor(input2, { pinned: true, preserveFocus: true }, SIDE_GROUP); + assert.equal(part.activeGroup, rootGroup); + assert.equal(part.count, 2); + assert.equal(editor!.group, part.groups[1]); }); test('active editor change / visible editor change events', async function () { diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 002a49d799c..ee813e14fe5 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -31,7 +31,7 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput { constructor(defaultSettingsResource: URI, @ITextModelService textModelResolverService: ITextModelService ) { - super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService); + super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, undefined, textModelResolverService); } getTypeId(): string { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 2b34169a796..199e8c65bb6 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -18,7 +18,7 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel' import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; +import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceScheduler, timeout } from 'vs/base/common/async'; @@ -63,8 +63,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private resource: URI; - private contentEncoding: string; // encoding as reported from disk - private preferredEncoding: string; // encoding as chosen by the user + private contentEncoding: string; // encoding as reported from disk + private preferredEncoding: string; // encoding as chosen by the user + + private preferredMode: string; // mode as chosen by the user private versionId: number; private bufferSavedVersionId: number; @@ -92,6 +94,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil constructor( resource: URI, preferredEncoding: string, + preferredMode: string, @INotificationService private readonly notificationService: INotificationService, @IModeService modeService: IModeService, @IModelService modelService: IModelService, @@ -108,6 +111,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.resource = resource; this.preferredEncoding = preferredEncoding; + this.preferredMode = preferredMode; this.inOrphanMode = false; this.dirty = false; this.versionId = 0; @@ -208,11 +212,17 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } const firstLineText = this.getFirstLineText(this.textEditorModel); - const languageSelection = this.getOrCreateMode(this.modeService, undefined, firstLineText); + const languageSelection = this.getOrCreateMode(this.resource, this.modeService, this.preferredMode, firstLineText); this.modelService.setMode(this.textEditorModel, languageSelection); } + setMode(mode: string): void { + super.setMode(mode); + + this.preferredMode = mode; + } + async backup(target = this.resource): Promise { if (this.isResolved()) { @@ -451,7 +461,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.logService.trace('load() - created text editor model', this.resource); // Create model - this.createTextEditorModel(value, resource); + this.createTextEditorModel(value, resource, this.preferredMode); // We restored a backup so we have to set the model as being dirty // We also want to trigger auto save if it is enabled to simulate the exact same behaviour @@ -481,7 +491,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Update model value in a block that ignores model content change events this.blockModelContentChange = true; try { - this.updateTextEditorModel(value); + this.updateTextEditorModel(value, this.preferredMode); } finally { this.blockModelContentChange = false; } @@ -502,10 +512,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - protected getOrCreateMode(modeService: IModeService, preferredModeIds: string | undefined, firstLineText?: string): ILanguageSelection { - return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); - } - private onModelContentChanged(): void { this.logService.trace(`onModelContentChanged() - enter`, this.resource); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 1baca93526c..af67898ef69 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -153,7 +153,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Model does not exist else { - const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined); + const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined, options ? options.mode : undefined); modelPromise = model.load(options); // Install state change listener @@ -204,6 +204,11 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Remove from pending loads this.mapResourceToPendingModelLoaders.delete(resource); + // Apply mode if provided + if (options && options.mode) { + resolvedModel.setMode(options.mode); + } + return resolvedModel; } catch (error) { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 3b09be969dc..4c1dee67a1a 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; +import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; @@ -367,6 +367,11 @@ export interface IModelLoadOrCreateOptions { */ reason?: LoadReason; + /** + * The language mode to use for the model text content. + */ + mode?: string; + /** * The encoding to use when resolving the model text content. */ @@ -443,7 +448,7 @@ export interface ILoadOptions { reason?: LoadReason; } -export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport { +export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport { readonly onDidContentChange: Event; readonly onDidStateChange: Event; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index f496e66234a..e22cbe034db 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -14,6 +14,7 @@ import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/commo import { FileOperationResult, FileOperationError, IFileService } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; class ServiceAccessor { constructor(@ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService, @IFileService public fileService: TestFileService) { @@ -45,7 +46,7 @@ suite('Files - TextFileEditorModel', () => { }); test('save', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); @@ -70,7 +71,7 @@ suite('Files - TextFileEditorModel', () => { }); test('save - touching also emits saved event', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); @@ -90,7 +91,7 @@ suite('Files - TextFileEditorModel', () => { }); test('setEncoding - encode', function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.setEncoding('utf8', EncodingMode.Encode); // no-op assert.equal(getLastModifiedTime(model), -1); @@ -103,7 +104,7 @@ suite('Files - TextFileEditorModel', () => { }); test('setEncoding - decode', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.setEncoding('utf16', EncodingMode.Decode); @@ -112,8 +113,24 @@ suite('Files - TextFileEditorModel', () => { model.dispose(); }); + test('create with mode', async function () { + const mode = 'text-file-model-test'; + ModesRegistry.registerLanguage({ + id: mode, + }); + + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', mode); + + await model.load(); + + assert.equal(model.textEditorModel!.getModeId(), mode); + + model.dispose(); + assert.ok(!accessor.modelService.getModel(model.getResource())); + }); + test('disposes when underlying model is destroyed', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); @@ -122,7 +139,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load does not trigger save', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined); assert.ok(model.hasState(ModelState.SAVED)); model.onDidStateChange(e => { @@ -136,7 +153,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load returns dirty model as long as model is dirty', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); model.textEditorModel!.setValue('foo'); @@ -151,7 +168,7 @@ suite('Files - TextFileEditorModel', () => { test('Revert', async function () { let eventCounter = 0; - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.onDidStateChange(e => { if (e === StateChange.REVERTED) { @@ -173,7 +190,7 @@ suite('Files - TextFileEditorModel', () => { test('Revert (soft)', async function () { let eventCounter = 0; - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.onDidStateChange(e => { if (e === StateChange.REVERTED) { @@ -193,7 +210,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load and undo turns model dirty', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); accessor.fileService.setContent('Hello Change'); @@ -203,7 +220,7 @@ suite('Files - TextFileEditorModel', () => { }); test('File not modified error is handled gracefully', async function () { - let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); @@ -218,7 +235,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load error is handled gracefully if model already exists', async function () { - let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_FOUND)); @@ -264,7 +281,7 @@ suite('Files - TextFileEditorModel', () => { test('Save Participant', async function () { let eventCounter = 0; - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.onDidStateChange(e => { if (e === StateChange.SAVED) { @@ -294,7 +311,7 @@ suite('Files - TextFileEditorModel', () => { test('Save Participant, async participant', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); TextFileEditorModel.setSaveParticipant({ participate: (model) => { @@ -312,7 +329,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Save Participant, bad participant', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); TextFileEditorModel.setSaveParticipant({ participate: (model) => { diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 15be3b8533f..1b5f490f490 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -13,6 +13,7 @@ import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/file import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; +import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; export class TestTextFileEditorModelManager extends TextFileEditorModelManager { @@ -42,9 +43,9 @@ suite('Files - TextFileEditorModelManager', () => { test('add, remove, clear, get, getAll', function () { const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); - const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8'); - const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8'); - const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8'); + const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); + const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); + const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8', undefined); manager.add(URI.file('/test.html'), model1); manager.add(URI.file('/some/other.html'), model2); @@ -117,9 +118,9 @@ suite('Files - TextFileEditorModelManager', () => { test('removed from cache when model disposed', function () { const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); - const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8'); - const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8'); - const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8'); + const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); + const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); + const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8', undefined); manager.add(URI.file('/test.html'), model1); manager.add(URI.file('/some/other.html'), model2); @@ -290,4 +291,24 @@ suite('Files - TextFileEditorModelManager', () => { assert.ok(model.isDisposed()); manager.dispose(); }); + + test('mode', async function () { + const mode = 'text-file-model-manager-test'; + ModesRegistry.registerLanguage({ + id: mode, + }); + + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const resource = toResource.call(this, '/path/index_something.txt'); + + let model = await manager.loadOrCreate(resource, { mode }); + assert.equal(model.textEditorModel!.getModeId(), mode); + + model = await manager.loadOrCreate(resource, { mode: 'text' }); + assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID); + + manager.disposeModel((model as TextFileEditorModel)); + manager.dispose(); + }); }); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index 67fce66d3a0..ce5dd5dd772 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -66,7 +66,7 @@ suite('Files - TextFileService', () => { }); test('confirm onWillShutdown - no veto', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const event = new BeforeShutdownEventImpl(); @@ -81,7 +81,7 @@ suite('Files - TextFileService', () => { }); test('confirm onWillShutdown - veto if user cancels', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -97,7 +97,7 @@ suite('Files - TextFileService', () => { }); test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -123,7 +123,7 @@ suite('Files - TextFileService', () => { }); test('confirm onWillShutdown - save (hot.exit: off)', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -142,7 +142,7 @@ suite('Files - TextFileService', () => { }); test('isDirty/getDirty - files and untitled', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -169,7 +169,7 @@ suite('Files - TextFileService', () => { }); test('save - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -185,11 +185,11 @@ suite('Files - TextFileService', () => { test('save - UNC path', async function () { const untitledUncUri = URI.from({ scheme: 'untitled', authority: 'server', path: '/share/path/file.txt' }); - model = instantiationService.createInstance(TextFileEditorModel, untitledUncUri, 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, untitledUncUri, 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const mockedFileUri = untitledUncUri.with({ scheme: Schemas.file }); - const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8'); + const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8', undefined); const loadOrCreateStub = sinon.stub(accessor.textFileService.models, 'loadOrCreate', () => Promise.resolve(mockedEditorInput)); sinon.stub(accessor.untitledEditorService, 'exists', () => true); @@ -209,7 +209,7 @@ suite('Files - TextFileService', () => { }); test('saveAll - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -226,7 +226,7 @@ suite('Files - TextFileService', () => { }); test('saveAs - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -242,7 +242,7 @@ suite('Files - TextFileService', () => { }); test('revert - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -258,7 +258,7 @@ suite('Files - TextFileService', () => { }); test('delete - dirty file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -272,8 +272,8 @@ suite('Files - TextFileService', () => { }); test('move - dirty file', async function () { - let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); - let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target.txt'), 'utf8'); + let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target.txt'), 'utf8', undefined); (accessor.textFileService.models).add(sourceModel.getResource(), sourceModel); (accessor.textFileService.models).add(targetModel.getResource(), targetModel); @@ -393,7 +393,7 @@ suite('Files - TextFileService', () => { }); async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: true, shouldVeto: boolean): Promise { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; diff --git a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts index 10e4b34c2fb..b924c55963d 100644 --- a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts @@ -53,7 +53,7 @@ suite('Workbench - TextModelResolverService', () => { accessor.untitledEditorService.revertAll(); }); - test('resolve resource', function () { + test('resolve resource', async () => { const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: function (resource: URI): Promise { if (resource.scheme === 'test') { @@ -67,67 +67,60 @@ suite('Workbench - TextModelResolverService', () => { }); let resource = URI.from({ scheme: 'test', authority: null!, path: 'thePath' }); - let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource); + let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, undefined); - return input.resolve().then(async model => { - assert.ok(model); - assert.equal(snapshotToString((model as ResourceEditorModel).createSnapshot()!), 'Hello Test'); - - let disposed = false; - let disposedPromise = new Promise(resolve => { - Event.once(model.onDispose)(() => { - disposed = true; - resolve(); - }); - }); - input.dispose(); - await disposedPromise; - assert.equal(disposed, true); - - dispose.dispose(); - }); - }); - - test('resolve file', function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8'); - (accessor.textFileService.models).add(model.getResource(), model); - - return model.load().then(() => { - return accessor.textModelResolverService.createModelReference(model.getResource()).then(ref => { - const model = ref.object; - const editorModel = model.textEditorModel; - - assert.ok(editorModel); - assert.equal(editorModel.getValue(), 'Hello Html'); - - let disposed = false; - Event.once(model.onDispose)(() => { - disposed = true; - }); - - ref.dispose(); - return timeout(0).then(() => { // due to the reference resolving the model first which is async - assert.equal(disposed, true); - }); + const model = await input.resolve(); + assert.ok(model); + assert.equal(snapshotToString(((model as ResourceEditorModel).createSnapshot()!)), 'Hello Test'); + let disposed = false; + let disposedPromise = new Promise(resolve => { + Event.once(model.onDispose)(() => { + disposed = true; + resolve(); }); }); + input.dispose(); + + await disposedPromise; + assert.equal(disposed, true); + dispose.dispose(); }); - test('resolve untitled', function () { + test('resolve file', async function () { + const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); + (accessor.textFileService.models).add(textModel.getResource(), textModel); + + await textModel.load(); + + const ref = await accessor.textModelResolverService.createModelReference(textModel.getResource()); + + const model = ref.object; + const editorModel = model.textEditorModel; + + assert.ok(editorModel); + assert.equal(editorModel.getValue(), 'Hello Html'); + + let disposed = false; + Event.once(model.onDispose)(() => { + disposed = true; + }); + + ref.dispose(); + await timeout(0); // due to the reference resolving the model first which is async + assert.equal(disposed, true); + }); + + test('resolve untitled', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); - return input.resolve().then(() => { - return accessor.textModelResolverService.createModelReference(input.getResource()).then(ref => { - const model = ref.object; - const editorModel = model.textEditorModel; - - assert.ok(editorModel); - ref.dispose(); - - input.dispose(); - }); - }); + await input.resolve(); + const ref = await accessor.textModelResolverService.createModelReference(input.getResource()); + const model = ref.object; + const editorModel = model.textEditorModel; + assert.ok(editorModel); + ref.dispose(); + input.dispose(); }); test('even loading documents should be refcounted', async () => { @@ -135,12 +128,12 @@ suite('Workbench - TextModelResolverService', () => { let waitForIt = new Promise(c => resolveModel = c); const disposable = accessor.textModelResolverService.registerTextModelContentProvider('test', { - provideTextContent: (resource: URI): Promise => { - return waitForIt.then(_ => { - let modelContent = 'Hello Test'; - let languageSelection = accessor.modeService.create('json'); - return accessor.modelService.createModel(modelContent, languageSelection, resource); - }); + provideTextContent: async (resource: URI): Promise => { + await waitForIt; + + let modelContent = 'Hello Test'; + let languageSelection = accessor.modeService.create('json'); + return accessor.modelService.createModel(modelContent, languageSelection, resource); } }); diff --git a/src/vs/workbench/services/untitled/common/untitledEditorService.ts b/src/vs/workbench/services/untitled/common/untitledEditorService.ts index 6b343944f7c..6e8e2f07711 100644 --- a/src/vs/workbench/services/untitled/common/untitledEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledEditorService.ts @@ -21,7 +21,7 @@ export const IUntitledEditorService = createDecorator('u export interface IModelLoadOrCreateOptions { resource?: URI; - modeId?: string; + mode?: string; initialValue?: string; encoding?: string; useResourcePath?: boolean; @@ -29,7 +29,7 @@ export interface IModelLoadOrCreateOptions { export interface IUntitledEditorService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; /** * Events for when untitled editors content changes (e.g. any keystroke). @@ -78,7 +78,7 @@ export interface IUntitledEditorService { * It is valid to pass in a file resource. In that case the path will be used as identifier. * The use case is to be able to create a new file with a specific path with VSCode. */ - createOrGet(resource?: URI, modeId?: string, initialValue?: string, encoding?: string): UntitledEditorInput; + createOrGet(resource?: URI, mode?: string, initialValue?: string, encoding?: string): UntitledEditorInput; /** * Creates a new untitled model with the optional resource URI or returns an existing one @@ -184,10 +184,10 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } loadOrCreate(options: IModelLoadOrCreateOptions = Object.create(null)): Promise { - return this.createOrGet(options.resource, options.modeId, options.initialValue, options.encoding, options.useResourcePath).resolve(); + return this.createOrGet(options.resource, options.mode, options.initialValue, options.encoding, options.useResourcePath).resolve(); } - createOrGet(resource?: URI, modeId?: string, initialValue?: string, encoding?: string, hasAssociatedFilePath: boolean = false): UntitledEditorInput { + createOrGet(resource?: URI, mode?: string, initialValue?: string, encoding?: string, hasAssociatedFilePath: boolean = false): UntitledEditorInput { if (resource) { // Massage resource if it comes with known file based resource @@ -207,10 +207,10 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } // Create new otherwise - return this.doCreate(resource, hasAssociatedFilePath, modeId, initialValue, encoding); + return this.doCreate(resource, hasAssociatedFilePath, mode, initialValue, encoding); } - private doCreate(resource?: URI, hasAssociatedFilePath?: boolean, modeId?: string, initialValue?: string, encoding?: string): UntitledEditorInput { + private doCreate(resource?: URI, hasAssociatedFilePath?: boolean, mode?: string, initialValue?: string, encoding?: string): UntitledEditorInput { if (!resource) { // Create new taking a resource URI that is not already taken @@ -222,14 +222,14 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } // Look up default language from settings if any - if (!modeId && !hasAssociatedFilePath) { + if (!mode && !hasAssociatedFilePath) { const configuration = this.configurationService.getValue(); if (configuration.files && configuration.files.defaultLanguage) { - modeId = configuration.files.defaultLanguage; + mode = configuration.files.defaultLanguage; } } - const input = this.instantiationService.createInstance(UntitledEditorInput, resource, hasAssociatedFilePath, modeId, initialValue, encoding); + const input = this.instantiationService.createInstance(UntitledEditorInput, resource, hasAssociatedFilePath, mode, initialValue, encoding); const contentListener = input.onDidModelChangeContent(() => { this._onDidChangeContent.fire(resource!); diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index 4f7400dbc91..c1ab20f2573 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -86,7 +86,7 @@ class MyResourceInput extends ResourceEditorInput { } suite('Workbench base editor', () => { - test('BaseEditor API', function () { + test('BaseEditor API', async () => { let e = new MyEditor(NullTelemetryService); let input = new MyOtherInput(); let options = new EditorOptions(); @@ -94,25 +94,24 @@ suite('Workbench base editor', () => { assert(!e.isVisible()); assert(!e.input); assert(!e.options); - return e.setInput(input, options, CancellationToken.None).then(() => { - assert.strictEqual(input, e.input); - assert.strictEqual(options, e.options); - const group = new TestEditorGroup(1); - e.setVisible(true, group); - assert(e.isVisible()); - assert.equal(e.group, group); - input.onDispose(() => { - assert(false); - }); - e.dispose(); - e.clearInput(); - e.setVisible(false, group); - assert(!e.isVisible()); - assert(!e.input); - assert(!e.options); - assert(!e.getControl()); + await e.setInput(input, options, CancellationToken.None); + assert.strictEqual(input, e.input); + assert.strictEqual(options, e.options); + const group = new TestEditorGroup(1); + e.setVisible(true, group); + assert(e.isVisible()); + assert.equal(e.group, group); + input.onDispose(() => { + assert(false); }); + e.dispose(); + e.clearInput(); + e.setVisible(false, group); + assert(!e.isVisible()); + assert(!e.input); + assert(!e.options); + assert(!e.getControl()); }); test('EditorDescriptor', () => { @@ -154,10 +153,10 @@ suite('Workbench base editor', () => { let inst = new TestInstantiationService(); - const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake')))!.instantiate(inst); + const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); assert.strictEqual(editor.getId(), 'myEditor'); - const otherEditor = EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', URI.file('/fake')))!.instantiate(inst); + const otherEditor = EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); assert.strictEqual(otherEditor.getId(), 'myOtherEditor'); (EditorRegistry).setEditors(oldEditors); @@ -173,7 +172,7 @@ suite('Workbench base editor', () => { let inst = new TestInstantiationService(); - const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake')))!.instantiate(inst); + const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); assert.strictEqual('myOtherEditor', editor.getId()); (EditorRegistry).setEditors(oldEditors); diff --git a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts index 3023add68ca..662bd56d224 100644 --- a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts @@ -35,7 +35,7 @@ suite('Workbench editor model', () => { accessor = instantiationService.createInstance(ServiceAccessor); }); - test('TextDiffEditorModel', () => { + test('TextDiffEditorModel', async () => { const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: function (resource: URI): Promise { if (resource.scheme === 'test') { @@ -48,27 +48,26 @@ suite('Workbench editor model', () => { } }); - let input = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' })); - let otherInput = instantiationService.createInstance(ResourceEditorInput, 'name2', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' })); + let input = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined); + let otherInput = instantiationService.createInstance(ResourceEditorInput, 'name2', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined); let diffInput = new DiffEditorInput('name', 'description', input, otherInput); - return diffInput.resolve().then((model: any) => { - assert(model); - assert(model instanceof TextDiffEditorModel); + let model = await diffInput.resolve() as TextDiffEditorModel; - let diffEditorModel = model.textDiffEditorModel; - assert(diffEditorModel.original); - assert(diffEditorModel.modified); + assert(model); + assert(model instanceof TextDiffEditorModel); - return diffInput.resolve().then((model: any) => { - assert(model.isResolved()); + let diffEditorModel = model.textDiffEditorModel!; + assert(diffEditorModel.original); + assert(diffEditorModel.modified); - assert(diffEditorModel !== model.textDiffEditorModel); - diffInput.dispose(); - assert(!model.textDiffEditorModel); + model = await diffInput.resolve() as TextDiffEditorModel; + assert(model.isResolved()); - dispose.dispose(); - }); - }); + assert(diffEditorModel !== model.textDiffEditorModel); + diffInput.dispose(); + assert(!model.textDiffEditorModel); + + dispose.dispose(); }); }); diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index fde39c097be..18d862a91eb 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -111,27 +111,17 @@ class TestFileEditorInput extends EditorInput implements IFileEditorInput { } getTypeId() { return 'testFileEditorInputForGroups'; } resolve(): Promise { return Promise.resolve(null!); } + setEncoding(encoding: string) { } + getEncoding(): string { return null!; } + setPreferredEncoding(encoding: string) { } + getResource(): URI { return this.resource; } + setForceOpenAsBinary(): void { } + setMode(mode: string) { } + setPreferredMode(mode: string) { } matches(other: TestFileEditorInput): boolean { return other && this.id === other.id && other instanceof TestFileEditorInput; } - - setEncoding(encoding: string) { - } - - getEncoding(): string { - return null!; - } - - setPreferredEncoding(encoding: string) { - } - - getResource(): URI { - return this.resource; - } - - setForceOpenAsBinary(): void { - } } function input(id = String(index++), nonSerializable?: boolean, resource?: URI): EditorInput { diff --git a/src/vs/workbench/test/common/editor/editorModel.test.ts b/src/vs/workbench/test/common/editor/editorModel.test.ts index 513d2783fa1..663f9268501 100644 --- a/src/vs/workbench/test/common/editor/editorModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorModel.test.ts @@ -21,8 +21,8 @@ import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTe class MyEditorModel extends EditorModel { } class MyTextEditorModel extends BaseTextEditorModel { - public createTextEditorModel(value: ITextBufferFactory, resource?: URI, modeId?: string) { - return super.createTextEditorModel(value, resource, modeId); + public createTextEditorModel(value: ITextBufferFactory, resource?: URI, preferredMode?: string) { + return super.createTextEditorModel(value, resource, preferredMode); } isReadonly(): boolean { @@ -40,7 +40,7 @@ suite('Workbench editor model', () => { modeService = instantiationService.stub(IModeService, ModeServiceImpl); }); - test('EditorModel', () => { + test('EditorModel', async () => { let counter = 0; let m = new MyEditorModel(); @@ -50,25 +50,23 @@ suite('Workbench editor model', () => { counter++; }); - return m.load().then(model => { - assert(model === m); - assert.strictEqual(m.isResolved(), true); - m.dispose(); - assert.equal(counter, 1); - }); + const model = await m.load(); + assert(model === m); + assert.strictEqual(m.isResolved(), true); + m.dispose(); + assert.equal(counter, 1); }); - test('BaseTextEditorModel', () => { + test('BaseTextEditorModel', async () => { let modelService = stubModelService(instantiationService); let m = new MyTextEditorModel(modelService, modeService); - return m.load().then((model: MyTextEditorModel) => { - assert(model === m); - model.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain'); - assert.strictEqual(m.isResolved(), true); - }).then(() => { - m.dispose(); - }); + const model = await m.load() as MyTextEditorModel; + + assert(model === m); + model.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain'); + assert.strictEqual(m.isResolved(), true); + m.dispose(); }); function stubModelService(instantiationService: TestInstantiationService): IModelService { diff --git a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts index 954b83d9384..d1e788e80f4 100644 --- a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts @@ -12,17 +12,16 @@ import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestSe import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; class ServiceAccessor { constructor( @IModelService public modelService: IModelService, @IModeService public modeService: IModeService - ) { - } + ) { } } suite('Workbench resource editor input', () => { - let instantiationService: IInstantiationService; let accessor: ServiceAccessor; @@ -31,14 +30,33 @@ suite('Workbench resource editor input', () => { accessor = instantiationService.createInstance(ServiceAccessor); }); - test('simple', () => { - let resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); + test('basics', async () => { + const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource); - let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource); - return input.resolve().then(model => { - assert.ok(model); - assert.equal(snapshotToString((model as ResourceEditorModel).createSnapshot()!), 'function test() {}'); + const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, undefined); + + const model = await input.resolve(); + + assert.ok(model); + assert.equal(snapshotToString(((model as ResourceEditorModel).createSnapshot()!)), 'function test() {}'); + }); + + test('custom mode', async () => { + ModesRegistry.registerLanguage({ + id: 'resource-input-test', }); + + const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); + accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource); + + const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, 'resource-input-test'); + + const model = await input.resolve(); + assert.ok(model); + assert.equal(model.textEditorModel.getModeId(), 'resource-input-test'); + + input.setMode('text'); + assert.equal(model.textEditorModel.getModeId(), PLAINTEXT_MODE_ID); }); }); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/untitledEditor.test.ts b/src/vs/workbench/test/common/editor/untitledEditor.test.ts index 3dc06265b44..4f9ea75cbda 100644 --- a/src/vs/workbench/test/common/editor/untitledEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledEditor.test.ts @@ -16,6 +16,7 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { timeout } from 'vs/base/common/async'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; export class TestUntitledEditorService extends UntitledEditorService { get(resource: URI) { return super.get(resource); } @@ -45,7 +46,7 @@ suite('Workbench untitled editors', () => { accessor.untitledEditorService.dispose(); }); - test('Untitled Editor Service', function (done) { + test('Untitled Editor Service', async (done) => { const service = accessor.untitledEditorService; assert.equal(service.getAll().length, 0); @@ -68,36 +69,35 @@ suite('Workbench untitled editors', () => { assert.equal(service.getAll().length, 1); // dirty - input2.resolve().then(model => { - assert.ok(!service.isDirty(input2.getResource())); + const model = await input2.resolve(); - const listener = service.onDidChangeDirty(resource => { - listener.dispose(); + assert.ok(!service.isDirty(input2.getResource())); - assert.equal(resource.toString(), input2.getResource().toString()); + const listener = service.onDidChangeDirty(resource => { + listener.dispose(); - assert.ok(service.isDirty(input2.getResource())); - assert.equal(service.getDirty()[0].toString(), input2.getResource().toString()); - assert.equal(service.getDirty([input2.getResource()])[0].toString(), input2.getResource().toString()); - assert.equal(service.getDirty([input1.getResource()]).length, 0); + assert.equal(resource.toString(), input2.getResource().toString()); - service.revertAll(); - assert.equal(service.getAll().length, 0); - assert.ok(!input2.isDirty()); - assert.ok(!model.isDirty()); + assert.ok(service.isDirty(input2.getResource())); + assert.equal(service.getDirty()[0].toString(), input2.getResource().toString()); + assert.equal(service.getDirty([input2.getResource()])[0].toString(), input2.getResource().toString()); + assert.equal(service.getDirty([input1.getResource()]).length, 0); - input2.dispose(); + service.revertAll(); + assert.equal(service.getAll().length, 0); + assert.ok(!input2.isDirty()); + assert.ok(!model.isDirty()); - assert.ok(!service.exists(input2.getResource())); + input2.dispose(); - done(); - }); + assert.ok(!service.exists(input2.getResource())); + done(); + }); - model.textEditorModel.setValue('foo bar'); - }, err => done(err)); + model.textEditorModel.setValue('foo bar'); }); - test('Untitled with associated resource', function () { + test('Untitled with associated resource', () => { const service = accessor.untitledEditorService; const file = URI.file(join('C:\\', '/foo/file.txt')); const untitled = service.createOrGet(file); @@ -107,53 +107,49 @@ suite('Workbench untitled editors', () => { untitled.dispose(); }); - test('Untitled no longer dirty when content gets empty', function () { + test('Untitled no longer dirty when content gets empty', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); // dirty - return input.resolve().then(model => { - model.textEditorModel.setValue('foo bar'); - assert.ok(model.isDirty()); - - model.textEditorModel.setValue(''); - assert.ok(!model.isDirty()); - - input.dispose(); - }); + const model = await input.resolve(); + model.textEditorModel.setValue('foo bar'); + assert.ok(model.isDirty()); + model.textEditorModel.setValue(''); + assert.ok(!model.isDirty()); + input.dispose(); }); - test('Untitled via loadOrCreate', function () { + test('Untitled via loadOrCreate', async () => { const service = accessor.untitledEditorService; - service.loadOrCreate().then(model1 => { - model1.textEditorModel!.setValue('foo bar'); - assert.ok(model1.isDirty()); - model1.textEditorModel!.setValue(''); - assert.ok(!model1.isDirty()); + const model1 = await service.loadOrCreate(); - return service.loadOrCreate({ initialValue: 'Hello World' }).then(model2 => { - assert.equal(snapshotToString(model2.createSnapshot()!), 'Hello World'); + model1.textEditorModel!.setValue('foo bar'); + assert.ok(model1.isDirty()); - const input = service.createOrGet(); + model1.textEditorModel!.setValue(''); + assert.ok(!model1.isDirty()); - return service.loadOrCreate({ resource: input.getResource() }).then(model3 => { - assert.equal(model3.getResource().toString(), input.getResource().toString()); + const model2 = await service.loadOrCreate({ initialValue: 'Hello World' }); + assert.equal(snapshotToString(model2.createSnapshot()!), 'Hello World'); - const file = URI.file(join('C:\\', '/foo/file44.txt')); - return service.loadOrCreate({ resource: file }).then(model4 => { - assert.ok(service.hasAssociatedFilePath(model4.getResource())); - assert.ok(model4.isDirty()); + const input = service.createOrGet(); - model1.dispose(); - model2.dispose(); - model3.dispose(); - model4.dispose(); - input.dispose(); - }); - }); - }); - }); + const model3 = await service.loadOrCreate({ resource: input.getResource() }); + + assert.equal(model3.getResource().toString(), input.getResource().toString()); + + const file = URI.file(join('C:\\', '/foo/file44.txt')); + const model4 = await service.loadOrCreate({ resource: file }); + assert.ok(service.hasAssociatedFilePath(model4.getResource())); + assert.ok(model4.isDirty()); + + model1.dispose(); + model2.dispose(); + model3.dispose(); + model4.dispose(); + input.dispose(); }); test('Untitled suggest name', function () { @@ -163,24 +159,21 @@ suite('Workbench untitled editors', () => { assert.ok(service.suggestFileName(input.getResource())); }); - test('Untitled with associated path remains dirty when content gets empty', function () { + test('Untitled with associated path remains dirty when content gets empty', async () => { const service = accessor.untitledEditorService; const file = URI.file(join('C:\\', '/foo/file.txt')); const input = service.createOrGet(file); // dirty - return input.resolve().then(model => { - model.textEditorModel.setValue('foo bar'); - assert.ok(model.isDirty()); - - model.textEditorModel.setValue(''); - assert.ok(model.isDirty()); - - input.dispose(); - }); + const model = await input.resolve(); + model.textEditorModel.setValue('foo bar'); + assert.ok(model.isDirty()); + model.textEditorModel.setValue(''); + assert.ok(model.isDirty()); + input.dispose(); }); - test('Untitled created with files.defaultLanguage setting', function () { + test('Untitled created with files.defaultLanguage setting', () => { const defaultLanguage = 'javascript'; const config = accessor.testConfigurationService; config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); @@ -188,30 +181,52 @@ suite('Workbench untitled editors', () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); - assert.equal(input.getModeId(), defaultLanguage); + assert.equal(input.getMode(), defaultLanguage); config.setUserConfiguration('files', { 'defaultLanguage': undefined }); input.dispose(); }); - test('Untitled created with modeId overrides files.defaultLanguage setting', function () { - const modeId = 'typescript'; + test('Untitled created with mode overrides files.defaultLanguage setting', () => { + const mode = 'typescript'; const defaultLanguage = 'javascript'; const config = accessor.testConfigurationService; config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); const service = accessor.untitledEditorService; - const input = service.createOrGet(null!, modeId); + const input = service.createOrGet(null!, mode); - assert.equal(input.getModeId(), modeId); + assert.equal(input.getMode(), mode); config.setUserConfiguration('files', { 'defaultLanguage': undefined }); input.dispose(); }); - test('encoding change event', function () { + test('Untitled can change mode afterwards', async () => { + const mode = 'untitled-input-test'; + + ModesRegistry.registerLanguage({ + id: mode, + }); + + const service = accessor.untitledEditorService; + const input = service.createOrGet(null!, mode); + + assert.equal(input.getMode(), mode); + + const model = await input.resolve(); + assert.equal(model.getMode(), mode); + + input.setMode('text'); + + assert.equal(input.getMode(), PLAINTEXT_MODE_ID); + + input.dispose(); + }); + + test('encoding change event', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); @@ -223,16 +238,13 @@ suite('Workbench untitled editors', () => { }); // dirty - return input.resolve().then(model => { - model.setEncoding('utf16'); - - assert.equal(counter, 1); - - input.dispose(); - }); + const model = await input.resolve(); + model.setEncoding('utf16'); + assert.equal(counter, 1); + input.dispose(); }); - test('onDidChangeContent event', () => { + test('onDidChangeContent event', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); @@ -245,39 +257,32 @@ suite('Workbench untitled editors', () => { assert.equal(r.toString(), input.getResource().toString()); }); - return input.resolve().then(model => { - model.textEditorModel.setValue('foo'); - assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); + const model = await input.resolve(); + model.textEditorModel.setValue('foo'); + assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); - return timeout(3).then(() => { - assert.equal(counter, 1, 'Dirty model should trigger event'); + await timeout(3); + assert.equal(counter, 1, 'Dirty model should trigger event'); + model.textEditorModel.setValue('bar'); - model.textEditorModel.setValue('bar'); - return timeout(3).then(() => { - assert.equal(counter, 2, 'Content change when dirty should trigger event'); + await timeout(3); + assert.equal(counter, 2, 'Content change when dirty should trigger event'); + model.textEditorModel.setValue(''); - model.textEditorModel.setValue(''); - return timeout(3).then(() => { - assert.equal(counter, 3, 'Manual revert should trigger event'); + await timeout(3); + assert.equal(counter, 3, 'Manual revert should trigger event'); + model.textEditorModel.setValue('foo'); - model.textEditorModel.setValue('foo'); - return timeout(3).then(() => { - assert.equal(counter, 4, 'Dirty model should trigger event'); + await timeout(3); + assert.equal(counter, 4, 'Dirty model should trigger event'); + model.revert(); - model.revert(); - return timeout(3).then(() => { - assert.equal(counter, 5, 'Revert should trigger event'); - - input.dispose(); - }); - }); - }); - }); - }); - }); + await timeout(3); + assert.equal(counter, 5, 'Revert should trigger event'); + input.dispose(); }); - test('onDidDisposeModel event', () => { + test('onDidDisposeModel event', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); @@ -288,10 +293,9 @@ suite('Workbench untitled editors', () => { assert.equal(r.toString(), input.getResource().toString()); }); - return input.resolve().then(model => { - assert.equal(counter, 0); - input.dispose(); - assert.equal(counter, 1); - }); + await input.resolve(); + assert.equal(counter, 0); + input.dispose(); + assert.equal(counter, 1); }); }); \ No newline at end of file diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts index 54cced7af70..502d3fae8e9 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts @@ -37,7 +37,7 @@ suite('MainThreadSaveParticipant', function () { }); test('insert final new line', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -70,7 +70,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -105,7 +105,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines bug#39750', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -132,7 +132,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines bug#46075', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 89df06ec141..b917b7ac10a 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -85,7 +85,7 @@ import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; import { BrowserTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { - return instantiationService.createInstance(FileEditorInput, resource, undefined); + return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); } export const TestEnvironmentService = new WorkbenchEnvironmentService(parseArgs(process.argv) as IWindowConfiguration, process.execPath); From be7df88d593053879f9c8df540d4a406aea0b0c7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 8 May 2019 08:02:41 +0200 Subject: [PATCH 16/99] :lipstick: --- src/vs/workbench/common/editor/untitledEditorModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 7716742e212..2b309bcd18e 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -81,7 +81,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin getMode(): string | undefined { if (this.textEditorModel) { - return this.textEditorModel.getLanguageIdentifier().language; + return this.textEditorModel.getModeId(); } return this.preferredMode; From 59886bab426eb9df686d56ff9f1b7df6e7d61211 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 8 May 2019 08:07:37 +0200 Subject: [PATCH 17/99] :lipstick: --- .../workbench/services/textfile/common/textFileService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index ebd718f913d..1b853691084 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -40,6 +40,7 @@ import { trim } from 'vs/base/common/strings'; import { VSBuffer } from 'vs/base/common/buffer'; import { ITextSnapshot } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; /** * The workbench file service implementation implements the raw file service spec and adds additional methods on top. @@ -857,9 +858,9 @@ export abstract class TextFileService extends Disposable implements ITextFileSer if (isResolvedTextEditorModel(sourceModel) && isResolvedTextEditorModel(targetModel)) { this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot())); - const language = sourceModel.textEditorModel.getLanguageIdentifier(); - if (language.id > 1) { - targetModel.textEditorModel.setMode(language); // only use if more specific than plain/text + const mode = sourceModel.textEditorModel.getLanguageIdentifier(); + if (mode.language !== PLAINTEXT_MODE_ID) { + targetModel.textEditorModel.setMode(mode); // only use if more specific than plain/text } } From 4064a323e1ffc68f11ad2af2b0f4ea862fc178e9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 8 May 2019 08:13:07 +0200 Subject: [PATCH 18/99] :lipstick: --- .../untitled/common/untitledEditorService.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/untitled/common/untitledEditorService.ts b/src/vs/workbench/services/untitled/common/untitledEditorService.ts index 6e8e2f07711..35b571ec52b 100644 --- a/src/vs/workbench/services/untitled/common/untitledEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledEditorService.ts @@ -211,14 +211,17 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } private doCreate(resource?: URI, hasAssociatedFilePath?: boolean, mode?: string, initialValue?: string, encoding?: string): UntitledEditorInput { - if (!resource) { + let untitledResource: URI; + if (resource) { + untitledResource = resource; + } else { // Create new taking a resource URI that is not already taken let counter = this.mapResourceToInput.size + 1; do { - resource = URI.from({ scheme: Schemas.untitled, path: `Untitled-${counter}` }); + untitledResource = URI.from({ scheme: Schemas.untitled, path: `Untitled-${counter}` }); counter++; - } while (this.mapResourceToInput.has(resource)); + } while (this.mapResourceToInput.has(untitledResource)); } // Look up default language from settings if any @@ -229,22 +232,22 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } } - const input = this.instantiationService.createInstance(UntitledEditorInput, resource, hasAssociatedFilePath, mode, initialValue, encoding); + const input = this.instantiationService.createInstance(UntitledEditorInput, untitledResource, hasAssociatedFilePath, mode, initialValue, encoding); const contentListener = input.onDidModelChangeContent(() => { - this._onDidChangeContent.fire(resource!); + this._onDidChangeContent.fire(untitledResource); }); const dirtyListener = input.onDidChangeDirty(() => { - this._onDidChangeDirty.fire(resource!); + this._onDidChangeDirty.fire(untitledResource); }); const encodingListener = input.onDidModelChangeEncoding(() => { - this._onDidChangeEncoding.fire(resource!); + this._onDidChangeEncoding.fire(untitledResource); }); const disposeListener = input.onDispose(() => { - this._onDidDisposeModel.fire(resource!); + this._onDidDisposeModel.fire(untitledResource); }); // Remove from cache on dispose @@ -259,7 +262,7 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor }); // Add to cache - this.mapResourceToInput.set(resource, input); + this.mapResourceToInput.set(untitledResource, input); return input; } From 7b0eede51b23a6bcfacd6c36d6e9185f2ebec168 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 8 May 2019 08:15:25 +0200 Subject: [PATCH 19/99] untitled - mark as dirty when having initial contents --- src/vs/workbench/common/editor/untitledEditorModel.ts | 2 +- .../test/common/editor/untitledEditor.test.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 2b309bcd18e..a658a8f3793 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -146,7 +146,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin const hasBackup = !!backup; // untitled associated to file path are dirty right away as well as untitled with content - this.setDirty(this._hasAssociatedFilePath || hasBackup); + this.setDirty(this._hasAssociatedFilePath || hasBackup || !!this.initialValue); let untitledContents: ITextBufferFactory; if (backup) { diff --git a/src/vs/workbench/test/common/editor/untitledEditor.test.ts b/src/vs/workbench/test/common/editor/untitledEditor.test.ts index 4f9ea75cbda..001ad7a6a2b 100644 --- a/src/vs/workbench/test/common/editor/untitledEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledEditor.test.ts @@ -173,6 +173,16 @@ suite('Workbench untitled editors', () => { input.dispose(); }); + test('Untitled with initial content is dirty', async () => { + const service = accessor.untitledEditorService; + const input = service.createOrGet(undefined, undefined, 'Hello World'); + + // dirty + const model = await input.resolve(); + assert.ok(model.isDirty()); + input.dispose(); + }); + test('Untitled created with files.defaultLanguage setting', () => { const defaultLanguage = 'javascript'; const config = accessor.testConfigurationService; From 6fa5b76a83b4d3dd67d1abde3ef589b052bdca16 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 8 May 2019 11:21:58 +0200 Subject: [PATCH 20/99] use vscodehub docker images --- build/azure-pipelines/product-build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 86868f6a2a6..c8bedfbffc0 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -1,9 +1,11 @@ resources: containers: - container: vscode-x64 - image: joaomoreno/vscode-linux-build-agent:x64 + endpoint: VSCodeHub + image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 - container: vscode-ia32 - image: joaomoreno/vscode-linux-build-agent:ia32 + endpoint: VSCodeHub + image: vscodehub.azurecr.io/vscode-linux-build-agent:ia32 - container: snapcraft image: snapcore/snapcraft From 04a2aa9fd733d6cb48f76ed376f0e73b408371f9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 9 May 2019 10:47:24 +0200 Subject: [PATCH 21/99] compare - use same mode when comparing to clipboard --- src/vs/workbench/contrib/files/browser/fileActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 5a106b857a7..ede015a2dc1 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -860,7 +860,7 @@ class ClipboardContentProvider implements ITextModelContentProvider { ) { } provideTextContent(resource: URI): Promise { - const model = this.modelService.createModel(this.clipboardService.readText(), this.modeService.create('text/plain'), resource); + const model = this.modelService.createModel(this.clipboardService.readText(), this.modeService.createByFilepathOrFirstLine(resource.path), resource); return Promise.resolve(model); } From e1c2fcf9550a24140245a0d58c478632d3c4e242 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 9 May 2019 10:50:31 +0200 Subject: [PATCH 22/99] debt - no more monaco-shell --- src/vs/code/electron-browser/workbench/workbench.js | 4 ++-- .../code/electron-browser/workbench/workbench.nodeless.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index dbec9eadbbb..28e3fefafcc 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -87,8 +87,8 @@ function showPartsSplash(configuration) { const style = document.createElement('style'); style.className = 'initialShellColors'; document.head.appendChild(style); - document.body.className = `monaco-shell ${baseTheme}`; - style.innerHTML = `.monaco-shell { background-color: ${shellBackground}; color: ${shellForeground}; }`; + document.body.className = baseTheme; + style.innerHTML = `body { background-color: ${shellBackground}; color: ${shellForeground}; }`; if (data && data.layoutInfo) { // restore parts if possible (we might not always store layout info) diff --git a/src/vs/code/electron-browser/workbench/workbench.nodeless.html b/src/vs/code/electron-browser/workbench/workbench.nodeless.html index e37abfdf5ae..de77f56d68e 100644 --- a/src/vs/code/electron-browser/workbench/workbench.nodeless.html +++ b/src/vs/code/electron-browser/workbench/workbench.nodeless.html @@ -4,7 +4,7 @@ - + From 4f0536ad73fa09452f30af6cca0ed9d0697ec1c0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 9 May 2019 09:14:05 +0200 Subject: [PATCH 23/99] add logs to shell env --- src/vs/code/electron-main/app.ts | 4 ++-- src/vs/code/node/shellEnv.ts | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 5ba6e094404..8b1c92ffa72 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -234,7 +234,7 @@ export class CodeApplication extends Disposable { ipc.on('vscode:fetchShellEnv', (event: Event) => { const webContents = event.sender; - getShellEnvironment().then(shellEnv => { + getShellEnvironment(this.logService).then(shellEnv => { if (!webContents.isDestroyed()) { webContents.send('vscode:acceptShellEnv', shellEnv); } @@ -678,7 +678,7 @@ export class CodeApplication extends Disposable { historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList()); // Start shared process after a while - const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment().then(userEnv => this.sharedProcess.spawn(userEnv)), 3000)); + const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment(this.logService).then(userEnv => this.sharedProcess.spawn(userEnv)), 3000)); sharedProcessSpawn.schedule(); } diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index 75c4863068e..baabaf7b959 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -7,11 +7,16 @@ import * as cp from 'child_process'; import { assign } from 'vs/base/common/objects'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; +import { ILogService } from 'vs/platform/log/common/log'; -function getUnixShellEnvironment(): Promise { +function getUnixShellEnvironment(logService: ILogService): Promise { const promise = new Promise((resolve, reject) => { const runAsNode = process.env['ELECTRON_RUN_AS_NODE']; + logService.trace('getUnixShellEnvironment#runAsNode', runAsNode); + const noAttach = process.env['ELECTRON_NO_ATTACH_CONSOLE']; + logService.trace('getUnixShellEnvironment#noAttach', noAttach); + const mark = generateUuid().replace(/-/g, '').substr(0, 12); const regex = new RegExp(mark + '(.*)' + mark); @@ -21,6 +26,9 @@ function getUnixShellEnvironment(): Promise { }); const command = `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`; + logService.trace('getUnixShellEnvironment#env', env); + logService.trace('getUnixShellEnvironment#spawn', command); + const child = cp.spawn(process.env.SHELL!, ['-ilc', command], { detached: true, stdio: ['ignore', 'pipe', process.stderr], @@ -37,6 +45,8 @@ function getUnixShellEnvironment(): Promise { } const raw = Buffer.concat(buffers).toString('utf8'); + logService.trace('getUnixShellEnvironment#raw', raw); + const match = regex.exec(raw); const rawStripped = match ? match[1] : '{}'; @@ -58,8 +68,10 @@ function getUnixShellEnvironment(): Promise { // https://github.com/Microsoft/vscode/issues/22593#issuecomment-336050758 delete env['XDG_RUNTIME_DIR']; + logService.trace('getUnixShellEnvironment#result', env); resolve(env); } catch (err) { + logService.error('getUnixShellEnvironment#error', err); reject(err); } }); @@ -77,14 +89,17 @@ let _shellEnv: Promise; * This should only be done when Code itself is not launched * from within a shell. */ -export function getShellEnvironment(): Promise { +export function getShellEnvironment(logService: ILogService): Promise { if (_shellEnv === undefined) { if (isWindows) { + logService.trace('getShellEnvironment: runing on windows, skipping'); _shellEnv = Promise.resolve({}); } else if (process.env['VSCODE_CLI'] === '1') { + logService.trace('getShellEnvironment: runing on CLI, skipping'); _shellEnv = Promise.resolve({}); } else { - _shellEnv = getUnixShellEnvironment(); + logService.trace('getShellEnvironment: running on Unix'); + _shellEnv = getUnixShellEnvironment(logService); } } From 88274285f3336ed254250bac00ab47456fdbfc42 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 9 May 2019 11:22:03 +0200 Subject: [PATCH 24/99] use keyvault for secrets: linux --- build/azure-pipelines/linux/product-build-linux.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 342d72e4969..31840aed609 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -7,6 +7,12 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'VS Code Setup, Update, and Build Services (8afa857f-ba38-4efa-bd5b-41fc50ac5200)' + KeyVaultName: vscode + - script: | set -e export npm_config_arch="$(VSCODE_ARCH)" @@ -55,7 +61,7 @@ steps: - script: | set -e - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \ From 4b5c68c1bd5aa4b0f6d4c27702397a34e0f95cea Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 9 May 2019 13:13:27 +0200 Subject: [PATCH 25/99] web - scaffold a basic dev setup via "yarn web" --- package.json | 5 +- scripts/code-web.js | 12 ++ src/buildfile.js | 2 +- src/vs/code/browser/workbench/workbench.html | 12 ++ src/vs/code/browser/workbench/workbench.js | 40 ++++++ .../workbench/workbench.nodeless.html | 12 -- .../workbench/workbench.nodeless.js | 67 ---------- src/vs/code/electron-main/window.ts | 18 +-- .../environment/common/environment.ts | 1 - src/vs/platform/environment/node/argv.ts | 1 - .../browser/{nodeless.main.ts => web.main.ts} | 2 +- ...impleservices.ts => web.simpleservices.ts} | 12 +- ...deless.main.css => workbench.web.main.css} | 0 ....main.nls.js => workbench.web.main.nls.js} | 0 ...nodeless.main.ts => workbench.web.main.ts} | 4 +- yarn.lock | 121 +++++++++++++++++- 16 files changed, 197 insertions(+), 112 deletions(-) create mode 100644 scripts/code-web.js create mode 100644 src/vs/code/browser/workbench/workbench.html create mode 100644 src/vs/code/browser/workbench/workbench.js delete mode 100644 src/vs/code/electron-browser/workbench/workbench.nodeless.html delete mode 100644 src/vs/code/electron-browser/workbench/workbench.nodeless.js rename src/vs/workbench/browser/{nodeless.main.ts => web.main.ts} (98%) rename src/vs/workbench/browser/{nodeless.simpleservices.ts => web.simpleservices.ts} (99%) rename src/vs/workbench/{workbench.nodeless.main.css => workbench.web.main.css} (100%) rename src/vs/workbench/{workbench.nodeless.main.nls.js => workbench.web.main.nls.js} (100%) rename src/vs/workbench/{workbench.nodeless.main.ts => workbench.web.main.ts} (99%) diff --git a/package.json b/package.json index ef8e675b8e6..6cd5890bd9a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "smoketest": "cd test/smoke && node test/index.js", "download-builtin-extensions": "node build/lib/builtInExtensions.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", - "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization" + "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization", + "web": "node scripts/code-web.js" }, "dependencies": { "applicationinsights": "1.0.8", @@ -102,6 +103,7 @@ "gulp-uglify": "^3.0.0", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", + "http-server": "^0.11.1", "husky": "^0.13.1", "innosetup-compiler": "^5.5.60", "is": "^3.1.0", @@ -114,6 +116,7 @@ "mkdirp": "^0.5.0", "mocha": "^2.2.5", "mocha-junit-reporter": "^1.17.0", + "opn": "^5.4.0", "optimist": "0.3.5", "p-all": "^1.0.0", "pump": "^1.0.1", diff --git a/scripts/code-web.js b/scripts/code-web.js new file mode 100644 index 00000000000..34e6b4e922a --- /dev/null +++ b/scripts/code-web.js @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const httpServer = require('http-server'); +const opn = require('opn'); + +httpServer.createServer({ root: '.', cache: 5 }).listen(8080); +console.log('LISTENING on 8080'); + +opn('http://127.0.0.1:8080/out/vs/code/browser/workbench/workbench.html'); \ No newline at end of file diff --git a/src/buildfile.js b/src/buildfile.js index 889763f49b2..a7c0376985e 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -12,7 +12,7 @@ exports.base = [{ }]; exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.main']); -exports.workbenchNodeless = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.nodeless.main']); +exports.workbenchWeb = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.web.main']); exports.code = require('./vs/code/buildfile').collectModules(); diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html new file mode 100644 index 00000000000..d164ad904d3 --- /dev/null +++ b/src/vs/code/browser/workbench/workbench.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js new file mode 100644 index 00000000000..76e66c5c8d2 --- /dev/null +++ b/src/vs/code/browser/workbench/workbench.js @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +(function () { + + function loadScript(path, callback) { + let script = document.createElement('script'); + script.onload = callback; + script.async = true; + script.type = 'text/javascript'; + script.src = path; + document.head.appendChild(script); + } + + loadScript('../../../../../out/vs/loader.js', function () { + + // @ts-ignore + require.config({ + baseUrl: `${window.location.origin}/out` + }); + + // @ts-ignore + require([ + 'vs/workbench/workbench.web.main', + 'vs/nls!vs/workbench/workbench.web.main', + 'vs/css!vs/workbench/workbench.web.main' + ], + // @ts-ignore + function () { + + // @ts-ignore + require('vs/workbench/browser/web.main').main().then(undefined, console.error); + }); + }); +})(); \ No newline at end of file diff --git a/src/vs/code/electron-browser/workbench/workbench.nodeless.html b/src/vs/code/electron-browser/workbench/workbench.nodeless.html deleted file mode 100644 index e37abfdf5ae..00000000000 --- a/src/vs/code/electron-browser/workbench/workbench.nodeless.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/vs/code/electron-browser/workbench/workbench.nodeless.js b/src/vs/code/electron-browser/workbench/workbench.nodeless.js deleted file mode 100644 index 4d7098fa3e4..00000000000 --- a/src/vs/code/electron-browser/workbench/workbench.nodeless.js +++ /dev/null @@ -1,67 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check -'use strict'; - -(function () { - - function uriFromPath(_path) { - let pathName = _path.replace(/\\/g, '/'); - if (pathName.length > 0 && pathName.charAt(0) !== '/') { - pathName = '/' + pathName; - } - - let uri; - if (navigator.userAgent.indexOf('Windows') >= 0 && pathName.startsWith('//')) { // specially handle Windows UNC paths - uri = encodeURI('file:' + pathName); - } else { - uri = encodeURI('file://' + pathName); - } - - return uri.replace(/#/g, '%23'); - } - - function parseURLQueryArgs() { - const search = window.location.search || ''; - - return search.split(/[?&]/) - .filter(function (param) { return !!param; }) - .map(function (param) { return param.split('='); }) - .filter(function (param) { return param.length === 2; }) - .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); - } - - function loadScript(path, callback) { - let script = document.createElement('script'); - script.onload = callback; - script.async = true; - script.type = 'text/javascript'; - script.src = path; - document.head.appendChild(script); - } - - loadScript('../../../../../out/vs/loader.js', function () { - - const args = parseURLQueryArgs(); - const configuration = JSON.parse(args['config'] || '{}') || {}; - - // @ts-ignore - require.config({ - baseUrl: uriFromPath(configuration.appRoot) + '/out', - }); - - // @ts-ignore - require([ - 'vs/workbench/workbench.nodeless.main', - 'vs/nls!vs/workbench/workbench.nodeless.main', - 'vs/css!vs/workbench/workbench.nodeless.main' - ], function () { - - // @ts-ignore - require('vs/workbench/browser/nodeless.main').main().then(undefined, console.error); - }); - }); -})(); \ No newline at end of file diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index fdfb2dcbc04..f84d121c740 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -80,8 +80,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { private readonly touchBarGroups: Electron.TouchBarSegmentedControl[]; - private nodeless: boolean; - constructor( config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, @@ -98,8 +96,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._readyState = ReadyState.NONE; this.whenReadyCallbacks = []; - this.nodeless = !!(environmentService.args.nodeless && !environmentService.isBuilt); - // create browser window this.createBrowserWindow(config); @@ -129,7 +125,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { height: this.windowState.height, x: this.windowState.x, y: this.windowState.y, - backgroundColor: this.nodeless ? undefined : getBackgroundColor(this.stateService), + backgroundColor: getBackgroundColor(this.stateService), minWidth: CodeWindow.MIN_WIDTH, minHeight: CodeWindow.MIN_HEIGHT, show: !isFullscreenOrMaximized, @@ -143,10 +139,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { } }; - if (this.nodeless) { - options.webPreferences!.nodeIntegration = false; // simulate Electron 5 behaviour - } - if (isLinux) { options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s) } @@ -198,10 +190,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - if (this.nodeless) { - this._win.webContents.toggleDevTools(); - } - this._lastFocusTime = Date.now(); // since we show directly, we need to set the last focus time too } @@ -637,10 +625,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private doGetUrl(config: object): string { - if (this.nodeless) { - return `${require.toUrl('vs/code/electron-browser/workbench/workbench.nodeless.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; - } - return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; } diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 58cb747afcd..f48e072b5e8 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -69,7 +69,6 @@ export interface ParsedArgs { 'driver'?: string; 'driver-verbose'?: boolean; remote?: string; - 'nodeless'?: boolean; // TODO@ben revisit electron5 nodeless support } export const IEnvironmentService = createDecorator('environmentService'); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 3c607e00807..5c7a4050b8a 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -95,7 +95,6 @@ export const options: Option[] = [ { id: 'trace-category-filter', type: 'string' }, { id: 'trace-options', type: 'string' }, { id: 'prof-code-loading', type: 'boolean' }, - { id: 'nodeless', type: 'boolean' }, // TODO@ben revisit electron5 nodeless support { id: '_', type: 'string' } ]; diff --git a/src/vs/workbench/browser/nodeless.main.ts b/src/vs/workbench/browser/web.main.ts similarity index 98% rename from src/vs/workbench/browser/nodeless.main.ts rename to src/vs/workbench/browser/web.main.ts index 74ff1e9037c..c4867584fa2 100644 --- a/src/vs/workbench/browser/nodeless.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -8,7 +8,7 @@ import { domContentLoaded, addDisposableListener, EventType } from 'vs/base/brow import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; -import { SimpleLogService, SimpleProductService, SimpleWorkbenchEnvironmentService } from 'vs/workbench/browser/nodeless.simpleservices'; +import { SimpleLogService, SimpleProductService, SimpleWorkbenchEnvironmentService } from 'vs/workbench/browser/web.simpleservices'; import { Workbench } from 'vs/workbench/browser/workbench'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; diff --git a/src/vs/workbench/browser/nodeless.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts similarity index 99% rename from src/vs/workbench/browser/nodeless.simpleservices.ts rename to src/vs/workbench/browser/web.simpleservices.ts index 474cd6f53e5..ba8a746494a 100644 --- a/src/vs/workbench/browser/nodeless.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -62,7 +62,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ export const workspaceResource = URI.from({ scheme: Schemas.vscodeRemote, authority: document.location.host, - path: (self).USER_HOME_DIR || '/' + path: '/' }); //#region Backup File @@ -234,14 +234,14 @@ export class SimpleWorkbenchEnvironmentService implements IWorkbenchEnvironmentS args = { _: [] }; execPath: string; cliPath: string; - appRoot: string = '/nodeless/'; + appRoot: string = '/web/'; userHome: string; userDataPath: string; appNameLong: string; appQuality?: string; - appSettingsHome: string = '/nodeless/settings'; - appSettingsPath: string = '/nodeless/settings/settings.json'; - appKeybindingsPath: string = '/nodeless/settings/keybindings.json'; + appSettingsHome: string = '/web/settings'; + appSettingsPath: string = '/web/settings/settings.json'; + appKeybindingsPath: string = '/web/settings/keybindings.json'; machineSettingsHome: string; machineSettingsPath: string; settingsSearchBuildId?: number; @@ -264,7 +264,7 @@ export class SimpleWorkbenchEnvironmentService implements IWorkbenchEnvironmentS wait: boolean; status: boolean; log?: string; - logsPath: string = '/nodeless/logs'; + logsPath: string = '/web/logs'; verbose: boolean; skipGettingStarted: boolean; skipReleaseNotes: boolean; diff --git a/src/vs/workbench/workbench.nodeless.main.css b/src/vs/workbench/workbench.web.main.css similarity index 100% rename from src/vs/workbench/workbench.nodeless.main.css rename to src/vs/workbench/workbench.web.main.css diff --git a/src/vs/workbench/workbench.nodeless.main.nls.js b/src/vs/workbench/workbench.web.main.nls.js similarity index 100% rename from src/vs/workbench/workbench.nodeless.main.nls.js rename to src/vs/workbench/workbench.web.main.nls.js diff --git a/src/vs/workbench/workbench.nodeless.main.ts b/src/vs/workbench/workbench.web.main.ts similarity index 99% rename from src/vs/workbench/workbench.nodeless.main.ts rename to src/vs/workbench/workbench.web.main.ts index 97cf1b80dda..3d23a2ecde7 100644 --- a/src/vs/workbench/workbench.nodeless.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -12,7 +12,7 @@ import 'vs/editor/editor.all'; // import 'vs/workbench/electron-browser/main.contribution'; import 'vs/workbench/browser/workbench.contribution'; -import 'vs/workbench/browser/nodeless.main'; +import 'vs/workbench/browser/web.main'; //#endregion @@ -94,7 +94,7 @@ import { IBroadcastService, NullBroadcastService } from 'vs/workbench/services/b import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import 'vs/workbench/browser/nodeless.simpleservices'; +import 'vs/workbench/browser/web.simpleservices'; import 'vs/platform/dialogs/browser/dialogService'; diff --git a/yarn.lock b/yarn.lock index 1e8975e4d53..0507c4234ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -757,7 +757,7 @@ async-settle@^1.0.0: dependencies: async-done "^1.2.2" -async@1.x, async@^1.4.0: +async@1.x, async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= @@ -1635,6 +1635,11 @@ colormin@^1.0.5: css-color-names "0.0.4" has "^1.0.1" +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + colors@^1.1.2: version "1.2.1" resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.1.tgz#f4a3d302976aaf042356ba1ade3b1a2c62d9d794" @@ -1850,6 +1855,11 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +corser@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" + integrity sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c= + coveralls@^2.11.11: version "2.13.3" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.13.3.tgz#9ad7c2ae527417f361e8b626483f48ee92dd2bc7" @@ -2095,6 +2105,13 @@ debug@3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + debug@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -2431,6 +2448,16 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" +ecstatic@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.3.1.tgz#b15b5b036c2233defc78d7bacbd8765226c95577" + integrity sha512-/rrctvxZ78HMI/tPIsqdvFKHHscxR3IJuKrZI2ZoUgkt2SiufyLFBmcco+aqQBIu6P1qBsUNG3drAAGLx80vTQ== + dependencies: + he "^1.1.1" + mime "^1.6.0" + minimist "^1.1.0" + url-join "^2.0.5" + editions@^1.1.1, editions@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" @@ -2889,6 +2916,11 @@ event-stream@~3.3.4: stream-combiner "^0.2.2" through "^2.3.8" +eventemitter3@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + events@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -3306,6 +3338,13 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@^1.0.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" + integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== + dependencies: + debug "^3.2.6" + for-in@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.5.tgz#007374e2b6d5c67420a1479bdb75a04872b738c4" @@ -4213,6 +4252,11 @@ hawk@~6.0.2: hoek "4.x.x" sntp "2.x.x" +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -4279,6 +4323,29 @@ http-proxy-agent@2.1.0, http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" +http-proxy@^1.8.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" + integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + dependencies: + eventemitter3 "^3.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-server@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/http-server/-/http-server-0.11.1.tgz#2302a56a6ffef7f9abea0147d838a5e9b6b6a79b" + integrity sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w== + dependencies: + colors "1.0.3" + corser "~2.0.0" + ecstatic "^3.0.0" + http-proxy "^1.8.1" + opener "~1.4.0" + optimist "0.6.x" + portfinder "^1.0.13" + union "~0.4.3" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -4863,6 +4930,11 @@ is-windows@^1.0.1, is-windows@^1.0.2: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + is@^3.1.0, is@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" @@ -5688,7 +5760,7 @@ mime@1.4.1, mime@^1.3.4: resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== -mime@^1.4.1: +mime@^1.4.1, mime@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -6324,6 +6396,18 @@ oniguruma@^7.0.0: dependencies: nan "^2.10.0" +opener@~1.4.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= + +opn@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + optimist@0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.5.tgz#03654b52417030312d109f39b159825b60309304" @@ -6331,7 +6415,7 @@ optimist@0.3.5: dependencies: wordwrap "~0.0.2" -optimist@^0.6.1: +optimist@0.6.x, optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= @@ -6756,6 +6840,15 @@ pluralize@^1.2.1: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= +portfinder@^1.0.13: + version "1.0.20" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" + integrity sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw== + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -7198,6 +7291,11 @@ qs@6.5.1, qs@~6.5.1: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== +qs@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-2.3.3.tgz#e9e85adbe75da0bbe4c8e0476a086290f863b404" + integrity sha1-6eha2+ddoLvkyOBHaghikPhjtAQ= + qs@~6.3.0: version "6.3.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" @@ -7667,6 +7765,11 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -9067,6 +9170,13 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^0.4.3" +union@~0.4.3: + version "0.4.6" + resolved "https://registry.yarnpkg.com/union/-/union-0.4.6.tgz#198fbdaeba254e788b0efcb630bc11f24a2959e0" + integrity sha1-GY+9rrolTniLDvy2MLwR8kopWeA= + dependencies: + qs "~2.3.3" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -9146,6 +9256,11 @@ url-join@^1.1.0: resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg= +url-join@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728" + integrity sha1-WvIvGMBSoACkjXuCxenC4v7tpyg= + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" From 772a652b3d52bb81f02f8c431113f39cd3d99400 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 9 May 2019 14:34:57 +0200 Subject: [PATCH 26/99] :lipstick: --- scripts/code-web.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/code-web.js b/scripts/code-web.js index 34e6b4e922a..da3afe33e47 100644 --- a/scripts/code-web.js +++ b/scripts/code-web.js @@ -6,7 +6,9 @@ const httpServer = require('http-server'); const opn = require('opn'); -httpServer.createServer({ root: '.', cache: 5 }).listen(8080); -console.log('LISTENING on 8080'); +const url = 'http://127.0.0.1:8080/out/vs/code/browser/workbench/workbench.html'; -opn('http://127.0.0.1:8080/out/vs/code/browser/workbench/workbench.html'); \ No newline at end of file +httpServer.createServer({ root: '.', cache: 5 }).listen(8080); +console.log(`Open ${url} in your browser`); + +opn(url); \ No newline at end of file From a477e1eafeb6a6ac48c43de93c8a985ec860145e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 9 May 2019 16:27:37 +0200 Subject: [PATCH 27/99] extract native modules ignore rules --- build/.nativeignore | 111 +++++++++++++++++++++++++++++++++++++++ build/gulpfile.vscode.js | 21 +------- build/lib/util.js | 23 ++++---- build/lib/util.ts | 24 ++++----- 4 files changed, 133 insertions(+), 46 deletions(-) create mode 100644 build/.nativeignore diff --git a/build/.nativeignore b/build/.nativeignore new file mode 100644 index 00000000000..cc88ae0d878 --- /dev/null +++ b/build/.nativeignore @@ -0,0 +1,111 @@ +# cleanup rules for native node modules, .gitignore style + +fsevents/binding.gyp +fsevents/fsevents.cc +fsevents/build/** +fsevents/src/** +fsevents/test/** +!fsevents/**/*.node + +vscode-sqlite3/binding.gyp +vscode-sqlite3/benchmark/** +vscode-sqlite3/cloudformation/** +vscode-sqlite3/deps/** +vscode-sqlite3/test/** +vscode-sqlite3/build/** +vscode-sqlite3/src/** +!vscode-sqlite3/build/Release/*.node + +oniguruma/binding.gyp +oniguruma/build/** +oniguruma/src/** +oniguruma/deps/** +!oniguruma/build/Release/*.node +!oniguruma/src/*.js + +windows-mutex/binding.gyp +windows-mutex/build/** +windows-mutex/src/** +!windows-mutex/**/*.node + +native-keymap/binding.gyp +native-keymap/build/** +native-keymap/src/** +native-keymap/deps/** +!native-keymap/build/Release/*.node + +native-is-elevated/binding.gyp +native-is-elevated/build/** +native-is-elevated/src/** +native-is-elevated/deps/** +!native-is-elevated/build/Release/*.node + +native-watchdog/binding.gyp +native-watchdog/build/** +native-watchdog/src/** +!native-watchdog/build/Release/*.node + +spdlog/binding.gyp +spdlog/build/** +spdlog/deps/** +spdlog/src/** +spdlog/test/** +!spdlog/build/Release/*.node + +jschardet/dist/** + +windows-foreground-love/binding.gyp +windows-foreground-love/build/** +windows-foreground-love/src/** +!windows-foreground-love/**/*.node + +windows-process-tree/binding.gyp +windows-process-tree/build/** +windows-process-tree/src/** +!windows-process-tree/**/*.node + +gc-signals/binding.gyp +gc-signals/build/** +gc-signals/src/** +gc-signals/deps/** + +!gc-signals/build/Release/*.node +!gc-signals/src/index.js + +keytar/binding.gyp +keytar/build/** +keytar/src/** +keytar/script/** +keytar/node_modules/** +!keytar/**/*.node + +node-pty/binding.gyp +node-pty/build/** +node-pty/src/** +node-pty/tools/** +!node-pty/build/Release/*.exe +!node-pty/build/Release/*.dll +!node-pty/build/Release/*.node + +vscode-nsfw/binding.gyp +vscode-nsfw/build/** +vscode-nsfw/src/** +vscode-nsfw/openpa/** +vscode-nsfw/includes/** +!vscode-nsfw/build/Release/*.node +!vscode-nsfw/**/*.a + +vsda/binding.gyp +vsda/README.md +vsda/build/** +vsda/*.bat +vsda/*.sh +vsda/*.cpp +vsda/*.h +!vsda/build/Release/vsda.node + +vscode-windows-ca-certs/**/* +!vscode-windows-ca-certs/package.json +!vscode-windows-ca-certs/**/*.node + +node-addon-api/**/* \ No newline at end of file diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index ae37a72fca0..57ca292ef73 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -323,28 +323,11 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const deps = gulp.src(depsSrc, { base: '.', dot: true }) .pipe(filter(['**', '!**/package-lock.json'])) - .pipe(util.cleanNodeModule('fsevents', ['binding.gyp', 'fsevents.cc', 'build/**', 'src/**', 'test/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('vscode-sqlite3', ['binding.gyp', 'benchmark/**', 'cloudformation/**', 'deps/**', 'test/**', 'build/**', 'src/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/*.js'])) - .pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('jschardet', ['dist/**'])) - .pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('windows-process-tree', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/index.js'])) - .pipe(util.cleanNodeModule('keytar', ['binding.gyp', 'build/**', 'src/**', 'script/**', 'node_modules/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/*.exe', 'build/Release/*.dll', 'build/Release/*.node'])) - .pipe(util.cleanNodeModule('vscode-nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['build/Release/*.node', '**/*.a'])) - .pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node'])) - .pipe(util.cleanNodeModule('vscode-windows-ca-certs', ['**/*'], ['package.json', '**/*.node'])) - .pipe(util.cleanNodeModule('node-addon-api', ['**/*'])) + .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))) .pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar')); let all = es.merge( - packageJsonStream, + packageJsonStream, productJsonStream, license, api, diff --git a/build/lib/util.js b/build/lib/util.js index 34ee13695fd..6a210d3decc 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -8,7 +8,6 @@ const es = require("event-stream"); const debounce = require("debounce"); const _filter = require("gulp-filter"); const rename = require("gulp-rename"); -const _ = require("underscore"); const path = require("path"); const fs = require("fs"); const _rimraf = require("rimraf"); @@ -100,22 +99,18 @@ function skipDirectories() { }); } exports.skipDirectories = skipDirectories; -function cleanNodeModule(name, excludes, includes) { - const toGlob = (path) => '**/node_modules/' + name + (path ? '/' + path : ''); - const negate = (str) => '!' + str; - const allFilter = _filter(toGlob('**'), { restore: true }); - const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob))); +function cleanNodeModules(rulePath) { + const rules = fs.readFileSync(rulePath, 'utf8') + .split(/\r?\n/g) + .map(line => line.trim()) + .filter(line => line && !/^#/.test(line)); + const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); + const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); const input = es.through(); - const nodeModuleInput = input.pipe(allFilter); - let output = nodeModuleInput.pipe(_filter(globs)); - if (includes) { - const includeGlobs = includes.map(toGlob); - output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); - } - output = output.pipe(allFilter.restore); + const output = es.merge(input.pipe(_filter(['**', ...excludes])), input.pipe(_filter(includes))); return es.duplex(input, output); } -exports.cleanNodeModule = cleanNodeModule; +exports.cleanNodeModules = cleanNodeModules; function loadSourcemaps() { const input = es.through(); const output = input diff --git a/build/lib/util.ts b/build/lib/util.ts index 44ac9d0dc74..e87c0650ec3 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -132,23 +132,21 @@ export function skipDirectories(): NodeJS.ReadWriteStream { }); } -export function cleanNodeModule(name: string, excludes: string[], includes?: string[]): NodeJS.ReadWriteStream { - const toGlob = (path: string) => '**/node_modules/' + name + (path ? '/' + path : ''); - const negate = (str: string) => '!' + str; +export function cleanNodeModules(rulePath: string): NodeJS.ReadWriteStream { + const rules = fs.readFileSync(rulePath, 'utf8') + .split(/\r?\n/g) + .map(line => line.trim()) + .filter(line => line && !/^#/.test(line)); - const allFilter = _filter(toGlob('**'), { restore: true }); - const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob) as (x: string) => string)); + const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); + const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); const input = es.through(); - const nodeModuleInput = input.pipe(allFilter); - let output: NodeJS.ReadWriteStream = nodeModuleInput.pipe(_filter(globs)); + const output = es.merge( + input.pipe(_filter(['**', ...excludes])), + input.pipe(_filter(includes)) + ); - if (includes) { - const includeGlobs = includes.map(toGlob); - output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); - } - - output = output.pipe(allFilter.restore); return es.duplex(input, output); } From 9c88839f00023afc876d875a32419a37d034e18f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 10 May 2019 16:17:32 -0700 Subject: [PATCH 28/99] vscode-xterm@3.14.0-beta1 Diff: https://github.com/microsoft/xterm.js/compare/199da84...5c9d66f --- package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index ac2965c6998..a4ee523d485 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "vscode-ripgrep": "^1.2.5", "vscode-sqlite3": "4.0.7", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.13.0-beta3", + "vscode-xterm": "3.14.0-beta1", "yauzl": "^2.9.1", "yazl": "^2.4.3" }, @@ -152,4 +152,4 @@ "windows-mutex": "0.2.1", "windows-process-tree": "0.2.3" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 1e8975e4d53..3cc66ed63a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9541,10 +9541,10 @@ vscode-windows-registry@1.0.1: dependencies: nan "^2.12.1" -vscode-xterm@3.13.0-beta3: - version "3.13.0-beta3" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.13.0-beta3.tgz#ab642ed77df07c2adfca7b15ae39c18328db3fc6" - integrity sha512-XvgD/P6CCV0+79UYM7CEL6Ziv2RiDopI3Wa1hIm3Dm8InWxkl3QwykINlempMNub+r0gwVwLKSQYr+Q/jg1IAw== +vscode-xterm@3.14.0-beta1: + version "3.14.0-beta1" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.14.0-beta1.tgz#3cf7ecd0b8fe995675cb8fd30239c481c420aba7" + integrity sha512-CFA3foOLrY7pUw2E8xzpPwP40hDbFubPNO4D8PTh2u2UDhJ9PYfQaJxXujCoic8qg9uEVVnLUbqqNlK2qGWFnA== vso-node-api@6.1.2-preview: version "6.1.2-preview" From 9e639154b65642ca23790993d9310ba2948415a2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 10 May 2019 16:17:54 -0700 Subject: [PATCH 29/99] Update typings --- src/typings/vscode-xterm.d.ts | 154 +++++++++++++++++++++++++++++----- 1 file changed, 134 insertions(+), 20 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index e425a37c43f..fd5d2e7f1b7 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -101,17 +101,6 @@ declare module 'vscode-xterm' { */ experimentalCharAtlas?: 'none' | 'static' | 'dynamic'; - /** - * (EXPERIMENTAL) Defines which implementation to use for buffer lines. - * - * - 'JsArray': The default/stable implementation. - * - 'TypedArray': The new experimental implementation based on TypedArrays that is expected to - * significantly boost performance and memory consumption. Use at your own risk. - * - * @deprecated This option will be removed in the future. - */ - experimentalBufferLineImpl?: 'JsArray' | 'TypedArray'; - /** * The font size used to render text. */ @@ -199,6 +188,18 @@ declare module 'vscode-xterm' { * The color theme of the terminal. */ theme?: ITheme; + + /** + * Whether "Windows mode" is enabled. Because Windows backends winpty and + * conpty operate by doing line wrapping on their side, xterm.js does not + * have access to wrapped lines. When Windows mode is enabled the following + * changes will be in effect: + * + * - Reflow is disabled. + * - Lines are assumed to be wrapped if the last character of the line is + * not whitespace. + */ + windowsMode?: boolean; } /** @@ -306,6 +307,14 @@ declare module 'vscode-xterm' { dispose(): void; } + /** + * An event that can be listened to. + * @returns an `IDisposable` to stop listening. + */ + export interface IEvent { + (listener: (e: T) => any): IDisposable; + } + export interface IMarker extends IDisposable { readonly id: number; readonly isDisposed: boolean; @@ -325,28 +334,32 @@ declare module 'vscode-xterm' { /** * The element containing the terminal. */ - element: HTMLElement; + readonly element: HTMLElement; /** * The textarea that accepts input for the terminal. */ - textarea: HTMLTextAreaElement; + readonly textarea: HTMLTextAreaElement; /** - * The number of rows in the terminal's viewport. + * The number of rows in the terminal's viewport. Use + * `ITerminalOptions.rows` to set this in the constructor and + * `Terminal.resize` for when the terminal exists. */ - rows: number; + readonly rows: number; /** - * The number of columns in the terminal's viewport. + * The number of columns in the terminal's viewport. Use + * `ITerminalOptions.cols` to set this in the constructor and + * `Terminal.resize` for when the terminal exists. */ - cols: number; + readonly cols: number; /** * (EXPERIMENTAL) Get all markers registered against the buffer. If the alt * buffer is active this will always return []. */ - markers: IMarker[]; + readonly markers: ReadonlyArray; /** * Natural language strings that can be localized. @@ -360,6 +373,70 @@ declare module 'vscode-xterm' { */ constructor(options?: ITerminalOptions); + /** + * Adds an event listener for the cursor moves. + * @returns an `IDisposable` to stop listening. + */ + onCursorMove: IEvent; + + /** + * Adds an event listener for when a data event fires. This happens for + * example when the user types or pastes into the terminal. The event value + * is whatever `string` results, in a typical setup, this should be passed + * on to the backing pty. + * @returns an `IDisposable` to stop listening. + */ + onData: IEvent; + + /** + * Adds an event listener for a key is pressed. The event value contains the + * string that will be sent in the data event as well as the DOM event that + * triggered it. + * @returns an `IDisposable` to stop listening. + */ + onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>; + + /** + * Adds an event listener for when a line feed is added. + * @returns an `IDisposable` to stop listening. + */ + onLineFeed: IEvent; + + /** + * Adds an event listener for when a scroll occurs. The event value is the + * new position of the viewport. + * @returns an `IDisposable` to stop listening. + */ + onScroll: IEvent; + + /** + * Adds an event listener for when a selection change occurs. + * @returns an `IDisposable` to stop listening. + */ + onSelectionChange: IEvent; + + /** + * Adds an event listener for when rows are rendered. The event value + * contains the start row and end rows of the rendered area (ranges from `0` + * to `Terminal.rows - 1`). + * @returns an `IDisposable` to stop listening. + */ + onRender: IEvent<{ start: number, end: number }>; + + /** + * Adds an event listener for when the terminal is resized. The event value + * contains the new size. + * @returns an `IDisposable` to stop listening. + */ + onResize: IEvent<{ cols: number, rows: number }>; + + /** + * Adds an event listener for when an OSC 0 or OSC 2 title change occurs. + * The event value is the new title. + * @returns an `IDisposable` to stop listening. + */ + onTitleChange: IEvent; + /** * Unfocus the terminal. */ @@ -374,54 +451,63 @@ declare module 'vscode-xterm' { * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'blur' | 'focus' | 'linefeed' | 'selection', listener: () => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'data', listener: (...args: any[]) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'key', listener: (key: string, event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'keypress' | 'keydown', listener: (event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'refresh', listener: (data: { start: number, end: number }) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'resize', listener: (data: { cols: number, rows: number }) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'scroll', listener: (ydisp: number) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'title', listener: (title: string) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: string, listener: (...args: any[]) => void): void; @@ -429,6 +515,7 @@ declare module 'vscode-xterm' { * Deregisters an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener).dispose()` instead. */ off(type: 'blur' | 'focus' | 'linefeed' | 'selection' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; @@ -446,11 +533,14 @@ declare module 'vscode-xterm' { * be used to conveniently remove the event listener. * @param type The type of event. * @param handler The event handler. + * @deprecated use `Terminal.onEvent(listener)` instead. */ addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; /** - * Resizes the terminal. + * Resizes the terminal. It's best practice to debounce calls to resize, + * this will help ensure that the pty can respond to the resize event + * before another one occurs. * @param x The number of columns to resize to. * @param y The number of rows to resize to. */ @@ -476,11 +566,35 @@ declare module 'vscode-xterm' { * should be processed by the terminal and what keys should not. * @param customKeyEventHandler The custom KeyboardEvent handler to attach. * This is a function that takes a KeyboardEvent, allowing consumers to stop - * propogation and/or prevent the default action. The function returns + * propagation and/or prevent the default action. The function returns * whether the event should be processed by xterm.js. */ attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; + /** + * (EXPERIMENTAL) Adds a handler for CSI escape sequences. + * @param flag The flag should be one-character string, which specifies the + * final character (e.g "m" for SGR) of the CSI sequence. + * @param callback The function to handle the escape sequence. The callback + * is called with the numerical params, as well as the special characters + * (e.g. "$" for DECSCPP). Return true if the sequence was handled; false if + * we should try a previous handler (set by addCsiHandler or setCsiHandler). + * The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; + + /** + * (EXPERIMENTAL) Adds a handler for OSC escape sequences. + * @param ident The number (first parameter) of the sequence. + * @param callback The function to handle the escape sequence. The callback + * is called with OSC data string. Return true if the sequence was handled; + * false if we should try a previous handler (set by addOscHandler or + * setOscHandler). The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; + /** * (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to * be matched and handled. From 415c7be4ae64296c718539e8335847142b0404fd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 10 May 2019 16:18:29 -0700 Subject: [PATCH 30/99] Fix compile, remove invalid setting --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 28b1d731cf7..6d08e786275 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -416,8 +416,7 @@ export class TerminalInstance implements ITerminalInstance { // TODO: Guess whether to use canvas or dom better rendererType: config.rendererType === 'auto' ? 'canvas' : config.rendererType, // TODO: Remove this once the setting is removed upstream - experimentalCharAtlas: 'dynamic', - experimentalBufferLineImpl: 'TypedArray' + experimentalCharAtlas: 'dynamic' }); if (this._shellLaunchConfig.initialText) { this._xterm.writeln(this._shellLaunchConfig.initialText); From 488e986f595027d7965bd5302035118b9e8e71d5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 10 May 2019 16:29:23 -0700 Subject: [PATCH 31/99] Replace winptyCompat with windowsMode --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 2 +- .../terminal/electron-browser/terminalInstanceService.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 6d08e786275..d235512c919 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -438,7 +438,7 @@ export class TerminalInstance implements ITerminalInstance { return; } if (this._processManager.os === platform.OperatingSystem.Windows) { - this._xterm.winptyCompatInit(); + this._xterm.setOption('windowsMode', true); } this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform, this._processManager); }); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index b3c85613d87..812db3a7073 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -36,7 +36,6 @@ export class TerminalInstanceService implements ITerminalInstanceService { // Enable xterm.js addons Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search')); Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks')); - Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/winptyCompat/winptyCompat')); Terminal.applyAddon(typeAheadAddon); // Localize strings Terminal.strings.blankLine = nls.localize('terminal.integrated.a11yBlankLine', 'Blank line'); From c68ad4f5f33e66ed1a7b11b928cb89b0ea93c8e1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 10 May 2019 17:08:04 -0700 Subject: [PATCH 32/99] Use new event api --- .../terminal/browser/terminalCommandTracker.ts | 2 +- .../contrib/terminal/browser/terminalInstance.ts | 14 +++++++------- .../contrib/terminal/node/windowsShellHelper.ts | 6 +++--- .../terminalCommandTracker.test.ts | 14 +++++++------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts b/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts index c7857624a7d..2ff3af60b2e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts @@ -30,7 +30,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa constructor( private _xterm: Terminal ) { - this._xterm.on('key', key => this._onKey(key)); + this._xterm.onKey(e => this._onKey(e.key)); } public dispose(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index d235512c919..77d251bee3e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -371,7 +371,7 @@ export class TerminalInstance implements ITerminalInstance { // it gets removed and then added back to the DOM (resetting scrollTop to 0). // Upstream issue: https://github.com/sourcelair/xterm.js/issues/291 if (this._xterm) { - this._xterm.emit('scroll', this._xterm._core.buffer.ydisp); + this._xterm._core._onScroll.fire(this._xterm._core.buffer.ydisp); } } @@ -421,12 +421,12 @@ export class TerminalInstance implements ITerminalInstance { if (this._shellLaunchConfig.initialText) { this._xterm.writeln(this._shellLaunchConfig.initialText); } - this._xterm.on('linefeed', () => this._onLineFeed()); - this._xterm.on('key', (key, ev) => this._onKey(key, ev)); + this._xterm.onLineFeed(() => this._onLineFeed()); + this._xterm.onKey(e => this._onKey(e.key, e.domEvent)); if (this._processManager) { this._processManager.onProcessData(data => this._onProcessData(data)); - this._xterm.on('data', data => this._processManager!.write(data)); + this._xterm.onData(data => this._processManager!.write(data)); // TODO: How does the cwd work on detached processes? this.processReady.then(async () => { this._linkHandler.processCwd = await this._processManager!.getInitialCwd(); @@ -445,11 +445,11 @@ export class TerminalInstance implements ITerminalInstance { } else if (this.shellLaunchConfig.isRendererOnly) { this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, undefined, undefined); } - this._xterm.on('focus', () => this._onFocus.fire(this)); + this._xterm.textarea.addEventListener('focus', () => this._onFocus.fire(this)); // Register listener to trigger the onInput ext API if the terminal is a renderer only if (this._shellLaunchConfig.isRendererOnly) { - this._xterm.on('data', (data) => this._sendRendererInput(data)); + this._xterm.onData(data => this._sendRendererInput(data)); } this._commandTracker = new TerminalCommandTracker(this._xterm); @@ -858,7 +858,7 @@ export class TerminalInstance implements ITerminalInstance { // necessary if the number of rows in the terminal has decreased while it was in the // background since scrollTop changes take no effect but the terminal's position does // change since the number of visible rows decreases. - this._xterm.emit('scroll', this._xterm._core.buffer.ydisp); + this._xterm._core._onScroll.fire(this._xterm._core.buffer.ydisp); if (this._container && this._container.parentElement) { // Force a layout when the instance becomes invisible. This is particularly important // for ensuring that terminals that are created in the background by an extension will diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts index d841d023695..1bc875456dd 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts @@ -61,15 +61,15 @@ export class WindowsShellHelper implements IWindowsShellHelper { // If this is done on every linefeed, parsing ends up taking // significantly longer due to resetting timers. Note that this is // private API. - this._xterm.on('linefeed', () => this._newLineFeed = true); - this._xterm.on('cursormove', () => { + this._xterm.onLineFeed(() => this._newLineFeed = true); + this._xterm.onCursorMove(() => { if (this._newLineFeed) { this._onCheckShell.fire(undefined); } }); // Fire a new check for the shell when any key is pressed. - this._xterm.on('keypress', () => this._onCheckShell.fire(undefined)); + this._xterm.onKey(() => this._onCheckShell.fire(undefined)); }); } diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts index ec94ff8a518..cd80a60ee1a 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts @@ -46,13 +46,13 @@ suite('Workbench - TerminalCommandTracker', () => { test('should track commands when the prompt is of sufficient size', () => { assert.equal(xterm.markers.length, 0); syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3 - xterm.emit('key', '\x0d'); + xterm._core._onKey.fire({ key: '\x0d' }); assert.equal(xterm.markers.length, 1); }); test('should not track commands when the prompt is too small', () => { assert.equal(xterm.markers.length, 0); syncWrite(xterm, '\x1b[2G'); // Move cursor to column 2 - xterm.emit('key', '\x0d'); + xterm._core._onKey.fire({ key: '\x0d' }); assert.equal(xterm.markers.length, 0); }); }); @@ -60,7 +60,7 @@ suite('Workbench - TerminalCommandTracker', () => { suite('Commands', () => { test('should scroll to the next and previous commands', () => { syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3 - xterm.emit('key', '\x0d'); // Mark line #10 + xterm._core._onKey.fire({ key: '\x0d' }); // Mark line #10 assert.equal(xterm.markers[0].line, 9); for (let i = 0; i < 20; i++) { @@ -94,11 +94,11 @@ suite('Workbench - TerminalCommandTracker', () => { syncWrite(xterm, '\r0'); syncWrite(xterm, '\n\r1'); syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3 - xterm.emit('key', '\x0d'); // Mark line + xterm._core._onKey.fire({ key: '\x0d' }); // Mark line assert.equal(xterm.markers[0].line, 10); syncWrite(xterm, '\n\r2'); syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3 - xterm.emit('key', '\x0d'); // Mark line + xterm._core._onKey.fire({ key: '\x0d' }); // Mark line assert.equal(xterm.markers[1].line, 11); syncWrite(xterm, '\n\r3'); @@ -124,11 +124,11 @@ suite('Workbench - TerminalCommandTracker', () => { syncWrite(xterm, '\r0'); syncWrite(xterm, '\n\r1'); syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3 - xterm.emit('key', '\x0d'); // Mark line + xterm._core._onKey.fire({ key: '\x0d' }); // Mark line assert.equal(xterm.markers[0].line, 10); syncWrite(xterm, '\n\r2'); syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3 - xterm.emit('key', '\x0d'); // Mark line + xterm._core._onKey.fire({ key: '\x0d' }); // Mark line assert.equal(xterm.markers[1].line, 11); syncWrite(xterm, '\n\r3'); From dc5b3d87c82aab46c9e5805cfb970ff40cb702bb Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 10 May 2019 17:18:44 -0700 Subject: [PATCH 33/99] Fix issue with focus listener --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 77d251bee3e..904729f21d6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -445,7 +445,6 @@ export class TerminalInstance implements ITerminalInstance { } else if (this.shellLaunchConfig.isRendererOnly) { this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, undefined, undefined); } - this._xterm.textarea.addEventListener('focus', () => this._onFocus.fire(this)); // Register listener to trigger the onInput ext API if the terminal is a renderer only if (this._shellLaunchConfig.isRendererOnly) { @@ -507,6 +506,7 @@ export class TerminalInstance implements ITerminalInstance { (this._wrapperElement).xterm = this._xterm; this._xterm.open(this._xtermElement); + this._xterm.textarea.addEventListener('focus', () => this._onFocus.fire(this)); this._xterm.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => { // Disable all input if the terminal is exiting if (this._isExiting) { From 758367eb3edb900c9dfef2ea002278b0f7f890c9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 10 May 2019 19:39:18 -0700 Subject: [PATCH 34/99] Fix overridden types --- src/typings/vscode-xterm.d.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index fd5d2e7f1b7..7feb37021af 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -876,10 +876,8 @@ declare module 'vscode-xterm' { handler(text: string): void; - /** - * Emit an event on the terminal. - */ - emit(type: string, data: any): void; + _onScroll: IEventEmitter2; + _onKey: IEventEmitter2<{ key: string }>; charMeasure?: { height: number, width: number }; @@ -889,6 +887,10 @@ declare module 'vscode-xterm' { }; } + interface IEventEmitter2 { + fire(e: T): void; + } + interface ISearchOptions { /** * Whether the find should be done as a regex. From c2ec1693358e3c81a35de4c664c1be2430b2a61c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 11 May 2019 06:22:08 -0700 Subject: [PATCH 35/99] fix #73529 --- src/vs/platform/history/electron-main/historyMainService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index 8a9db6d0aca..4429cc10574 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -299,11 +299,11 @@ export class HistoryMainService implements IHistoryMainService { description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(dirname(workspace), this.environmentService)); args = `--folder-uri "${workspace.toString()}"`; } else { - description = nls.localize('codeWorkspace', "Code Workspace"); + description = nls.localize('workspaceDesc', "{0} {1}", getBaseLabel(workspace.configPath), getPathLabel(dirname(workspace.configPath), this.environmentService)); args = `--file-uri "${workspace.configPath.toString()}"`; } - return { + return { type: 'task', title, description, From 905b76e24dcf396c5ba6750aafd27dd59da3f968 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 12 May 2019 06:16:04 -0700 Subject: [PATCH 36/99] :lipstick: --- .../workbench/common/editor/resourceEditorModel.ts | 12 +++++++++--- src/vs/workbench/common/editor/textEditorModel.ts | 11 +++++------ .../services/textfile/common/textFileService.ts | 4 ++-- .../workbench/services/textfile/common/textfiles.ts | 8 +------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/common/editor/resourceEditorModel.ts b/src/vs/workbench/common/editor/resourceEditorModel.ts index 8177f346457..ae5d8ff2e90 100644 --- a/src/vs/workbench/common/editor/resourceEditorModel.ts +++ b/src/vs/workbench/common/editor/resourceEditorModel.ts @@ -19,12 +19,18 @@ export class ResourceEditorModel extends BaseTextEditorModel { @IModelService modelService: IModelService ) { super(modelService, modeService, resource); - - // TODO@Joao: force this class to dispose the underlying model - this.createdEditorModel = true; } isReadonly(): boolean { return true; } + + dispose(): void { + // TODO@Joao: force this class to dispose the underlying model + if (this.textEditorModelHandle) { + this.modelService.destroyModel(this.textEditorModelHandle); + } + + super.dispose(); + } } \ No newline at end of file diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 9f01a364979..2d3bb2da2f2 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -15,10 +15,9 @@ import { IDisposable } from 'vs/base/common/lifecycle'; * The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated. */ export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel { + protected textEditorModelHandle: URI | null; + private createdEditorModel: boolean; - protected createdEditorModel: boolean; - - private textEditorModelHandle: URI | null; private modelDisposeListener: IDisposable | null; constructor( @@ -128,11 +127,11 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd createSnapshot(this: IResolvedTextEditorModel): ITextSnapshot; createSnapshot(this: ITextEditorModel): ITextSnapshot | null; createSnapshot(): ITextSnapshot | null { - if (this.isResolved()) { - return this.textEditorModel.createSnapshot(true /* preserve BOM */); + if (!this.textEditorModel) { + return null; } - return null; + return this.textEditorModel.createSnapshot(true /* preserve BOM */); } isResolved(): this is IResolvedTextEditorModel { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index ebd718f913d..887841d38a9 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -11,7 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, isResolvedTextEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -854,7 +854,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // take over encoding, mode and model value from source model targetModel.updatePreferredEncoding(sourceModel.getEncoding()); - if (isResolvedTextEditorModel(sourceModel) && isResolvedTextEditorModel(targetModel)) { + if (sourceModel.isResolved() && targetModel.isResolved()) { this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot())); const language = sourceModel.textEditorModel.getLanguageIdentifier(); diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 3b09be969dc..97924ae5e57 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { ITextBufferFactory, ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; @@ -476,12 +476,6 @@ export interface IResolvedTextFileEditorModel extends ITextFileEditorModel { createSnapshot(): ITextSnapshot; } -export function isResolvedTextEditorModel(model: ITextEditorModel): model is IResolvedTextEditorModel; -export function isResolvedTextEditorModel(model: ITextFileEditorModel): model is IResolvedTextFileEditorModel; -export function isResolvedTextEditorModel(model: ITextEditorModel | ITextFileEditorModel): model is IResolvedTextEditorModel | IResolvedTextFileEditorModel { - return !!model.textEditorModel; -} - export interface IWillMoveEvent { oldResource: URI; newResource: URI; From 137b312c07ebc389fd6149be1baae5627c89a506 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 13 May 2019 08:24:08 -0700 Subject: [PATCH 37/99] Remove winptyCompat custom vscode-xterm typing --- src/typings/vscode-xterm.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index 7feb37021af..9ca7342a340 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -910,7 +910,6 @@ declare module 'vscode-xterm' { _core: TerminalCore; webLinksInit(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void; - winptyCompatInit(): void; /** * Find the next instance of the term, then scroll to and select it. If it From 70bf1f2509e8de3a5d3745c7cba132445b35c28d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 13 May 2019 10:37:24 -0700 Subject: [PATCH 38/99] Send line data to tasks when using ConPTY Part of #73677 --- src/typings/vscode-xterm.d.ts | 2 ++ .../contrib/terminal/browser/terminalInstance.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index e425a37c43f..569af6027a6 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -813,5 +813,7 @@ declare module 'vscode-xterm' { * @return Whether a result was found. */ findPrevious(term: string, findOptions: ISearchOptions): boolean; + + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 1fe700d0ad5..94b419556af 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -440,6 +440,13 @@ export class TerminalInstance implements ITerminalInstance { } if (this._processManager.os === platform.OperatingSystem.Windows) { this._xterm.winptyCompatInit(); + // Force line data to be sent when the cursor is moved, the main purpose for + // this is because ConPTY will often not do a line feed but instead move the + // cursor, in which case we still want to send the current line's data to tasks. + this._xterm.addCsiHandler('H', () => { + this._onCursorMove(); + return false; + }); } this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform, this._processManager); }); @@ -1115,6 +1122,11 @@ export class TerminalInstance implements ITerminalInstance { } } + private _onCursorMove(): void { + const buffer = (this._xterm._core.buffer); + this._sendLineData(buffer, buffer.ybase + buffer.y); + } + private _sendLineData(buffer: any, lineIndex: number): void { let lineData = buffer.translateBufferLineToString(lineIndex, true); while (lineIndex >= 0 && buffer.lines.get(lineIndex--).isWrapped) { From 8a983657576fad3d0ad86dbd2312a0a61a6bcabe Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 13 May 2019 10:52:43 -0700 Subject: [PATCH 39/99] build --- build/azure-pipelines/linux/product-build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 31840aed609..06ff32d5f7c 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -10,7 +10,7 @@ steps: - task: AzureKeyVault@1 displayName: 'Azure Key Vault: Get Secrets' inputs: - azureSubscription: 'VS Code Setup, Update, and Build Services (8afa857f-ba38-4efa-bd5b-41fc50ac5200)' + azureSubscription: 'vscode-builds-subscription' KeyVaultName: vscode - script: | From bc1308a33e1583a229a78c763850acb759144188 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 13 May 2019 11:00:33 -0700 Subject: [PATCH 40/99] build --- build/azure-pipelines/darwin/product-build-darwin.yml | 8 +++++++- build/azure-pipelines/linux/snap-build-linux.yml | 8 +++++++- build/azure-pipelines/sync-mooncake.yml | 8 +++++++- build/azure-pipelines/win32/product-build-win32.yml | 8 +++++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index f5d5ef626b1..d53bdbe1c2f 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -7,6 +7,12 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e @@ -78,7 +84,7 @@ steps: - script: | set -e VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \ diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 9588ebcb36d..9941d13d08c 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -7,6 +7,12 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - task: DownloadPipelineArtifact@0 displayName: 'Download Pipeline Artifact' inputs: @@ -44,6 +50,6 @@ steps: (cd $SNAP_ROOT/code-* && sudo snapcraft snap --output "$SNAP_PATH") # Publish snap package - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH" \ No newline at end of file diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index c422839de1c..36af0f0265d 100644 --- a/build/azure-pipelines/sync-mooncake.yml +++ b/build/azure-pipelines/sync-mooncake.yml @@ -7,12 +7,18 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e (cd build ; yarn) - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index a49349150d2..907b93a86da 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -12,6 +12,12 @@ steps: versionSpec: '2.x' addToPath: true +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" @@ -133,7 +139,7 @@ steps: . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(AZURE_DOCUMENTDB_MASTERKEY)" + $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" $env:VSCODE_HOCKEYAPP_TOKEN = "$(VSCODE_HOCKEYAPP_TOKEN)" .\build\azure-pipelines\win32\publish.ps1 displayName: Publish From e044bee27815112670f7adae996abce8016adfdf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 May 2019 11:06:27 -0700 Subject: [PATCH 41/99] fix #65747 (#73680) * fix #65747 * :lipstick: * restore previous behaviour --- .../editor/common/services/getIconClasses.ts | 73 +++++++++++-------- src/vs/workbench/browser/labels.ts | 39 +++++++--- 2 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index ca49de2528f..af148c324af 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -19,14 +19,11 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe // Get the path and name of the resource. For data-URIs, we need to parse specially let name: string | undefined; - let path: string | undefined; if (resource.scheme === Schemas.data) { const metadata = DataUri.parseMetaData(resource); name = metadata.get(DataUri.META_DATA_LABEL); - path = name; } else { name = cssEscape(basenameOrAuthority(resource).toLowerCase()); - path = resource.path.toLowerCase(); } // Folders @@ -47,46 +44,60 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe classes.push(`ext-file-icon`); // extra segment to increase file-ext score } - // Configured Language - let configuredLangId: string | null = getConfiguredLangId(modelService, modeService, resource); - configuredLangId = configuredLangId || (path ? modeService.getModeIdByFilepathOrFirstLine(path) : null); - if (configuredLangId) { - classes.push(`${cssEscape(configuredLangId)}-lang-file-icon`); + // Detected Mode + const detectedModeId = detectModeId(modelService, modeService, resource); + if (detectedModeId) { + classes.push(`${cssEscape(detectedModeId)}-lang-file-icon`); } } } return classes; } -export function getConfiguredLangId(modelService: IModelService, modeService: IModeService, resource: uri): string | null { - let configuredLangId: string | null = null; - if (resource) { - let modeId: string | null = null; +export function detectModeId(modelService: IModelService, modeService: IModeService, resource: uri): string | null { + if (!resource) { + return null; // we need a resource at least + } - // Data URI: check for encoded metadata - if (resource.scheme === Schemas.data) { - const metadata = DataUri.parseMetaData(resource); - const mime = metadata.get(DataUri.META_DATA_MIME); + let modeId: string | null = null; - if (mime) { - modeId = modeService.getModeId(mime); - } - } + // Data URI: check for encoded metadata + if (resource.scheme === Schemas.data) { + const metadata = DataUri.parseMetaData(resource); + const mime = metadata.get(DataUri.META_DATA_MIME); - // Any other URI: check for model if existing - else { - const model = modelService.getModel(resource); - if (model) { - modeId = model.getLanguageIdentifier().language; - } - } - - if (modeId && modeId !== PLAINTEXT_MODE_ID) { - configuredLangId = modeId; // only take if the mode is specific (aka no just plain text) + if (mime) { + modeId = modeService.getModeId(mime); } } - return configuredLangId; + // Any other URI: check for model if existing + else { + const model = modelService.getModel(resource); + if (model) { + modeId = model.getModeId(); + } + } + + // only take if the mode is specific (aka no just plain text) + if (modeId && modeId !== PLAINTEXT_MODE_ID) { + return modeId; + } + + // otherwise fallback to path based detection + let path: string | undefined; + if (resource.scheme === Schemas.data) { + const metadata = DataUri.parseMetaData(resource); + path = metadata.get(DataUri.META_DATA_LABEL); + } else { + path = resource.path.toLowerCase(); + } + + if (path) { + return modeService.getModeIdByFilepathOrFirstLine(path); + } + + return null; // finally - we do not know the mode id } export function cssEscape(val: string): string { diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index d4cc5b6851f..8eaff3f7e37 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -21,7 +21,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; import { ILabelService } from 'vs/platform/label/common/label'; -import { getIconClasses, getConfiguredLangId } from 'vs/editor/common/services/getIconClasses'; +import { getIconClasses, detectModeId } from 'vs/editor/common/services/getIconClasses'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -121,7 +121,16 @@ export class ResourceLabels extends Disposable { return; // ignore transitions in files from no mode to specific mode because this happens each time a model is created } - this._widgets.forEach(widget => widget.notifyModelModeChanged(e)); + this._widgets.forEach(widget => widget.notifyModelModeChanged(e.model)); + })); + + // notify when model is added + this._register(this.modelService.onModelAdded(model => { + if (!model.uri) { + return; // we need the resource to compare + } + + this._widgets.forEach(widget => widget.notifyModelAdded(model)); })); // notify when file decoration changes @@ -228,7 +237,7 @@ class ResourceLabelWidget extends IconLabel { private label?: IResourceLabelProps; private options?: IResourceLabelOptions; private computedIconClasses?: string[]; - private lastKnownConfiguredLangId?: string; + private lastKnownDetectedModeId?: string; private computedPathLabel?: string; private needsRedraw?: Redraw; @@ -258,13 +267,21 @@ class ResourceLabelWidget extends IconLabel { } } - notifyModelModeChanged(e: { model: ITextModel; oldModeId: string; }): void { + notifyModelModeChanged(model: ITextModel): void { + this.handleModelEvent(model); + } + + notifyModelAdded(model: ITextModel): void { + this.handleModelEvent(model); + } + + private handleModelEvent(model: ITextModel): void { if (!this.label || !this.label.resource) { return; // only update if label exists } - if (e.model.uri.toString() === this.label.resource.toString()) { - if (this.lastKnownConfiguredLangId !== e.model.getLanguageIdentifier().language) { + if (model.uri.toString() === this.label.resource.toString()) { + if (this.lastKnownDetectedModeId !== model.getModeId()) { this.render(true); // update if the language id of the model has changed from our last known state } } @@ -367,7 +384,7 @@ class ResourceLabelWidget extends IconLabel { clear(): void { this.label = undefined; this.options = undefined; - this.lastKnownConfiguredLangId = undefined; + this.lastKnownDetectedModeId = undefined; this.computedIconClasses = undefined; this.computedPathLabel = undefined; @@ -388,10 +405,10 @@ class ResourceLabelWidget extends IconLabel { } if (this.label) { - const configuredLangId = this.label.resource ? withNullAsUndefined(getConfiguredLangId(this.modelService, this.modeService, this.label.resource)) : undefined; - if (this.lastKnownConfiguredLangId !== configuredLangId) { + const detectedModeId = this.label.resource ? withNullAsUndefined(detectModeId(this.modelService, this.modeService, this.label.resource)) : undefined; + if (this.lastKnownDetectedModeId !== detectedModeId) { clearIconCache = true; - this.lastKnownConfiguredLangId = configuredLangId; + this.lastKnownDetectedModeId = detectedModeId; } } @@ -465,7 +482,7 @@ class ResourceLabelWidget extends IconLabel { this.label = undefined; this.options = undefined; - this.lastKnownConfiguredLangId = undefined; + this.lastKnownDetectedModeId = undefined; this.computedIconClasses = undefined; this.computedPathLabel = undefined; } From dc8fc80d09285d5b77e09ca34ebdbb85fd997790 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 May 2019 11:07:57 -0700 Subject: [PATCH 42/99] fix #50830 (#73681) --- src/vs/code/electron-main/window.ts | 62 +++++++++++++++++++---------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index fdfb2dcbc04..915cf30ba61 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { IStateService } from 'vs/platform/state/common/state'; -import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage } from 'electron'; +import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display } from 'electron'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -740,32 +740,30 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Single Monitor: be strict about x/y positioning if (displays.length === 1) { - const displayBounds = displays[0].bounds; - - // Careful with maximized: in that mode x/y can well be negative! - if (state.mode !== WindowMode.Maximized && displayBounds.width > 0 && displayBounds.height > 0 /* Linux X11 sessions sometimes report wrong display bounds */) { - if (state.x < displayBounds.x) { - state.x = displayBounds.x; // prevent window from falling out of the screen to the left + const displayWorkingArea = this.getWorkingArea(displays[0]); + if (state.mode !== WindowMode.Maximized && displayWorkingArea) { + if (state.x < displayWorkingArea.x) { + state.x = displayWorkingArea.x; // prevent window from falling out of the screen to the left } - if (state.y < displayBounds.y) { - state.y = displayBounds.y; // prevent window from falling out of the screen to the top + if (state.y < displayWorkingArea.y) { + state.y = displayWorkingArea.y; // prevent window from falling out of the screen to the top } - if (state.x > (displayBounds.x + displayBounds.width)) { - state.x = displayBounds.x; // prevent window from falling out of the screen to the right + if (state.x > (displayWorkingArea.x + displayWorkingArea.width)) { + state.x = displayWorkingArea.x; // prevent window from falling out of the screen to the right } - if (state.y > (displayBounds.y + displayBounds.height)) { - state.y = displayBounds.y; // prevent window from falling out of the screen to the bottom + if (state.y > (displayWorkingArea.y + displayWorkingArea.height)) { + state.y = displayWorkingArea.y; // prevent window from falling out of the screen to the bottom } - if (state.width > displayBounds.width) { - state.width = displayBounds.width; // prevent window from exceeding display bounds width + if (state.width > displayWorkingArea.width) { + state.width = displayWorkingArea.width; // prevent window from exceeding display bounds width } - if (state.height > displayBounds.height) { - state.height = displayBounds.height; // prevent window from exceeding display bounds height + if (state.height > displayWorkingArea.height) { + state.height = displayWorkingArea.height; // prevent window from exceeding display bounds height } } @@ -791,12 +789,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Multi Monitor (non-fullscreen): be less strict because metrics can be crazy const bounds = { x: state.x, y: state.y, width: state.width, height: state.height }; const display = screen.getDisplayMatching(bounds); + const displayWorkingArea = this.getWorkingArea(display); if ( - display && // we have a display matching the desired bounds - bounds.x < display.bounds.x + display.bounds.width && // prevent window from falling out of the screen to the right - bounds.y < display.bounds.y + display.bounds.height && // prevent window from falling out of the screen to the bottom - bounds.x + bounds.width > display.bounds.x && // prevent window from falling out of the screen to the left - bounds.y + bounds.height > display.bounds.y // prevent window from falling out of the scree nto the top + display && // we have a display matching the desired bounds + displayWorkingArea && // we have valid working area bounds + bounds.x < displayWorkingArea.x + displayWorkingArea.width && // prevent window from falling out of the screen to the right + bounds.y < displayWorkingArea.y + displayWorkingArea.height && // prevent window from falling out of the screen to the bottom + bounds.x + bounds.width > displayWorkingArea.x && // prevent window from falling out of the screen to the left + bounds.y + bounds.height > displayWorkingArea.y // prevent window from falling out of the scree nto the top ) { if (state.mode === WindowMode.Maximized) { const defaults = defaultWindowState(WindowMode.Maximized); // when maximized, make sure we have good values when the user restores the window @@ -812,6 +812,24 @@ export class CodeWindow extends Disposable implements ICodeWindow { return null; } + private getWorkingArea(display: Display): Rectangle | undefined { + + // Prefer the working area of the display to account for taskbars on the + // desktop being positioned somewhere (https://github.com/Microsoft/vscode/issues/50830). + // + // Linux X11 sessions sometimes report wrong display bounds, so we validate + // the reported sizes are positive. + if (display.workArea.width > 0 && display.workArea.height > 0) { + return display.workArea; + } + + if (display.bounds.width > 0 && display.bounds.height > 0) { + return display.bounds; + } + + return undefined; + } + getBounds(): Electron.Rectangle { const pos = this._win.getPosition(); const dimension = this._win.getSize(); From 91c02d76f30aa954402e71680bfde7a355d719d1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 May 2019 11:09:35 -0700 Subject: [PATCH 43/99] fix #72573 (#73682) * fix #72573 * :lipstick: --- src/vs/workbench/electron-browser/window.ts | 68 ++++++++++++++------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 7687183710f..6141ad0cfee 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -43,6 +43,8 @@ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessi import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { coalesce } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { isEqual } from 'vs/base/common/resources'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -88,7 +90,8 @@ export class ElectronWindow extends Disposable { @IIntegrityService private readonly integrityService: IIntegrityService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @ITextFileService private readonly textFileService: ITextFileService ) { super(); @@ -228,11 +231,10 @@ export class ElectronWindow extends Disposable { // Listen to editor closing (if we run with --wait) const filesToWait = this.environmentService.configuration.filesToWait; if (filesToWait) { - const resourcesToWaitFor = coalesce(filesToWait.paths.map(p => p.fileUri)); const waitMarkerFile = filesToWait.waitMarkerFileUri; - const listenerDispose = this.editorService.onDidCloseEditor(() => this.onEditorClosed(listenerDispose, resourcesToWaitFor, waitMarkerFile)); + const resourcesToWaitFor = coalesce(filesToWait.paths.map(p => p.fileUri)); - this._register(listenerDispose); + this._register(this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor)); } } @@ -257,17 +259,6 @@ export class ElectronWindow extends Disposable { } } - private onEditorClosed(listenerDispose: IDisposable, resourcesToWaitFor: URI[], waitMarkerFile: URI): void { - - // In wait mode, listen to changes to the editors and wait until the files - // are closed that the user wants to wait for. When this happens we delete - // the wait marker file to signal to the outside that editing is done. - if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { - listenerDispose.dispose(); - this.fileService.del(waitMarkerFile); - } - } - private onContextMenu(e: MouseEvent): void { if (e.target instanceof HTMLElement) { const target = e.target; @@ -488,15 +479,50 @@ export class ElectronWindow extends Disposable { // In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. - const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.revive(p.fileUri)); const waitMarkerFile = URI.revive(request.filesToWait.waitMarkerFileUri); - const unbind = this.editorService.onDidCloseEditor(() => { - if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { - unbind.dispose(); - this.fileService.del(waitMarkerFile); + const resourcesToWaitFor = coalesce(request.filesToWait.paths.map(p => URI.revive(p.fileUri))); + this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor); + } + } + + private trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): IDisposable { + const listener = this.editorService.onDidCloseEditor(async () => { + // In wait mode, listen to changes to the editors and wait until the files + // are closed that the user wants to wait for. When this happens we delete + // the wait marker file to signal to the outside that editing is done. + if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { + // If auto save is configured with the default delay (1s) it is possible + // to close the editor while the save still continues in the background. As such + // we have to also check if the files to wait for are dirty and if so wait + // for them to get saved before deleting the wait marker file. + const dirtyFilesToWait = this.textFileService.getDirty(resourcesToWaitFor); + if (dirtyFilesToWait.length > 0) { + await Promise.all(dirtyFilesToWait.map(async dirtyFileToWait => await this.joinResourceSaved(dirtyFileToWait))); + } + + listener.dispose(); + await this.fileService.del(waitMarkerFile); + } + }); + + return listener; + } + + private joinResourceSaved(resource: URI): Promise { + return new Promise(resolve => { + if (!this.textFileService.isDirty(resource)) { + return resolve(); // return early if resource is not dirty + } + + // Otherwise resolve promise when resource is saved + const listener = this.textFileService.models.onModelSaved(e => { + if (isEqual(resource, e.resource)) { + listener.dispose(); + + resolve(); } }); - } + }); } private openResources(resources: Array, diffMode: boolean): void { From 631b5f14f09e0f072200fb012cb827f3b8db76c7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 May 2019 11:10:41 -0700 Subject: [PATCH 44/99] fix #73355 (#73684) --- src/vs/base/common/mime.ts | 6 +++++- src/vs/base/test/common/mime.test.ts | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 81bdd5e9416..70e70de33a1 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -197,7 +197,11 @@ function guessMimeTypeByFirstline(firstLine: string): string | null { } if (firstLine.length > 0) { - for (const association of registeredAssociations) { + + // We want to prioritize associations based on the order they are registered so that the last registered + // association wins over all other. This is for https://github.com/Microsoft/vscode/issues/20074 + for (let i = registeredAssociations.length - 1; i >= 0; i--) { + const association = registeredAssociations[i]; if (!association.firstline) { continue; } diff --git a/src/vs/base/test/common/mime.test.ts b/src/vs/base/test/common/mime.test.ts index 75f6d531fe7..4cea0feb565 100644 --- a/src/vs/base/test/common/mime.test.ts +++ b/src/vs/base/test/common/mime.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { guessMimeTypes, registerTextMime, suggestFilename } from 'vs/base/common/mime'; suite('Mime', () => { + test('Dynamically Register Text Mime', () => { let guess = guessMimeTypes('foo.monaco'); assert.deepEqual(guess, ['application/unknown']); @@ -56,6 +57,11 @@ suite('Mime', () => { registerTextMime({ id: 'docker', filepattern: 'dockerfile*', mime: 'text/looser' }); guess = guessMimeTypes('dockerfile'); assert.deepEqual(guess, ['text/winner', 'text/plain']); + + registerTextMime({ id: 'azure-looser', mime: 'text/azure-looser', firstline: /azure/ }); + registerTextMime({ id: 'azure-winner', mime: 'text/azure-winner', firstline: /azure/ }); + guess = guessMimeTypes('azure', 'azure'); + assert.deepEqual(guess, ['text/azure-winner', 'text/plain']); }); test('Specificity priority 1', () => { From d13f01bd6909dfbb1e40a6312424a07f31cd1c54 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 13 May 2019 11:11:13 -0700 Subject: [PATCH 45/99] Disable path checking in terminal temporarily This causes 'cmd.exe' not to work for example, need to verify against PATH Part of #72650 --- .../contrib/terminal/node/terminalProcess.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index cfcd7826dcb..eb250416998 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -11,7 +11,7 @@ import * as fs from 'fs'; import { Event, Emitter } from 'vs/base/common/event'; import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IShellLaunchConfig, ITerminalChildProcess, SHELL_PATH_INVALID_EXIT_CODE } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { exec } from 'child_process'; export class TerminalProcess implements ITerminalChildProcess, IDisposable { @@ -69,15 +69,16 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { experimentalUseConpty: useConpty }; - fs.stat(shellLaunchConfig.executable!, (err) => { - if (err && err.code === 'ENOENT') { - this._exitCode = SHELL_PATH_INVALID_EXIT_CODE; - this._queueProcessExit(); - this._processStartupComplete = Promise.resolve(undefined); - return; - } - this.setupPtyProcess(shellLaunchConfig, options); - }); + // TODO: Need to verify whether executable is on $PATH, otherwise things like cmd.exe will break + // fs.stat(shellLaunchConfig.executable!, (err) => { + // if (err && err.code === 'ENOENT') { + // this._exitCode = SHELL_PATH_INVALID_EXIT_CODE; + // this._queueProcessExit(); + // this._processStartupComplete = Promise.resolve(undefined); + // return; + // } + this.setupPtyProcess(shellLaunchConfig, options); + // }); } private setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): void { From 1dd5245a83042b56172f126a203ba6de5108c9ec Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 13 May 2019 11:15:50 -0700 Subject: [PATCH 46/99] Remove unneeded vscode-xterm function --- src/typings/vscode-xterm.d.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index fa2ac025970..9ca7342a340 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -928,7 +928,5 @@ declare module 'vscode-xterm' { * @return Whether a result was found. */ findPrevious(term: string, findOptions: ISearchOptions): boolean; - - addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; } } From 0815c3667562833be3d3e14604eda1b01e033f24 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 13 May 2019 11:34:44 -0700 Subject: [PATCH 47/99] use keyvault for all secrets --- .../darwin/product-build-darwin.yml | 16 ++++++++-------- build/azure-pipelines/distro-build.yml | 2 +- .../linux/product-build-linux.yml | 12 ++++++------ build/azure-pipelines/linux/snap-build-linux.yml | 2 +- build/azure-pipelines/sync-mooncake.yml | 4 ++-- .../win32/product-build-win32.yml | 10 +++++----- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index d53bdbe1c2f..5d6ec8c2cff 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -18,10 +18,10 @@ steps: cat << EOF > ~/.netrc machine monacotools.visualstudio.com - password $(VSO_PAT) + password $(devops-pat) machine github.com login vscode - password $(VSCODE_MIXIN_PASSWORD) + password $(github-distro-mixin-password) EOF git config user.email "vscode@microsoft.com" @@ -40,8 +40,8 @@ steps: - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ ./build/azure-pipelines/darwin/build.sh displayName: Build @@ -83,11 +83,11 @@ steps: - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ ./build/azure-pipelines/darwin/publish.sh displayName: Publish diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index dc55bce808c..54b9ff77d6c 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -16,7 +16,7 @@ steps: cat << EOF > ~/.netrc machine github.com login vscode - password $(VSCODE_MIXIN_PASSWORD) + password $(github-distro-mixin-password) EOF git config user.email "vscode@microsoft.com" diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 06ff32d5f7c..f727e30d252 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -22,10 +22,10 @@ steps: cat << EOF > ~/.netrc machine monacotools.visualstudio.com - password $(VSO_PAT) + password $(devops-pat) machine github.com login vscode - password $(VSCODE_MIXIN_PASSWORD) + password $(github-distro-mixin-password) EOF git config user.email "vscode@microsoft.com" @@ -44,7 +44,7 @@ steps: - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ ./build/azure-pipelines/linux/build.sh displayName: Build @@ -62,9 +62,9 @@ steps: - script: | set -e AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ ./build/azure-pipelines/linux/publish.sh displayName: Publish diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 9941d13d08c..9d98e9fa472 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -51,5 +51,5 @@ steps: # Publish snap package AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH" \ No newline at end of file diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index 36af0f0265d..f3e8bc07856 100644 --- a/build/azure-pipelines/sync-mooncake.yml +++ b/build/azure-pipelines/sync-mooncake.yml @@ -19,6 +19,6 @@ steps: (cd build ; yarn) AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \ node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 907b93a86da..8a6a6abb658 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -21,7 +21,7 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - "machine monacotools.visualstudio.com`npassword $(VSO_PAT)`nmachine github.com`nlogin vscode`npassword $(VSCODE_MIXIN_PASSWORD)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + "machine monacotools.visualstudio.com`npassword $(devops-pat)`nmachine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII $env:npm_config_arch="$(VSCODE_ARCH)" $env:CHILD_CONCURRENCY="1" @@ -42,7 +42,7 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" .\build\azure-pipelines\win32\build.ps1 displayName: Build @@ -132,15 +132,15 @@ steps: - powershell: | $ErrorActionPreference = "Stop" - .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) + .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key) displayName: Import ESRP Auth Certificate - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)" + $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" - $env:VSCODE_HOCKEYAPP_TOKEN = "$(VSCODE_HOCKEYAPP_TOKEN)" + $env:VSCODE_HOCKEYAPP_TOKEN = "$(vscode-hockeyapp-token)" .\build\azure-pipelines\win32\publish.ps1 displayName: Publish From 14206913318a4e9f121fc1a8f021330e0c85d725 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 May 2019 11:39:26 -0700 Subject: [PATCH 48/99] debt - merge contextkeys into one --- .../browser/actions/layoutActions.ts | 2 +- src/vs/workbench/browser/contextkeys.ts | 22 ++++++++++++++-- src/vs/workbench/common/contextkeys.ts | 25 ------------------- .../electron-browser/extensionsViewlet.ts | 2 +- .../files/browser/fileActions.contribution.ts | 2 +- .../preferences.contribution.ts | 2 +- .../electron-browser/main.contribution.ts | 2 +- .../dialogs/browser/remoteFileDialog.ts | 2 +- 8 files changed, 26 insertions(+), 33 deletions(-) delete mode 100644 src/vs/workbench/common/contextkeys.ts diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 8a7236e8d0f..5b42967e77f 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -19,7 +19,7 @@ import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { MenuBarVisibility } from 'vs/platform/windows/common/windows'; import { isWindows, isLinux } from 'vs/base/common/platform'; -import { IsMacContext } from 'vs/workbench/common/contextkeys'; +import { IsMacContext } from 'vs/workbench/browser/contextkeys'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { InEditorZenModeContext } from 'vs/workbench/common/editor'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index f696004578e..a7846a7d257 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -5,11 +5,10 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { IWindowsConfiguration } from 'vs/platform/windows/common/windows'; import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext } from 'vs/workbench/common/editor'; -import { IsMacContext, IsLinuxContext, IsWindowsContext, HasMacNativeTabsContext, IsDevelopmentContext, SupportsWorkspacesContext, WorkbenchStateContext, WorkspaceFolderCountContext, RemoteAuthorityContext } from 'vs/workbench/common/contextkeys'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -19,6 +18,25 @@ import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/ import { SideBarVisibleContext } from 'vs/workbench/common/viewlet'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform'; + +export const IsMacContext = new RawContextKey('isMac', isMacintosh); +export const IsLinuxContext = new RawContextKey('isLinux', isLinux); +export const IsWindowsContext = new RawContextKey('isWindows', isWindows); + +export const RemoteAuthorityContext = new RawContextKey('remoteAuthority', ''); + +export const HasMacNativeTabsContext = new RawContextKey('hasMacNativeTabs', false); + +export const SupportsWorkspacesContext = new RawContextKey('supportsWorkspaces', true); + +export const IsDevelopmentContext = new RawContextKey('isDevelopment', false); + +export const WorkbenchStateContext = new RawContextKey('workbenchState', undefined); + +export const WorkspaceFolderCountContext = new RawContextKey('workspaceFolderCount', 0); + +export const RemoteFileDialogContext = new RawContextKey('remoteFileDialogVisible', false); export class WorkbenchContextKeysHandler extends Disposable { private inputFocusedContext: IContextKey; diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts deleted file mode 100644 index cb129ec8507..00000000000 --- a/src/vs/workbench/common/contextkeys.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform'; - -export const IsMacContext = new RawContextKey('isMac', isMacintosh); -export const IsLinuxContext = new RawContextKey('isLinux', isLinux); -export const IsWindowsContext = new RawContextKey('isWindows', isWindows); - -export const RemoteAuthorityContext = new RawContextKey('remoteAuthority', ''); - -export const HasMacNativeTabsContext = new RawContextKey('hasMacNativeTabs', false); - -export const SupportsWorkspacesContext = new RawContextKey('supportsWorkspaces', true); - -export const IsDevelopmentContext = new RawContextKey('isDevelopment', false); - -export const WorkbenchStateContext = new RawContextKey('workbenchState', undefined); - -export const WorkspaceFolderCountContext = new RawContextKey('workspaceFolderCount', 0); - -export const RemoteFileDialogContext = new RawContextKey('remoteFileDialogVisible', false); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts index 4a72a6bbddc..5658c7df148 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts @@ -54,7 +54,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { RemoteAuthorityContext } from 'vs/workbench/common/contextkeys'; +import { RemoteAuthorityContext } from 'vs/workbench/browser/contextkeys'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { ILabelService } from 'vs/platform/label/common/label'; diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index cfaeef24118..5dc462a1263 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -23,7 +23,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { SupportsWorkspacesContext } from 'vs/workbench/common/contextkeys'; +import { SupportsWorkspacesContext } from 'vs/workbench/browser/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; // Contribute Global Actions diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts index 6962f5dbd9e..c5890ae1ee1 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts @@ -13,7 +13,7 @@ import * as nls from 'vs/nls'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { WorkbenchStateContext, RemoteAuthorityContext } from 'vs/workbench/common/contextkeys'; +import { WorkbenchStateContext, RemoteAuthorityContext } from 'vs/workbench/browser/contextkeys'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 0fbffedbc36..71f07611ed0 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -21,7 +21,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext, RemoteFileDialogContext } from 'vs/workbench/common/contextkeys'; +import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext, RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { LogStorageAction } from 'vs/platform/storage/node/storageService'; diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 2a5d47b85cc..8955b687a91 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -22,7 +22,7 @@ import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { RemoteFileDialogContext } from 'vs/workbench/common/contextkeys'; +import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; import { equalsIgnoreCase, format } from 'vs/base/common/strings'; import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; From 43f2710fde2c0d48135da3672ba79d6d7931ded8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 13 May 2019 11:58:36 -0700 Subject: [PATCH 49/99] Reenable disabled test - fix #73604 --- .../services/search/node/fileSearch.ts | 2 +- .../services/search/test/node/search.test.ts | 36 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index a5c31f63549..5ade08b60ca 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -77,7 +77,7 @@ export class FileWalker { this.errors = []; if (this.filePattern) { - this.normalizedFilePatternLowercase = prepareQuery(this.filePattern).value; + this.normalizedFilePatternLowercase = prepareQuery(this.filePattern).lowercase; } this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern); diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts index 03fc9cde860..34d9c2bad1c 100644 --- a/src/vs/workbench/services/search/test/node/search.test.ts +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -290,25 +290,25 @@ suite('FileSearchEngine', () => { }); }); - // test('Files: NPE (CamelCase)', function (done: () => void) { - // this.timeout(testTimeout); - // const engine = new FileSearchEngine({ - // type: QueryType.File, - // folderQueries: ROOT_FOLDER_QUERY, - // filePattern: 'NullPE' - // }); + test('Files: NPE (CamelCase)', function (done: () => void) { + this.timeout(testTimeout); + const engine = new FileSearchEngine({ + type: QueryType.File, + folderQueries: ROOT_FOLDER_QUERY, + filePattern: 'NullPE' + }); - // let count = 0; - // engine.search((result) => { - // if (result) { - // count++; - // } - // }, () => { }, (error) => { - // assert.ok(!error); - // assert.equal(count, 1); - // done(); - // }); - // }); + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error) => { + assert.ok(!error); + assert.equal(count, 1); + done(); + }); + }); test('Files: *.*', function (done: () => void) { this.timeout(testTimeout); From 3ac45b81e7fe131b3f8cf53972ff811d42981b1a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 13 May 2019 12:00:00 -0700 Subject: [PATCH 50/99] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff9ec2cf5fe..4b425aaf293 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.35.0", - "distro": "b9fb31b7caa6d9f506aa458342ab71183bc90b52", + "distro": "89ef32d88a650c9b30b23486c8fcda874c98953d", "author": { "name": "Microsoft Corporation" }, From d5fce51880990ab39516f1cc1c51b816eeaa5783 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 May 2019 18:00:01 -0700 Subject: [PATCH 51/99] update references view extension --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index f678b7bec07..b460362095f 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.26", + "version": "0.0.27", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From fd1ac7561b05cdd0fdeb8a89c4eeac5eb2677226 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 13 May 2019 18:00:06 -0700 Subject: [PATCH 52/99] Fix TS 3.5 compile errors Fixing errors related to https://github.com/microsoft/TypeScript/issues/31380 --- .../src/utils/surveyor.ts | 2 +- src/vs/base/common/objects.ts | 2 +- src/vs/base/common/parsers.ts | 2 +- src/vs/base/node/request.ts | 2 +- src/vs/base/test/node/config.test.ts | 2 +- .../contrib/colorPicker/colorDetector.ts | 4 ++-- .../common/instantiationService.ts | 2 +- src/vs/workbench/browser/dnd.ts | 8 ++++++- .../browser/parts/editor/breadcrumbsPicker.ts | 23 +++++++------------ .../browser/parts/editor/editorStatus.ts | 2 +- .../browser/parts/editor/sideBySideEditor.ts | 6 ++--- src/vs/workbench/common/editor.ts | 2 +- .../debug/electron-browser/debugService.ts | 2 +- .../contrib/output/browser/outputPanel.ts | 2 +- .../preferences/browser/preferencesEditor.ts | 4 ++-- .../contrib/search/browser/searchView.ts | 2 +- .../contrib/tasks/common/problemMatcher.ts | 8 +++---- .../walkThrough/common/walkThroughInput.ts | 2 +- .../extensions/node/extensionPoints.ts | 2 +- .../keybinding/common/keybindingEditing.ts | 2 +- 20 files changed, 40 insertions(+), 41 deletions(-) diff --git a/extensions/typescript-language-features/src/utils/surveyor.ts b/extensions/typescript-language-features/src/utils/surveyor.ts index bf3ba4ad601..2af183be12e 100644 --- a/extensions/typescript-language-features/src/utils/surveyor.ts +++ b/extensions/typescript-language-features/src/utils/surveyor.ts @@ -103,7 +103,7 @@ class Survey { } private get triggerCount(): number { - const count = this.memento.get(this.triggerCountMementoKey); + const count = this.memento.get(this.triggerCountMementoKey); return !count || isNaN(+count) ? 0 : +count; } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 661863eb27a..97c8f9196c0 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -14,7 +14,7 @@ export function deepClone(obj: T): T { return obj as any; } const result: any = Array.isArray(obj) ? [] : {}; - Object.keys(obj).forEach((key: string) => { + Object.keys(obj as any).forEach((key: string) => { if (obj[key] && typeof obj[key] === 'object') { result[key] = deepClone(obj[key]); } else { diff --git a/src/vs/base/common/parsers.ts b/src/vs/base/common/parsers.ts index 4ea4e95373d..748abd4fd47 100644 --- a/src/vs/base/common/parsers.ts +++ b/src/vs/base/common/parsers.ts @@ -79,7 +79,7 @@ export abstract class Parser { this._problemReporter.fatal(message); } - protected static merge(destination: T, source: T, overwrite: boolean): void { + protected static merge(destination: T, source: T, overwrite: boolean): void { Object.keys(source).forEach((key: string) => { const destValue = destination[key]; const sourceValue = source[key]; diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts index ea24cdb7589..17731debc5b 100644 --- a/src/vs/base/node/request.ts +++ b/src/vs/base/node/request.ts @@ -159,7 +159,7 @@ export function asText(context: IRequestContext): Promise { }); } -export function asJson(context: IRequestContext): Promise { +export function asJson(context: IRequestContext): Promise { return new Promise((c, e) => { if (!isSuccess(context)) { return e('Server returned ' + context.res.statusCode); diff --git a/src/vs/base/test/node/config.test.ts b/src/vs/base/test/node/config.test.ts index 05539350074..3fa8f78de68 100644 --- a/src/vs/base/test/node/config.test.ts +++ b/src/vs/base/test/node/config.test.ts @@ -20,7 +20,7 @@ suite('Config', () => { const newDir = path.join(parentDir, 'config', id); const testFile = path.join(newDir, 'config.json'); - let watcher = new ConfigWatcher(testFile); + let watcher = new ConfigWatcher<{}>(testFile); let config = watcher.getConfig(); assert.ok(config); diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 1de0bda0312..e20d0d532a3 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -76,9 +76,9 @@ export class ColorDetector implements IEditorContribution { } const languageId = model.getLanguageIdentifier(); // handle deprecated settings. [languageId].colorDecorators.enable - let deprecatedConfig = this._configurationService.getValue(languageId.language); + const deprecatedConfig = this._configurationService.getValue<{}>(languageId.language); if (deprecatedConfig) { - let colorDecorators = deprecatedConfig['colorDecorators']; // deprecatedConfig.valueOf('.colorDecorators.enable'); + const colorDecorators = deprecatedConfig['colorDecorators']; // deprecatedConfig.valueOf('.colorDecorators.enable'); if (colorDecorators && colorDecorators['enable'] !== undefined && !colorDecorators['enable']) { return colorDecorators['enable']; } diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 76ab881a236..6b3e8d76c17 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -220,7 +220,7 @@ export class InstantiationService implements IInstantiationService { // Return a proxy object that's backed by an idle value. That // strategy is to instantiate services in our idle time or when actually // needed but not when injected into a consumer - const idle = new IdleValue(() => this._createInstance(ctor, args, _trace)); + const idle = new IdleValue(() => this._createInstance(ctor, args, _trace)); return new Proxy(Object.create(null), { get(_target: T, prop: PropertyKey): any { return idle.getValue()[prop]; diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 3751e3bf4b2..870076ae23d 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -30,6 +30,7 @@ import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IRecentFile } from 'vs/platform/history/common/history'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { withNullAsUndefined } from 'vs/base/common/types'; export interface IDraggedResource { resource: URI; @@ -81,7 +82,12 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array { - resources.push({ resource: URI.parse(draggedEditor.resource), backupResource: draggedEditor.backupResource ? URI.parse(draggedEditor.backupResource) : undefined, viewState: draggedEditor.viewState, isExternal: false }); + resources.push({ + resource: URI.parse(draggedEditor.resource), + backupResource: draggedEditor.backupResource ? URI.parse(draggedEditor.backupResource) : undefined, + viewState: withNullAsUndefined(draggedEditor.viewState), + isExternal: false + }); }); } catch (error) { // Invalid transfer diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 9b038dbf6a0..d2147f4c505 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -388,21 +388,14 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { const labels = this._instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER /* TODO@Jo visibility propagation */); this._disposables.push(labels); - return this._instantiationService.createInstance( - WorkbenchAsyncDataTree, - container, - new FileVirtualDelegate(), - [this._instantiationService.createInstance(FileRenderer, labels)], - this._instantiationService.createInstance(FileDataSource), - { - filterOnType: true, - multipleSelectionSupport: false, - sorter: new FileSorter(), - filter: this._instantiationService.createInstance(FileFilter), - identityProvider: new FileIdentityProvider(), - keyboardNavigationLabelProvider: new FileNavigationLabelProvider() - } - ) as WorkbenchAsyncDataTree; + return this._instantiationService.createInstance(WorkbenchAsyncDataTree, container, new FileVirtualDelegate(), [this._instantiationService.createInstance(FileRenderer, labels)], this._instantiationService.createInstance(FileDataSource), { + filterOnType: true, + multipleSelectionSupport: false, + sorter: new FileSorter(), + filter: this._instantiationService.createInstance(FileFilter), + identityProvider: new FileIdentityProvider(), + keyboardNavigationLabelProvider: new FileNavigationLabelProvider() + }) as WorkbenchAsyncDataTree; } _setInput(element: BreadcrumbElement): Promise { diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 5c6df3b90ae..5bc68a04a6f 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -1033,7 +1033,7 @@ export class ChangeModeAction extends Action { setTimeout(() => { this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || base) }).then(language => { if (language) { - const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG); + const fileAssociationsConfig = this.configurationService.inspect<{}>(FILES_ASSOCIATIONS_CONFIG); let associationKey: string; if (extension && base[0] !== '.') { diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 6068f26f55a..90650e7cdff 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -93,10 +93,10 @@ export class SideBySideEditor extends BaseEditor { this.updateStyles(); } - setInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Promise { - const oldInput = this.input; + setInput(newInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + const oldInput = this.input as SideBySideEditorInput; return super.setInput(newInput, options, token) - .then(() => this.updateInput(oldInput, newInput, options, token)); + .then(() => this.updateInput(oldInput, newInput as SideBySideEditorInput, options, token)); } setOptions(options: EditorOptions): void { diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 37bd1a1eee6..bb8cc56f95a 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -582,7 +582,7 @@ export class SideBySideEditorInput extends EditorInput { return this.master.revert(); } - getTelemetryDescriptor(): object { + getTelemetryDescriptor(): { [key: string]: unknown } { const descriptor = this.master.getTelemetryDescriptor(); return assign(descriptor, super.getTelemetryDescriptor()); diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts index 2e4ef24b083..2dd13b297e3 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts @@ -719,7 +719,7 @@ export class DebugService implements IDebugService { // If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340 let taskStarted = false; - const promise = this.taskService.getActiveTasks().then(tasks => { + const promise: Promise = this.taskService.getActiveTasks().then(tasks => { if (tasks.filter(t => t._id === task._id).length) { // task is already running - nothing to do. return Promise.resolve(null); diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index c626a799910..4976e8620ed 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -95,7 +95,7 @@ export class OutputPanel extends AbstractTextResourceEditor { options.renderLineHighlight = 'none'; options.minimap = { enabled: false }; - const outputConfig = this.baseConfigurationService.getValue('[Log]'); + const outputConfig = this.baseConfigurationService.getValue<{}>('[Log]'); if (outputConfig) { if (outputConfig['editor.minimap.enabled']) { options.minimap = { enabled: true }; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index edc231b62a1..c8eacc925a2 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -154,14 +154,14 @@ export class PreferencesEditor extends BaseEditor { this.preferencesRenderers.editFocusedPreference(); } - setInput(newInput: PreferencesEditorInput, options: SettingsEditorOptions, token: CancellationToken): Promise { + setInput(newInput: EditorInput, options: SettingsEditorOptions, token: CancellationToken): Promise { this.defaultSettingsEditorContextKey.set(true); this.defaultSettingsJSONEditorContextKey.set(true); if (options && options.query) { this.focusSearch(options.query); } - return super.setInput(newInput, options, token).then(() => this.updateInput(newInput, options, token)); + return super.setInput(newInput, options, token).then(() => this.updateInput(newInput as PreferencesEditorInput, options, token)); } layout(dimension: DOM.Dimension): void { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2a602bafd96..b62e0cce8b6 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -682,7 +682,7 @@ export class SearchView extends ViewletPanel { })); } - private onContextMenu(e: ITreeContextMenuEvent): void { + private onContextMenu(e: ITreeContextMenuEvent): void { if (!e.element) { return; } diff --git a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts index 8d9c2184962..2f0a4cc5acc 100644 --- a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts @@ -265,7 +265,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { if (trim) { value = Strings.trim(value)!; } - data[property] += endOfLine + value; + (data as any)[property] += endOfLine + value; } } @@ -277,7 +277,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { if (trim) { value = Strings.trim(value)!; } - data[property] = value; + (data as any)[property] = value; } } } @@ -894,9 +894,9 @@ export class ProblemPatternParser extends Parser { } function copyProperty(result: ProblemPattern, source: Config.ProblemPattern, resultKey: keyof ProblemPattern, sourceKey: keyof Config.ProblemPattern) { - let value = source[sourceKey]; + const value = source[sourceKey]; if (typeof value === 'number') { - result[resultKey] = value; + (result as any)[resultKey] = value; } } copyProperty(result, value, 'file', 'file'); diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts index 338d372c657..42feb610289 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts @@ -80,7 +80,7 @@ export class WalkThroughInput extends EditorInput { return this.options.telemetryFrom; } - getTelemetryDescriptor(): object { + getTelemetryDescriptor(): { [key: string]: unknown } { const descriptor = super.getTelemetryDescriptor(); descriptor['target'] = this.getTelemetryFrom(); /* __GDPR__FRAGMENT__ diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 6e2179ddd8e..6a450c7a9f4 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -251,7 +251,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { * This routine makes the following assumptions: * The root element is an object literal */ - private static _replaceNLStrings(nlsConfig: NlsConfiguration, literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string } | null, log: ILog, messageScope: string): void { + private static _replaceNLStrings(nlsConfig: NlsConfiguration, literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string } | null, log: ILog, messageScope: string): void { function processEntry(obj: any, key: string | number, command?: boolean) { let value = obj[key]; if (types.isString(value)) { diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index ffc63601409..33b5fedb99f 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -210,7 +210,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding private resolveModelReference(): Promise> { return this.fileService.exists(this.resource) .then(exists => { - const EOL = this.configurationService.getValue('files', { overrideIdentifier: 'json' })['eol']; + const EOL = this.configurationService.getValue<{}>('files', { overrideIdentifier: 'json' })['eol']; const result: Promise = exists ? Promise.resolve(null) : this.textFileService.write(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' }); return result.then(() => this.textModelResolverService.createModelReference(this.resource)); }); From a6774a6961a802453d7ae985d4f555b8f3e0bb88 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 May 2019 20:11:23 -0700 Subject: [PATCH 53/99] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b425aaf293..26cd1d64333 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.35.0", - "distro": "89ef32d88a650c9b30b23486c8fcda874c98953d", + "distro": "1af7bc1a5b301bfe694a21382ca96e03a7d434f7", "author": { "name": "Microsoft Corporation" }, From 4c1b302a934138a05f80f073f153430e41d6cf41 Mon Sep 17 00:00:00 2001 From: Howard Hung Date: Wed, 15 May 2019 00:10:14 +0800 Subject: [PATCH 54/99] Fix typo isScrollWhellKeyPressed => isScrollWheelKeyPressed --- src/vs/workbench/browser/parts/editor/resourceViewer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index 8ecf2131c31..51ec38a3f7b 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -501,8 +501,8 @@ class InlineImageView { return; } - const isScrollWhellKeyPressed = platform.isMacintosh ? altPressed : ctrlPressed; - if (!isScrollWhellKeyPressed && !e.ctrlKey) { // pinching is reported as scroll wheel + ctrl + const isScrollWheelKeyPressed = platform.isMacintosh ? altPressed : ctrlPressed; + if (!isScrollWheelKeyPressed && !e.ctrlKey) { // pinching is reported as scroll wheel + ctrl return; } @@ -516,7 +516,7 @@ class InlineImageView { let delta = e.deltaY < 0 ? 1 : -1; // Pinching should increase the scale - if (e.ctrlKey && !isScrollWhellKeyPressed) { + if (e.ctrlKey && !isScrollWheelKeyPressed) { delta *= -1; } updateScale(scale as number * (1 - delta * InlineImageView.SCALE_PINCH_FACTOR)); From f607c9bae7cf88562bcca6a21ec04fdb62167ab7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 May 2019 09:30:37 -0700 Subject: [PATCH 55/99] web - backport some changes --- src/vs/code/browser/workbench/workbench.html | 3 ++ src/vs/workbench/browser/media/style.css | 4 +++ .../workbench/browser/web.simpleservices.ts | 30 +++++++------------ src/vs/workbench/browser/workbench.ts | 8 +++-- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index d164ad904d3..832b6033bf7 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -3,6 +3,9 @@ + + + diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 488552dc37c..a467be6485b 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -39,6 +39,10 @@ body { user-select: none; } +body.web { + position: fixed; /* prevent bounce effect */ +} + @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .monaco-workbench { diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index f42be7f155b..dd6c2fed8e9 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -58,11 +58,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { Color, RGBA } from 'vs/base/common/color'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IFileService } from 'vs/platform/files/common/files'; -export const workspaceResource = URI.from({ +export const workspaceResource = URI.file((self).USER_HOME_DIR || '/').with({ scheme: Schemas.vscodeRemote, - authority: document.location.host, - path: '/' + authority: document.location.host }); //#region Backup File @@ -702,7 +702,8 @@ export class SimpleSearchService implements ISearchService { constructor( @IModelService private modelService: IModelService, @IEditorService private editorService: IEditorService, - @IUntitledEditorService private untitledEditorService: IUntitledEditorService + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IFileService private fileService: IFileService ) { } @@ -732,8 +733,8 @@ export class SimpleSearchService implements ISearchService { return Disposable.None; } - private getLocalResults(query: ITextQuery): ResourceMap { - const localResults = new ResourceMap(); + private getLocalResults(query: ITextQuery): ResourceMap { + const localResults = new ResourceMap(); if (query.type === QueryType.Text) { const models = this.modelService.getModels(); @@ -754,10 +755,8 @@ export class SimpleSearchService implements ISearchService { } } - // Don't support other resource schemes than files for now - // why is that? we should search for resources from other - // schemes - else if (resource.scheme !== Schemas.file) { + // Block walkthrough, webview, etc. + else if (!this.fileService.canHandleResource(resource)) { return; } @@ -766,8 +765,7 @@ export class SimpleSearchService implements ISearchService { } // Use editor API to find matches - // @ts-ignore - const matches = model.findMatches(query.contentPattern.pattern, false, query.contentPattern.isRegExp, query.contentPattern.isCaseSensitive, query.contentPattern.isWordMatch ? query.contentPattern.wordSeparators : null, false, query.maxResults); + const matches = model.findMatches(query.contentPattern.pattern, false, !!query.contentPattern.isRegExp, !!query.contentPattern.isCaseSensitive, query.contentPattern.isWordMatch ? query.contentPattern.wordSeparators! : null, false, query.maxResults); if (matches.length) { const fileMatch = new FileMatch(resource); localResults.set(resource, fileMatch); @@ -775,7 +773,6 @@ export class SimpleSearchService implements ISearchService { const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); } else { - // @ts-ignore localResults.set(resource, null); } }); @@ -785,13 +782,6 @@ export class SimpleSearchService implements ISearchService { } private matches(resource: URI, query: ITextQuery): boolean { - // includes - if (query.includePattern) { - if (resource.scheme !== Schemas.file) { - return false; // if we match on file patterns, we have to ignore non file resources - } - } - return pathIncludedInQuery(query, resource.fsPath); } } diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 1f9fdde9191..0aab890a5c2 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -14,7 +14,7 @@ import { getZoomLevel } from 'vs/base/browser/browser'; import { mark } from 'vs/base/common/performance'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { Registry } from 'vs/platform/registry/common/platform'; -import { isWindows, isLinux } from 'vs/base/common/platform'; +import { isWindows, isLinux, isWeb } from 'vs/base/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; @@ -263,7 +263,11 @@ export class Workbench extends Layout { ]); addClasses(this.container, ...workbenchClasses); - addClasses(document.body, platformClass); // used by our fonts + addClass(document.body, platformClass); // used by our fonts + + if (isWeb) { + addClass(document.body, 'web'); + } // Apply font aliasing this.setFontAliasing(configurationService); From 185ec15c6cd03f75bf99c46fa0df94b2f0da2d19 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 May 2019 09:34:43 -0700 Subject: [PATCH 56/99] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 26cd1d64333..1de3da7057b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.35.0", - "distro": "1af7bc1a5b301bfe694a21382ca96e03a7d434f7", + "distro": "64154bcc07e6621913c55f0daa25bef184b758a6", "author": { "name": "Microsoft Corporation" }, From 0cfb9ad1c3a4ea5983c8dbb458ed14f7581a6846 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 14 May 2019 09:41:57 -0700 Subject: [PATCH 57/99] fix distro build --- build/azure-pipelines/distro-build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 54b9ff77d6c..639456ad4ce 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -10,6 +10,12 @@ steps: inputs: versionSpec: "10.15.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e From afe9a74dbf14bed2908712739d01faf48e158e69 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 May 2019 10:11:14 -0700 Subject: [PATCH 58/99] remote fs provider to support chunked read/write --- .../common/remoteAgentFileSystemChannel.ts | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts b/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts index d3b15083c23..fdc022b18d9 100644 --- a/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts +++ b/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts @@ -8,7 +8,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileChange, IFileSystemProvider, IStat, IWatchOptions } from 'vs/platform/files/common/files'; +import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, IFileChange, IFileSystemProvider, IStat, IWatchOptions, FileOpenOptions } from 'vs/platform/files/common/files'; import { VSBuffer } from 'vs/base/common/buffer'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { OperatingSystem } from 'vs/base/common/platform'; @@ -50,7 +50,7 @@ export class RemoteExtensionsFileSystemProvider extends Disposable implements IF setCaseSensitive(isCaseSensitive: boolean) { let capabilities = ( - FileSystemProviderCapabilities.FileReadWrite + FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileFolderCopy ); @@ -68,14 +68,28 @@ export class RemoteExtensionsFileSystemProvider extends Disposable implements IF return this.channel.call('stat', [resource]); } - async readFile(resource: URI): Promise { - const buff = await this.channel.call('readFile', [resource]); - - return buff.buffer; + open(resource: URI, opts: FileOpenOptions): Promise { + return this.channel.call('open', [resource, opts]); } - writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { - return this.channel.call('writeFile', [resource, VSBuffer.wrap(content), opts]); + close(fd: number): Promise { + return this.channel.call('close', [fd]); + } + + async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { + const [bytes, bytesRead]: [VSBuffer, number] = await this.channel.call('read', [fd, pos, length]); + + // copy back the data that was written into the buffer on the remote + // side. we need to do this because buffers are not referenced by + // pointer, but only by value and as such cannot be directly written + // to from the other process. + data.set(bytes.buffer.slice(0, bytesRead), offset); + + return bytesRead; + } + + write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { + return this.channel.call('write', [fd, pos, VSBuffer.wrap(data), offset, length]); } delete(resource: URI, opts: FileDeleteOptions): Promise { From 9de06a375bdfc8a17f492fecb3cb55a255393ebf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 14 May 2019 10:42:31 -0700 Subject: [PATCH 59/99] vscode-xterm@3.14.0-beta2 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1de3da7057b..57b9aca4fb5 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "vscode-ripgrep": "^1.2.5", "vscode-sqlite3": "4.0.7", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.14.0-beta1", + "vscode-xterm": "3.14.0-beta2", "yauzl": "^2.9.1", "yazl": "^2.4.3" }, diff --git a/yarn.lock b/yarn.lock index 7039bfc94a4..55d45368f2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9656,10 +9656,10 @@ vscode-windows-registry@1.0.1: dependencies: nan "^2.12.1" -vscode-xterm@3.14.0-beta1: - version "3.14.0-beta1" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.14.0-beta1.tgz#3cf7ecd0b8fe995675cb8fd30239c481c420aba7" - integrity sha512-CFA3foOLrY7pUw2E8xzpPwP40hDbFubPNO4D8PTh2u2UDhJ9PYfQaJxXujCoic8qg9uEVVnLUbqqNlK2qGWFnA== +vscode-xterm@3.14.0-beta2: + version "3.14.0-beta2" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.14.0-beta2.tgz#5c9e0b9ef72de80f8ba4c9a89cec78cac73bbc1b" + integrity sha512-J/aXupjdOQmHE19tsO8Jrtdv5zMHhluVHBkLdEo2SIZtkkjkhviuwR6vtLotRJX8rcgwcC3z8LbVx0Phi+pvKg== vso-node-api@6.1.2-preview: version "6.1.2-preview" From 2aee035c95e58ab7bb1c6b09a4f8dba5b1731998 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 14 May 2019 10:42:40 -0700 Subject: [PATCH 60/99] Update vscode-xterm typings --- src/typings/vscode-xterm.d.ts | 172 ++++++++++++++++++++++++++++++++-- 1 file changed, 163 insertions(+), 9 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index 9ca7342a340..38dd9fae766 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -275,7 +275,7 @@ declare module 'vscode-xterm' { * A callback that fires when the mouse leaves a link. Note that this can * happen even when tooltipCallback hasn't fired for the link yet. */ - leaveCallback?: (event: MouseEvent, uri: string) => boolean | void; + leaveCallback?: () => void; /** * The priority of the link matcher, this defines the order in which the link @@ -355,6 +355,13 @@ declare module 'vscode-xterm' { */ readonly cols: number; + /** + * (EXPERIMENTAL) The terminal's current buffer, this might be either the + * normal buffer or the alt buffer depending on what's running in the + * terminal. + */ + readonly buffer: IBuffer; + /** * (EXPERIMENTAL) Get all markers registered against the buffer. If the alt * buffer is active this will always return []. @@ -546,12 +553,6 @@ declare module 'vscode-xterm' { */ resize(columns: number, rows: number): void; - /** - * Writes text to the terminal, followed by a break line character (\n). - * @param data The text to write to the terminal. - */ - writeln(data: string): void; - /** * Opens the terminal within an element. * @param parent The element to create the terminal within. This element @@ -669,11 +670,24 @@ declare module 'vscode-xterm' { */ getSelection(): string; + /** + * Gets the selection position or undefined if there is no selection. + */ + getSelectionPosition(): ISelectionPosition | undefined; + /** * Clears the current terminal selection. */ clearSelection(): void; + /** + * Selects text within the terminal. + * @param column The column the selection starts at.. + * @param row The row the selection starts at. + * @param length The length of the selection. + */ + select(column: number, row: number, length: number): void; + /** * Selects all text within the terminal. */ @@ -738,6 +752,20 @@ declare module 'vscode-xterm' { */ write(data: string): void; + /** + * Writes text to the terminal, followed by a break line character (\n). + * @param data The text to write to the terminal. + */ + writeln(data: string): void; + + /** + * Writes UTF8 data to the terminal. + * This has a slight performance advantage over the string based write method + * due to lesser data conversions needed on the way from the pty to xterm.js. + * @param data The data to write to the terminal. + */ + writeUtf8(data: Uint8Array): void; + /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -747,7 +775,7 @@ declare module 'vscode-xterm' { * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -798,7 +826,7 @@ declare module 'vscode-xterm' { * @param key The option key. * @param value The option value. */ - setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. @@ -853,8 +881,134 @@ declare module 'vscode-xterm' { * Applies an addon to the Terminal prototype, making it available to all * newly created Terminals. * @param addon The addon to apply. + * @deprecated Use the new loadAddon API/addon format. */ static applyAddon(addon: any): void; + + /** + * (EXPERIMENTAL) Loads an addon into this instance of xterm.js. + * @param addon The addon to load. + */ + loadAddon(addon: ITerminalAddon): void; + } + + /** + * An addon that can provide additional functionality to the terminal. + */ + export interface ITerminalAddon extends IDisposable { + /** + * (EXPERIMENTAL) This is called when the addon is activated within xterm.js. + */ + activate(terminal: Terminal): void; + } + + /** + * An object representing a selecrtion within the terminal. + */ + interface ISelectionPosition { + /** + * The start column of the selection. + */ + startColumn: number; + + /** + * The start row of the selection. + */ + startRow: number; + + /** + * The end column of the selection. + */ + endColumn: number; + + /** + * The end row of the selection. + */ + endRow: number; + } + + interface IBuffer { + /** + * The y position of the cursor. This ranges between `0` (when the + * cursor is at baseY) and `Terminal.rows - 1` (when the cursor is on the + * last row). + */ + readonly cursorY: number; + + /** + * The x position of the cursor. This ranges between `0` (left side) and + * `Terminal.cols - 1` (right side). + */ + readonly cursorX: number; + + /** + * The line within the buffer where the top of the viewport is. + */ + readonly viewportY: number; + + /** + * The line within the buffer where the top of the bottom page is (when + * fully scrolled down); + */ + readonly baseY: number; + + /** + * The amount of lines in the buffer. + */ + readonly length: number; + + /** + * Gets a line from the buffer, or undefined if the line index does not exist. + * + * Note that the result of this function should be used immediately after calling as when the + * terminal updates it could lead to unexpected behavior. + * + * @param y The line index to get. + */ + getLine(y: number): IBufferLine | undefined; + } + + interface IBufferLine { + /** + * Whether the line is wrapped from the previous line. + */ + readonly isWrapped: boolean; + + /** + * Gets a cell from the line, or undefined if the line index does not exist. + * + * Note that the result of this function should be used immediately after calling as when the + * terminal updates it could lead to unexpected behavior. + * + * @param x The character index to get. + */ + getCell(x: number): IBufferCell; + + /** + * Gets the line as a string. Note that this is gets only the string for the line, not taking + * isWrapped into account. + * + * @param trimRight Whether to trim any whitespace at the right of the line. + * @param startColumn The column to start from (inclusive). + * @param endColumn The column to end at (exclusive). + */ + translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string; + } + + interface IBufferCell { + /** + * The character within the cell. + */ + readonly char: string; + + /** + * The width of the character. Some examples: + * + * - This is `1` for most cells. + * - This is `2` for wide character like CJK glyphs. + * - This is `0` for cells immediately following cells with a width of `2`. + */ + readonly width: number; } } From e44608d1f06f88cf093100fc27a43977e306a681 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 14 May 2019 10:52:12 -0700 Subject: [PATCH 61/99] Move xterm buffer usage to stable API --- src/typings/vscode-xterm.d.ts | 10 ------- .../driver/electron-browser/driver.ts | 4 +-- .../browser/terminalCommandTracker.ts | 8 ++--- .../terminal/browser/terminalInstance.ts | 30 +++++++++---------- .../terminalCommandTracker.test.ts | 20 ++++++------- 5 files changed, 31 insertions(+), 41 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index 38dd9fae766..0e5e59a9b51 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -1018,16 +1018,6 @@ declare module 'vscode-xterm' { interface TerminalCore { debug: boolean; - buffer: { - y: number; - ybase: number; - ydisp: number; - x: number; - lines: any[]; - - translateBufferLineToString(lineIndex: number, trimRight: boolean): string; - }; - handler(text: string): void; _onScroll: IEventEmitter2; diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index 24644613845..36246b59ad3 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -186,8 +186,8 @@ class WindowDriver implements IWindowDriver { const lines: string[] = []; - for (let i = 0; i < xterm._core.buffer.lines.length; i++) { - lines.push(xterm._core.buffer.translateBufferLineToString(i, true)); + for (let i = 0; i < xterm.buffer.length; i++) { + lines.push(xterm.buffer.getLine(i)!.translateToString(true)); } return lines; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts b/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts index 2ff3af60b2e..a3189db77c4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts @@ -48,7 +48,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa } private _onEnter(): void { - if (this._xterm._core.buffer.x >= MINIMUM_PROMPT_LENGTH) { + if (this._xterm.buffer.cursorX >= MINIMUM_PROMPT_LENGTH) { this._xterm.addMarker(0); } } @@ -175,7 +175,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa private _getLine(marker: IMarker | Boundary): number { // Use the _second last_ row as the last row is likely the prompt if (marker === Boundary.Bottom) { - return this._xterm._core.buffer.ybase + this._xterm.rows - 1; + return this._xterm.buffer.baseY + this._xterm.rows - 1; } if (marker === Boundary.Top) { @@ -235,10 +235,10 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa if (this._currentMarker === Boundary.Bottom) { return 0; } else if (this._currentMarker === Boundary.Top) { - return 0 - (this._xterm._core.buffer.ybase + this._xterm._core.buffer.y); + return 0 - (this._xterm.buffer.baseY + this._xterm.buffer.cursorY); } else { let offset = this._getLine(this._currentMarker); - offset -= this._xterm._core.buffer.ybase + this._xterm._core.buffer.y; + offset -= this._xterm.buffer.baseY + this._xterm.buffer.cursorY; return offset; } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 122250c8ea2..f9f4b106efd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -32,7 +32,7 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ISearchOptions, Terminal as XTermTerminal } from 'vscode-xterm'; +import { ISearchOptions, Terminal as XTermTerminal, IBuffer } from 'vscode-xterm'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -371,7 +371,7 @@ export class TerminalInstance implements ITerminalInstance { // it gets removed and then added back to the DOM (resetting scrollTop to 0). // Upstream issue: https://github.com/sourcelair/xterm.js/issues/291 if (this._xterm) { - this._xterm._core._onScroll.fire(this._xterm._core.buffer.ydisp); + this._xterm._core._onScroll.fire(this._xterm.buffer.viewportY); } } @@ -751,8 +751,8 @@ export class TerminalInstance implements ITerminalInstance { } } if (this._xterm) { - const buffer = (this._xterm._core.buffer); - this._sendLineData(buffer, buffer.ybase + buffer.y); + const buffer = this._xterm.buffer; + this._sendLineData(buffer, buffer.baseY + buffer.cursorY); this._xterm.dispose(); } @@ -865,7 +865,7 @@ export class TerminalInstance implements ITerminalInstance { // necessary if the number of rows in the terminal has decreased while it was in the // background since scrollTop changes take no effect but the terminal's position does // change since the number of visible rows decreases. - this._xterm._core._onScroll.fire(this._xterm._core.buffer.ydisp); + this._xterm._core._onScroll.fire(this._xterm.buffer.viewportY); if (this._container && this._container.parentElement) { // Force a layout when the instance becomes invisible. This is particularly important // for ensuring that terminals that are created in the background by an extension will @@ -1114,22 +1114,22 @@ export class TerminalInstance implements ITerminalInstance { } private _onLineFeed(): void { - const buffer = (this._xterm._core.buffer); - const newLine = buffer.lines.get(buffer.ybase + buffer.y); - if (!newLine.isWrapped) { - this._sendLineData(buffer, buffer.ybase + buffer.y - 1); + const buffer = this._xterm.buffer; + const newLine = buffer.getLine(buffer.baseY + buffer.cursorY); + if (newLine && !newLine.isWrapped) { + this._sendLineData(buffer, buffer.baseY + buffer.cursorY - 1); } } private _onCursorMove(): void { - const buffer = (this._xterm._core.buffer); - this._sendLineData(buffer, buffer.ybase + buffer.y); + const buffer = this._xterm.buffer; + this._sendLineData(buffer, buffer.baseY + buffer.cursorY); } - private _sendLineData(buffer: any, lineIndex: number): void { - let lineData = buffer.translateBufferLineToString(lineIndex, true); - while (lineIndex >= 0 && buffer.lines.get(lineIndex--).isWrapped) { - lineData = buffer.translateBufferLineToString(lineIndex, false) + lineData; + private _sendLineData(buffer: IBuffer, lineIndex: number): void { + let lineData = buffer.getLine(lineIndex)!.translateToString(true); + while (lineIndex >= 0 && buffer.getLine(lineIndex--)!.isWrapped) { + lineData = buffer.getLine(lineIndex)!.translateToString(false) + lineData; } this._onLineData.fire(lineData); } diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts index cd80a60ee1a..a2d20eb2c2e 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts @@ -66,24 +66,24 @@ suite('Workbench - TerminalCommandTracker', () => { for (let i = 0; i < 20; i++) { syncWrite(xterm, `\r\n`); } - assert.equal(xterm._core.buffer.ybase, 20); - assert.equal(xterm._core.buffer.ydisp, 20); + assert.equal(xterm.buffer.baseY, 20); + assert.equal(xterm.buffer.viewportY, 20); // Scroll to marker commandTracker.scrollToPreviousCommand(); - assert.equal(xterm._core.buffer.ydisp, 9); + assert.equal(xterm.buffer.viewportY, 9); // Scroll to top boundary commandTracker.scrollToPreviousCommand(); - assert.equal(xterm._core.buffer.ydisp, 0); + assert.equal(xterm.buffer.viewportY, 0); // Scroll to marker commandTracker.scrollToNextCommand(); - assert.equal(xterm._core.buffer.ydisp, 9); + assert.equal(xterm.buffer.viewportY, 9); // Scroll to bottom boundary commandTracker.scrollToNextCommand(); - assert.equal(xterm._core.buffer.ydisp, 20); + assert.equal(xterm.buffer.viewportY, 20); }); test('should select to the next and previous commands', () => { (window).matchMedia = () => { @@ -102,8 +102,8 @@ suite('Workbench - TerminalCommandTracker', () => { assert.equal(xterm.markers[1].line, 11); syncWrite(xterm, '\n\r3'); - assert.equal(xterm._core.buffer.ybase, 3); - assert.equal(xterm._core.buffer.ydisp, 3); + assert.equal(xterm.buffer.baseY, 3); + assert.equal(xterm.buffer.viewportY, 3); assert.equal(xterm.getSelection(), ''); commandTracker.selectToPreviousCommand(); @@ -132,8 +132,8 @@ suite('Workbench - TerminalCommandTracker', () => { assert.equal(xterm.markers[1].line, 11); syncWrite(xterm, '\n\r3'); - assert.equal(xterm._core.buffer.ybase, 3); - assert.equal(xterm._core.buffer.ydisp, 3); + assert.equal(xterm.buffer.baseY, 3); + assert.equal(xterm.buffer.viewportY, 3); assert.equal(xterm.getSelection(), ''); commandTracker.selectToPreviousLine(); From 9e4b65600db8f8527b65ff1e8252d6e378b6afc6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 11:26:22 -0700 Subject: [PATCH 62/99] Pick up TS 3.5@next --- extensions/package.json | 4 ++-- extensions/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 8d41ef689c1..1c7d369b234 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.4.5" + "typescript": "3.5.0-dev.20190514" }, "scripts": { "postinstall": "node ./postinstall" } -} \ No newline at end of file +} diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 149346bbb92..33a730f9993 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.4.5: - version "3.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" - integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== +typescript@3.5.0-dev.20190514: + version "3.5.0-dev.20190514" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.0-dev.20190514.tgz#f791a398f2b57cc514434726f690da68144ee73c" + integrity sha512-XFEpXv7n2nnYek4SaL09QcN8JnxwVplaPKPquPO12qc9okhN7Afmiu54LT1WP/6RmcXRFma1QKrr8Kfj1nLxYA== From 189e9a4c28f79782f6f98572bf6ea693f0a58c64 Mon Sep 17 00:00:00 2001 From: David Linskey Date: Tue, 14 May 2019 21:35:55 -0700 Subject: [PATCH 63/99] Fix docblock for FileSystemError --- src/vs/vscode.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index ca44a26a121..a4bf1796b7d 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5500,8 +5500,8 @@ declare module 'vscode' { /** * A type that filesystem providers should use to signal errors. * - * This class has factory methods for common error-cases, like `EntryNotFound` when - * a file or folder doesn't exist, use them like so: `throw vscode.FileSystemError.EntryNotFound(someUri);` + * This class has factory methods for common error-cases, like `FileNotFound` when + * a file or folder doesn't exist, use them like so: `throw vscode.FileSystemError.FileNotFound(someUri);` */ export class FileSystemError extends Error { From 5b8b82c39533661b36b53cf58d47c80fdf1e5887 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 May 2019 09:21:52 -0700 Subject: [PATCH 64/99] :lipstick: file on disk provider --- .../debug/electron-browser/debugService.ts | 2 +- .../files/browser/fileActions.contribution.ts | 4 +- .../contrib/files/browser/fileCommands.ts | 8 +-- .../contrib/files/browser/saveErrorHandler.ts | 19 +++---- .../files/common/editors/fileEditorInput.ts | 2 +- .../workbench/contrib/files/common/files.ts | 50 ++++++++++++------- .../test/common/fileOnDiskProvider.test.ts | 6 +-- .../textfile/common/textFileEditorModel.ts | 46 ++++++++--------- 8 files changed, 71 insertions(+), 66 deletions(-) diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts index 2dd13b297e3..8bd52bacb4d 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts @@ -392,7 +392,7 @@ export class DebugService implements IDebugService { return this.showError(err.message).then(() => false); } if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")) + return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved and that you have a debug extension installed for that file type.")) .then(() => false); } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 5dc462a1263..5ef1b8eb032 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -160,11 +160,11 @@ function appendEditorTitleContextMenuItem(id: string, title: string, when: Conte } // Editor Title Menu for Conflict Resolution -appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite disk contents"), { +appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), { light: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/check.svg`)), dark: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/check-inverse.svg`)) }, -10, acceptLocalChangesCommand); -appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to content on disk"), { +appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), { light: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/undo.svg`)), dark: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/undo-inverse.svg`)) }, -9, revertLocalChangesCommand); diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 9bda6cadedf..747a78e70b8 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -10,7 +10,7 @@ import { IWindowsService, IWindowService, IURIToOpen, IOpenSettings, INewWindowO import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ExplorerFocusCondition, FileOnDiskContentProvider, VIEWLET_ID, IExplorerService, resourceToFileOnDisk } from 'vs/workbench/contrib/files/common/files'; +import { ExplorerFocusCondition, TextFileContentProvider, VIEWLET_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; @@ -319,7 +319,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ if (providerDisposables.length === 0) { registerEditorListener = true; - const provider = instantiationService.createInstance(FileOnDiskContentProvider); + const provider = instantiationService.createInstance(TextFileContentProvider); providerDisposables.push(provider); providerDisposables.push(textModelService.registerTextModelContentProvider(COMPARE_WITH_SAVED_SCHEMA, provider)); } @@ -328,9 +328,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const uri = getResourceForCommand(resource, accessor.get(IListService), editorService); if (uri && fileService.canHandleResource(uri)) { const name = basename(uri); - const editorLabel = nls.localize('modifiedLabel', "{0} (on disk) ↔ {1}", name, name); + const editorLabel = nls.localize('modifiedLabel', "{0} (in file) ↔ {1}", name, name); - editorService.openEditor({ leftResource: resourceToFileOnDisk(COMPARE_WITH_SAVED_SCHEMA, uri), rightResource: uri, label: editorLabel }).then(() => { + TextFileContentProvider.open(uri, COMPARE_WITH_SAVED_SCHEMA, editorLabel, editorService).then(() => { // Dispose once no more diff editor is opened with the scheme if (registerEditorListener) { diff --git a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts index 0e8c29a2706..a6ca3697b60 100644 --- a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts @@ -19,7 +19,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { FileOnDiskContentProvider, resourceToFileOnDisk } from 'vs/workbench/contrib/files/common/files'; +import { TextFileContentProvider } from 'vs/workbench/contrib/files/common/files'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { IModelService } from 'vs/editor/common/services/modelService'; import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; @@ -39,7 +39,7 @@ export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; const LEARN_MORE_DIRTY_WRITE_IGNORE_KEY = 'learnMoreDirtyWriteError'; -const conflictEditorHelp = nls.localize('userGuide', "Use the actions in the editor tool bar to either undo your changes or overwrite the content on disk with your changes."); +const conflictEditorHelp = nls.localize('userGuide', "Use the actions in the editor tool bar to either undo your changes or overwrite the content of the file with your changes."); // A handler for save error happening with conflict resolution actions export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, IWorkbenchContribution { @@ -61,7 +61,7 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I this.messages = new ResourceMap(); this.conflictResolutionContext = new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false).bindTo(contextKeyService); - const provider = this._register(instantiationService.createInstance(FileOnDiskContentProvider)); + const provider = this._register(instantiationService.createInstance(TextFileContentProvider)); this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider)); // Hook into model @@ -125,7 +125,7 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I // Otherwise show the message that will lead the user into the save conflict editor. else { - message = nls.localize('staleSaveError', "Failed to save '{0}': The content on disk is newer. Please compare your version with the one on disk.", basename(resource)); + message = nls.localize('staleSaveError', "Failed to save '{0}': The content of the file is newer. Please compare your version with the file contents.", basename(resource)); actions.primary!.push(this.instantiationService.createInstance(ResolveSaveConflictAction, model)); } @@ -244,16 +244,9 @@ class ResolveSaveConflictAction extends Action { if (!this.model.isDisposed()) { const resource = this.model.getResource(); const name = basename(resource); - const editorLabel = nls.localize('saveConflictDiffLabel', "{0} (on disk) ↔ {1} (in {2}) - Resolve save conflict", name, name, this.environmentService.appNameLong); + const editorLabel = nls.localize('saveConflictDiffLabel', "{0} (in file) ↔ {1} (in {2}) - Resolve save conflict", name, name, this.environmentService.appNameLong); - return this.editorService.openEditor( - { - leftResource: resourceToFileOnDisk(CONFLICT_RESOLUTION_SCHEME, resource), - rightResource: resource, - label: editorLabel, - options: { pinned: true } - } - ).then(() => { + return TextFileContentProvider.open(resource, CONFLICT_RESOLUTION_SCHEME, editorLabel, this.editorService, { pinned: true }).then(() => { if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) { return; // return if this message is ignored } diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 02eab17958b..ecd630e356a 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -219,7 +219,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private decorateLabel(label: string): string { const model = this.textFileService.models.get(this.resource); if (model && model.hasState(ModelState.ORPHAN)) { - return localize('orphanedFile', "{0} (deleted from disk)", label); + return localize('orphanedFile', "{0} (deleted)", label); } if (model && model.isReadonly()) { diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index f0b2a14c926..43bbbb92786 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -23,6 +23,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { once } from 'vs/base/common/functional'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; /** * Explorer viewlet id. @@ -58,8 +60,8 @@ export interface IExplorerService { isCut(stat: ExplorerItem): boolean; /** - * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to - * resolve the path from the disk in case the explorer is not yet expanded to the file yet. + * Selects and reveal the file element provided by the given resource if its found in the explorer. + * Will try to resolve the path in case the explorer is not yet expanded to the file yet. */ select(resource: URI, reveal?: boolean): Promise; } @@ -131,15 +133,7 @@ export const SortOrderConfiguration = { export type SortOrder = 'default' | 'mixed' | 'filesFirst' | 'type' | 'modified'; -export function resourceToFileOnDisk(scheme: string, resource: URI): URI { - return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme }) }); -} - -export function fileOnDiskToResource(resource: URI): URI { - return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null }); -} - -export class FileOnDiskContentProvider implements ITextModelContentProvider { +export class TextFileContentProvider implements ITextModelContentProvider { private fileWatcherDisposable: IDisposable | undefined; constructor( @@ -147,16 +141,34 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { @IFileService private readonly fileService: IFileService, @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService - ) { + ) { } + + static open(resource: URI, scheme: string, label: string, editorService: IEditorService, options?: ITextEditorOptions): Promise { + return editorService.openEditor( + { + leftResource: TextFileContentProvider.resourceToTextFile(scheme, resource), + rightResource: resource, + label, + options + } + ).then(); + } + + private static resourceToTextFile(scheme: string, resource: URI): URI { + return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme }) }); + } + + private static textFileToResource(resource: URI): URI { + return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null }); } provideTextContent(resource: URI): Promise { - const savedFileResource = fileOnDiskToResource(resource); + const savedFileResource = TextFileContentProvider.textFileToResource(resource); - // Make sure our file from disk is resolved up to date + // Make sure our text file is resolved up to date return this.resolveEditorModel(resource).then(codeEditorModel => { - // Make sure to keep contents on disk up to date when it changes + // Make sure to keep contents up to date when it changes if (!this.fileWatcherDisposable) { this.fileWatcherDisposable = this.fileService.onFileChanges(changes => { if (changes.contains(savedFileResource, FileChangeType.UPDATED)) { @@ -179,18 +191,18 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise; private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise; private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise { - const savedFileResource = fileOnDiskToResource(resource); + const savedFileResource = TextFileContentProvider.textFileToResource(resource); return this.textFileService.readStream(savedFileResource).then(content => { let codeEditorModel = this.modelService.getModel(resource); if (codeEditorModel) { this.modelService.updateModel(codeEditorModel, content.value); } else if (createAsNeeded) { - const fileOnDiskModel = this.modelService.getModel(savedFileResource); + const textFileModel = this.modelService.getModel(savedFileResource); let languageSelector: ILanguageSelection; - if (fileOnDiskModel) { - languageSelector = this.modeService.create(fileOnDiskModel.getModeId()); + if (textFileModel) { + languageSelector = this.modeService.create(textFileModel.getModeId()); } else { languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource.fsPath); } diff --git a/src/vs/workbench/contrib/files/test/common/fileOnDiskProvider.test.ts b/src/vs/workbench/contrib/files/test/common/fileOnDiskProvider.test.ts index 62fcf91ca42..9c2064028d2 100644 --- a/src/vs/workbench/contrib/files/test/common/fileOnDiskProvider.test.ts +++ b/src/vs/workbench/contrib/files/test/common/fileOnDiskProvider.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { workbenchInstantiationService, TestFileService } from 'vs/workbench/test/workbenchTestServices'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { FileOnDiskContentProvider, resourceToFileOnDisk } from 'vs/workbench/contrib/files/common/files'; +import { TextFileContentProvider } from 'vs/workbench/contrib/files/common/files'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService } from 'vs/platform/files/common/files'; @@ -29,10 +29,10 @@ suite('Files - FileOnDiskContentProvider', () => { }); test('provideTextContent', async () => { - const provider = instantiationService.createInstance(FileOnDiskContentProvider); + const provider = instantiationService.createInstance(TextFileContentProvider); const uri = URI.parse('testFileOnDiskContentProvider://foo'); - const content = await provider.provideTextContent(resourceToFileOnDisk('conflictResolution', uri)); + const content = await provider.provideTextContent(uri.with({ scheme: 'conflictResolution', query: JSON.stringify({ scheme: uri.scheme }) })); assert.equal(snapshotToString(content.createSnapshot()), 'Hello Html'); assert.equal(accessor.fileService.getLastReadFileUri().toString(), uri.toString()); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 199e8c65bb6..c114ccbe3b9 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -72,7 +72,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private bufferSavedVersionId: number; private blockModelContentChange: boolean; - private lastResolvedDiskStat: IFileStatWithMetadata; + private lastResolvedFileStat: IFileStatWithMetadata; private autoSaveAfterMillies?: number; private autoSaveAfterMilliesEnabled: boolean; @@ -228,11 +228,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Only fill in model metadata if resource matches let meta: IBackupMetaData | undefined = undefined; - if (isEqual(target, this.resource) && this.lastResolvedDiskStat) { + if (isEqual(target, this.resource) && this.lastResolvedFileStat) { meta = { - mtime: this.lastResolvedDiskStat.mtime, - size: this.lastResolvedDiskStat.size, - etag: this.lastResolvedDiskStat.etag, + mtime: this.lastResolvedFileStat.mtime, + size: this.lastResolvedFileStat.size, + etag: this.lastResolvedFileStat.etag, orphaned: this.inOrphanMode }; } @@ -345,8 +345,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil let etag: string | undefined; if (forceReadFromDisk) { etag = ETAG_DISABLED; // disable ETag if we enforce to read from disk - } else if (this.lastResolvedDiskStat) { - etag = this.lastResolvedDiskStat.etag; // otherwise respect etag to support caching + } else if (this.lastResolvedFileStat) { + etag = this.lastResolvedFileStat.etag; // otherwise respect etag to support caching } // Ensure to track the versionId before doing a long running operation @@ -402,7 +402,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.logService.trace('load() - resolved content', this.resource); // Update our resolved disk stat model - this.updateLastResolvedDiskStat({ + this.updateLastResolvedFileStat({ resource: this.resource, name: content.name, mtime: content.mtime, @@ -720,12 +720,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) this.logService.trace(`doSave(${versionId}) - before write()`, this.resource); - return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedDiskStat.resource, this.createSnapshot(), { + return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, overwriteEncoding: options.overwriteEncoding, - mtime: this.lastResolvedDiskStat.mtime, + mtime: this.lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: this.lastResolvedDiskStat.etag, + etag: this.lastResolvedFileStat.etag, writeElevated: options.writeElevated }).then(stat => { this.logService.trace(`doSave(${versionId}) - after write()`, this.resource); @@ -739,7 +739,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Updated resolved stat with updated stat - this.updateLastResolvedDiskStat(stat); + this.updateLastResolvedFileStat(stat); // Cancel any content change event promises as they are no longer valid this.contentChangeEventScheduler.cancel(); @@ -857,14 +857,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return Promise.resolve(); } - return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedDiskStat.resource, this.createSnapshot(), { - mtime: this.lastResolvedDiskStat.mtime, + return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), { + mtime: this.lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: this.lastResolvedDiskStat.etag + etag: this.lastResolvedFileStat.etag }).then(stat => { // Updated resolved stat with updated stat since touching it might have changed mtime - this.updateLastResolvedDiskStat(stat); + this.updateLastResolvedFileStat(stat); // Emit File Saved Event this._onDidStateChange.fire(StateChange.SAVED); @@ -907,18 +907,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private updateLastResolvedDiskStat(newVersionOnDiskStat: IFileStatWithMetadata): void { + private updateLastResolvedFileStat(newFileStat: IFileStatWithMetadata): void { // First resolve - just take - if (!this.lastResolvedDiskStat) { - this.lastResolvedDiskStat = newVersionOnDiskStat; + if (!this.lastResolvedFileStat) { + this.lastResolvedFileStat = newFileStat; } // Subsequent resolve - make sure that we only assign it if the mtime is equal or has advanced. // This prevents race conditions from loading and saving. If a save comes in late after a revert // was called, the mtime could be out of sync. - else if (this.lastResolvedDiskStat.mtime <= newVersionOnDiskStat.mtime) { - this.lastResolvedDiskStat = newVersionOnDiskStat; + else if (this.lastResolvedFileStat.mtime <= newFileStat.mtime) { + this.lastResolvedFileStat = newFileStat; } } @@ -1027,7 +1027,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } isReadonly(): boolean { - return !!(this.lastResolvedDiskStat && this.lastResolvedDiskStat.isReadonly); + return !!(this.lastResolvedFileStat && this.lastResolvedFileStat.isReadonly); } isDisposed(): boolean { @@ -1039,7 +1039,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } getStat(): IFileStatWithMetadata { - return this.lastResolvedDiskStat; + return this.lastResolvedFileStat; } dispose(): void { From b7cc96f7c4cfad70fbdbf30ccb1821ac6e54d04c Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 15 May 2019 09:39:28 -0700 Subject: [PATCH 65/99] Remove tasks specific conpty setting Part of #73677 --- .../contrib/tasks/electron-browser/task.contribution.ts | 1 - .../contrib/tasks/electron-browser/terminalTaskSystem.ts | 5 ----- .../contrib/terminal/browser/terminal.contribution.ts | 5 ----- .../contrib/terminal/browser/terminalProcessManager.ts | 2 +- src/vs/workbench/contrib/terminal/common/terminal.ts | 6 ------ 5 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts index 4a7a6f9a9a7..5947afebd05 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts @@ -1360,7 +1360,6 @@ class TaskService extends Disposable implements ITaskService { this.modelService, this.configurationResolverService, this.telemetryService, this.contextService, this._environmentService, TaskService.OutputChannelId, - this.configurationService, (workspaceFolder: IWorkspaceFolder) => { if (!workspaceFolder) { return undefined; diff --git a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts index 2509f820f03..4c647da9057 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts @@ -44,7 +44,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { Schemas } from 'vs/base/common/network'; import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; interface TerminalData { terminal: ITerminalInstance; @@ -172,7 +171,6 @@ export class TerminalTaskSystem implements ITaskSystem { private contextService: IWorkspaceContextService, private environmentService: IWorkbenchEnvironmentService, private outputChannelId: string, - private readonly configurationService: IConfigurationService, taskSystemInfoResolver: TaskSystemInfoResovler, ) { @@ -879,9 +877,6 @@ export class TerminalTaskSystem implements ITaskSystem { if (options.env) { shellLaunchConfig.env = options.env; } - - // Conpty doesn't do linefeeds in an expected way. Force winpty unless the user has requested otherwise. - shellLaunchConfig.forceWinpty = !this.configurationService.getValue('terminal.integrated.windowsAllowConptyTasks'); return shellLaunchConfig; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index eb7a270b494..d467ac1b6b3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -273,11 +273,6 @@ configurationRegistry.registerConfiguration({ description: nls.localize('terminal.integrated.experimentalRefreshOnResume', "An experimental setting that will refresh the terminal renderer when the system is resumed."), type: 'boolean', default: false - }, - 'terminal.integrated.windowsAllowConptyTasks': { - markdownDescription: nls.localize('terminal.integrated.windowsAllowConptyTasks', "Works in conjunction with the `#terminal.integrated.windowsEnableConpty#` setting. Both must be enabled for tasks to use conpty. Defaults to `false`."), - type: 'boolean', - default: false } } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 04447e63c4a..bced2593716 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -182,7 +182,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables); this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env); - const useConpty = (shellLaunchConfig.forceWinpty !== true) && this._configHelper.config.windowsEnableConpty; + const useConpty = this._configHelper.config.windowsEnableConpty; return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 4cc7a962912..b28e2955420 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -193,12 +193,6 @@ export interface IShellLaunchConfig { * provided as nothing will be inherited from the process or any configuration. */ strictEnv?: boolean; - - /** - * Moving forward, conpty will be the default. However, there are cases where conpty is not ready - * to be the default. This property will force winpty to be used, even when conpty would normally be used. - */ - forceWinpty?: boolean; } export interface ITerminalService { From fcf7f6749e1b36598a7d0ccaf0ee129b46f7cbe8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 May 2019 09:48:54 -0700 Subject: [PATCH 66/99] :lipstick: --- src/vs/workbench/common/editor/textEditorModel.ts | 2 +- src/vs/workbench/contrib/backup/common/backupRestorer.ts | 2 +- src/vs/workbench/contrib/files/common/files.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index a8644fbc39e..8dd3c4716e7 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -126,7 +126,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd // lookup mode via resource path if the provided mode is unspecific if (!preferredMode || preferredMode === PLAINTEXT_MODE_ID) { - return modeService.createByFilepathOrFirstLine(resource ? resource.fsPath : null, firstLineText); + return modeService.createByFilepathOrFirstLine(resource ? resource.path : null, firstLineText); } // otherwise take the preferred mode for granted diff --git a/src/vs/workbench/contrib/backup/common/backupRestorer.ts b/src/vs/workbench/contrib/backup/common/backupRestorer.ts index 0505702958c..a3a17011b46 100644 --- a/src/vs/workbench/contrib/backup/common/backupRestorer.ts +++ b/src/vs/workbench/contrib/backup/common/backupRestorer.ts @@ -79,7 +79,7 @@ export class BackupRestorer implements IWorkbenchContribution { // this is a (weak) strategy to find out if the untitled input had // an associated file path or not by just looking at the path. and // if so, we must ensure to restore the local resource it had. - if (resource.scheme === Schemas.untitled && !BackupRestorer.UNTITLED_REGEX.test(resource.fsPath)) { + if (resource.scheme === Schemas.untitled && !BackupRestorer.UNTITLED_REGEX.test(resource.path)) { return { resource: toLocalResource(resource, this.environmentService.configuration.remoteAuthority), options, forceUntitled: true }; } diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 43bbbb92786..19f1c4c83d5 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -204,7 +204,7 @@ export class TextFileContentProvider implements ITextModelContentProvider { if (textFileModel) { languageSelector = this.modeService.create(textFileModel.getModeId()); } else { - languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource.fsPath); + languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource.path); } codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource); From 982e7bc2f195b323cc819529b7d4b7463011e2b7 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 15 May 2019 10:21:05 -0700 Subject: [PATCH 67/99] refactor actionItem naming (#73600) --- src/vs/base/browser/contextmenu.ts | 4 +- src/vs/base/browser/htmlContentRenderer.ts | 6 +- src/vs/base/browser/ui/actionbar/actionbar.ts | 116 +++++++++--------- src/vs/base/browser/ui/checkbox/checkbox.ts | 4 +- src/vs/base/browser/ui/dropdown/dropdown.ts | 18 +-- src/vs/base/browser/ui/menu/menu.ts | 74 +++++------ src/vs/base/browser/ui/toolbar/toolbar.ts | 36 +++--- src/vs/base/common/actions.ts | 2 +- .../editor/contrib/contextmenu/contextmenu.ts | 14 +-- ...tionItem.ts => menuEntryActionViewItem.ts} | 18 +-- .../contextview/browser/contextMenuHandler.ts | 2 +- src/vs/workbench/browser/composite.ts | 6 +- .../parts/activitybar/activitybarActions.ts | 4 +- .../parts/activitybar/activitybarPart.ts | 6 +- .../workbench/browser/parts/compositeBar.ts | 36 +++--- .../browser/parts/compositeBarActions.ts | 20 +-- .../workbench/browser/parts/compositePart.ts | 8 +- .../browser/parts/editor/editorGroupView.ts | 2 +- .../browser/parts/editor/titleControl.ts | 18 +-- .../notifications/notificationsViewer.ts | 6 +- .../browser/parts/quickinput/quickInput.ts | 10 +- .../browser/parts/sidebar/sidebarPart.ts | 2 +- .../browser/parts/views/customView.ts | 22 ++-- .../browser/parts/views/panelViewlet.ts | 12 +- src/vs/workbench/common/composite.ts | 4 +- .../contrib/comments/browser/commentNode.ts | 52 ++++---- .../comments/browser/reactionsAction.ts | 4 +- .../contrib/debug/browser/callStackView.ts | 2 +- ...ActionItems.ts => debugActionViewItems.ts} | 8 +- .../contrib/debug/browser/debugToolBar.ts | 10 +- .../contrib/debug/browser/debugViewlet.ts | 22 ++-- .../workbench/contrib/debug/browser/repl.ts | 10 +- .../electron-browser/extensionEditor.ts | 4 +- .../electron-browser/extensionsActions.ts | 24 ++-- .../electron-browser/extensionsList.ts | 12 +- .../files/browser/views/explorerView.ts | 2 +- .../files/browser/views/openEditorsView.ts | 2 +- .../contrib/markers/browser/markersPanel.ts | 32 ++--- .../markers/browser/markersPanelActions.ts | 6 +- .../markers/browser/markersTreeViewer.ts | 10 +- .../contrib/output/browser/outputActions.ts | 6 +- .../contrib/output/browser/outputPanel.ts | 10 +- .../preferences/browser/keybindingsEditor.ts | 8 +- .../preferences/browser/preferencesWidgets.ts | 12 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 25 +--- .../workbench/contrib/scm/browser/scmMenus.ts | 2 +- .../contrib/scm/browser/scmViewlet.ts | 32 ++--- .../contrib/search/browser/searchView.ts | 2 +- .../terminal/browser/terminalActions.ts | 4 +- .../contrib/terminal/browser/terminalPanel.ts | 10 +- src/vs/workbench/electron-browser/window.ts | 2 +- .../progress/test/progressService.test.ts | 4 +- 52 files changed, 376 insertions(+), 391 deletions(-) rename src/vs/platform/actions/browser/{menuItemActionItem.ts => menuEntryActionViewItem.ts} (91%) rename src/vs/workbench/contrib/debug/browser/{debugActionItems.ts => debugActionViewItems.ts} (96%) diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 4fa0bf17ce0..d943a131474 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { SubmenuAction } from 'vs/base/browser/ui/menu/menu'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; @@ -25,7 +25,7 @@ export class ContextSubMenu extends SubmenuAction { export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; getActions(): Array; - getActionItem?(action: IAction): IActionItem | undefined; + getActionViewItem?(action: IAction): IActionViewItem | undefined; getActionsContext?(event?: IContextMenuEvent): any; getKeyBinding?(action: IAction): ResolvedKeybinding | undefined; getMenuClassName?(): string; diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index 93144774216..5d94e04008d 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -315,7 +315,7 @@ function parseFormattedText(content: string): IFormatParseTree { children: [] }; - let actionItemIndex = 0; + let actionViewItemIndex = 0; let current = root; const stack: IFormatParseTree[] = []; const stream = new StringStream(content); @@ -345,8 +345,8 @@ function parseFormattedText(content: string): IFormatParseTree { }; if (type === FormatType.Action) { - newCurrent.index = actionItemIndex; - actionItemIndex++; + newCurrent.index = actionViewItemIndex; + actionViewItemIndex++; } current.children!.push(newCurrent); diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index bbe176c19c0..f39e9903a0c 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -18,7 +18,7 @@ import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview import { Event, Emitter } from 'vs/base/common/event'; import { asArray } from 'vs/base/common/arrays'; -export interface IActionItem { +export interface IActionViewItem { actionRunner: IActionRunner; setActionContext(context: any): void; render(element: HTMLElement): void; @@ -28,12 +28,12 @@ export interface IActionItem { dispose(): void; } -export interface IBaseActionItemOptions { +export interface IBaseActionViewItemOptions { draggable?: boolean; isMenu?: boolean; } -export class BaseActionItem extends Disposable implements IActionItem { +export class BaseActionViewItem extends Disposable implements IActionViewItem { element?: HTMLElement; _context: any; @@ -41,7 +41,7 @@ export class BaseActionItem extends Disposable implements IActionItem { private _actionRunner: IActionRunner; - constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) { + constructor(context: any, action: IAction, protected options?: IBaseActionViewItemOptions) { super(); this._context = context || this; @@ -226,20 +226,20 @@ export class Separator extends Action { } } -export interface IActionItemOptions extends IBaseActionItemOptions { +export interface IActionViewItemOptions extends IBaseActionViewItemOptions { icon?: boolean; label?: boolean; keybinding?: string | null; } -export class ActionItem extends BaseActionItem { +export class ActionViewItem extends BaseActionViewItem { protected label: HTMLElement; - protected options: IActionItemOptions; + protected options: IActionViewItemOptions; private cssClass?: string; - constructor(context: any, action: IAction, options: IActionItemOptions = {}) { + constructor(context: any, action: IAction, options: IActionViewItemOptions = {}) { super(context, action, options); this.options = options; @@ -363,14 +363,14 @@ export interface ActionTrigger { keyDown: boolean; } -export interface IActionItemProvider { - (action: IAction): IActionItem | undefined; +export interface IActionViewItemProvider { + (action: IAction): IActionViewItem | undefined; } export interface IActionBarOptions { orientation?: ActionsOrientation; context?: any; - actionItemProvider?: IActionItemProvider; + actionViewItemProvider?: IActionViewItemProvider; actionRunner?: IActionRunner; ariaLabel?: string; animated?: boolean; @@ -386,7 +386,7 @@ const defaultOptions: IActionBarOptions = { } }; -export interface IActionOptions extends IActionItemOptions { +export interface IActionOptions extends IActionViewItemOptions { index?: number; } @@ -397,8 +397,8 @@ export class ActionBar extends Disposable implements IActionRunner { private _actionRunner: IActionRunner; private _context: any; - // Items - items: IActionItem[]; + // View Items + viewItems: IActionViewItem[]; protected focusedItem?: number; private focusTracker: DOM.IFocusTracker; @@ -438,7 +438,7 @@ export class ActionBar extends Disposable implements IActionRunner { this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); - this.items = []; + this.viewItems = []; this.focusedItem = undefined; this.domNode = document.createElement('div'); @@ -575,7 +575,7 @@ export class ActionBar extends Disposable implements IActionRunner { set context(context: any) { this._context = context; - this.items.forEach(i => i.setActionContext(context)); + this.viewItems.forEach(i => i.setActionContext(context)); } get actionRunner(): IActionRunner { @@ -585,7 +585,7 @@ export class ActionBar extends Disposable implements IActionRunner { set actionRunner(actionRunner: IActionRunner) { if (actionRunner) { this._actionRunner = actionRunner; - this.items.forEach(item => item.actionRunner = actionRunner); + this.viewItems.forEach(item => item.actionRunner = actionRunner); } } @@ -599,36 +599,36 @@ export class ActionBar extends Disposable implements IActionRunner { let index = types.isNumber(options.index) ? options.index : null; actions.forEach((action: IAction) => { - const actionItemElement = document.createElement('li'); - actionItemElement.className = 'action-item'; - actionItemElement.setAttribute('role', 'presentation'); + const actionViewItemElement = document.createElement('li'); + actionViewItemElement.className = 'action-item'; + actionViewItemElement.setAttribute('role', 'presentation'); // Prevent native context menu on actions - this._register(DOM.addDisposableListener(actionItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { + this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { e.preventDefault(); e.stopPropagation(); })); - let item: IActionItem | undefined; + let item: IActionViewItem | undefined; - if (this.options.actionItemProvider) { - item = this.options.actionItemProvider(action); + if (this.options.actionViewItemProvider) { + item = this.options.actionViewItemProvider(action); } if (!item) { - item = new ActionItem(this.context, action, options); + item = new ActionViewItem(this.context, action, options); } item.actionRunner = this._actionRunner; item.setActionContext(this.context); - item.render(actionItemElement); + item.render(actionViewItemElement); if (index === null || index < 0 || index >= this.actionsList.children.length) { - this.actionsList.appendChild(actionItemElement); - this.items.push(item); + this.actionsList.appendChild(actionViewItemElement); + this.viewItems.push(item); } else { - this.actionsList.insertBefore(actionItemElement, this.actionsList.children[index]); - this.items.splice(index, 0, item); + this.actionsList.insertBefore(actionViewItemElement, this.actionsList.children[index]); + this.viewItems.splice(index, 0, item); index++; } }); @@ -657,23 +657,23 @@ export class ActionBar extends Disposable implements IActionRunner { } pull(index: number): void { - if (index >= 0 && index < this.items.length) { + if (index >= 0 && index < this.viewItems.length) { this.actionsList.removeChild(this.actionsList.childNodes[index]); - dispose(this.items.splice(index, 1)); + dispose(this.viewItems.splice(index, 1)); } } clear(): void { - this.items = dispose(this.items); + this.viewItems = dispose(this.viewItems); DOM.clearNode(this.actionsList); } length(): number { - return this.items.length; + return this.viewItems.length; } isEmpty(): boolean { - return this.items.length === 0; + return this.viewItems.length === 0; } focus(index?: number): void; @@ -691,7 +691,7 @@ export class ActionBar extends Disposable implements IActionRunner { if (selectFirst && typeof this.focusedItem === 'undefined') { // Focus the first enabled item - this.focusedItem = this.items.length - 1; + this.focusedItem = this.viewItems.length - 1; this.focusNext(); } else { if (index !== undefined) { @@ -704,15 +704,15 @@ export class ActionBar extends Disposable implements IActionRunner { protected focusNext(): void { if (typeof this.focusedItem === 'undefined') { - this.focusedItem = this.items.length - 1; + this.focusedItem = this.viewItems.length - 1; } const startIndex = this.focusedItem; - let item: IActionItem; + let item: IActionViewItem; do { - this.focusedItem = (this.focusedItem + 1) % this.items.length; - item = this.items[this.focusedItem]; + this.focusedItem = (this.focusedItem + 1) % this.viewItems.length; + item = this.viewItems[this.focusedItem]; } while (this.focusedItem !== startIndex && !item.isEnabled()); if (this.focusedItem === startIndex && !item.isEnabled()) { @@ -728,16 +728,16 @@ export class ActionBar extends Disposable implements IActionRunner { } const startIndex = this.focusedItem; - let item: IActionItem; + let item: IActionViewItem; do { this.focusedItem = this.focusedItem - 1; if (this.focusedItem < 0) { - this.focusedItem = this.items.length - 1; + this.focusedItem = this.viewItems.length - 1; } - item = this.items[this.focusedItem]; + item = this.viewItems[this.focusedItem]; } while (this.focusedItem !== startIndex && !item.isEnabled()); if (this.focusedItem === startIndex && !item.isEnabled()) { @@ -752,21 +752,21 @@ export class ActionBar extends Disposable implements IActionRunner { this.actionsList.focus(); } - for (let i = 0; i < this.items.length; i++) { - const item = this.items[i]; - const actionItem = item; + for (let i = 0; i < this.viewItems.length; i++) { + const item = this.viewItems[i]; + const actionViewItem = item; if (i === this.focusedItem) { - if (types.isFunction(actionItem.isEnabled)) { - if (actionItem.isEnabled() && types.isFunction(actionItem.focus)) { - actionItem.focus(fromRight); + if (types.isFunction(actionViewItem.isEnabled)) { + if (actionViewItem.isEnabled() && types.isFunction(actionViewItem.focus)) { + actionViewItem.focus(fromRight); } else { this.actionsList.focus(); } } } else { - if (types.isFunction(actionItem.blur)) { - actionItem.blur(); + if (types.isFunction(actionViewItem.blur)) { + actionViewItem.blur(); } } } @@ -778,10 +778,10 @@ export class ActionBar extends Disposable implements IActionRunner { } // trigger action - const actionItem = this.items[this.focusedItem]; - if (actionItem instanceof BaseActionItem) { - const context = (actionItem._context === null || actionItem._context === undefined) ? event : actionItem._context; - this.run(actionItem._action, context); + const actionViewItem = this.viewItems[this.focusedItem]; + if (actionViewItem instanceof BaseActionViewItem) { + const context = (actionViewItem._context === null || actionViewItem._context === undefined) ? event : actionViewItem._context; + this.run(actionViewItem._action, context); } } @@ -798,8 +798,8 @@ export class ActionBar extends Disposable implements IActionRunner { } dispose(): void { - dispose(this.items); - this.items = []; + dispose(this.viewItems); + this.viewItems = []; DOM.removeNode(this.getContainer()); @@ -807,7 +807,7 @@ export class ActionBar extends Disposable implements IActionRunner { } } -export class SelectActionItem extends BaseActionItem { +export class SelectActionViewItem extends BaseActionViewItem { protected selectBox: SelectBox; constructor(ctx: any, action: IAction, options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions) { diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 73f7ca9722e..a4419eda426 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -11,7 +11,7 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as objects from 'vs/base/common/objects'; -import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; export interface ICheckboxOpts extends ICheckboxStyles { @@ -28,7 +28,7 @@ const defaultOpts = { inputActiveOptionBorder: Color.fromHex('#007ACC') }; -export class CheckboxActionItem extends BaseActionItem { +export class CheckboxActionViewItem extends BaseActionViewItem { private checkbox: Checkbox; private disposables: IDisposable[] = []; diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index d033d75efab..ee87b9a4fed 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -6,7 +6,7 @@ import 'vs/css!./dropdown'; import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch'; import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; -import { BaseActionItem, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionViewItem, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IContextViewProvider, IAnchor, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IMenuOptions } from 'vs/base/browser/ui/menu/menu'; @@ -247,7 +247,7 @@ export class DropdownMenu extends BaseDropdown { getAnchor: () => this.element, getActions: () => this.actions, getActionsContext: () => this.menuOptions ? this.menuOptions.context : null, - getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : undefined, + getActionViewItem: action => this.menuOptions && this.menuOptions.actionViewItemProvider ? this.menuOptions.actionViewItemProvider(action) : undefined, getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : undefined, getMenuClassName: () => this.menuClassName, onHide: () => this.onHide(), @@ -266,23 +266,23 @@ export class DropdownMenu extends BaseDropdown { } } -export class DropdownMenuActionItem extends BaseActionItem { +export class DropdownMenuActionViewItem extends BaseActionViewItem { private menuActionsOrProvider: any; private dropdownMenu: DropdownMenu; private contextMenuProvider: IContextMenuProvider; - private actionItemProvider?: IActionItemProvider; + private actionViewItemProvider?: IActionViewItemProvider; private keybindings?: (action: IAction) => ResolvedKeybinding | undefined; private clazz: string | undefined; private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; - constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment) { + constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment) { super(null, action); this.menuActionsOrProvider = menuActionsOrProvider; this.contextMenuProvider = contextMenuProvider; - this.actionItemProvider = actionItemProvider; + this.actionViewItemProvider = actionViewItemProvider; this.actionRunner = actionRunner; this.keybindings = keybindings; this.clazz = clazz; @@ -319,7 +319,7 @@ export class DropdownMenuActionItem extends BaseActionItem { this.dropdownMenu = this._register(new DropdownMenu(container, options)); this.dropdownMenu.menuOptions = { - actionItemProvider: this.actionItemProvider, + actionViewItemProvider: this.actionViewItemProvider, actionRunner: this.actionRunner, getKeyBinding: this.keybindings, context: this._context diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 26d89db30a2..7be39253dfc 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -7,7 +7,7 @@ import 'vs/css!./menu'; import * as nls from 'vs/nls'; import * as strings from 'vs/base/common/strings'; import { IActionRunner, IAction, Action } from 'vs/base/common/actions'; -import { ActionBar, IActionItemProvider, ActionsOrientation, Separator, ActionItem, IActionItemOptions, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionViewItemProvider, ActionsOrientation, Separator, ActionViewItem, IActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -39,7 +39,7 @@ export const MENU_ESCAPED_MNEMONIC_REGEX: RegExp = createMenuEscapedMnemonicRegE export interface IMenuOptions { context?: any; - actionItemProvider?: IActionItemProvider; + actionViewItemProvider?: IActionViewItemProvider; actionRunner?: IActionRunner; getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined; ariaLabel?: string; @@ -70,7 +70,7 @@ interface ISubMenuData { } export class Menu extends ActionBar { - private mnemonics: Map>; + private mnemonics: Map>; private menuDisposables: IDisposable[]; private scrollableElement: DomScrollableElement; private menuElement: HTMLElement; @@ -88,7 +88,7 @@ export class Menu extends ActionBar { super(menuElement, { orientation: ActionsOrientation.VERTICAL, - actionItemProvider: action => this.doGetActionItem(action, options, parentData), + actionViewItemProvider: action => this.doGetActionViewItem(action, options, parentData), context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, @@ -113,7 +113,7 @@ export class Menu extends ActionBar { const actions = this.mnemonics.get(key)!; if (actions.length === 1) { - if (actions[0] instanceof SubmenuActionItem) { + if (actions[0] instanceof SubmenuMenuActionViewItem) { this.focusItemByElement(actions[0].container); } @@ -138,7 +138,7 @@ export class Menu extends ActionBar { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Home) || event.equals(KeyCode.PageUp)) { - this.focusedItem = this.items.length - 1; + this.focusedItem = this.viewItems.length - 1; this.focusNext(); EventHelper.stop(e, true); } else if (event.equals(KeyCode.End) || event.equals(KeyCode.PageDown)) { @@ -189,7 +189,7 @@ export class Menu extends ActionBar { parent: this }; - this.mnemonics = new Map>(); + this.mnemonics = new Map>(); this.push(actions, { icon: true, label: true, isMenu: true }); @@ -223,7 +223,7 @@ export class Menu extends ActionBar { container.appendChild(this.scrollableElement.getDomNode()); this.scrollableElement.scanDomNode(); - this.items.filter(item => !(item instanceof MenuSeparatorActionItem)).forEach((item: MenuActionItem, index: number, array: any[]) => { + this.viewItems.filter(item => !(item instanceof MenuSeparatorActionViewItem)).forEach((item: BaseMenuActionViewItem, index: number, array: any[]) => { item.updatePositionInSet(index + 1, array.length); }); } @@ -241,9 +241,9 @@ export class Menu extends ActionBar { this.domNode.style.backgroundColor = bgColor; container.style.boxShadow = shadow; - if (this.items) { - this.items.forEach(item => { - if (item instanceof MenuActionItem || item instanceof MenuSeparatorActionItem) { + if (this.viewItems) { + this.viewItems.forEach(item => { + if (item instanceof BaseMenuActionViewItem || item instanceof MenuSeparatorActionViewItem) { item.style(style); } }); @@ -263,12 +263,12 @@ export class Menu extends ActionBar { } trigger(index: number): void { - if (index <= this.items.length && index >= 0) { - const item = this.items[index]; - if (item instanceof SubmenuActionItem) { + if (index <= this.viewItems.length && index >= 0) { + const item = this.viewItems[index]; + if (item instanceof SubmenuMenuActionViewItem) { super.focus(index); item.open(true); - } else if (item instanceof MenuActionItem) { + } else if (item instanceof BaseMenuActionViewItem) { super.run(item._action, item._context); } else { return; @@ -295,27 +295,27 @@ export class Menu extends ActionBar { } } - private doGetActionItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): BaseActionItem { + private doGetActionViewItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): BaseActionViewItem { if (action instanceof Separator) { - return new MenuSeparatorActionItem(options.context, action, { icon: true }); + return new MenuSeparatorActionViewItem(options.context, action, { icon: true }); } else if (action instanceof SubmenuAction) { - const menuActionItem = new SubmenuActionItem(action, action.entries, parentData, options); + const menuActionViewItem = new SubmenuMenuActionViewItem(action, action.entries, parentData, options); if (options.enableMnemonics) { - const mnemonic = menuActionItem.getMnemonic(); - if (mnemonic && menuActionItem.isEnabled()) { - let actionItems: MenuActionItem[] = []; + const mnemonic = menuActionViewItem.getMnemonic(); + if (mnemonic && menuActionViewItem.isEnabled()) { + let actionViewItems: BaseMenuActionViewItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic)!; + actionViewItems = this.mnemonics.get(mnemonic)!; } - actionItems.push(menuActionItem); + actionViewItems.push(menuActionViewItem); - this.mnemonics.set(mnemonic, actionItems); + this.mnemonics.set(mnemonic, actionViewItems); } } - return menuActionItem; + return menuActionViewItem; } else { const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics }; if (options.getKeyBinding) { @@ -329,32 +329,32 @@ export class Menu extends ActionBar { } } - const menuActionItem = new MenuActionItem(options.context, action, menuItemOptions); + const menuActionViewItem = new BaseMenuActionViewItem(options.context, action, menuItemOptions); if (options.enableMnemonics) { - const mnemonic = menuActionItem.getMnemonic(); - if (mnemonic && menuActionItem.isEnabled()) { - let actionItems: MenuActionItem[] = []; + const mnemonic = menuActionViewItem.getMnemonic(); + if (mnemonic && menuActionViewItem.isEnabled()) { + let actionViewItems: BaseMenuActionViewItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic)!; + actionViewItems = this.mnemonics.get(mnemonic)!; } - actionItems.push(menuActionItem); + actionViewItems.push(menuActionViewItem); - this.mnemonics.set(mnemonic, actionItems); + this.mnemonics.set(mnemonic, actionViewItems); } } - return menuActionItem; + return menuActionViewItem; } } } -interface IMenuItemOptions extends IActionItemOptions { +interface IMenuItemOptions extends IActionViewItemOptions { enableMnemonics?: boolean; } -class MenuActionItem extends BaseActionItem { +class BaseMenuActionViewItem extends BaseActionViewItem { public container: HTMLElement; @@ -562,7 +562,7 @@ class MenuActionItem extends BaseActionItem { } } -class SubmenuActionItem extends MenuActionItem { +class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { private mysubmenu: Menu | null; private submenuContainer: HTMLElement | undefined; private submenuIndicator: HTMLElement; @@ -778,7 +778,7 @@ class SubmenuActionItem extends MenuActionItem { } } -class MenuSeparatorActionItem extends ActionItem { +class MenuSeparatorActionViewItem extends ActionViewItem { style(style: IMenuStyles): void { this.label.style.borderBottomColor = style.separatorColor ? `${style.separatorColor}` : null; } diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 1c144b24096..557df5fada7 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -6,8 +6,8 @@ import 'vs/css!./toolbar'; import * as nls from 'vs/nls'; import { Action, IActionRunner, IAction } from 'vs/base/common/actions'; -import { ActionBar, ActionsOrientation, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IContextMenuProvider, DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown'; +import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IContextMenuProvider, DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; @@ -17,7 +17,7 @@ export const CONTEXT = 'context.toolbar'; export interface IToolBarOptions { orientation?: ActionsOrientation; - actionItemProvider?: IActionItemProvider; + actionViewItemProvider?: IActionViewItemProvider; ariaLabel?: string; getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined; actionRunner?: IActionRunner; @@ -32,7 +32,7 @@ export class ToolBar extends Disposable { private options: IToolBarOptions; private actionBar: ActionBar; private toggleMenuAction: ToggleMenuAction; - private toggleMenuActionItem?: DropdownMenuActionItem; + private toggleMenuActionViewItem?: DropdownMenuActionViewItem; private hasSecondaryActions: boolean; private lookupKeybindings: boolean; @@ -42,7 +42,7 @@ export class ToolBar extends Disposable { this.options = options; this.lookupKeybindings = typeof this.options.getKeyBinding === 'function'; - this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionItem && this.toggleMenuActionItem.show(), options.toggleMenuTitle)); + this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem && this.toggleMenuActionViewItem.show(), options.toggleMenuTitle)); let element = document.createElement('div'); element.className = 'monaco-toolbar'; @@ -52,33 +52,33 @@ export class ToolBar extends Disposable { orientation: options.orientation, ariaLabel: options.ariaLabel, actionRunner: options.actionRunner, - actionItemProvider: (action: Action) => { + actionViewItemProvider: (action: Action) => { // Return special action item for the toggle menu action if (action.id === ToggleMenuAction.ID) { // Dispose old - if (this.toggleMenuActionItem) { - this.toggleMenuActionItem.dispose(); + if (this.toggleMenuActionViewItem) { + this.toggleMenuActionViewItem.dispose(); } // Create new - this.toggleMenuActionItem = new DropdownMenuActionItem( + this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( action, (action).menuActions, contextMenuProvider, - this.options.actionItemProvider, + this.options.actionViewItemProvider, this.actionRunner, this.options.getKeyBinding, 'toolbar-toggle-more', this.options.anchorAlignmentProvider ); - this.toggleMenuActionItem!.setActionContext(this.actionBar.context); + this.toggleMenuActionViewItem!.setActionContext(this.actionBar.context); - return this.toggleMenuActionItem; + return this.toggleMenuActionViewItem; } - return options.actionItemProvider ? options.actionItemProvider(action) : undefined; + return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; } })); } @@ -93,8 +93,8 @@ export class ToolBar extends Disposable { set context(context: any) { this.actionBar.context = context; - if (this.toggleMenuActionItem) { - this.toggleMenuActionItem.setActionContext(context); + if (this.toggleMenuActionViewItem) { + this.toggleMenuActionViewItem.setActionContext(context); } } @@ -156,9 +156,9 @@ export class ToolBar extends Disposable { } dispose(): void { - if (this.toggleMenuActionItem) { - this.toggleMenuActionItem.dispose(); - this.toggleMenuActionItem = undefined; + if (this.toggleMenuActionViewItem) { + this.toggleMenuActionViewItem.dispose(); + this.toggleMenuActionViewItem = undefined; } super.dispose(); diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 2e4fbf152d0..067704d7c21 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -29,7 +29,7 @@ export interface IActionRunner extends IDisposable { onDidBeforeRun: Event; } -export interface IActionItem { +export interface IActionViewItem { actionRunner: IActionRunner; setActionContext(context: any): void; render(element: any /* HTMLElement */): void; diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index 8fa07282fd8..89af0a637a6 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IAction } from 'vs/base/common/actions'; import { KeyCode, KeyMod, ResolvedKeybinding } from 'vs/base/common/keyCodes'; @@ -176,18 +176,18 @@ export class ContextMenuController implements IEditorContribution { getActions: () => actions, - getActionItem: (action) => { + getActionViewItem: (action) => { const keybinding = this._keybindingFor(action); if (keybinding) { - return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel(), isMenu: true }); + return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel(), isMenu: true }); } - const customActionItem = action; - if (typeof customActionItem.getActionItem === 'function') { - return customActionItem.getActionItem(); + const customActionViewItem = action; + if (typeof customActionViewItem.getActionViewItem === 'function') { + return customActionViewItem.getActionViewItem(); } - return new ActionItem(action, action, { icon: true, label: true, isMenu: true }); + return new ActionViewItem(action, action, { icon: true, label: true, isMenu: true }); }, getKeyBinding: (action): ResolvedKeybinding | undefined => { diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts similarity index 91% rename from src/vs/platform/actions/browser/menuItemActionItem.ts rename to src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 4151c09390a..d6c83d65768 100644 --- a/src/vs/platform/actions/browser/menuItemActionItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -5,7 +5,7 @@ import { addClasses, createCSSRule, removeClasses } from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; -import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAction } from 'vs/base/common/actions'; import { Emitter } from 'vs/base/common/event'; import { IdGenerator } from 'vs/base/common/idGenerator'; @@ -113,16 +113,16 @@ function fillInActions(groups: [string, Array = new Map(); @@ -230,13 +230,13 @@ export class MenuItemActionItem extends ActionItem { const iconPathMapKey = item.iconLocation.dark.toString(); - if (MenuItemActionItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) { - iconClass = MenuItemActionItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!; + if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) { + iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!; } else { iconClass = ids.nextId(); createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`); createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${item.iconLocation.dark.toString()}")`); - MenuItemActionItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); + MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); } addClasses(this.label, 'icon', iconClass); @@ -254,10 +254,10 @@ export class MenuItemActionItem extends ActionItem { } } -// Need to subclass MenuItemActionItem in order to respect +// Need to subclass MenuEntryActionViewItem in order to respect // the action context coming from any action bar, without breaking // existing users -export class ContextAwareMenuItemActionItem extends MenuItemActionItem { +export class ContextAwareMenuEntryActionViewItem extends MenuEntryActionViewItem { onClick(event: MouseEvent): void { event.preventDefault(); diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index 3b7a0c51d5b..53ba4d21e04 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -73,7 +73,7 @@ export class ContextMenuHandler { actionRunner.onDidBeforeRun(this.onActionRun, this, menuDisposables); actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables); menu = new Menu(container, actions, { - actionItemProvider: delegate.getActionItem, + actionViewItemProvider: delegate.getActionViewItem, context: delegate.getActionsContext ? delegate.getActionsContext() : null, actionRunner, getKeyBinding: delegate.getKeyBinding ? delegate.getKeyBinding : action => this.keybindingService.lookupKeybinding(action.id) diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index fbea24f3bce..801b038b879 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Component } from 'vs/workbench/common/component'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IComposite, ICompositeControl } from 'vs/workbench/common/composite'; @@ -170,12 +170,12 @@ export abstract class Composite extends Component implements IComposite { } /** - * For any of the actions returned by this composite, provide an IActionItem in + * For any of the actions returned by this composite, provide an IActionViewItem in * cases where the implementor of the composite wants to override the presentation * of an action. Returns undefined to indicate that the action is not rendered through * an action item. */ - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { return undefined; } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 0b90d082300..4e4f5523191 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -18,7 +18,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { ActivityAction, ActivityActionItem, ICompositeBar, ICompositeBarColors, ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; +import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarColors, ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { IActivity, IGlobalActivity } from 'vs/workbench/common/activity'; @@ -110,7 +110,7 @@ export class GlobalActivityAction extends ActivityAction { } } -export class GlobalActivityActionItem extends ActivityActionItem { +export class GlobalActivityActionViewItem extends ActivityActionViewItem { constructor( action: GlobalActivityAction, diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 948c7323b1b..897dca453ab 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -10,7 +10,7 @@ import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/acti import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity'; import { Registry } from 'vs/platform/registry/common/platform'; import { Part } from 'vs/workbench/browser/part'; -import { GlobalActivityActionItem, GlobalActivityAction, ViewletActivityAction, ToggleViewletAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewletActivityAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; +import { GlobalActivityActionViewItem, GlobalActivityAction, ViewletActivityAction, ToggleViewletAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewletActivityAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService'; @@ -236,7 +236,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { .map(a => new GlobalActivityAction(a)); this.globalActionBar = this._register(new ActionBar(container, { - actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a, (theme: ITheme) => this.getActivitybarItemColors(theme)), + actionViewItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionViewItem, a, (theme: ITheme) => this.getActivitybarItemColors(theme)), orientation: ActionsOrientation.VERTICAL, ariaLabel: nls.localize('globalActions', "Global Actions"), animated: false @@ -380,7 +380,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { // Layout composite bar let availableHeight = contentAreaSize.height; if (this.globalActionBar) { - availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT); // adjust height for global actions showing + availableHeight -= (this.globalActionBar.viewItems.length * ActivitybarPart.ACTION_HEIGHT); // adjust height for global actions showing } this.compositeBar.layout(new Dimension(width, availableHeight)); } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index ac4cacb55f0..8e9b5bd0a4f 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -11,7 +11,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors, DraggedCompositeIdentifier } from 'vs/workbench/browser/parts/compositeBarActions'; +import { CompositeActionViewItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionViewItem, ActivityAction, ICompositeBar, ICompositeBarColors, DraggedCompositeIdentifier } from 'vs/workbench/browser/parts/compositeBarActions'; import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -50,7 +50,7 @@ export class CompositeBar extends Widget implements ICompositeBar { private compositeSwitcherBar: ActionBar; private compositeOverflowAction: CompositeOverflowActivityAction | null; - private compositeOverflowActionItem: CompositeOverflowActivityActionItem | undefined; + private compositeOverflowActionViewItem: CompositeOverflowActivityActionViewItem | undefined; private model: CompositeBarModel; private visibleComposites: string[]; @@ -93,12 +93,12 @@ export class CompositeBar extends Widget implements ICompositeBar { create(parent: HTMLElement): HTMLElement { const actionBarDiv = parent.appendChild($('.composite-bar')); this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, { - actionItemProvider: (action: Action) => { + actionViewItemProvider: (action: Action) => { if (action instanceof CompositeOverflowActivityAction) { - return this.compositeOverflowActionItem; + return this.compositeOverflowActionViewItem; } const item = this.model.findItem(action.id); - return item && this.instantiationService.createInstance(CompositeActionItem, action, item.pinnedAction, () => this.getContextMenuActions(), this.options.colors, this.options.icon, this); + return item && this.instantiationService.createInstance(CompositeActionViewItem, action, item.pinnedAction, () => this.getContextMenuActions(), this.options.colors, this.options.icon, this); }, orientation: this.options.orientation, ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"), @@ -278,13 +278,13 @@ export class CompositeBar extends Widget implements ICompositeBar { } else { if (this.dimension && this.dimension.height !== 0 && this.dimension.width !== 0) { // Compute sizes only if visible. Otherwise the size measurment would be computed wrongly. - const currentItemsLength = this.compositeSwitcherBar.items.length; + const currentItemsLength = this.compositeSwitcherBar.viewItems.length; this.compositeSwitcherBar.push(items.map(composite => composite.activityAction)); items.map((composite, index) => this.compositeSizeInBar.set(composite.id, this.options.orientation === ActionsOrientation.VERTICAL ? this.compositeSwitcherBar.getHeight(currentItemsLength + index) : this.compositeSwitcherBar.getWidth(currentItemsLength + index) )); - items.forEach(() => this.compositeSwitcherBar.pull(this.compositeSwitcherBar.items.length - 1)); + items.forEach(() => this.compositeSwitcherBar.pull(this.compositeSwitcherBar.viewItems.length - 1)); } } } @@ -343,10 +343,10 @@ export class CompositeBar extends Widget implements ICompositeBar { this.compositeOverflowAction.dispose(); this.compositeOverflowAction = null; - if (this.compositeOverflowActionItem) { - this.compositeOverflowActionItem.dispose(); + if (this.compositeOverflowActionViewItem) { + this.compositeOverflowActionViewItem.dispose(); } - this.compositeOverflowActionItem = undefined; + this.compositeOverflowActionViewItem = undefined; } // Pull out composites that overflow or got hidden @@ -357,9 +357,9 @@ export class CompositeBar extends Widget implements ICompositeBar { } }); compositesToRemove.reverse().forEach(index => { - const actionItem = this.compositeSwitcherBar.items[index]; + const actionViewItem = this.compositeSwitcherBar.viewItems[index]; this.compositeSwitcherBar.pull(index); - actionItem.dispose(); + actionViewItem.dispose(); this.visibleComposites.splice(index, 1); }); @@ -368,9 +368,9 @@ export class CompositeBar extends Widget implements ICompositeBar { const currentIndex = this.visibleComposites.indexOf(compositeId); if (newIndex !== currentIndex) { if (currentIndex !== -1) { - const actionItem = this.compositeSwitcherBar.items[currentIndex]; + const actionViewItem = this.compositeSwitcherBar.viewItems[currentIndex]; this.compositeSwitcherBar.pull(currentIndex); - actionItem.dispose(); + actionViewItem.dispose(); this.visibleComposites.splice(currentIndex, 1); } @@ -382,12 +382,12 @@ export class CompositeBar extends Widget implements ICompositeBar { // Add overflow action as needed if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) { this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => { - if (this.compositeOverflowActionItem) { - this.compositeOverflowActionItem.showMenu(); + if (this.compositeOverflowActionViewItem) { + this.compositeOverflowActionViewItem.showMenu(); } }); - this.compositeOverflowActionItem = this.instantiationService.createInstance( - CompositeOverflowActivityActionItem, + this.compositeOverflowActionViewItem = this.instantiationService.createInstance( + CompositeOverflowActivityActionViewItem, this.compositeOverflowAction, () => this.getOverflowingComposites(), () => this.model.activeItem ? this.model.activeItem.id : undefined, diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index f6763258e5e..59e89ca7df4 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; -import { BaseActionItem, IBaseActionItemOptions, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionViewItem, IBaseActionViewItemOptions, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { dispose, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -118,16 +118,16 @@ export interface ICompositeBarColors { dragAndDropBackground?: Color; } -export interface IActivityActionItemOptions extends IBaseActionItemOptions { +export interface IActivityActionViewItemOptions extends IBaseActionViewItemOptions { icon?: boolean; colors: (theme: ITheme) => ICompositeBarColors; } -export class ActivityActionItem extends BaseActionItem { +export class ActivityActionViewItem extends BaseActionViewItem { protected container: HTMLElement; protected label: HTMLElement; protected badge: HTMLElement; - protected options: IActivityActionItemOptions; + protected options: IActivityActionViewItemOptions; private badgeContent: HTMLElement; private badgeDisposable: IDisposable = Disposable.None; @@ -135,7 +135,7 @@ export class ActivityActionItem extends BaseActionItem { constructor( action: ActivityAction, - options: IActivityActionItemOptions, + options: IActivityActionViewItemOptions, @IThemeService protected themeService: IThemeService ) { super(null, action, options); @@ -347,7 +347,7 @@ export class CompositeOverflowActivityAction extends ActivityAction { } } -export class CompositeOverflowActivityActionItem extends ActivityActionItem { +export class CompositeOverflowActivityActionViewItem extends ActivityActionViewItem { private actions: Action[]; constructor( @@ -428,7 +428,7 @@ export class DraggedCompositeIdentifier { } } -export class CompositeActionItem extends ActivityActionItem { +export class CompositeActionViewItem extends ActivityActionViewItem { private static manageExtensionAction: ManageExtensionAction; @@ -451,8 +451,8 @@ export class CompositeActionItem extends ActivityActionItem { this.compositeTransfer = LocalSelectionTransfer.getInstance(); - if (!CompositeActionItem.manageExtensionAction) { - CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); + if (!CompositeActionViewItem.manageExtensionAction) { + CompositeActionViewItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); } this._register(compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = null; this.updateActivity(); }, this)); @@ -569,7 +569,7 @@ export class CompositeActionItem extends ActivityActionItem { const actions: Action[] = [this.toggleCompositePinnedAction]; if ((this.compositeActivityAction.activity).extensionId) { actions.push(new Separator()); - actions.push(CompositeActionItem.manageExtensionAction); + actions.push(CompositeActionViewItem.manageExtensionAction); } const isPinned = this.compositeBar.isPinned(this.activity.id); diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 064d003ebde..adbbceb087c 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -11,7 +11,7 @@ import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { prepareActions } from 'vs/workbench/browser/actions'; import { IAction } from 'vs/base/common/actions'; @@ -399,7 +399,7 @@ export abstract class CompositePart extends Part { // Toolbar this.toolBar = this._register(new ToolBar(titleActionsContainer, this.contextMenuService, { - actionItemProvider: action => this.actionItemProvider(action), + actionViewItemProvider: action => this.actionViewItemProvider(action), orientation: ActionsOrientation.HORIZONTAL, getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment() @@ -432,11 +432,11 @@ export abstract class CompositePart extends Part { this.titleLabel.updateStyles(); } - protected actionItemProvider(action: IAction): IActionItem | undefined { + protected actionViewItemProvider(action: IAction): IActionViewItem | undefined { // Check Active Composite if (this.activeComposite) { - return this.activeComposite.getActionItem(action); + return this.activeComposite.getActionViewItem(action); } return undefined; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 57d29393caf..fa7d3c0dd69 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -40,7 +40,7 @@ import { CLOSE_EDITOR_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { isErrorWithActions, IErrorWithActions } from 'vs/base/common/errorsWithActions'; import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index fd79f843f1c..164c7ad43ea 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -6,7 +6,7 @@ import { applyDragImage } from 'vs/base/browser/dnd'; import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { ActionsOrientation, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionsOrientation, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction, IRunEvent } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; @@ -15,7 +15,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./media/titlecontrol'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { localize } from 'vs/nls'; -import { createActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { createActionViewItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ExecuteCommandAction, IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -128,7 +128,7 @@ export abstract class TitleControl extends Themable { const context: IEditorCommandsContext = { groupId: this.group.id }; this.editorActionsToolbar = this._register(new ToolBar(container, this.contextMenuService, { - actionItemProvider: action => this.actionItemProvider(action), + actionViewItemProvider: action => this.actionViewItemProvider(action), orientation: ActionsOrientation.HORIZONTAL, ariaLabel: localize('araLabelEditorActions', "Editor actions"), getKeyBinding: action => this.getKeybinding(action), @@ -158,21 +158,21 @@ export abstract class TitleControl extends Themable { })); } - private actionItemProvider(action: IAction): IActionItem | undefined { + private actionViewItemProvider(action: IAction): IActionViewItem | undefined { const activeControl = this.group.activeControl; // Check Active Editor - let actionItem: IActionItem | undefined = undefined; + let actionViewItem: IActionViewItem | undefined = undefined; if (activeControl instanceof BaseEditor) { - actionItem = activeControl.getActionItem(action); + actionViewItem = activeControl.getActionViewItem(action); } // Check extensions - if (!actionItem) { - actionItem = createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + if (!actionViewItem) { + actionViewItem = createActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); } - return actionItem; + return actionViewItem; } protected updateEditorActionsToolbar(): void { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 248e71586c2..918fafdc244 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -16,7 +16,7 @@ import { IAction, IActionRunner } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { INotificationViewItem, NotificationViewItem, NotificationViewItemLabelKind, INotificationMessage, ChoiceAction } from 'vs/workbench/common/notifications'; import { ClearNotificationAction, ExpandNotificationAction, CollapseNotificationAction, ConfigureNotificationAction } from 'vs/workbench/browser/parts/notifications/notificationsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -219,9 +219,9 @@ export class NotificationRenderer implements IListRenderer { + actionViewItemProvider: action => { if (action && action instanceof ConfigureNotificationAction) { - const item = new DropdownMenuActionItem(action, action.configurationActions, this.contextMenuService, undefined, this.actionRunner, undefined, action.class); + const item = new DropdownMenuActionViewItem(action, action.configurationActions, this.contextMenuService, undefined, this.actionRunner, undefined, action.class); data.toDispose.push(item); return item; diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index a7e9999bac7..2872d0a6c9e 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -33,7 +33,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { inQuickOpenContext, InQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen'; -import { ActionBar, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -1442,11 +1442,11 @@ export class QuickInputService extends Component implements IQuickInputService { private setEnabled(enabled: boolean) { if (enabled !== this.enabled) { this.enabled = enabled; - for (const item of this.ui.leftActionBar.items) { - (item as ActionItem).getAction().enabled = enabled; + for (const item of this.ui.leftActionBar.viewItems) { + (item as ActionViewItem).getAction().enabled = enabled; } - for (const item of this.ui.rightActionBar.items) { - (item as ActionItem).getAction().enabled = enabled; + for (const item of this.ui.rightActionBar.viewItems) { + (item as ActionViewItem).getAction().enabled = enabled; } this.ui.checkAll.disabled = !enabled; // this.ui.inputBox.enabled = enabled; Avoid loosing focus. diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index f291f110653..8ac0298db71 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -248,7 +248,7 @@ export class SidebarPart extends CompositePart implements IViewletServi this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActions: () => contextMenuActions, - getActionItem: action => this.actionItemProvider(action as Action), + getActionViewItem: action => this.actionViewItemProvider(action as Action), actionRunner: activeViewlet.getActionRunner() }); } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index c12e152107e..8c4e0f8337c 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -7,11 +7,11 @@ import 'vs/css!./media/views'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IAction, IActionItem, ActionRunner, Action } from 'vs/base/common/actions'; +import { IAction, IActionViewItem, ActionRunner, Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; -import { ContextAwareMenuItemActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { ContextAwareMenuEntryActionViewItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions } from 'vs/workbench/common/views'; import { IViewletViewOptions, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -24,7 +24,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import * as DOM from 'vs/base/browser/dom'; import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; -import { ActionBar, IActionItemProvider, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionViewItemProvider, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { URI } from 'vs/base/common/uri'; import { dirname, basename } from 'vs/base/common/resources'; import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -87,8 +87,8 @@ export class CustomTreeViewPanel extends ViewletPanel { return [...this.treeView.getSecondaryActions()]; } - getActionItem(action: IAction): IActionItem | undefined { - return action instanceof MenuItemAction ? new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : undefined; + getActionViewItem(action: IAction): IActionViewItem | undefined { + return action instanceof MenuItemAction ? new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : undefined; } getOptimalWidth(): number { @@ -378,11 +378,11 @@ export class CustomTreeView extends Disposable implements ITreeView { } private createTree() { - const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined; + const actionViewItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action) : undefined; const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.viewContainer.id }, () => task)); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, this.treeLabels, actionItemProvider); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, this.treeLabels, actionViewItemProvider); const controller = this.instantiationService.createInstance(TreeController, this.id, menus); this.tree = this._register(this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {})); this.tree.contextKeyService.createKey(this.id, true); @@ -645,7 +645,7 @@ class TreeRenderer implements IRenderer { private treeViewId: string, private menus: TreeMenus, private labels: ResourceLabels, - private actionItemProvider: IActionItemProvider, + private actionViewItemProvider: IActionViewItemProvider, @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILabelService private readonly labelService: ILabelService @@ -668,7 +668,7 @@ class TreeRenderer implements IRenderer { DOM.addClass(resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); const actionBar = new ActionBar(actionsContainer, { - actionItemProvider: this.actionItemProvider, + actionViewItemProvider: this.actionViewItemProvider, actionRunner: new MultipleSelectionActionRunner(() => tree.getSelection()) }); @@ -809,10 +809,10 @@ class TreeController extends WorkbenchTreeController { getActions: () => actions, - getActionItem: (action) => { + getActionViewItem: (action) => { const keybinding = this._keybindingService.lookupKeybinding(action.id); if (keybinding) { - return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); } return undefined; }, diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 2469435ae57..e4d2caf6247 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -13,7 +13,7 @@ import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, a import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { firstIndex } from 'vs/base/common/arrays'; import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { Registry } from 'vs/platform/registry/common/platform'; import { prepareActions } from 'vs/workbench/browser/actions'; import { Viewlet, ViewletRegistry, Extensions } from 'vs/workbench/browser/viewlet'; @@ -127,7 +127,7 @@ export abstract class ViewletPanel extends Panel implements IView { const actions = append(container, $('.actions')); this.toolbar = new ToolBar(actions, this.contextMenuService, { orientation: ActionsOrientation.HORIZONTAL, - actionItemProvider: action => this.getActionItem(action), + actionViewItemProvider: action => this.getActionViewItem(action), ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title), getKeyBinding: action => withNullAsUndefined(this.keybindingService.lookupKeybinding(action.id)), actionRunner: this.actionRunner @@ -180,7 +180,7 @@ export abstract class ViewletPanel extends Panel implements IView { return []; } - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { return undefined; } @@ -289,12 +289,12 @@ export class PanelViewlet extends Viewlet { return []; } - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { if (this.isSingleView()) { - return this.panelItems[0].panel.getActionItem(action); + return this.panelItems[0].panel.getActionViewItem(action); } - return super.getActionItem(action); + return super.getActionViewItem(action); } focus(): void { diff --git a/src/vs/workbench/common/composite.ts b/src/vs/workbench/common/composite.ts index 9d27d30d840..50b5bd7917c 100644 --- a/src/vs/workbench/common/composite.ts +++ b/src/vs/workbench/common/composite.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionItem } from 'vs/base/common/actions'; +import { IAction, IActionViewItem } from 'vs/base/common/actions'; export interface IComposite { @@ -35,7 +35,7 @@ export interface IComposite { /** * Returns the action item for a specific action. */ - getActionItem(action: IAction): IActionItem | undefined; + getActionViewItem(action: IAction): IActionViewItem | undefined; /** * Returns the underlying control of this composite. diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 975bb6c6837..8b4b7bbfe0a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import * as modes from 'vs/editor/common/modes'; -import { ActionsOrientation, ActionItem, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionsOrientation, ActionViewItem, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; import { Action, IActionRunner } from 'vs/base/common/actions'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; @@ -29,9 +29,9 @@ import { assign } from 'vs/base/common/objects'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; -import { ToggleReactionsAction, ReactionAction, ReactionActionItem } from './reactionsAction'; +import { ToggleReactionsAction, ReactionAction, ReactionActionViewItem } from './reactionsAction'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; @@ -165,14 +165,14 @@ export class CommentNode extends Disposable { if (actions.length) { this.toolbar = new ToolBar(this._actionsToolbarContainer, this.contextMenuService, { - actionItemProvider: action => { + actionViewItemProvider: action => { if (action.id === ToggleReactionsAction.ID) { - return new DropdownMenuActionItem( + return new DropdownMenuActionViewItem( action, (action).menuActions, this.contextMenuService, action => { - return this.actionItemProvider(action as Action); + return this.actionViewItemProvider(action as Action); }, this.actionRunner!, undefined, @@ -180,7 +180,7 @@ export class CommentNode extends Disposable { () => { return AnchorAlignment.RIGHT; } ); } - return this.actionItemProvider(action as Action); + return this.actionViewItemProvider(action as Action); }, orientation: ActionsOrientation.HORIZONTAL }); @@ -191,7 +191,7 @@ export class CommentNode extends Disposable { } } - actionItemProvider(action: Action) { + actionViewItemProvider(action: Action) { let options = {}; if (action.id === 'comment.delete' || action.id === 'comment.edit' || action.id === ToggleReactionsAction.ID) { options = { label: false, icon: true }; @@ -200,19 +200,19 @@ export class CommentNode extends Disposable { } if (action.id === ReactionAction.ID) { - let item = new ReactionActionItem(action); + let item = new ReactionActionViewItem(action); return item; } else { - let item = new ActionItem({}, action, options); + let item = new ActionViewItem({}, action, options); return item; } } private createReactionPicker2(): ToggleReactionsAction { - let toggleReactionActionItem: DropdownMenuActionItem; + let toggleReactionActionViewItem: DropdownMenuActionViewItem; let toggleReactionAction = this._register(new ToggleReactionsAction(() => { - if (toggleReactionActionItem) { - toggleReactionActionItem.show(); + if (toggleReactionActionViewItem) { + toggleReactionActionViewItem.show(); } }, nls.localize('commentToggleReaction', "Toggle Reaction"))); @@ -235,15 +235,15 @@ export class CommentNode extends Disposable { toggleReactionAction.menuActions = reactionMenuActions; - toggleReactionActionItem = new DropdownMenuActionItem( + toggleReactionActionViewItem = new DropdownMenuActionViewItem( toggleReactionAction, (toggleReactionAction).menuActions, this.contextMenuService, action => { if (action.id === ToggleReactionsAction.ID) { - return toggleReactionActionItem; + return toggleReactionActionViewItem; } - return this.actionItemProvider(action as Action); + return this.actionViewItemProvider(action as Action); }, this.actionRunner!, undefined, @@ -255,10 +255,10 @@ export class CommentNode extends Disposable { } private createReactionPicker(): ToggleReactionsAction { - let toggleReactionActionItem: DropdownMenuActionItem; + let toggleReactionActionViewItem: DropdownMenuActionViewItem; let toggleReactionAction = this._register(new ToggleReactionsAction(() => { - if (toggleReactionActionItem) { - toggleReactionActionItem.show(); + if (toggleReactionActionViewItem) { + toggleReactionActionViewItem.show(); } }, nls.localize('commentAddReaction', "Add Reaction"))); @@ -281,15 +281,15 @@ export class CommentNode extends Disposable { toggleReactionAction.menuActions = reactionMenuActions; - toggleReactionActionItem = new DropdownMenuActionItem( + toggleReactionActionViewItem = new DropdownMenuActionViewItem( toggleReactionAction, (toggleReactionAction).menuActions, this.contextMenuService, action => { if (action.id === ToggleReactionsAction.ID) { - return toggleReactionActionItem; + return toggleReactionActionViewItem; } - return this.actionItemProvider(action as Action); + return this.actionViewItemProvider(action as Action); }, this.actionRunner!, undefined, @@ -303,14 +303,14 @@ export class CommentNode extends Disposable { private createReactionsContainer(commentDetailsContainer: HTMLElement): void { this._reactionActionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); this._reactionsActionBar = new ActionBar(this._reactionActionsContainer, { - actionItemProvider: action => { + actionViewItemProvider: action => { if (action.id === ToggleReactionsAction.ID) { - return new DropdownMenuActionItem( + return new DropdownMenuActionViewItem( action, (action).menuActions, this.contextMenuService, action => { - return this.actionItemProvider(action as Action); + return this.actionViewItemProvider(action as Action); }, this.actionRunner!, undefined, @@ -318,7 +318,7 @@ export class CommentNode extends Disposable { () => { return AnchorAlignment.RIGHT; } ); } - return this.actionItemProvider(action as Action); + return this.actionViewItemProvider(action as Action); } }); this._toDispose.push(this._reactionsActionBar); diff --git a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts index 0b8bc2e8bf9..c14030dab80 100644 --- a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts +++ b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import { ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action, IAction } from 'vs/base/common/actions'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -29,7 +29,7 @@ export class ToggleReactionsAction extends Action { this._menuActions = actions; } } -export class ReactionActionItem extends ActionItem { +export class ReactionActionViewItem extends ActionViewItem { constructor(action: ReactionAction) { super(null, action, {}); } diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index e7ef34d2dd4..af8854bac34 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -21,7 +21,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { TreeResourceNavigator2, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; diff --git a/src/vs/workbench/contrib/debug/browser/debugActionItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts similarity index 96% rename from src/vs/workbench/contrib/debug/browser/debugActionItems.ts rename to src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index afd9dbbc666..f07ca7f2219 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -9,7 +9,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; -import { SelectActionItem, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { SelectActionViewItem, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDebugService, IDebugSession, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug'; @@ -23,7 +23,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; const $ = dom.$; -export class StartDebugActionItem implements IActionItem { +export class StartDebugActionViewItem implements IActionViewItem { private static readonly SEPARATOR = '─────────'; @@ -171,7 +171,7 @@ export class StartDebugActionItem implements IActionItem { if (this.options.length === 0) { this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: () => false }); } else { - this.options.push({ label: StartDebugActionItem.SEPARATOR, handler: undefined }); + this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: undefined }); } const disabledIdx = this.options.length - 1; @@ -189,7 +189,7 @@ export class StartDebugActionItem implements IActionItem { } } -export class FocusSessionActionItem extends SelectActionItem { +export class FocusSessionActionViewItem extends SelectActionViewItem { constructor( action: IAction, @IDebugService protected readonly debugService: IDebugService, diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 1932720ed59..84a184174ff 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -14,7 +14,7 @@ import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/act import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; -import { FocusSessionActionItem } from 'vs/workbench/contrib/debug/browser/debugActionItems'; +import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -27,7 +27,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { INotificationService } from 'vs/platform/notification/common/notification'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { fillInActionBarActions, MenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; @@ -86,12 +86,12 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.activeActions = []; this.actionBar = this._register(new ActionBar(actionBarContainer, { orientation: ActionsOrientation.HORIZONTAL, - actionItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction) => { if (action.id === FocusSessionAction.ID) { - return this.instantiationService.createInstance(FocusSessionActionItem, action); + return this.instantiationService.createInstance(FocusSessionActionViewItem, action); } if (action instanceof MenuItemAction) { - return new MenuItemActionItem(action, this.keybindingService, this.notificationService, contextMenuService); + return new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, contextMenuService); } return undefined; diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index 7bb82f5bf93..2f2d7c2ea09 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -7,11 +7,11 @@ import 'vs/css!./media/debugViewlet'; import * as nls from 'vs/nls'; import { IAction } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, REPL_ID } from 'vs/workbench/contrib/debug/common/debug'; import { StartAction, ConfigureAction, SelectAndStartAction, FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; -import { StartDebugActionItem, FocusSessionActionItem } from 'vs/workbench/contrib/debug/browser/debugActionItems'; +import { StartDebugActionViewItem, FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; @@ -29,14 +29,14 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IMenu, MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { MenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem'; +import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; export class DebugViewlet extends ViewContainerViewlet { - private startDebugActionItem: StartDebugActionItem; + private startDebugActionViewItem: StartDebugActionViewItem; private progressRunner: IProgressRunner; private breakpointView: ViewletPanel; private panelListeners = new Map(); @@ -80,8 +80,8 @@ export class DebugViewlet extends ViewContainerViewlet { focus(): void { super.focus(); - if (this.startDebugActionItem) { - this.startDebugActionItem.focus(); + if (this.startDebugActionViewItem) { + this.startDebugActionViewItem.focus(); } } @@ -130,16 +130,16 @@ export class DebugViewlet extends ViewContainerViewlet { return [this.selectAndStartAction, this.configureAction, this.toggleReplAction]; } - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { if (action.id === StartAction.ID) { - this.startDebugActionItem = this.instantiationService.createInstance(StartDebugActionItem, null, action); - return this.startDebugActionItem; + this.startDebugActionViewItem = this.instantiationService.createInstance(StartDebugActionViewItem, null, action); + return this.startDebugActionViewItem; } if (action.id === FocusSessionAction.ID) { - return new FocusSessionActionItem(action, this.debugService, this.themeService, this.contextViewService, this.configurationService); + return new FocusSessionActionViewItem(action, this.debugService, this.themeService, this.contextViewService, this.configurationService); } if (action instanceof MenuItemAction) { - return new MenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); } return undefined; diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 75b1edfd663..d2acc3d92b4 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -7,7 +7,7 @@ import 'vs/css!vs/workbench/contrib/debug/browser/media/repl'; import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; import * as errors from 'vs/base/common/errors'; -import { IAction, IActionItem, Action } from 'vs/base/common/actions'; +import { IAction, IActionViewItem, Action } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -39,7 +39,7 @@ import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/wor import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { transparent, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { FocusSessionActionItem } from 'vs/workbench/contrib/debug/browser/debugActionItems'; +import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { CompletionContext, CompletionList, CompletionProviderRegistry } from 'vs/editor/common/modes'; import { first } from 'vs/base/common/arrays'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -309,9 +309,9 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.replInput.focus(); } - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { if (action.id === SelectReplAction.ID) { - return this.instantiationService.createInstance(SelectReplActionItem, this.selectReplAction); + return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); } return undefined; @@ -852,7 +852,7 @@ class ReplCopyAllAction extends EditorAction { registerEditorAction(AcceptReplInputAction); registerEditorAction(ReplCopyAllAction); -class SelectReplActionItem extends FocusSessionActionItem { +class SelectReplActionViewItem extends FocusSessionActionViewItem { protected getActionContext(_: string, index: number): any { return this.debugService.getModel().getSessions(true)[index]; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts index 800d6521023..1a69fcb21de 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts @@ -249,9 +249,9 @@ export class ExtensionEditor extends BaseEditor { const extensionActions = append(details, $('.actions')); this.extensionActionBar = new ActionBar(extensionActions, { animated: false, - actionItemProvider: (action: Action) => { + actionViewItemProvider: (action: Action) => { if (action instanceof ExtensionEditorDropDownAction) { - return action.createActionItem(); + return action.createActionViewItem(); } return undefined; } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index 4b33e76faa7..15e4a5ca265 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -10,7 +10,7 @@ import { Delayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; -import { ActionItem, Separator, IActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionViewItem, Separator, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -629,15 +629,15 @@ export class UpdateAction extends ExtensionAction { } } -interface IExtensionActionItemOptions extends IActionItemOptions { +interface IExtensionActionViewItemOptions extends IActionViewItemOptions { tabOnlyOnFocus?: boolean; } -export class ExtensionActionItem extends ActionItem { +export class ExtensionActionViewItem extends ActionViewItem { - protected options: IExtensionActionItemOptions; + protected options: IExtensionActionViewItemOptions; - constructor(context: any, action: IAction, options: IExtensionActionItemOptions = {}) { + constructor(context: any, action: IAction, options: IExtensionActionViewItemOptions = {}) { super(context, action, options); } @@ -680,15 +680,15 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { super(id, label, cssClass, enabled); } - private _actionItem: DropDownMenuActionItem; - createActionItem(): DropDownMenuActionItem { - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, this.tabOnlyOnFocus); - return this._actionItem; + private _actionViewItem: DropDownMenuActionViewItem; + createActionViewItem(): DropDownMenuActionViewItem { + this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this, this.tabOnlyOnFocus); + return this._actionViewItem; } public run({ actionGroups, disposeActionsOnHide }: { actionGroups: IAction[][], disposeActionsOnHide: boolean }): Promise { - if (this._actionItem) { - this._actionItem.showMenu(actionGroups, disposeActionsOnHide); + if (this._actionViewItem) { + this._actionViewItem.showMenu(actionGroups, disposeActionsOnHide); } return Promise.resolve(); } @@ -699,7 +699,7 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { } } -export class DropDownMenuActionItem extends ExtensionActionItem { +export class DropDownMenuActionViewItem extends ExtensionActionViewItem { private disposables: IDisposable[] = []; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts index 8c3911ef7a1..aac51bff673 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts @@ -13,7 +13,7 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, DisabledLabelAction, LocalInstallAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; +import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionViewItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, DisabledLabelAction, LocalInstallAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -80,11 +80,11 @@ export class Renderer implements IPagedRenderer { const author = append(footer, $('.author.ellipsis')); const actionbar = new ActionBar(footer, { animated: false, - actionItemProvider: (action: Action) => { + actionViewItemProvider: (action: Action) => { if (action.id === ManageExtensionAction.ID) { - return (action).createActionItem(); + return (action).createActionViewItem(); } - return new ExtensionActionItem(null, action, actionOptions); + return new ExtensionActionViewItem(null, action, actionOptions); } }); actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); @@ -188,13 +188,13 @@ export class Renderer implements IPagedRenderer { this.extensionViewState.onFocus(e => { if (areSameExtensions(extension.identifier, e.identifier)) { - data.actionbar.items.forEach(item => (item).setFocus(true)); + data.actionbar.viewItems.forEach(item => (item).setFocus(true)); } }, this, data.extensionDisposables); this.extensionViewState.onBlur(e => { if (areSameExtensions(extension.identifier, e.identifier)) { - data.actionbar.items.forEach(item => (item).setFocus(false)); + data.actionbar.viewItems.forEach(item => (item).setFocus(false)); } }, this, data.extensionDisposables); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index f6526869465..466ee3bb498 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -35,7 +35,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; -import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { onUnexpectedError } from 'vs/base/common/errors'; diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 02842a2c222..ba4b39603fe 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -30,7 +30,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { DirtyEditorContext, OpenEditorsGroupContext } from 'vs/workbench/contrib/files/browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index a26bb074b17..bad977ca281 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -7,14 +7,14 @@ import 'vs/css!./media/markers'; import { URI } from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; -import { IAction, IActionItem, Action } from 'vs/base/common/actions'; +import { IAction, IActionViewItem, Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Panel } from 'vs/workbench/browser/panel'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { Marker, ResourceMarkers, RelatedInformation, MarkersModel } from 'vs/workbench/contrib/markers/browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { MarkersFilterActionItem, MarkersFilterAction, IMarkersFilterActionChangeEvent, IMarkerFilterController } from 'vs/workbench/contrib/markers/browser/markersPanelActions'; +import { MarkersFilterActionViewItem, MarkersFilterAction, IMarkersFilterActionChangeEvent, IMarkerFilterController } from 'vs/workbench/contrib/markers/browser/markersPanelActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; @@ -34,7 +34,7 @@ import { deepClone } from 'vs/base/common/objects'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewModel, ResourceDragAndDrop } from 'vs/workbench/contrib/markers/browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Separator, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -74,7 +74,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private actions: IAction[]; private collapseAllAction: IAction; private filterAction: MarkersFilterAction; - private filterInputActionItem: MarkersFilterActionItem; + private filterInputActionViewItem: MarkersFilterActionViewItem; private treeContainer: HTMLElement; private messageBoxContainer: HTMLElement; @@ -152,8 +152,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { public layout(dimension: dom.Dimension): void { this.treeContainer.style.height = `${dimension.height}px`; this.tree.layout(dimension.height, dimension.width); - if (this.filterInputActionItem) { - this.filterInputActionItem.toggleLayout(dimension.width < 1200); + if (this.filterInputActionViewItem) { + this.filterInputActionViewItem.toggleLayout(dimension.width < 1200); } } @@ -166,8 +166,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } public focusFilter(): void { - if (this.filterInputActionItem) { - this.filterInputActionItem.focus(); + if (this.filterInputActionViewItem) { + this.filterInputActionViewItem.focus(); } } @@ -358,8 +358,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { // move focus to input, whenever a key is pressed in the panel container this._register(domEvent(parent, 'keydown')(e => { - if (this.filterInputActionItem && this.keybindingService.mightProducePrintableCharacter(new StandardKeyboardEvent(e))) { - this.filterInputActionItem.focus(); + if (this.filterInputActionViewItem && this.keybindingService.mightProducePrintableCharacter(new StandardKeyboardEvent(e))) { + this.filterInputActionViewItem.focus(); } })); @@ -624,10 +624,10 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor!, getActions: () => this.getMenuActions(element), - getActionItem: (action) => { + getActionViewItem: (action) => { const keybinding = this.keybindingService.lookupKeybinding(action.id); if (keybinding) { - return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); } return undefined; }, @@ -671,12 +671,12 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { return this.tree.getFocus()[0]; } - public getActionItem(action: IAction): IActionItem | undefined { + public getActionViewItem(action: IAction): IActionViewItem | undefined { if (action.id === MarkersFilterAction.ID) { - this.filterInputActionItem = this.instantiationService.createInstance(MarkersFilterActionItem, this.filterAction, this); - return this.filterInputActionItem; + this.filterInputActionViewItem = this.instantiationService.createInstance(MarkersFilterActionViewItem, this.filterAction, this); + return this.filterInputActionViewItem; } - return super.getActionItem(action); + return super.getActionViewItem(action); } getFilterOptions(): FilterOptions { diff --git a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts index 80b5ccd81a2..3d633a1686b 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts @@ -19,7 +19,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachStylerCallback, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; import { IMarkersWorkbenchService } from 'vs/workbench/contrib/markers/browser/markers'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; -import { BaseActionItem, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionViewItem, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; @@ -115,7 +115,7 @@ export interface IMarkerFilterController { getFilterStats(): { total: number, filtered: number }; } -export class MarkersFilterActionItem extends BaseActionItem { +export class MarkersFilterActionViewItem extends BaseActionViewItem { private delayedFilterUpdate: Delayer; private container: HTMLElement; @@ -331,7 +331,7 @@ export class QuickFixAction extends Action { } } -export class QuickFixActionItem extends ActionItem { +export class QuickFixActionViewItem extends ActionViewItem { constructor(action: QuickFixAction, @IContextMenuService private readonly contextMenuService: IContextMenuService, diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 0f9144e224b..235154cc1ff 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -17,7 +17,7 @@ import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { QuickFixAction, QuickFixActionItem } from 'vs/workbench/contrib/markers/browser/markersPanelActions'; +import { QuickFixAction, QuickFixActionViewItem } from 'vs/workbench/contrib/markers/browser/markersPanelActions'; import { ILabelService } from 'vs/platform/label/common/label'; import { dirname, basename, isEqual } from 'vs/base/common/resources'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -253,7 +253,7 @@ class MarkerWidget extends Disposable { ) { super(); this.actionBar = this._register(new ActionBar(dom.append(parent, dom.$('.actions')), { - actionItemProvider: (action) => action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionItem, action) : undefined + actionViewItemProvider: (action) => action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionViewItem, action) : undefined })); this.icon = dom.append(parent, dom.$('.icon')); this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')))); @@ -290,9 +290,9 @@ class MarkerWidget extends Disposable { } }, this, this.disposables); quickFixAction.onShowQuickFixes(() => { - const quickFixActionItem = this.actionBar.items[0]; - if (quickFixActionItem) { - quickFixActionItem.showQuickFixes(); + const quickFixActionViewItem = this.actionBar.viewItems[0]; + if (quickFixActionViewItem) { + quickFixActionViewItem.showQuickFixes(); } }, this, this.disposables); } diff --git a/src/vs/workbench/contrib/output/browser/outputActions.ts b/src/vs/workbench/contrib/output/browser/outputActions.ts index 02bf8197c34..a7d2ae2d3cc 100644 --- a/src/vs/workbench/contrib/output/browser/outputActions.ts +++ b/src/vs/workbench/contrib/output/browser/outputActions.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction, Action } from 'vs/base/common/actions'; import { IOutputService, OUTPUT_PANEL_ID, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelDescriptor, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; -import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; @@ -125,7 +125,7 @@ export class SwitchOutputAction extends Action { } } -export class SwitchOutputActionItem extends SelectActionItem { +export class SwitchOutputActionViewItem extends SelectActionViewItem { private static readonly SEPARATOR = '─────────'; @@ -168,7 +168,7 @@ export class SwitchOutputActionItem extends SelectActionItem { this.logChannels = groups[1] || []; const showSeparator = this.outputChannels.length && this.logChannels.length; const separatorIndex = showSeparator ? this.outputChannels.length : -1; - const options: string[] = [...this.outputChannels.map(c => c.label), ...(showSeparator ? [SwitchOutputActionItem.SEPARATOR] : []), ...this.logChannels.map(c => nls.localize('logChannel', "Log ({0})", c.label))]; + const options: string[] = [...this.outputChannels.map(c => c.label), ...(showSeparator ? [SwitchOutputActionViewItem.SEPARATOR] : []), ...this.logChannels.map(c => nls.localize('logChannel', "Log ({0})", c.label))]; let selected = 0; const activeChannel = this.outputService.getActiveChannel(); if (activeChannel) { diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index 4976e8620ed..7537b9381b6 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/output'; import * as nls from 'vs/nls'; import { Action, IAction } from 'vs/base/common/actions'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -18,7 +18,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/contrib/output/common/output'; -import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction, ToggleOrSetOutputScrollLockAction, OpenLogOutputFile } from 'vs/workbench/contrib/output/browser/outputActions'; +import { SwitchOutputAction, SwitchOutputActionViewItem, ClearOutputAction, ToggleOrSetOutputScrollLockAction, OpenLogOutputFile } from 'vs/workbench/contrib/output/browser/outputActions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -75,12 +75,12 @@ export class OutputPanel extends AbstractTextResourceEditor { return this.actions; } - public getActionItem(action: Action): IActionItem | undefined { + public getActionViewItem(action: Action): IActionViewItem | undefined { if (action.id === SwitchOutputAction.ID) { - return this.instantiationService.createInstance(SwitchOutputActionItem, action); + return this.instantiationService.createInstance(SwitchOutputActionViewItem, action); } - return super.getActionItem(action); + return super.getActionViewItem(action); } protected getConfigurationOverrides(): IEditorOptions { diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index faf67123d19..aa8da6134ca 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -9,7 +9,7 @@ import { Delayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; import { OS } from 'vs/base/common/platform'; import { dispose, Disposable, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; -import { CheckboxActionItem } from 'vs/base/browser/ui/checkbox/checkbox'; +import { CheckboxActionViewItem } from 'vs/base/browser/ui/checkbox/checkbox'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { IAction, Action } from 'vs/base/common/actions'; @@ -372,12 +372,12 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.actionBar = this._register(new ActionBar(this.actionsContainer, { animated: false, - actionItemProvider: (action: Action) => { + actionViewItemProvider: (action: Action) => { if (action.id === this.sortByPrecedenceAction.id) { - return new CheckboxActionItem(null, action); + return new CheckboxActionViewItem(null, action); } if (action.id === this.recordKeysAction.id) { - return new CheckboxActionItem(null, action); + return new CheckboxActionViewItem(null, action); } return undefined; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 570e96d97cf..f93d3624048 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -5,7 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionBar, ActionsOrientation, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, ActionsOrientation, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { Widget } from 'vs/base/browser/ui/widget'; import { Action, IAction } from 'vs/base/common/actions'; @@ -291,7 +291,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { } } -export class FolderSettingsActionItem extends BaseActionItem { +export class FolderSettingsActionViewItem extends BaseActionViewItem { private _folder: IWorkspaceFolder | null; private _folderSettingCounts = new Map(); @@ -427,7 +427,7 @@ export class FolderSettingsActionItem extends BaseActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => this.container, getActions: () => this.getDropdownMenuActions(), - getActionItem: () => undefined, + getActionViewItem: () => undefined, onHide: () => { this.anchorElement.blur(); } @@ -479,7 +479,7 @@ export class SettingsTargetsWidget extends Widget { private userLocalSettings: Action; private userRemoteSettings: Action; private workspaceSettings: Action; - private folderSettings: FolderSettingsActionItem; + private folderSettings: FolderSettingsActionViewItem; private options: ISettingsTargetsWidgetOptions; private _settingsTarget: SettingsTarget; @@ -508,7 +508,7 @@ export class SettingsTargetsWidget extends Widget { orientation: ActionsOrientation.HORIZONTAL, ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"), animated: false, - actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined + actionViewItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined })); this.userLocalSettings = new Action('userSettings', localize('userSettings', "User"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL)); @@ -525,7 +525,7 @@ export class SettingsTargetsWidget extends Widget { this.workspaceSettings.tooltip = this.workspaceSettings.label; const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder.uri)); - this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction); + this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionViewItem, folderSettingsAction); this.update(); diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index adec1c2ae4b..d1531247ed7 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -35,11 +35,11 @@ import { peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekV import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Action, IAction, ActionRunner } from 'vs/base/common/actions'; -import { IActionBarOptions, ActionsOrientation, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionBarOptions, ActionsOrientation, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { basename } from 'vs/base/common/resources'; import { MenuId, IMenuService, IMenu, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { MenuItemActionItem, fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IChange, IEditorModel, ScrollType, IEditorContribution, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { OverviewRulerLane, ITextModel, IModelDecorationOptions } from 'vs/editor/common/model'; import { sortedDiff, firstIndex } from 'vs/base/common/arrays'; @@ -50,21 +50,6 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { INotificationService } from 'vs/platform/notification/common/notification'; import { createStyleSheet } from 'vs/base/browser/dom'; -// TODO@Joao -// Need to subclass MenuItemActionItem in order to respect -// the action context coming from any action bar, without breaking -// existing users -class DiffMenuItemActionItem extends MenuItemActionItem { - - onClick(event: MouseEvent): void { - event.preventDefault(); - event.stopPropagation(); - - this.actionRunner.run(this._commandAction, this._context) - .then(undefined, err => this._notificationService.error(err)); - } -} - class DiffActionRunner extends ActionRunner { runAction(action: IAction, context: any): Promise { @@ -289,17 +274,17 @@ class DirtyDiffWidget extends PeekViewWidget { return { actionRunner, - actionItemProvider: action => this.getActionItem(action), + actionViewItemProvider: action => this.getActionViewItem(action), orientation: ActionsOrientation.HORIZONTAL_REVERSE }; } - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { if (!(action instanceof MenuItemAction)) { return undefined; } - return new DiffMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); } protected _fillBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/scm/browser/scmMenus.ts b/src/vs/workbench/contrib/scm/browser/scmMenus.ts index cac3550b561..6234e5b1eff 100644 --- a/src/vs/workbench/contrib/scm/browser/scmMenus.ts +++ b/src/vs/workbench/contrib/scm/browser/scmMenus.ts @@ -9,7 +9,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; -import { fillInContextMenuActions, fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInContextMenuActions, fillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ISCMProvider, ISCMResource, ISCMResourceGroup } from 'vs/workbench/contrib/scm/common/scm'; import { isSCMResource } from './scmUtil'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 0b0cba6c374..caa82d3520f 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -24,10 +24,10 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MenuItemAction, IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; -import { IAction, Action, IActionItem, ActionRunner } from 'vs/base/common/actions'; -import { fillInContextMenuActions, ContextAwareMenuItemActionItem, fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { IAction, Action, IActionViewItem, ActionRunner } from 'vs/base/common/actions'; +import { fillInContextMenuActions, ContextAwareMenuEntryActionViewItem, fillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { SCMMenus } from './scmMenus'; -import { ActionBar, IActionItemProvider, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionViewItemProvider, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; import { isSCMResource } from './scmUtil'; import { attachBadgeStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; @@ -94,7 +94,7 @@ class StatusBarAction extends Action { } } -class StatusBarActionItem extends ActionItem { +class StatusBarActionViewItem extends ActionViewItem { constructor(action: StatusBarAction) { super(null, action, {}); @@ -160,7 +160,7 @@ class ProviderRenderer implements IListRenderer new StatusBarActionItem(a as StatusBarAction) }); + const actionBar = new ActionBar(provider, { actionViewItemProvider: a => new StatusBarActionViewItem(a as StatusBarAction) }); const disposable = Disposable.None; const templateDisposable = combinedDisposable([actionBar, badgeStyler]); @@ -366,7 +366,7 @@ class ResourceGroupRenderer implements IListRenderer constructor( private labels: ResourceLabels, - private actionItemProvider: IActionItemProvider, + private actionViewItemProvider: IActionViewItemProvider, private getSelectedResources: () => ISCMResource[], private themeService: IThemeService, private menus: SCMMenus @@ -466,7 +466,7 @@ class ResourceRenderer implements IListRenderer const fileLabel = this.labels.create(name); const actionsContainer = append(fileLabel.element, $('.actions')); const actionBar = new ActionBar(actionsContainer, { - actionItemProvider: this.actionItemProvider, + actionViewItemProvider: this.actionViewItemProvider, actionRunner: new MultipleSelectionActionRunner(this.getSelectedResources) }); @@ -827,14 +827,14 @@ export class RepositoryPanel extends ViewletPanel { const delegate = new ProviderListDelegate(); - const actionItemProvider = (action: IAction) => this.getActionItem(action); + const actionViewItemProvider = (action: IAction) => this.getActionViewItem(action); this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); this.disposables.push(this.listLabels); const renderers = [ - new ResourceGroupRenderer(actionItemProvider, this.themeService, this.menus), - new ResourceRenderer(this.listLabels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) + new ResourceGroupRenderer(actionViewItemProvider, this.themeService, this.menus), + new ResourceRenderer(this.listLabels, actionViewItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) ]; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { @@ -918,12 +918,12 @@ export class RepositoryPanel extends ViewletPanel { return this.menus.getTitleSecondaryActions(); } - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { if (!(action instanceof MenuItemAction)) { return undefined; } - return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); } getActionsContext(): any { @@ -1227,12 +1227,12 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { } } - getActionItem(action: IAction): IActionItem | undefined { + getActionViewItem(action: IAction): IActionViewItem | undefined { if (!(action instanceof MenuItemAction)) { return undefined; } - return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); } getActions(): IAction[] { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index b62e0cce8b6..3cdbb2c79d6 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -23,7 +23,7 @@ import 'vs/css!./media/searchview'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as nls from 'vs/nls'; -import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 1082a570790..8f33f667a3f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -8,7 +8,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; -import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -726,7 +726,7 @@ export class SwitchTerminalAction extends Action { } } -export class SwitchTerminalActionItem extends SelectActionItem { +export class SwitchTerminalActionViewItem extends SelectActionViewItem { constructor( action: IAction, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 780147cb952..01d79e0c592 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; import { Action, IAction } from 'vs/base/common/actions'; -import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -16,7 +16,7 @@ import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/termin import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { editorHoverBackground, editorHoverBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionViewItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { Panel } from 'vs/workbench/browser/panel'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { URI } from 'vs/base/common/uri'; @@ -161,12 +161,12 @@ export class TerminalPanel extends Panel { return this._contextMenuActions; } - public getActionItem(action: Action): IActionItem | undefined { + public getActionViewItem(action: Action): IActionViewItem | undefined { if (action.id === SwitchTerminalAction.ID) { - return this._instantiationService.createInstance(SwitchTerminalActionItem, action); + return this._instantiationService.createInstance(SwitchTerminalActionViewItem, action); } - return super.getActionItem(action); + return super.getActionViewItem(action); } public focus(): void { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 6141ad0cfee..c3d153bcb44 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -26,7 +26,7 @@ import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { fillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressService.test.ts index 58ac7214e7a..205b49e082e 100644 --- a/src/vs/workbench/services/progress/test/progressService.test.ts +++ b/src/vs/workbench/services/progress/test/progressService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IAction, IActionItem } from 'vs/base/common/actions'; +import { IAction, IActionViewItem } from 'vs/base/common/actions'; import { IEditorControl } from 'vs/workbench/common/editor'; import { ScopedProgressService, ScopedService } from 'vs/workbench/services/progress/browser/progressService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -51,7 +51,7 @@ class TestViewlet implements IViewlet { /** * Returns the action item for a specific action. */ - getActionItem(action: IAction): IActionItem { + getActionViewItem(action: IAction): IActionViewItem { return null!; } From 4f0b658c244991256c84c13e6c1c9d35896cc815 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 15 May 2019 10:51:19 -0700 Subject: [PATCH 68/99] Make simple file dialog setting a real setting Fixes #73783 --- src/vs/workbench/browser/workbench.contribution.ts | 5 +++++ .../workbench/services/dialogs/browser/fileDialogService.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 696f351bc09..e9cf5a91312 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -239,6 +239,11 @@ import { isMacintosh } from 'vs/base/common/platform'; 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), 'default': false, 'scope': ConfigurationScope.APPLICATION + }, + 'workbench.simpleFileDialog.enable': { + 'type': 'boolean', + 'description': nls.localize('workbench.simpleFileDialog.enable', "Enables the simple file dialog. The simple file dialog replaces the system file dialog when enabled."), + 'default': false, } } }); diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index 4d50a0b8ff0..b54e64c6e21 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -85,8 +85,8 @@ export class FileDialogService implements IFileDialogService { } private shouldUseSimplified(schema: string): boolean { - const setting = this.configurationService.getValue('workbench.dialogs.useSimplified'); - return (schema !== Schemas.file) || ((setting === 'true') || (setting === true)); + const setting = this.configurationService.getValue('workbench.simpleFileDialog.enable'); + return (schema !== Schemas.file) || (setting === true); } private ensureFileSchema(schema: string): string[] { From 10477a98967b2a65e120c78c09aae947112e56a3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 15 May 2019 11:11:26 -0700 Subject: [PATCH 69/99] Log every native call to node-pty Part of #71966 --- .../api/node/extHostTerminalService.ts | 2 +- .../terminal/browser/terminalProcessManager.ts | 1 - .../electron-browser/terminalInstanceService.ts | 2 +- .../contrib/terminal/node/terminalProcess.ts | 17 ++++++++++++++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index bb9505c65f9..7bf6960304c 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -504,7 +504,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Fork the process and listen for messages this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); - const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty') as boolean); + const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty') as boolean, this._logService); p.onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid)); p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); p.onProcessData(data => this._proxy.$sendProcessData(id, data)); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index bced2593716..33079c36940 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -181,7 +181,6 @@ export class TerminalProcessManager implements ITerminalProcessManager { const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables); - this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env); const useConpty = this._configHelper.config.windowsEnableConpty; return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index 812db3a7073..53445235342 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -54,7 +54,7 @@ export class TerminalInstanceService implements ITerminalInstanceService { } public createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess { - return new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, windowsEnableConpty); + return this._instantiationService.createInstance(TerminalProcess, shellLaunchConfig, cwd, cols, rows, env, windowsEnableConpty); } public getDefaultShell(p: Platform): string { diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index eb250416998..128576e59fe 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -13,6 +13,7 @@ import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/termin import { IDisposable } from 'vs/base/common/lifecycle'; import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { exec } from 'child_process'; +import { ILogService } from 'vs/platform/log/common/log'; export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _exitCode: number; @@ -39,7 +40,8 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { cols: number, rows: number, env: platform.IProcessEnvironment, - windowsEnableConpty: boolean + windowsEnableConpty: boolean, + @ILogService private readonly _logService: ILogService ) { let shellName: string; if (os.platform() === 'win32') { @@ -82,7 +84,9 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { } private setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): void { - const ptyProcess = pty.spawn(shellLaunchConfig.executable!, shellLaunchConfig.args || [], options); + const args = shellLaunchConfig.args || []; + this._logService.trace('IPty#spawn', shellLaunchConfig.executable, args, options); + const ptyProcess = pty.spawn(shellLaunchConfig.executable!, args, options); this._ptyProcess = ptyProcess; this._processStartupComplete = new Promise(c => { this.onProcessIdReady(() => c()); @@ -150,6 +154,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { // point but we want to make sure try { if (this._ptyProcess) { + this._logService.trace('IPty#kill'); this._ptyProcess.kill(); } } catch (ex) { @@ -184,6 +189,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { if (this._isDisposed || !this._ptyProcess) { return; } + this._logService.trace('IPty#write', data); this._ptyProcess.write(data); } @@ -194,7 +200,10 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { // Ensure that cols and rows are always >= 1, this prevents a native // exception in winpty. if (this._ptyProcess) { - this._ptyProcess.resize(Math.max(cols, 1), Math.max(rows, 1)); + cols = Math.max(cols, 1); + rows = Math.max(rows, 1); + this._logService.trace('IPty#resize', cols, rows); + this._ptyProcess.resize(cols, rows); } } @@ -209,6 +218,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { resolve(this._initialCwd); return; } + this._logService.trace('IPty#pid'); exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => { if (stdout !== '') { resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1)); @@ -223,6 +233,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { resolve(this._initialCwd); return; } + this._logService.trace('IPty#pid'); fs.readlink('/proc/' + this._ptyProcess.pid + '/cwd', (err, linkedstr) => { if (err) { resolve(this._initialCwd); From c759b3e0dc4fccb25e26811084ea1242f9dc137a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 15 May 2019 11:37:44 -0700 Subject: [PATCH 70/99] Fix quick pick spacing between input box and list Fixes #73732 --- src/vs/workbench/browser/parts/quickinput/quickInput.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.css b/src/vs/workbench/browser/parts/quickinput/quickInput.css index af9ece2c2df..39c4a74b246 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.css +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.css @@ -119,6 +119,7 @@ .quick-input-list { line-height: 22px; + margin-top: 6px; } .quick-input-list .monaco-list { From 376061acc43cc38f3027fe07d937b6517b459d18 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 May 2019 11:42:43 -0700 Subject: [PATCH 71/99] debt - less type cases --- src/vs/base/browser/browser.ts | 12 +++++------ src/vs/base/browser/ui/actionbar/actionbar.ts | 2 +- .../browser/ui/contextview/contextview.ts | 16 +++++++-------- src/vs/base/browser/ui/inputbox/inputBox.ts | 2 +- .../browser/ui/selectBox/selectBoxCustom.ts | 20 +++++++++---------- src/vs/base/browser/ui/splitview/splitview.ts | 4 ++-- src/vs/base/common/console.ts | 2 +- src/vs/base/common/strings.ts | 2 +- src/vs/base/parts/ipc/common/ipc.net.ts | 2 +- src/vs/code/electron-main/main.ts | 2 +- src/vs/platform/list/browser/listService.ts | 6 +++--- src/vs/platform/registry/common/platform.ts | 2 +- src/vs/workbench/browser/editor.ts | 4 ++-- src/vs/workbench/browser/layout.ts | 4 ++-- .../parts/activitybar/activitybarPart.ts | 12 +++++------ .../browser/parts/editor/editorActions.ts | 2 +- .../browser/parts/editor/editorStatus.ts | 8 ++++---- .../browser/parts/editor/sideBySideEditor.ts | 4 ++-- .../browser/parts/panel/panelPart.ts | 6 +++--- src/vs/workbench/common/editor.ts | 3 +-- .../common/editor/dataUriEditorInput.ts | 6 ++---- .../common/editor/diffEditorInput.ts | 2 +- .../common/editor/resourceEditorInput.ts | 6 ++---- .../common/editor/untitledEditorInput.ts | 6 ++---- .../configuration/browser/configuration.ts | 2 +- .../browser/configurationService.ts | 2 +- .../electron-browser/contextmenuService.ts | 2 +- .../textfile/common/textFileService.ts | 4 ++-- 28 files changed, 68 insertions(+), 77 deletions(-) diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 16414ef34d3..6a0cc217897 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -45,13 +45,13 @@ class WindowManager { // --- Pixel Ratio public getPixelRatio(): number { - let ctx = document.createElement('canvas').getContext('2d'); + let ctx: any = document.createElement('canvas').getContext('2d'); let dpr = window.devicePixelRatio || 1; - let bsr = (ctx).webkitBackingStorePixelRatio || - (ctx).mozBackingStorePixelRatio || - (ctx).msBackingStorePixelRatio || - (ctx).oBackingStorePixelRatio || - (ctx).backingStorePixelRatio || 1; + let bsr = ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1; return dpr / bsr; } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index f39e9903a0c..131c756a817 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -787,7 +787,7 @@ export class ActionBar extends Disposable implements IActionRunner { private cancel(): void { if (document.activeElement instanceof HTMLElement) { - (document.activeElement).blur(); // remove focus from focused action + document.activeElement.blur(); // remove focus from focused action } this._onDidCancel.fire(); diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 193359ecc21..02ead6f7eaf 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -132,13 +132,13 @@ export class ContextView extends Disposable { ContextView.BUBBLE_UP_EVENTS.forEach(event => { toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { - this.onDOMEvent(e, document.activeElement, false); + this.onDOMEvent(e, false); })); }); ContextView.BUBBLE_DOWN_EVENTS.forEach(event => { toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { - this.onDOMEvent(e, document.activeElement, true); + this.onDOMEvent(e, true); }, true)); }); @@ -213,13 +213,11 @@ export class ContextView extends Disposable { height: elementPosition.height }; } else { - let realAnchor = anchor; - around = { - top: realAnchor.y, - left: realAnchor.x, - width: realAnchor.width || 1, - height: realAnchor.height || 2 + top: anchor.y, + left: anchor.x, + width: anchor.width || 1, + height: anchor.height || 2 }; } @@ -277,7 +275,7 @@ export class ContextView extends Disposable { return !!this.delegate; } - private onDOMEvent(e: Event, element: HTMLElement, onCapture: boolean): void { + private onDOMEvent(e: Event, onCapture: boolean): void { if (this.delegate) { if (this.delegate.onDOMEvent) { this.delegate.onDOMEvent(e, document.activeElement); diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 83e914e0012..7f79949da48 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -150,7 +150,7 @@ export class InputBox extends Widget { let tagName = this.options.flexibleHeight ? 'textarea' : 'input'; let wrapper = dom.append(this.element, $('.wrapper')); - this.input = dom.append(wrapper, $(tagName + '.input')); + this.input = dom.append(wrapper, $(tagName + '.input')); this.input.setAttribute('autocorrect', 'off'); this.input.setAttribute('autocapitalize', 'off'); this.input.setAttribute('spellcheck', 'false'); diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 8908d68e309..d7ed35c9239 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -38,8 +38,8 @@ class SelectListRenderer implements IListRendererObject.create(null); + renderTemplate(container: HTMLElement): ISelectListTemplateData { + const data: ISelectListTemplateData = Object.create(null); data.disposables = []; data.root = container; data.text = dom.append(container, $('.option-text')); @@ -51,10 +51,10 @@ class SelectListRenderer implements IListRenderertemplateData; - const text = (element).text; - const decoratorRight = (element).decoratorRight; - const isDisabled = (element).isDisabled; + const data: ISelectListTemplateData = templateData; + const text = element.text; + const decoratorRight = element.decoratorRight; + const isDisabled = element.isDisabled; data.text.textContent = text; data.decoratorRight.innerText = (!!decoratorRight ? decoratorRight : ''); @@ -68,10 +68,10 @@ class SelectListRenderer implements IListRendererdata.root), 'option-disabled'); + dom.addClass(data.root, 'option-disabled'); } else { // Make sure we do class removal from prior template rendering - dom.removeClass((data.root), 'option-disabled'); + dom.removeClass(data.root, 'option-disabled'); } } @@ -820,9 +820,9 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { for (let i = 0; i < element.childNodes.length; i++) { - const child = element.childNodes.item(i); + const child = element.childNodes.item(i); - const tagName = (child).tagName && (child).tagName.toLowerCase(); + const tagName = child.tagName && child.tagName.toLowerCase(); if (tagName === 'img') { element.removeChild(child); } else { diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index b4142731b13..48b0cef6866 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -234,8 +234,8 @@ export class SplitView extends Disposable { }); const sashEventMapper = this.orientation === Orientation.VERTICAL - ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey } as ISashEvent) - : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey } as ISashEvent); + ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey }) + : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey }); const onStart = Event.map(sash.onDidStart, sashEventMapper); const onStartDisposable = onStart(this.onSashStart, this); diff --git a/src/vs/base/common/console.ts b/src/vs/base/common/console.ts index 2ac39e3d707..ccd30d07a1e 100644 --- a/src/vs/base/common/console.ts +++ b/src/vs/base/common/console.ts @@ -79,7 +79,7 @@ export function getFirstFrame(arg0: IRemoteConsoleLog | string | undefined): ISt uri: URI.file(matches[1]), line: Number(matches[2]), column: Number(matches[3]) - } as IStackFrame; + }; } } diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 37c57c26f1c..d4397d3279d 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -233,7 +233,7 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean { // We check against an empty string. If the regular expression doesn't advance // (e.g. ends in an endless loop) it will match an empty string. const match = regexp.exec(''); - return !!(match && regexp.lastIndex === 0); + return !!(match && regexp.lastIndex === 0); } export function regExpContainsBackreference(regexpValue: string): boolean { diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 873c0a8d630..f22f6c0aa73 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -218,7 +218,7 @@ class ProtocolReader extends Disposable { // save new state => next time will read the body this._state.readHead = false; this._state.readLen = buff.readUInt32BE(9); - this._state.messageType = buff.readUInt8(0); + this._state.messageType = buff.readUInt8(0); this._state.id = buff.readUInt32BE(1); this._state.ack = buff.readUInt32BE(5); } else { diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 76d042d1e91..e356b0463ef 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -55,7 +55,7 @@ function setupIPC(accessor: ServicesAccessor): Promise { logService.trace('Sending some foreground love to the running instance:', processId); try { - const { allowSetForegroundWindow } = require.__$__nodeRequire('windows-foreground-love'); + const { allowSetForegroundWindow } = require.__$__nodeRequire('windows-foreground-love'); allowSetForegroundWindow(processId); } catch (e) { // noop diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 73704291d8e..d9d37956a76 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -266,7 +266,7 @@ export class WorkbenchList extends List { ...computeStyles(themeService.getTheme(), defaultListStyles), ...workbenchListOptions, horizontalScrolling - } as IListOptions + } ); this.disposables.push(workbenchListOptionsDisposable); @@ -348,7 +348,7 @@ export class WorkbenchPagedList extends PagedList { ...computeStyles(themeService.getTheme(), defaultListStyles), ...workbenchListOptions, horizontalScrolling - } as IListOptions + } ); this.disposables = [workbenchListOptionsDisposable]; @@ -689,7 +689,7 @@ export class TreeResourceNavigator2 extends Disposable { super(); this.options = { - ...{ + ...{ openOnSelection: true }, ...(options || {}) diff --git a/src/vs/platform/registry/common/platform.ts b/src/vs/platform/registry/common/platform.ts index 71c3909e02b..a81c001af82 100644 --- a/src/vs/platform/registry/common/platform.ts +++ b/src/vs/platform/registry/common/platform.ts @@ -54,4 +54,4 @@ class RegistryImpl implements IRegistry { } } -export const Registry = new RegistryImpl(); +export const Registry: IRegistry = new RegistryImpl(); diff --git a/src/vs/workbench/browser/editor.ts b/src/vs/workbench/browser/editor.ts index 5778b3ff65e..f65ab63ac54 100644 --- a/src/vs/workbench/browser/editor.ts +++ b/src/vs/workbench/browser/editor.ts @@ -77,7 +77,7 @@ export class EditorDescriptor implements IEditorDescriptor { } describes(obj: unknown): boolean { - return obj instanceof BaseEditor && (obj).getId() === this.id; + return obj instanceof BaseEditor && obj.getId() === this.id; } } @@ -108,7 +108,7 @@ class EditorRegistry implements IEditorRegistry { const matchingDescriptors: EditorDescriptor[] = []; for (const editor of this.editors) { - const inputDescriptors = []>editor[INPUT_DESCRIPTORS_PROPERTY]; + const inputDescriptors: SyncDescriptor[] = editor[INPUT_DESCRIPTORS_PROPERTY]; for (const inputDescriptor of inputDescriptors) { const inputClass = inputDescriptor.ctor; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 647c20203c0..313743ea6e3 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -10,7 +10,7 @@ import { onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/brow import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { IUntitledResourceInput, pathsToEditors } from 'vs/workbench/common/editor'; +import { pathsToEditors } from 'vs/workbench/common/editor'; import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; @@ -425,7 +425,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return []; // do not open any empty untitled file if we have backups to restore } - return [{}]; + return [Object.create(null)]; // open empty untitled file }); } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 897dca453ab..2b4586e1c88 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -87,7 +87,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } } - this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, this.cachedViewlets.map(v => ({ id: v.id, name: undefined, visible: v.visible, order: v.order, pinned: v.pinned })), { + this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, this.cachedViewlets.map(v => ({ id: v.id, name: undefined, visible: v.visible, order: v.order, pinned: v.pinned })), { icon: true, orientation: ActionsOrientation.VERTICAL, openComposite: (compositeId: string) => this.viewletService.openViewlet(compositeId, true), @@ -218,7 +218,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private getActivitybarItemColors(theme: ITheme): ICompositeBarColors { - return { + return { activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND), inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND), badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), @@ -446,9 +446,9 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private getCachedViewlets(): ICachedViewlet[] { - const storedStates = >JSON.parse(this.cachedViewletsValue); - const cachedViewlets = storedStates.map(c => { - const serialized: ICachedViewlet = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true, iconUrl: undefined, views: undefined } : c; + const storedStates: Array = JSON.parse(this.cachedViewletsValue); + const cachedViewlets = storedStates.map(c => { + const serialized: ICachedViewlet = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true, iconUrl: undefined, views: undefined } : c; serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible; return serialized; }); @@ -466,7 +466,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private loadOldCachedViewlets(): ICachedViewlet[] { const previousState = this.storageService.get('workbench.activity.placeholderViewlets', StorageScope.GLOBAL, '[]'); - const result = (JSON.parse(previousState)); + const result: ICachedViewlet[] = JSON.parse(previousState); this.storageService.remove('workbench.activity.placeholderViewlets', StorageScope.GLOBAL); return result; diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 1ae07b4a378..1d6d3e15151 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -449,7 +449,7 @@ export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry | nu // QuickOpenEntryGroup if (element instanceof QuickOpenEntryGroup) { - const group = element; + const group = element; if (group.getEntry()) { element = group.getEntry(); } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 5bc68a04a6f..48bbdff9a7f 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -811,7 +811,7 @@ export class EditorStatus implements IStatusbarItem { if (activeControl) { const activeResource = toResource(activeControl.input, { supportSideBySide: SideBySideEditor.MASTER }); if (activeResource && activeResource.toString() === resource.toString()) { - return this.onEncodingChange(activeControl); // only update if the encoding changed for the active resource + return this.onEncodingChange(activeControl); // only update if the encoding changed for the active resource } } } @@ -924,7 +924,7 @@ export class ChangeModeAction extends Action { } } - return { + return { label: lang, iconClasses: getIconClasses(this.modelService, this.modeService, fakeResource), description @@ -1021,9 +1021,9 @@ export class ChangeModeAction extends Action { const languages = this.modeService.getRegisteredLanguageNames(); const picks: IQuickPickItem[] = languages.sort().map((lang, index) => { - const id = this.modeService.getModeIdForLanguageName(lang.toLowerCase()); + const id = withNullAsUndefined(this.modeService.getModeIdForLanguageName(lang.toLowerCase())); - return { + return { id, label: lang, description: (id === currentAssociation) ? nls.localize('currentAssociation', "Current Association") : undefined diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 90650e7cdff..54a09e72921 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -177,8 +177,8 @@ export class SideBySideEditor extends BaseEditor { } private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Promise { - const detailsEditor = this.doCreateEditor(newInput.details, this.detailsEditorContainer); - const masterEditor = this.doCreateEditor(newInput.master, this.masterEditorContainer); + const detailsEditor = this.doCreateEditor(newInput.details, this.detailsEditorContainer); + const masterEditor = this.doCreateEditor(newInput.master, this.masterEditorContainer); return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 4120e525cec..ba440bfb2c2 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -394,9 +394,9 @@ export class PanelPart extends CompositePart implements IPanelService { private getCachedPanels(): ICachedPanel[] { const registeredPanels = this.getPanels(); - const storedStates = >JSON.parse(this.cachedPanelsValue); - const cachedPanels = storedStates.map(c => { - const serialized: ICachedPanel = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true } : c; + const storedStates: Array = JSON.parse(this.cachedPanelsValue); + const cachedPanels = storedStates.map(c => { + const serialized: ICachedPanel = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true } : c; const registered = registeredPanels.some(p => p.id === serialized.id); serialized.visible = registered ? isUndefinedOrNull(serialized.visible) ? true : serialized.visible : false; return serialized; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index bb8cc56f95a..dd7224c579d 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -636,8 +636,7 @@ export class SideBySideEditorInput extends EditorInput { return false; } - const otherDiffInput = otherInput; - return this.details.matches(otherDiffInput.details) && this.master.matches(otherDiffInput.master); + return this.details.matches(otherInput.details) && this.master.matches(otherInput.master); } return false; diff --git a/src/vs/workbench/common/editor/dataUriEditorInput.ts b/src/vs/workbench/common/editor/dataUriEditorInput.ts index 9b154d2783f..7c82702e21c 100644 --- a/src/vs/workbench/common/editor/dataUriEditorInput.ts +++ b/src/vs/workbench/common/editor/dataUriEditorInput.ts @@ -68,11 +68,9 @@ export class DataUriEditorInput extends EditorInput { return true; } + // Compare by resource if (otherInput instanceof DataUriEditorInput) { - const otherDataUriEditorInput = otherInput; - - // Compare by resource - return otherDataUriEditorInput.resource.toString() === this.resource.toString(); + return otherInput.resource.toString() === this.resource.toString(); } return false; diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index b8ed8c9ec0f..b962443bd7e 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -67,7 +67,7 @@ export class DiffEditorInput extends SideBySideEditorInput { // If both are text models, return textdiffeditor model if (modifiedEditorModel instanceof BaseTextEditorModel && originalEditorModel instanceof BaseTextEditorModel) { - return new TextDiffEditorModel(originalEditorModel, modifiedEditorModel); + return new TextDiffEditorModel(originalEditorModel, modifiedEditorModel); } // Otherwise return normal diff model diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index aad672b42e5..6ff63c0a617 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -108,11 +108,9 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { return true; } + // Compare by properties if (otherInput instanceof ResourceEditorInput) { - const otherResourceEditorInput = otherInput; - - // Compare by properties - return otherResourceEditorInput.resource.toString() === this.resource.toString(); + return otherInput.resource.toString() === this.resource.toString(); } return false; diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index 37bd6eab9af..585543b6560 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -232,11 +232,9 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport return true; } + // Otherwise compare by properties if (otherInput instanceof UntitledEditorInput) { - const otherUntitledEditorInput = otherInput; - - // Otherwise compare by properties - return otherUntitledEditorInput.resource.toString() === this.resource.toString(); + return otherInput.resource.toString() === this.resource.toString(); } return false; diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 1c107e7d785..b091f4451ff 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -666,7 +666,7 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati private readonly configurationCache: IConfigurationCache ) { super(); - this.key = createSHA1(join(folder.path, configFolderRelativePath)).then(key => ({ type: 'folder', key })); + this.key = createSHA1(join(folder.path, configFolderRelativePath)).then(key => ({ type: 'folder', key })); this.configurationModel = new ConfigurationModel(); } diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 547484bfb5d..a8b346d524d 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -396,7 +396,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private compareFolders(currentFolders: IWorkspaceFolder[], newFolders: IWorkspaceFolder[]): IWorkspaceFoldersChangeEvent { - const result = { added: [], removed: [], changed: [] } as IWorkspaceFoldersChangeEvent; + const result: IWorkspaceFoldersChangeEvent = { added: [], removed: [], changed: [] }; result.added = newFolders.filter(newFolder => !currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString())); for (let currentIndex = 0; currentIndex < currentFolders.length; currentIndex++) { let currentFolder = currentFolders[currentIndex]; diff --git a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts index f4dae2a33c1..ec0319b4d8a 100644 --- a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts @@ -97,7 +97,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService x = elementPosition.left; y = elementPosition.top + elementPosition.height; } else { - const pos = <{ x: number; y: number; }>anchor; + const pos: { x: number; y: number; } = anchor; x = pos.x + 1; /* prevent first item from being selected automatically under mouse */ y = pos.y; } diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index f7884a3e7b2..97f68a63f06 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -740,14 +740,14 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private getFileModels(arg1?: URI | URI[]): ITextFileEditorModel[] { if (Array.isArray(arg1)) { const models: ITextFileEditorModel[] = []; - (arg1).forEach(resource => { + arg1.forEach(resource => { models.push(...this.getFileModels(resource)); }); return models; } - return this._models.getAll(arg1); + return this._models.getAll(arg1); } private getDirtyFileModels(resources?: URI | URI[]): ITextFileEditorModel[] { From 40ae7f0312e051b8fcfac5653a588d4efdd3b396 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 May 2019 11:48:06 -0700 Subject: [PATCH 72/99] debt - inline file constants into pfs --- src/vs/base/node/pfs.ts | 13 ++++++++++++- src/vs/platform/files/node/fileConstants.ts | 15 --------------- .../services/search/node/rawSearchService.ts | 2 +- .../services/textfile/node/textFileService.ts | 3 +-- 4 files changed, 14 insertions(+), 19 deletions(-) delete mode 100644 src/vs/platform/files/node/fileConstants.ts diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 37693ce1042..6e6783f9f1f 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -670,4 +670,15 @@ export async function mkdirp(path: string, mode?: number, token?: CancellationTo // Any other error return Promise.reject(error); } -} \ No newline at end of file +} + +// See https://github.com/Microsoft/vscode/issues/30180 +const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB +const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB + +// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149 +const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB +const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB + +export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE; +export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; \ No newline at end of file diff --git a/src/vs/platform/files/node/fileConstants.ts b/src/vs/platform/files/node/fileConstants.ts deleted file mode 100644 index 629a67e8499..00000000000 --- a/src/vs/platform/files/node/fileConstants.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// See https://github.com/Microsoft/vscode/issues/30180 -const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB -const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB - -// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149 -const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB -const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB - -export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE; -export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 021aa7bb235..13d778c6955 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -16,7 +16,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; -import { MAX_FILE_SIZE } from 'vs/platform/files/node/fileConstants'; +import { MAX_FILE_SIZE } from 'vs/base/node/pfs'; import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery, IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; diff --git a/src/vs/workbench/services/textfile/node/textFileService.ts b/src/vs/workbench/services/textfile/node/textFileService.ts index aa80862877c..2e9fe43b8a2 100644 --- a/src/vs/workbench/services/textfile/node/textFileService.ts +++ b/src/vs/workbench/services/textfile/node/textFileService.ts @@ -11,7 +11,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; import { IFileStatWithMetadata, ICreateFileOptions, FileOperationError, FileOperationResult, IFileStreamContent, IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; -import { exists, stat, chmod, rimraf } from 'vs/base/node/pfs'; +import { exists, stat, chmod, rimraf, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs'; import { join, dirname } from 'vs/base/common/path'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; @@ -26,7 +26,6 @@ import { VSBufferReadable, VSBuffer, VSBufferReadableStream } from 'vs/base/comm import { Readable } from 'stream'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/fileConstants'; import { ITextSnapshot } from 'vs/editor/common/model'; export class NodeTextFileService extends TextFileService { From 45db52ba6bd211cecd03897caee0531350071721 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 May 2019 10:17:43 -0700 Subject: [PATCH 73/99] Update grammars --- extensions/clojure/cgmanifest.json | 4 +- .../clojure/syntaxes/clojure.tmLanguage.json | 18 +- extensions/cpp/cgmanifest.json | 4 +- extensions/cpp/syntaxes/c.tmLanguage.json | 335 +++++- extensions/cpp/syntaxes/cpp.tmLanguage.json | 1052 ++++++++++++----- .../cpp/test/colorize-results/test_c.json | 18 +- .../cpp/test/colorize-results/test_cpp.json | 15 +- extensions/fsharp/cgmanifest.json | 2 +- .../fsharp/syntaxes/fsharp.tmLanguage.json | 69 +- .../fsharp/test/colorize-results/test_fs.json | 171 +-- extensions/handlebars/cgmanifest.json | 4 +- .../syntaxes/Handlebars.tmLanguage.json | 4 +- extensions/java/cgmanifest.json | 4 +- extensions/java/syntaxes/java.tmLanguage.json | 4 +- .../test/colorize-results/test_m.json | 164 ++- .../syntaxes/powershell.tmLanguage.json | 81 +- .../test/colorize-results/test_ps1.json | 80 +- 17 files changed, 1488 insertions(+), 541 deletions(-) diff --git a/extensions/clojure/cgmanifest.json b/extensions/clojure/cgmanifest.json index 1a9d69c5633..3a72fefb369 100644 --- a/extensions/clojure/cgmanifest.json +++ b/extensions/clojure/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "atom/language-clojure", "repositoryUrl": "https://github.com/atom/language-clojure", - "commitHash": "ecc790326bc8e14220e4d2d72a392a30876c3219" + "commitHash": "de877502aa4a77ccdc2c7f0c9180436aea3effff" } }, "license": "MIT", - "version": "0.22.6", + "version": "0.22.7", "description": "The file syntaxes/clojure.tmLanguage.json was derived from the Atom package https://github.com/atom/language-clojure which was originally converted from the TextMate bundle https://github.com/mmcgrana/textmate-clojure." } ], diff --git a/extensions/clojure/syntaxes/clojure.tmLanguage.json b/extensions/clojure/syntaxes/clojure.tmLanguage.json index 3d059513af0..29c25edfa24 100644 --- a/extensions/clojure/syntaxes/clojure.tmLanguage.json +++ b/extensions/clojure/syntaxes/clojure.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-clojure/commit/ecc790326bc8e14220e4d2d72a392a30876c3219", + "version": "https://github.com/atom/language-clojure/commit/de877502aa4a77ccdc2c7f0c9180436aea3effff", "name": "Clojure", "scopeName": "source.clojure", "patterns": [ @@ -83,7 +83,7 @@ "name": "constant.numeric.ratio.clojure" }, { - "match": "(-?\\d+[rR][0-9a-zA-Z]+)", + "match": "(-?\\d+[rR]\\w+)", "name": "constant.numeric.arbitrary-radix.clojure" }, { @@ -116,17 +116,17 @@ ] }, "keyword": { - "match": "(?<=(\\s|\\(|\\[|\\{)):[a-zA-Z0-9\\#\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}|\\,))", + "match": "(?<=(\\s|\\(|\\[|\\{)):[\\w\\#\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}|\\,))", "name": "constant.keyword.clojure" }, "keyfn": { "patterns": [ { - "match": "(?<=(\\s|\\(|\\[|\\{))(if(-[-a-z\\?]*)?|when(-[-a-z]*)?|for(-[-a-z]*)?|cond|do|let(-[-a-z\\?]*)?|binding|loop|recur|fn|throw[a-z\\-]*|try|catch|finally|([a-z]*case))(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{))(if(-[-\\p{Ll}\\?]*)?|when(-[-\\p{Ll}]*)?|for(-[-\\p{Ll}]*)?|cond|do|let(-[-\\p{Ll}\\?]*)?|binding|loop|recur|fn|throw[\\p{Ll}\\-]*|try|catch|finally|([\\p{Ll}]*case))(?=(\\s|\\)|\\]|\\}))", "name": "storage.control.clojure" }, { - "match": "(?<=(\\s|\\(|\\[|\\{))(declare-?|(in-)?ns|import|use|require|load|compile|(def[a-z\\-]*))(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{))(declare-?|(in-)?ns|import|use|require|load|compile|(def[\\p{Ll}\\-]*))(?=(\\s|\\)|\\]|\\}))", "name": "keyword.control.clojure" } ] @@ -309,7 +309,7 @@ "include": "#dynamic-variables" }, { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", "name": "entity.global.clojure" }, { @@ -387,7 +387,7 @@ "namespace-symbol": { "patterns": [ { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)/", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)/", "captures": { "1": { "name": "meta.symbol.namespace.clojure" @@ -399,13 +399,13 @@ "symbol": { "patterns": [ { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", "name": "meta.symbol.clojure" } ] }, "var": { - "match": "(?<=(\\s|\\(|\\[|\\{)\\#)'[a-zA-Z0-9\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{)\\#)'[\\w\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}))", "name": "meta.var.clojure" }, "vector": { diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index aba5bb7f100..a1b745d8a43 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/cpp-textmate-grammar", "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", - "commitHash": "3fa2a8862b6a06ca381f8e46eb782e5dd014d426" + "commitHash": "dc404ccf4eb08a5f76434e759b519f59051a32e5" } }, "license": "MIT", - "version": "1.8.8", + "version": "1.8.13", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/syntaxes/c.tmLanguage.json b/extensions/cpp/syntaxes/c.tmLanguage.json index e02a6d88391..ec2ee36d25d 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/98cbae6aca391825a7612825f9677f22fe70dd68", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/48734a7a8b365c12aeb7551aed0090b49601ed70", "name": "C", "scopeName": "source.c", "patterns": [ @@ -570,13 +570,13 @@ } }, "match": "^// =(\\s*.*?)\\s*=\\s*$\\n?", - "name": "comment.line.banner.cpp.c" + "name": "comment.line.banner.c" }, { "begin": "(^[ \\t]+)?(?=//)", "beginCaptures": { "1": { - "name": "punctuation.whitespace.comment.leading.cpp.c" + "name": "punctuation.whitespace.comment.leading.c" } }, "end": "(?!\\G)", @@ -585,11 +585,11 @@ "begin": "//", "beginCaptures": { "0": { - "name": "punctuation.definition.comment.cpp.c" + "name": "punctuation.definition.comment.c" } }, "end": "(?=\\n)", - "name": "comment.line.double-slash.cpp.c", + "name": "comment.line.double-slash.c", "patterns": [ { "include": "#line_continuation_character" @@ -624,14 +624,6 @@ } ] }, - "numbers": { - "patterns": [ - { - "match": "\\b((0(x|X)[0-9a-fA-F]([0-9a-fA-F']*[0-9a-fA-F])?)|(0(b|B)[01]([01']*[01])?)|(([0-9]([0-9']*[0-9])?\\.?[0-9]*([0-9']*[0-9])?)|(\\.[0-9]([0-9']*[0-9])?))((e|E)(\\+|-)?[0-9]([0-9']*[0-9])?)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b", - "name": "constant.numeric.c" - } - ] - }, "parens": { "name": "meta.parens.c", "begin": "\\(", @@ -672,7 +664,7 @@ }, { "match": "(?-mix:(?\\[\\]=]))", "patterns": [ { - "include": "#switch_conditional_parentheses" - }, - { - "name": "meta.head.switch.cpp.c", - "begin": "\\G| ", + "name": "meta.head.switch.c", + "begin": "\\G ?", "end": "((?:\\{|(?=;)))", "endCaptures": { "1": { - "name": "punctuation.section.block.begin.bracket.curly.switch.cpp.c" + "name": "punctuation.section.block.begin.bracket.curly.switch.c" } }, "patterns": [ @@ -2156,12 +2145,12 @@ ] }, { - "name": "meta.body.switch.cpp.c", + "name": "meta.body.switch.c", "begin": "(?<=\\{)", "end": "(\\})", "endCaptures": { "1": { - "name": "punctuation.section.block.end.bracket.curly.switch.cpp.c" + "name": "punctuation.section.block.end.bracket.curly.switch.c" } }, "patterns": [ @@ -2177,7 +2166,7 @@ ] }, { - "name": "meta.tail.switch.cpp.c", + "name": "meta.tail.switch.c", "begin": "(?<=})[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ @@ -2193,13 +2182,13 @@ "begin": "(\\()", "beginCaptures": { "1": { - "name": "punctuation.section.parens.begin.bracket.round.conditional.switch.cpp.c" + "name": "punctuation.section.parens.begin.bracket.round.conditional.switch.c" } }, "end": "(\\))", "endCaptures": { "1": { - "name": "punctuation.section.parens.end.bracket.round.conditional.switch.cpp.c" + "name": "punctuation.section.parens.end.bracket.round.conditional.switch.c" } }, "patterns": [ @@ -2208,6 +2197,46 @@ } ] }, + "static_assert": { + "begin": "(static_assert|_Static_assert)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.static_assert.c" + }, + "2": { + "name": "punctuation.section.arguments.begin.bracket.round.c" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.c" + } + }, + "patterns": [ + { + "name": "meta.static_assert.message.c", + "begin": "(,)\\s*(?=(?:L|u8|u|U\\s*\\\")?)", + "beginCaptures": { + "1": { + "name": "comma.c punctuation.separator.delimiter.c" + } + }, + "end": "(?=\\))", + "patterns": [ + { + "include": "#string_context" + }, + { + "include": "#string_context_c" + } + ] + }, + { + "include": "#function_call_context_c" + } + ] + }, "conditional_context": { "patterns": [ { @@ -2311,6 +2340,250 @@ "include": "#function-call-innards" } ] + }, + "numbers": { + "begin": "(?\\[\\]=]))", "patterns": [ - { - "include": "#switch_conditional_parentheses" - }, { "name": "meta.head.switch.cpp", - "begin": "\\G| ", + "begin": "\\G ?", "end": "((?:\\{|(?=;)))", "endCaptures": { "1": { @@ -816,7 +892,7 @@ "name": "comma.cpp punctuation.separator.template.argument.cpp" }, "template_call_innards": { - "match": "<(?:[\\s<>:,\\w])*>\\s*", + "match": "(?:,\\w])*>\\s*", "captures": { "0": { "name": "meta.template.call.cpp", @@ -953,7 +1029,7 @@ } }, "scope_resolution": { - "match": "((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:<(?:[\\s<>:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(<(?:[\\s<>:,\\w])*>\\s*))?(::)", + "match": "((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)", "captures": { "1": { "patterns": [ @@ -979,8 +1055,230 @@ }, "name": "meta.scope-resolution.cpp" }, + "qualified_type": { + "match": "\\s*(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.])", + "captures": { + "0": { + "name": "entity.name.type.cpp meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + } + ] + }, + "1": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "2": { + "name": "meta.scope-resolution.cpp" + }, + "3": { + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "4": { + "name": "entity.name.type.namespace.scope-resolution.cpp" + }, + "5": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "6": { + "name": "punctuation.separator.namespace.access.cpp" + } + } + }, + "type_alias": { + "match": "(using)\\s*(?!namespace)(\\s*(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))\\s*(\\=)\\s*(typename)?\\s*((?:(?-mix:(?:(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))|(.+(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(<(?:[\\s<>:,\\w])*>\\s*))?(\\()", + "begin": "(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(\\()", "beginCaptures": { "1": { "patterns": [ @@ -1081,6 +1407,32 @@ } ] }, + "legacy_function_definition": { + "name": "meta.function.definition.parameters.cpp", + "begin": "(?!(?:(?:::|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\.|\\->|\\+\\+|\\-\\-|\\+|\\-|!|not|~|compl|\\*|&|sizeof|sizeof\\.\\.\\.|new|new\\[\\]|delete|delete\\[\\]|\\.\\*|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|not_eq|&|bitand|\\^|xor|\\||bitor|&&|and|\\|\\||or|\\?:|throw|=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|and_eq|\\^=|xor_eq|\\|=|or_eq|,|alignof|alignas|typeid|noexcept|static_cast|dynamic_cast|const_cast|reinterpret_cast)|(?:throw|while|for|do|if|else|goto|switch|try|catch|return|break|case|continue|default))\\s*\\()((?:(?:(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*|::))+|(?<=operator)(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|new|new\\[\\]|delete|delete\\[\\]|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.cpp" + }, + "2": { + "name": "punctuation.section.parameters.begin.bracket.round.cpp" + } + }, + "end": "(\\)|:)", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#probably_a_parameter" + }, + { + "include": "#function_context_c" + } + ] + }, "operators": { "patterns": [ { @@ -1148,8 +1500,7 @@ "name": "keyword.operator.cpp" }, { - "match": "=", - "name": "keyword.operator.assignment.cpp" + "include": "#assignment_operator" }, { "match": "%|\\*|/|-|\\+", @@ -1186,6 +1537,117 @@ } ] }, + "function_pointer": { + "begin": "(\\s*(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))\\s*(((?:\\*\\s*)*)((?:\\&\\s*?){0,2})\\s*)(\\()(\\*)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)?\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.type.cpp meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "3": { + "name": "meta.scope-resolution.cpp" + }, + "4": { + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "5": { + "name": "entity.name.type.namespace.scope-resolution.cpp" + }, + "6": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "7": { + "name": "punctuation.separator.namespace.access.cpp" + }, + "9": { + "name": "storage.modifier.pointer.cpp" + }, + "10": { + "name": "storage.modifier.reference.cpp" + }, + "11": { + "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" + }, + "12": { + "name": "punctuation.definition.function.pointer.dereference.cpp" + }, + "13": { + "name": "variable.other.definition.pointer.function.cpp" + }, + "14": { + "name": "punctuation.definition.begin.bracket.square.cpp" + }, + "15": { + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "16": { + "name": "punctuation.definition.end.bracket.square.cpp" + }, + "17": { + "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" + }, + "18": { + "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" + } + }, + "end": "(\\))\\s*(?=[{=,);]|\\n)(?!\\()", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" + } + }, + "patterns": [ + { + "include": "#parameter_struct" + }, + { + "include": "#probably_a_parameter" + }, + { + "include": "#function_context_c" + } + ] + }, "probably_a_parameter": { "match": "(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?==))|((?<=\\w |\\*\\/|[&*>\\]\\)]|\\.\\.\\.)\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?=(?:\\[\\]\\s*)?(?:,|\\)))))", "captures": { @@ -1199,7 +1661,7 @@ }, "operator_overload": { "name": "meta.function.definition.parameters.operator-overload.cpp", - "begin": "(operator)((?:\\s*(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)|\\s+(?:(?:new|new\\[\\]|delete|delete\\[\\])|(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:<(?:[\\s<>:,\\w])*>\\s*)))?::)*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:&)?)))\\s*(\\()", + "begin": "(operator)((?:\\s*(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)|\\s+(?:(?:new|new\\[\\]|delete|delete\\[\\])|(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?::)*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:&)?)))\\s*(\\()", "beginCaptures": { "1": { "name": "keyword.other.operator.overload.cpp" @@ -1350,7 +1812,7 @@ }, "using_namespace": { "name": "meta.using-namespace.cpp", - "begin": "(?:,\\w])*>\\s*)))?::)*\\s*))?((?:,\\w])*>\\s*)))?::)*\\s*))?((?:,\\w])*>\\s*)))?::)*\\s*)\\s*(?:((?:,\\w])*>\\s*)))?::)*\\s*)\\s*(?:(?:((?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(<(?:[\\s<>:,\\w])*>\\s*))?(::)))?\\s*((?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*((?\\[\\]=]))", @@ -1611,7 +2109,7 @@ "patterns": [ { "name": "meta.head.enum.cpp", - "begin": "\\G| ", + "begin": "\\G ?", "end": "((?:\\{|(?=;)))", "endCaptures": { "1": { @@ -1635,7 +2133,16 @@ }, "patterns": [ { - "include": "$base" + "include": "#enumerator_list" + }, + { + "include": "#comments_context" + }, + { + "include": "#comma" + }, + { + "include": "#semicolon" } ] }, @@ -1662,7 +2169,11 @@ "name": "storage.type.modifier.access.$0.cpp" }, { - "match": "(?<=private|protected|public|,|:)\\s*(?!(?:private|protected|public))((?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))", "captures": { "1": { "name": "entity.name.type.inherited.cpp" @@ -1673,7 +2184,7 @@ }, "class_block": { "name": "meta.block.class.cpp", - "begin": "((((?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))+)*))?))", "beginCaptures": { "1": { "name": "meta.head.class.cpp" @@ -1724,7 +2235,7 @@ "patterns": [ { "name": "meta.head.class.cpp", - "begin": "\\G| ", + "begin": "\\G ?", "end": "((?:\\{|(?=;)))", "endCaptures": { "1": { @@ -1756,6 +2267,9 @@ } }, "patterns": [ + { + "include": "#function_pointer" + }, { "include": "#constructor_context" }, @@ -1778,7 +2292,7 @@ }, "struct_block": { "name": "meta.block.struct.cpp", - "begin": "((((?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))+)*))?))", "beginCaptures": { "1": { "name": "meta.head.struct.cpp" @@ -1829,7 +2343,7 @@ "patterns": [ { "name": "meta.head.struct.cpp", - "begin": "\\G| ", + "begin": "\\G ?", "end": "((?:\\{|(?=;)))", "endCaptures": { "1": { @@ -1861,6 +2375,9 @@ } }, "patterns": [ + { + "include": "#function_pointer" + }, { "include": "#constructor_context" }, @@ -1883,7 +2400,7 @@ }, "union_block": { "name": "meta.block.union.cpp", - "begin": "((((?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))+)*))?))", "beginCaptures": { "1": { "name": "meta.head.union.cpp" @@ -1934,7 +2451,7 @@ "patterns": [ { "name": "meta.head.union.cpp", - "begin": "\\G| ", + "begin": "\\G ?", "end": "((?:\\{|(?=;)))", "endCaptures": { "1": { @@ -1966,6 +2483,9 @@ } }, "patterns": [ + { + "include": "#function_pointer" + }, { "include": "#constructor_context" }, @@ -2009,7 +2529,7 @@ "patterns": [ { "name": "meta.head.extern.cpp", - "begin": "\\G| ", + "begin": "\\G ?", "end": "((?:\\{|(?=;)))", "endCaptures": { "1": { @@ -2417,6 +2937,9 @@ { "include": "#using_namespace" }, + { + "include": "#type_alias" + }, { "include": "#namespace_block" }, @@ -2595,7 +3118,7 @@ ] }, "function_call_c": { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas|constexpr|volatile|operator|(?:::)?new|(?:::)?delete)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*(?-mix:(?:(?-mix:(?:<(?:[\\s<>:,\\w])*>\\s*)))?)\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas|constexpr|volatile|operator|(?:::)?new|(?:::)?delete)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*(?-mix:(?:(?-mix:(?:(?:,\\w])*>\\s*)))?)\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", "end": "(?<=\\))(?!\\w)", "name": "meta.function-call.cpp", "patterns": [ @@ -3886,30 +4409,7 @@ "include": "#vararg_ellipses" }, { - "name": "meta.function.definition.parameters.cpp", - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", - "beginCaptures": { - "1": { - "name": "entity.name.function.cpp" - }, - "2": { - "name": "punctuation.section.parameters.begin.bracket.round.cpp" - } - }, - "end": "(?-mix:\\)|:)", - "endCaptures": { - "0": { - "name": "punctuation.section.parameters.end.bracket.round.cpp" - } - }, - "patterns": [ - { - "include": "#probably_a_parameter" - }, - { - "include": "#function_context_c" - } - ] + "include": "#legacy_function_definition" }, { "begin": "\\(", @@ -3956,7 +4456,7 @@ "include": "#operators" }, { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:new)\\s*((?-mix:(?:(?-mix:(?:<(?:[\\s<>:,\\w])*>\\s*)))?)) # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:new)\\s*((?-mix:(?:(?-mix:(?:(?:,\\w])*>\\s*)))?)) # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "keyword.operator.wordlike.cpp memory.cpp keyword.operator.new.cpp" diff --git a/extensions/cpp/test/colorize-results/test_c.json b/extensions/cpp/test/colorize-results/test_c.json index 5a83a3ac616..74be734c5ca 100644 --- a/extensions/cpp/test/colorize-results/test_c.json +++ b/extensions/cpp/test/colorize-results/test_c.json @@ -848,7 +848,7 @@ }, { "c": "4", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -980,7 +980,7 @@ }, { "c": "0", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1178,7 +1178,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1387,7 +1387,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1717,7 +1717,7 @@ }, { "c": "0", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1860,7 +1860,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2223,7 +2223,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2388,7 +2388,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2762,7 +2762,7 @@ }, { "c": "0", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index d82030e7d00..9702dc50b0b 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -286,8 +286,19 @@ } }, { - "c": "public:", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public:.cpp", + "c": "public", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ":", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public.cpp colon.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index 851e97ca639..d24f6a3d010 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "be0bdfd1e272b6633f5edf1429052fe9fa4df7c1" + "commitHash": "b2100c95d7857c5421d111a860fcdd20954a0263" } }, "license": "MIT", diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 9a9b1b1f2fd..82e6d33548f 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/ionide/ionide-fsgrammar/commit/be0bdfd1e272b6633f5edf1429052fe9fa4df7c1", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/b2100c95d7857c5421d111a860fcdd20954a0263", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -285,6 +285,33 @@ } ] }, + { + "begin": "(\\()", + "end": "(\\))", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "(([?[:alpha:]0-9'`^._ ]+))+", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#tuple_signature" + } + ] + }, { "match": "(?!when|and|or\\b)\\b([\\w0-9'`^._]+)", "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", @@ -571,7 +598,7 @@ "include": "#common_declaration" }, { - "match": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([[:alpha:]0-9'`^._ ]+)){0,1}", + "match": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)((?!with\\b)\\b([\\w0-9'`^._ ]+)){0,1}", "captures": { "1": { "name": "keyword.symbol.fsharp" @@ -772,9 +799,9 @@ } }, { - "begin": "(<(?![[:space:]]*\\)))", + "begin": "(<+(?![[:space:]]*\\)))", "beginComment": "The group (?![[:space:]]*\\) is for protection against overload operator. static member (<)", - "end": "((?)", + "end": "((?|\\))", "endComment": "The group (? when using SRTP synthax", "beginCaptures": { "1": { @@ -814,6 +841,14 @@ { "include": "#definition" }, + { + "match": "(?<=>)\\s*(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, { "include": "#variables" }, @@ -826,7 +861,7 @@ "patterns": [ { "name": "binding.fsharp", - "begin": "\\b(let mutable|static let mutable|let inline|let|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", + "begin": "\\b(let mutable|static let mutable|let inline|let|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", "end": "\\s*(with\\b|=|\\n+=|(?<=\\=))", "beginCaptures": { "1": { @@ -856,6 +891,26 @@ } ] }, + { + "name": "binding.fsharp", + "begin": "\\b((get|set)\\s*(?=\\())(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", + "end": "\\s*(=|\\n+=|(?<=\\=))", + "beginCaptures": { + "3": { + "name": "variable.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "patterns": [ + { + "include": "#common_binding_definition" + } + ] + }, { "name": "binding.fsharp", "begin": "\\b(static val mutable|val mutable|val)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", @@ -920,7 +975,7 @@ } }, { - "match": "([[:alpha:]0-9'`^._]+)|``([[:alpha:]0-9'^._ ]+)``", + "match": "(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -1142,7 +1197,7 @@ "match": "\\(\\)" }, { - "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|[[:alpha:]0-9'`<>^._ ]\\w*)", + "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|(?!private\\b)\\b[\\w[:alpha:]0-9'`<>^._ ]+)", "captures": { "1": { "name": "keyword.symbol.fsharp" diff --git a/extensions/fsharp/test/colorize-results/test_fs.json b/extensions/fsharp/test/colorize-results/test_fs.json index 37c0b61c14a..e5736fc10aa 100644 --- a/extensions/fsharp/test/colorize-results/test_fs.json +++ b/extensions/fsharp/test/colorize-results/test_fs.json @@ -604,28 +604,6 @@ "hc_black": "keyword: #569CD6" } }, - { - "c": " get", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "()", - "t": "source.fsharp constant.language.unit.fsharp", - "r": { - "dark_plus": "constant.language: #569CD6", - "light_plus": "constant.language: #0000FF", - "dark_vs": "constant.language: #569CD6", - "light_vs": "constant.language: #0000FF", - "hc_black": "constant.language: #569CD6" - } - }, { "c": " ", "t": "source.fsharp", @@ -637,9 +615,42 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "get", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "()", + "t": "source.fsharp binding.fsharp constant.language.unit.fsharp", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": " ", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "=", - "t": "source.fsharp keyword.symbol.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -681,50 +692,6 @@ "hc_black": "keyword: #569CD6" } }, - { - "c": " set", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "(", - "t": "source.fsharp keyword.symbol.fsharp", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, - { - "c": "value", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ")", - "t": "source.fsharp keyword.symbol.fsharp", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, { "c": " ", "t": "source.fsharp", @@ -736,9 +703,64 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "set", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.fsharp binding.fsharp keyword.symbol.fsharp", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "value", + "t": "source.fsharp binding.fsharp variable.parameter.fsharp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.fsharp binding.fsharp keyword.symbol.fsharp", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "=", - "t": "source.fsharp keyword.symbol.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -979,7 +1001,7 @@ } }, { - "c": " targetAge", + "c": " targetAge ", "t": "source.fsharp binding.fsharp variable.parameter.fsharp", "r": { "dark_plus": "variable: #9CDCFE", @@ -989,17 +1011,6 @@ "hc_black": "variable: #9CDCFE" } }, - { - "c": " ", - "t": "source.fsharp binding.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, { "c": "=", "t": "source.fsharp binding.fsharp keyword.fsharp", diff --git a/extensions/handlebars/cgmanifest.json b/extensions/handlebars/cgmanifest.json index 4d30387e915..39f8efc676d 100644 --- a/extensions/handlebars/cgmanifest.json +++ b/extensions/handlebars/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "daaain/Handlebars", "repositoryUrl": "https://github.com/daaain/Handlebars", - "commitHash": "790f2b0222098a3a236bd9e91bb9a039eeca4d8e" + "commitHash": "85a153a6f759df4e8da7533e1b3651f007867c51" } }, "licenseDetail": [ @@ -29,7 +29,7 @@ "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ], "license": "MIT", - "version": "1.7.1" + "version": "1.8.0" } ], "version": 1 diff --git a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json b/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json index 957f16ae035..be8b06fa085 100644 --- a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json +++ b/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/daaain/Handlebars/commit/790f2b0222098a3a236bd9e91bb9a039eeca4d8e", + "version": "https://github.com/daaain/Handlebars/commit/85a153a6f759df4e8da7533e1b3651f007867c51", "name": "Handlebars", "scopeName": "text.html.handlebars", "patterns": [ @@ -653,7 +653,7 @@ ] }, "else_token": { - "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\./]+))?", + "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\.\\(\\s\\)/]+))?", "end": "(~?\\}\\}\\}*)", "name": "meta.function.inline.else.handlebars", "beginCaptures": { diff --git a/extensions/java/cgmanifest.json b/extensions/java/cgmanifest.json index fb1791f0ab4..8616c4fcc53 100644 --- a/extensions/java/cgmanifest.json +++ b/extensions/java/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "atom/language-java", "repositoryUrl": "https://github.com/atom/language-java", - "commitHash": "a91b17906a2142bc61c06c6f214f135a2e3cdc96" + "commitHash": "9fc8f699e55284c0a8ddf03d929504064eb4f757" } }, "license": "MIT", - "version": "0.31.1" + "version": "0.31.2" } ], "version": 1 diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index f73c132ff03..736861a6043 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-java/commit/a91b17906a2142bc61c06c6f214f135a2e3cdc96", + "version": "https://github.com/atom/language-java/commit/9fc8f699e55284c0a8ddf03d929504064eb4f757", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -1576,7 +1576,7 @@ ] }, "variables": { - "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", + "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n \\s*\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n \\s*\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", "end": "(?=\\=|:|;)", "name": "meta.definition.variable.java", "patterns": [ diff --git a/extensions/objective-c/test/colorize-results/test_m.json b/extensions/objective-c/test/colorize-results/test_m.json index c6a4fb2573b..7245315d4db 100644 --- a/extensions/objective-c/test/colorize-results/test_m.json +++ b/extensions/objective-c/test/colorize-results/test_m.json @@ -1,7 +1,7 @@ [ { "c": "//", - "t": "source.objc comment.line.double-slash.cpp.c punctuation.definition.comment.cpp.c", + "t": "source.objc comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -12,7 +12,7 @@ }, { "c": "//", - "t": "source.objc comment.line.double-slash.cpp.c punctuation.definition.comment.cpp.c", + "t": "source.objc comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -23,7 +23,7 @@ }, { "c": " Copyright (c) Microsoft Corporation. All rights reserved.", - "t": "source.objc comment.line.double-slash.cpp.c", + "t": "source.objc comment.line.double-slash.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -34,7 +34,7 @@ }, { "c": "//", - "t": "source.objc comment.line.double-slash.cpp.c punctuation.definition.comment.cpp.c", + "t": "source.objc comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -749,7 +749,7 @@ }, { "c": "0", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c constant.numeric.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1727,8 +1727,19 @@ } }, { - "c": "0xFEF1F0F", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "0x", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.other.unit.hexadecimal.c", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "FEF1F0F", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.hexadecimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1804,8 +1815,30 @@ } }, { - "c": "3.14", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "3", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.point.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "14", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1859,8 +1892,52 @@ } }, { - "c": "3.14e0", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "3", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.point.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "14", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "e", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.other.unit.exponent.decimal.c", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "0", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.exponent.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1914,8 +1991,63 @@ } }, { - "c": "31.4e-2", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "31", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.point.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "4", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "e", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.other.unit.exponent.decimal.c", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "-", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.operator.minus.exponent.decimal.c", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "2", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.exponent.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2245,7 +2377,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.whitespace.comment.leading.cpp.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.whitespace.comment.leading.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2256,7 +2388,7 @@ }, { "c": "//", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.cpp.c punctuation.definition.comment.cpp.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -2267,7 +2399,7 @@ }, { "c": " add a tap gesture recognizer", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.cpp.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", diff --git a/extensions/powershell/syntaxes/powershell.tmLanguage.json b/extensions/powershell/syntaxes/powershell.tmLanguage.json index 09e2aa4d85a..59ee6fbd130 100644 --- a/extensions/powershell/syntaxes/powershell.tmLanguage.json +++ b/extensions/powershell/syntaxes/powershell.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/PowerShell/EditorSyntax/commit/12b7d7257eb493e45a9af0af9094ec0c2a996712", + "version": "https://github.com/PowerShell/EditorSyntax/commit/44eac8702f3cbe55a4ec70c1fdb163d42b4162fc", "name": "PowerShell", "scopeName": "source.powershell", "patterns": [ @@ -274,7 +274,7 @@ ] }, "attribute": { - "begin": "(\\[)\\s*\\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength)\\b", + "begin": "(\\[)\\s*\\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength|supportswildcards)\\b", "beginCaptures": { "1": { "name": "punctuation.section.bracket.begin.powershell" @@ -305,33 +305,6 @@ } }, "patterns": [ - { - "include": "#variable" - }, - { - "include": "#variableNoProperty" - }, - { - "include": "#hashtable" - }, - { - "include": "#scriptblock" - }, - { - "include": "#doubleQuotedStringEscapes" - }, - { - "include": "#doubleQuotedString" - }, - { - "include": "#type" - }, - { - "include": "#numericConstant" - }, - { - "include": "#doubleQuotedString" - }, { "include": "$self" }, @@ -345,27 +318,6 @@ "name": "keyword.operator.assignment.powershell" } } - }, - { - "begin": "(?{1,5})}", + "name": "constant.character.escape.powershell" + }, + { + "match": "`u(?:\\{[0-9a-fA-F]{,6}.)?", + "name": "invalid.character.escape.powershell" + } + ] + }, "function": { "begin": "^(?:\\s*+)(?i)(function|filter|configuration|workflow)\\s+(?:(global|local|script|private):)?((?:\\p{L}|\\d|_|-|\\.)+)", "beginCaptures": { @@ -677,7 +645,7 @@ { "captures": { "0": { - "name": "support.constant.automatic.powershell" + "name": "support.variable.automatic.powershell" }, "1": { "name": "punctuation.definition.variable.powershell" @@ -687,7 +655,7 @@ } }, "comment": "Automatic variables are not constants, but they are read-only. In monokai (default) color schema support.variable doesn't have color, so we use constant.", - "match": "(\\$)(?i:(\\$|\\^|\\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?\\b" + "match": "(\\$)((?:[$^?])|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\\b)((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?" }, { "captures": { @@ -865,7 +833,7 @@ } }, "comment": "Automatic variables are not constants, but they are read-only...", - "match": "(\\$)(?i:(\\$|\\^|\\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))\\b" + "match": "(\\$)((?:[$^?])|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\\b)" }, { "captures": { @@ -897,7 +865,7 @@ "name": "variable.other.member.powershell" } }, - "match": "(?i:(\\$|@)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))" + "match": "(?i:(\\$)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))" }, { "captures": { @@ -1023,9 +991,6 @@ { "include": "#variableNoProperty" }, - { - "include": "#variable" - }, { "include": "#doubleQuotedStringEscapes" }, diff --git a/extensions/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json index fd82cda536b..e41d1306499 100644 --- a/extensions/powershell/test/colorize-results/test_ps1.json +++ b/extensions/powershell/test/colorize-results/test_ps1.json @@ -716,24 +716,24 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "_", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1090,24 +1090,24 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "_", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1299,24 +1299,24 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "_", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1486,24 +1486,24 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "matches", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1563,24 +1563,24 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "matches", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { From c368342fa8cd5a55b409e8b310ef57ea402de37b Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 3 May 2019 10:08:54 +0000 Subject: [PATCH 74/99] Handle newline characters in file picker Fixes #71962 --- .../dialogs/browser/remoteFileDialog.ts | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 8955b687a91..893bc78f376 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -280,10 +280,11 @@ export class RemoteFileDialog { // If the user has just entered more bad path, don't change anything if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) { this.filePickBox.validationMessage = undefined; - const valueUri = this.remoteUriFrom(this.trimTrailingSlash(this.filePickBox.value)); + const filePickBoxUri = this.filePickBoxValue(); + const valueUri = resources.removeTrailingPathSeparator(filePickBoxUri); let updated: UpdateResult = UpdateResult.NotUpdated; - if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), valueUri, true)) { - updated = await this.tryUpdateItems(value, this.remoteUriFrom(this.filePickBox.value)); + if (!resources.isEqual(resources.removeTrailingPathSeparator(this.currentFolder), valueUri, true)) { + updated = await this.tryUpdateItems(value, filePickBoxUri); } if (updated === UpdateResult.NotUpdated) { this.setActiveItems(value); @@ -331,6 +332,22 @@ export class RemoteFileDialog { return this.pathAppend(this.currentFolder, this.userEnteredPathSegment); } + private filePickBoxValue(): URI { + // The file pick box can't render everything, so we use the current folder to create the uri so that it is an existing path. + const directUri = this.remoteUriFrom(this.filePickBox.value); + const currentPath = this.pathFromUri(this.currentFolder); + if (equalsIgnoreCase(this.filePickBox.value, currentPath)) { + return this.currentFolder; + } + const currentDisplayUri = this.remoteUriFrom(currentPath); + const relativePath = resources.relativePath(currentDisplayUri, directUri); + if (relativePath) { + return resources.joinPath(this.currentFolder, relativePath); + } else { + return directUri; + } + } + private async onDidAccept(): Promise { this.filePickBox.busy = true; let resolveValue: URI | undefined; @@ -340,7 +357,7 @@ export class RemoteFileDialog { let stat: IFileStat | undefined; let statDirname: IFileStat | undefined; try { - inputUri = resources.removeTrailingPathSeparator(this.remoteUriFrom(this.filePickBox.value)); + inputUri = resources.removeTrailingPathSeparator(this.filePickBoxValue()); inputUriDirname = resources.dirname(inputUri); statDirname = await this.fileService.resolve(inputUriDirname); stat = await this.fileService.resolve(inputUri); @@ -415,7 +432,7 @@ export class RemoteFileDialog { return UpdateResult.InvalidPath; } else { const inputUriDirname = resources.dirname(valueUri); - if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), inputUriDirname, true)) { + if (!resources.isEqual(resources.removeTrailingPathSeparator(this.currentFolder), inputUriDirname, true)) { let statWithoutTrailing: IFileStat | undefined; try { statWithoutTrailing = await this.fileService.resolve(inputUriDirname); @@ -633,6 +650,16 @@ export class RemoteFileDialog { return Promise.resolve(true); } + private ensureTrailingSeparator(uri: URI): URI { + if (resources.hasTrailingPathSeparator(uri)) { + return uri; + } else { + const dir = resources.dirname(uri); + const base = resources.basename(uri) + this.labelService.getSeparator(uri.scheme, uri.authority); + return resources.joinPath(dir, base); + } + } + private async updateItems(newFolder: URI, force: boolean = false, trailing?: string) { this.filePickBox.busy = true; this.userEnteredPathSegment = trailing ? trailing : ''; @@ -640,7 +667,7 @@ export class RemoteFileDialog { const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true); const oldFolder = this.currentFolder; const newFolderPath = this.pathFromUri(newFolder, true); - this.currentFolder = this.remoteUriFrom(newFolderPath); + this.currentFolder = this.ensureTrailingSeparator(newFolder); return this.createItems(this.currentFolder).then(items => { this.filePickBox.items = items; if (this.allowFolderSelection) { @@ -669,11 +696,11 @@ export class RemoteFileDialog { private pathFromUri(uri: URI, endWithSeparator: boolean = false): string { const sep = this.labelService.getSeparator(uri.scheme, uri.authority); - let result: string; + let result: string = uri.fsPath.replace(/\n/g, ''); if (sep === '/') { - result = uri.fsPath.replace(/\\/g, sep); + result = result.replace(/\\/g, sep); } else { - result = uri.fsPath.replace(/\//g, sep); + result = result.replace(/\//g, sep); } if (endWithSeparator && !this.endsWithSlash(result)) { result = result + sep; From d0f109bc45a57f06d9cd0fd0b6af8d471064d0dc Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 3 May 2019 14:37:08 +0200 Subject: [PATCH 75/99] Update for windows --- src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 893bc78f376..662f6e5c232 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -341,7 +341,8 @@ export class RemoteFileDialog { } const currentDisplayUri = this.remoteUriFrom(currentPath); const relativePath = resources.relativePath(currentDisplayUri, directUri); - if (relativePath) { + const isSameRoot = (this.filePickBox.value.length > 1 && currentPath.length > 1) ? equalsIgnoreCase(this.filePickBox.value.substr(0, 2), currentPath.substr(0, 2)) : false; + if (relativePath && isSameRoot) { return resources.joinPath(this.currentFolder, relativePath); } else { return directUri; From cd1b7130ae6e5cbb2d3dda17b20416d78e864d39 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 11:31:43 -0700 Subject: [PATCH 76/99] Remove call to withNullAsUndefined --- src/vs/code/electron-main/window.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 8f0afb68cd9..d725d6f3d64 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -25,7 +25,6 @@ import * as perf from 'vs/base/common/performance'; import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { getBackgroundColor } from 'vs/code/electron-main/theme'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { endsWith } from 'vs/base/common/strings'; export interface IWindowCreationOptions { @@ -690,7 +689,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private restoreWindowState(state?: IWindowState): IWindowState { if (state) { try { - state = withNullAsUndefined(this.validateWindowState(state)); + state = this.validateWindowState(state); } catch (err) { this.logService.warn(`Unexpected error validating window state: ${err}\n${err.stack}`); // somehow display API can be picky about the state to validate } @@ -703,9 +702,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { return state; } - private validateWindowState(state: IWindowState): IWindowState | null { + private validateWindowState(state: IWindowState): IWindowState | undefined { if (!state) { - return null; + return undefined; } if (typeof state.x !== 'number' @@ -713,11 +712,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { || typeof state.width !== 'number' || typeof state.height !== 'number' ) { - return null; + return undefined; } if (state.width <= 0 || state.height <= 0) { - return null; + return undefined; } const displays = screen.getAllDisplays(); @@ -793,7 +792,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { return state; } - return null; + return undefined; } private getWorkingArea(display: Display): Rectangle | undefined { From 2b28a35028fedcbb11e9ed17d7daaeb951b987a8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 11:36:01 -0700 Subject: [PATCH 77/99] Remove unreachable null check --- src/vs/code/electron-main/window.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index d725d6f3d64..a2b26377ec2 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -694,19 +694,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.logService.warn(`Unexpected error validating window state: ${err}\n${err.stack}`); // somehow display API can be picky about the state to validate } } - - if (!state) { - state = defaultWindowState(); - } - - return state; + return state || defaultWindowState(); } private validateWindowState(state: IWindowState): IWindowState | undefined { - if (!state) { - return undefined; - } - if (typeof state.x !== 'number' || typeof state.y !== 'number' || typeof state.width !== 'number' From bfc7b7f55ab2ed319ccd79bcaf0a70b687a8881c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 11:40:47 -0700 Subject: [PATCH 78/99] Make getResource return undefined --- src/vs/base/parts/quickopen/browser/quickOpenModel.ts | 6 +++--- src/vs/workbench/browser/parts/editor/editorPicker.ts | 5 +++-- .../browser/parts/quickopen/quickOpenController.ts | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 0347e161b09..3e79edb43ae 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -140,8 +140,8 @@ export class QuickOpenEntry { * A resource for this entry. Resource URIs can be used to compare different kinds of entries and group * them together. */ - getResource(): URI | null { - return null; + getResource(): URI | undefined { + return undefined; } /** @@ -245,7 +245,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.getDetail() : super.getDetail(); } - getResource(): URI | null { + getResource(): URI | undefined { return this.entry ? this.entry.getResource() : super.getResource(); } diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index edb8142f7de..3b87ee6b7b8 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -18,6 +18,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { EditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class EditorPickerEntry extends QuickOpenEntryGroup { @@ -32,7 +33,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { getLabelOptions(): IIconLabelValueOptions { return { - extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource() || undefined), + extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()), italic: !this._group.isPinned(this.editor) }; } @@ -50,7 +51,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { } getResource() { - return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER }); + return withNullAsUndefined(toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER })); } getAriaLabel(): string { diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 68fd3569f5a..5ca5e588a35 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -782,8 +782,8 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { return this.description; } - getResource(): URI | null { - return types.withUndefinedAsNull(this.resource); + getResource(): URI | undefined { + return this.resource; } getInput(): IEditorInput | IResourceInput { @@ -848,7 +848,7 @@ export class RemoveFromEditorHistoryAction extends Action { return { input: h, - iconClasses: getIconClasses(this.modelService, this.modeService, types.withNullAsUndefined(entry.getResource())), + iconClasses: getIconClasses(this.modelService, this.modeService, entry.getResource()), label: entry.getLabel(), description: entry.getDescription() }; From 09fb2a357c17776f66edc3ad671a67961c10a16d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 11:52:28 -0700 Subject: [PATCH 79/99] convert getDescription to return undefined instead of null --- .../parts/quickopen/browser/quickOpenModel.ts | 20 +++++++++---------- .../browser/quickOpen/quickOutline.ts | 10 +++++----- .../browser/parts/editor/editorPicker.ts | 2 +- .../parts/quickopen/quickOpenController.ts | 8 ++++---- .../search/browser/openSymbolHandler.ts | 5 ++--- .../contrib/tasks/browser/quickOpen.ts | 6 +++--- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 3e79edb43ae..213ba2c2d31 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -40,7 +40,7 @@ export class QuickOpenItemAccessorClass implements IItemAccessor } getItemDescription(entry: QuickOpenEntry): string | null { - return entry.getDescription(); + return types.withUndefinedAsNull(entry.getDescription()); } getItemPath(entry: QuickOpenEntry): string | undefined { @@ -111,8 +111,8 @@ export class QuickOpenEntry { /** * A secondary description that is optional and can be shown right to the label */ - getDescription(): string | null { - return null; + getDescription(): string | undefined { + return undefined; } /** @@ -125,15 +125,15 @@ export class QuickOpenEntry { /** * A tooltip to show when hovering over the description portion of the entry. */ - getDescriptionTooltip(): string | null { - return null; + getDescriptionTooltip(): string | undefined { + return undefined; } /** * An optional keybinding to show for an entry. */ - getKeybinding(): ResolvedKeybinding | null { - return null; + getKeybinding(): ResolvedKeybinding | undefined { + return undefined; } /** @@ -253,7 +253,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.getIcon() : super.getIcon(); } - getDescription(): string | null { + getDescription(): string | undefined { return this.entry ? this.entry.getDescription() : super.getDescription(); } @@ -460,9 +460,9 @@ class Renderer implements IRenderer { const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null); options.matches = labelHighlights || []; options.title = types.withNullAsUndefined(entry.getTooltip()); - options.descriptionTitle = entry.getDescriptionTooltip() || types.withNullAsUndefined(entry.getDescription()); // tooltip over description because it could overflow + options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow options.descriptionMatches = descriptionHighlights || []; - data.label.setLabel(types.withNullAsUndefined(entry.getLabel()), types.withNullAsUndefined(entry.getDescription()), options); + data.label.setLabel(types.withNullAsUndefined(entry.getLabel()), entry.getDescription(), options); // Meta data.detail.set(types.withNullAsUndefined(entry.getDetail()), detailHighlights); diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts index 92cc30a7b53..eeeaf6f32c8 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -26,12 +26,12 @@ let SCOPE_PREFIX = ':'; export class SymbolEntry extends QuickOpenEntryGroup { private readonly name: string; private readonly type: string; - private readonly description: string | null; + private readonly description: string | undefined; private readonly range: Range; private readonly editor: ICodeEditor; private readonly decorator: IDecorator; - constructor(name: string, type: string, description: string | null, range: Range, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator) { + constructor(name: string, type: string, description: string | undefined, range: Range, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator) { super(); this.name = name; @@ -55,7 +55,7 @@ export class SymbolEntry extends QuickOpenEntryGroup { return this.type; } - public getDescription(): string | null { + public getDescription(): string | undefined { return this.description; } @@ -169,7 +169,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { }); } - private symbolEntry(name: string, type: string, description: string | null, range: IRange, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator): SymbolEntry { + private symbolEntry(name: string, type: string, description: string | undefined, range: IRange, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator): SymbolEntry { return new SymbolEntry(name, type, description, Range.lift(range), highlights, editor, decorator); } @@ -192,7 +192,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { if (highlights) { // Show parent scope as description - let description: string | null = null; + let description: string | undefined = undefined; if (element.containerName) { description = element.containerName; } diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index 3b87ee6b7b8..c7a2019aac7 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -59,7 +59,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { } getDescription() { - return this.editor.getDescription(); + return withNullAsUndefined(this.editor.getDescription()); } run(mode: Mode, context: IEntryRunContext): boolean { diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 5ca5e588a35..065b07b916a 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -710,7 +710,7 @@ class EditorHistoryItemAccessorClass extends QuickOpenItemAccessorClass { } getItemDescription(entry: QuickOpenEntry): string | null { - return this.allowMatchOnDescription ? entry.getDescription() : null; + return this.allowMatchOnDescription ? types.withUndefinedAsNull(entry.getDescription()) : null; } } @@ -725,7 +725,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { private input: IEditorInput | IResourceInput; private resource: URI | undefined; private label: string | null; - private description: string | null; + private description?: string; private dirty: boolean; constructor( @@ -745,7 +745,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { if (input instanceof EditorInput) { this.resource = resourceForEditorHistory(input, fileService); this.label = input.getName(); - this.description = input.getDescription(); + this.description = types.withNullAsUndefined(input.getDescription()); this.dirty = input.isDirty(); } else { const resourceInput = input as IResourceInput; @@ -778,7 +778,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { return nls.localize('entryAriaLabel', "{0}, recently opened", this.getLabel()); } - getDescription(): string | null { + getDescription(): string | undefined { return this.description; } diff --git a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts index 32f606bb2ca..6599e4982e7 100644 --- a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts @@ -25,7 +25,6 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { withUndefinedAsNull } from 'vs/base/common/types'; class SymbolEntry extends EditorQuickOpenEntry { private bearingResolve: Promise; @@ -49,7 +48,7 @@ class SymbolEntry extends EditorQuickOpenEntry { return nls.localize('entryAriaLabel', "{0}, symbols picker", this.getLabel()); } - getDescription(): string | null { + getDescription(): string | undefined { const containerName = this.bearing.containerName; if (this.bearing.location.uri) { if (containerName) { @@ -59,7 +58,7 @@ class SymbolEntry extends EditorQuickOpenEntry { return this.labelService.getUriLabel(this.bearing.location.uri, { relative: true }); } - return withUndefinedAsNull(containerName); + return containerName; } getIcon(): string { diff --git a/src/vs/workbench/contrib/tasks/browser/quickOpen.ts b/src/vs/workbench/contrib/tasks/browser/quickOpen.ts index 8d7b2e358de..ea5960b4eab 100644 --- a/src/vs/workbench/contrib/tasks/browser/quickOpen.ts +++ b/src/vs/workbench/contrib/tasks/browser/quickOpen.ts @@ -28,13 +28,13 @@ export class TaskEntry extends Model.QuickOpenEntry { return this.task._label; } - public getDescription(): string | null { + public getDescription(): string | undefined { if (!this.taskService.needsFolderQualification()) { - return null; + return undefined; } let workspaceFolder = this.task.getWorkspaceFolder(); if (!workspaceFolder) { - return null; + return undefined; } return `${workspaceFolder.name}`; } From d3d842cbe2c4835d87a21ccc2660f330bd059773 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 11:56:51 -0700 Subject: [PATCH 80/99] Convert a few more interfaces to return undefined instead of null --- .../parts/quickopen/browser/quickOpenModel.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 213ba2c2d31..75e4d3798c0 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -82,8 +82,8 @@ export class QuickOpenEntry { /** * The options for the label to use for this entry */ - getLabelOptions(): IIconLabelValueOptions | null { - return null; + getLabelOptions(): IIconLabelValueOptions | undefined { + return undefined; } /** @@ -97,15 +97,15 @@ export class QuickOpenEntry { /** * Detail information about the entry that is optional and can be shown below the label */ - getDetail(): string | null { - return null; + getDetail(): string | undefined { + return undefined; } /** * The icon of the entry to identify it from others in the list */ - getIcon(): string | null { - return null; + getIcon(): string | undefined { + return undefined; } /** @@ -118,8 +118,8 @@ export class QuickOpenEntry { /** * A tooltip to show when hovering over the entry. */ - getTooltip(): string | null { - return null; + getTooltip(): string | undefined { + return undefined; } /** @@ -233,7 +233,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.getLabel() : super.getLabel(); } - getLabelOptions(): IIconLabelValueOptions | null { + getLabelOptions(): IIconLabelValueOptions | undefined { return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions(); } @@ -241,7 +241,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.getAriaLabel() : super.getAriaLabel(); } - getDetail(): string | null { + getDetail(): string | undefined { return this.entry ? this.entry.getDetail() : super.getDetail(); } @@ -249,7 +249,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.getResource() : super.getResource(); } - getIcon(): string | null { + getIcon(): string | undefined { return this.entry ? this.entry.getIcon() : super.getIcon(); } @@ -459,13 +459,13 @@ class Renderer implements IRenderer { // Label const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null); options.matches = labelHighlights || []; - options.title = types.withNullAsUndefined(entry.getTooltip()); + options.title = entry.getTooltip(); options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow options.descriptionMatches = descriptionHighlights || []; data.label.setLabel(types.withNullAsUndefined(entry.getLabel()), entry.getDescription(), options); // Meta - data.detail.set(types.withNullAsUndefined(entry.getDetail()), detailHighlights); + data.detail.set(entry.getDetail(), detailHighlights); // Keybinding data.keybinding.set(entry.getKeybinding()!); From aeb7669fbafd365134257be27198573c7e9cf0f7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 12:01:26 -0700 Subject: [PATCH 81/99] Convert getLabel to return undefined instead of null --- src/vs/base/parts/quickopen/browser/quickOpenModel.ts | 10 +++++----- src/vs/workbench/browser/parts/editor/editorPicker.ts | 2 +- .../browser/parts/quickopen/quickOpenController.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 75e4d3798c0..165db403b5d 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -36,7 +36,7 @@ let IDS = 0; export class QuickOpenItemAccessorClass implements IItemAccessor { getItemLabel(entry: QuickOpenEntry): string | null { - return entry.getLabel(); + return types.withUndefinedAsNull(entry.getLabel()); } getItemDescription(entry: QuickOpenEntry): string | null { @@ -75,8 +75,8 @@ export class QuickOpenEntry { /** * The label of the entry to identify it from others in the list */ - getLabel(): string | null { - return null; + getLabel(): string | undefined { + return undefined; } /** @@ -229,7 +229,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { this.withBorder = showBorder; } - getLabel(): string | null { + getLabel(): string | undefined { return this.entry ? this.entry.getLabel() : super.getLabel(); } @@ -556,7 +556,7 @@ export class QuickOpenModel implements } getLabel(entry: QuickOpenEntry): string | null { - return entry.getLabel(); + return types.withUndefinedAsNull(entry.getLabel()); } getAriaLabel(entry: QuickOpenEntry): string { diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index c7a2019aac7..5844f8eaebe 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -39,7 +39,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { } getLabel() { - return this.editor.getName(); + return withNullAsUndefined(this.editor.getName()); } getIcon(): string { diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 065b07b916a..6683de3ce6c 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -724,7 +724,7 @@ export class EditorHistoryEntryGroup extends QuickOpenEntryGroup { export class EditorHistoryEntry extends EditorQuickOpenEntry { private input: IEditorInput | IResourceInput; private resource: URI | undefined; - private label: string | null; + private label: string | undefined; private description?: string; private dirty: boolean; @@ -744,7 +744,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { if (input instanceof EditorInput) { this.resource = resourceForEditorHistory(input, fileService); - this.label = input.getName(); + this.label = types.withNullAsUndefined(input.getName()); this.description = types.withNullAsUndefined(input.getDescription()); this.dirty = input.isDirty(); } else { @@ -764,7 +764,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { return this.dirty ? 'dirty' : ''; } - getLabel(): string | null { + getLabel(): string | undefined { return this.label; } From 4ce2cdd6d74e8c4b5ea6fdbcaf53698ed22aee75 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 May 2019 12:11:22 -0700 Subject: [PATCH 82/99] ResolvedKeybindingItem as undefined instead of null --- src/vs/editor/standalone/browser/simpleServices.ts | 2 +- .../platform/keybinding/common/abstractKeybindingService.ts | 5 ++--- src/vs/platform/keybinding/common/resolvedKeybindingItem.ts | 4 ++-- .../keybinding/test/common/abstractKeybindingService.test.ts | 2 +- .../keybinding/test/common/keybindingResolver.test.ts | 2 +- .../keybinding/electron-browser/keybindingService.ts | 4 ++-- .../test/electron-browser/keybindingEditing.test.ts | 2 +- .../services/preferences/common/keybindingsEditorModel.ts | 2 +- .../preferences/test/common/keybindingsEditorModel.test.ts | 2 +- 9 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 349f1403657..6bd70b0ac3e 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -352,7 +352,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { if (!keybinding) { // This might be a removal keybinding item in user settings => accept it - result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault); } else { const resolvedKeybindings = this.resolveKeybinding(keybinding); for (const resolvedKeybinding of resolvedKeybindings) { diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 4e9f67f367b..33e1efbebb2 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -17,7 +17,6 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { withNullAsUndefined } from 'vs/base/common/types'; interface CurrentChord { keypress: string; @@ -96,11 +95,11 @@ export abstract class AbstractKeybindingService extends Disposable implements IK } public lookupKeybinding(commandId: string): ResolvedKeybinding | undefined { - let result = this._getResolver().lookupPrimaryKeybinding(commandId); + const result = this._getResolver().lookupPrimaryKeybinding(commandId); if (!result) { return undefined; } - return withNullAsUndefined(result.resolvedKeybinding); + return result.resolvedKeybinding; } public dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean { diff --git a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts index c7cf8f7af1e..00b05e05213 100644 --- a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts +++ b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts @@ -10,7 +10,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export class ResolvedKeybindingItem { _resolvedKeybindingItemBrand: void; - public readonly resolvedKeybinding: ResolvedKeybinding | null; + public readonly resolvedKeybinding: ResolvedKeybinding | undefined; public readonly keypressParts: string[]; public readonly bubble: boolean; public readonly command: string | null; @@ -18,7 +18,7 @@ export class ResolvedKeybindingItem { public readonly when: ContextKeyExpr | undefined; public readonly isDefault: boolean; - constructor(resolvedKeybinding: ResolvedKeybinding | null, command: string | null, commandArgs: any, when: ContextKeyExpr | undefined, isDefault: boolean) { + constructor(resolvedKeybinding: ResolvedKeybinding | undefined, command: string | null, commandArgs: any, when: ContextKeyExpr | undefined, isDefault: boolean) { this.resolvedKeybinding = resolvedKeybinding; this.keypressParts = resolvedKeybinding ? removeElementsAfterNulls(resolvedKeybinding.getDispatchParts()) : []; this.bubble = (command ? command.charCodeAt(0) === CharCode.Caret : false); diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index d2b97151250..72c2062202c 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -180,7 +180,7 @@ suite('AbstractKeybindingService', () => { }); function kbItem(keybinding: number, command: string, when?: ContextKeyExpr): ResolvedKeybindingItem { - const resolvedKeybinding = (keybinding !== 0 ? new USLayoutResolvedKeybinding(createKeybinding(keybinding, OS)!, OS) : null); + const resolvedKeybinding = (keybinding !== 0 ? new USLayoutResolvedKeybinding(createKeybinding(keybinding, OS)!, OS) : undefined); return new ResolvedKeybindingItem( resolvedKeybinding, command, diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index 0174bcefd8b..a4488cffb17 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -21,7 +21,7 @@ function createContext(ctx: any) { suite('KeybindingResolver', () => { function kbItem(keybinding: number, command: string, commandArgs: any, when: ContextKeyExpr, isDefault: boolean): ResolvedKeybindingItem { - const resolvedKeybinding = (keybinding !== 0 ? new USLayoutResolvedKeybinding(createKeybinding(keybinding, OS)!, OS) : null); + const resolvedKeybinding = (keybinding !== 0 ? new USLayoutResolvedKeybinding(createKeybinding(keybinding, OS)!, OS) : undefined); return new ResolvedKeybindingItem( resolvedKeybinding, command, diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 0c3d16f07b2..85e921e9229 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -396,7 +396,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { const keybinding = item.keybinding; if (!keybinding) { // This might be a removal keybinding item in user settings => accept it - result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault); } else { const resolvedKeybindings = this.resolveKeybinding(keybinding); for (const resolvedKeybinding of resolvedKeybindings) { @@ -415,7 +415,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { const parts = item.parts; if (parts.length === 0) { // This might be a removal keybinding item in user settings => accept it - result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault); } else { const resolvedKeybindings = this._keyboardMapper.resolveUserBinding(parts); for (const resolvedKeybinding of resolvedKeybindings) { diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index c9327d4205b..1c560bd3d57 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -274,7 +274,7 @@ suite('KeybindingsEditing', () => { parts.push(aSimpleKeybinding(chordPart)); } } - let keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : null; + const keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : undefined; return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : undefined, isDefault === undefined ? true : isDefault); } diff --git a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts index fed784141bf..0f80e25b1d2 100644 --- a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts @@ -174,7 +174,7 @@ export class KeybindingsEditorModel extends EditorModel { const commandsWithDefaultKeybindings = this.keybindingsService.getDefaultKeybindings().map(keybinding => keybinding.command); for (const command of KeybindingResolver.getAllUnboundCommands(boundCommands)) { - const keybindingItem = new ResolvedKeybindingItem(null, command, null, undefined, commandsWithDefaultKeybindings.indexOf(command) === -1); + const keybindingItem = new ResolvedKeybindingItem(undefined, command, null, undefined, commandsWithDefaultKeybindings.indexOf(command) === -1); this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(command, keybindingItem, workbenchActionsRegistry, editorActionsLabels)); } this._keybindingItems = this._keybindingItemsSortedByPrecedence.slice(0).sort((a, b) => KeybindingsEditorModel.compareKeybindingData(a, b)); diff --git a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts index 4c256284b39..c70d6d94927 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -617,7 +617,7 @@ suite('KeybindingsEditorModel test', () => { parts.push(aSimpleKeybinding(chordPart)); } } - let keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : null; + const keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : undefined; return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : undefined, isDefault === undefined ? true : isDefault); } From 36bae8d0560835c4843856896c61a89f20c00be5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 May 2019 08:58:46 -0700 Subject: [PATCH 83/99] Resolve promise to null instead of empty As far as I can understand, the intent here is to return 'no result', which this code uses `null` for. Promises allow resolving with `undefined` since the argument to `then` is optional --- src/vs/workbench/contrib/debug/electron-browser/debugService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts index 8bd52bacb4d..98980e88909 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts @@ -733,7 +733,7 @@ export class DebugService implements IDebugService { if (task.configurationProperties.isBackground) { return new Promise((c, e) => once(e => e.kind === TaskEventKind.Inactive && e.taskId === task._id, this.taskService.onDidStateChange)(() => { taskStarted = true; - c(undefined); + c(null); })); } From d1b9e6d8d9ba00125ad285083e7fd15d4b38b798 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 May 2019 09:00:10 -0700 Subject: [PATCH 84/99] null -> undefined --- src/vs/editor/contrib/hover/modesContentHover.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index f66e00856f0..94d846e38a0 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -40,7 +40,6 @@ import { applyCodeAction, QuickFixAction } from 'vs/editor/contrib/codeAction/co import { Action } from 'vs/base/common/actions'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; const $ = dom.$; @@ -68,14 +67,13 @@ class ModesContentComputer implements IHoverComputer { private readonly _editor: ICodeEditor; private _result: HoverPart[]; - private _range: Range | null; + private _range?: Range; constructor( editor: ICodeEditor, private readonly _markerDecorationsService: IMarkerDecorationsService ) { this._editor = editor; - this._range = null; } setRange(range: Range): void { @@ -183,7 +181,7 @@ class ModesContentComputer implements IHoverComputer { private _getLoadingMessage(): HoverPart { return { - range: withNullAsUndefined(this._range), + range: this._range, contents: [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))] }; } From 653edff568d70e9fb89874543789dd2f6c60f914 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 May 2019 09:30:09 -0700 Subject: [PATCH 85/99] null -> undefined --- .../browser/parts/views/panelViewlet.ts | 3 +-- src/vs/workbench/browser/quickopen.ts | 20 +++++++++---------- .../quickopen/browser/gotoLineHandler.ts | 4 ++-- .../quickopen/browser/gotoSymbolHandler.ts | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index e4d2caf6247..10e2fd42d31 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -28,7 +28,6 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IView } from 'vs/workbench/common/views'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { withNullAsUndefined } from 'vs/base/common/types'; export interface IPanelColors extends IColorMapping { dropBackground?: ColorIdentifier; @@ -129,7 +128,7 @@ export abstract class ViewletPanel extends Panel implements IView { orientation: ActionsOrientation.HORIZONTAL, actionViewItemProvider: action => this.getActionViewItem(action), ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title), - getKeyBinding: action => withNullAsUndefined(this.keybindingService.lookupKeybinding(action.id)), + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), actionRunner: this.actionRunner }); diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index 8fe1e477f72..230a461907f 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -229,12 +229,12 @@ export interface IEditorQuickOpenEntry { /** * The editor input used for this entry when opening. */ - getInput(): IResourceInput | IEditorInput | null; + getInput(): IResourceInput | IEditorInput | undefined; /** * The editor options used for this entry when opening. */ - getOptions(): IEditorOptions | null; + getOptions(): IEditorOptions | undefined; } /** @@ -250,12 +250,12 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick return this._editorService; } - getInput(): IResourceInput | IEditorInput | null { - return null; + getInput(): IResourceInput | IEditorInput | undefined { + return undefined; } - getOptions(): IEditorOptions | null { - return null; + getOptions(): IEditorOptions | undefined { + return undefined; } run(mode: Mode, context: IEntryRunContext): boolean { @@ -301,12 +301,12 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick */ export class EditorQuickOpenEntryGroup extends QuickOpenEntryGroup implements IEditorQuickOpenEntry { - getInput(): IEditorInput | IResourceInput | null { - return null; + getInput(): IEditorInput | IResourceInput | undefined { + return undefined; } - getOptions(): IEditorOptions | null { - return null; + getOptions(): IEditorOptions | undefined { + return undefined; } } diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts index 6af674415aa..724509b2d05 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts @@ -139,8 +139,8 @@ class GotoLineEntry extends EditorQuickOpenEntry { return this.runPreview(); } - getInput(): IEditorInput | null { - return types.withUndefinedAsNull(this.editorService.activeEditor); + getInput(): IEditorInput | undefined { + return this.editorService.activeEditor; } getOptions(pinned?: boolean): ITextEditorOptions { diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts index e81454d8db5..97fba151082 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts @@ -288,8 +288,8 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { return this.range; } - getInput(): IEditorInput | null { - return types.withUndefinedAsNull(this.editorService.activeEditor); + getInput(): IEditorInput | undefined { + return this.editorService.activeEditor; } getOptions(pinned?: boolean): ITextEditorOptions { From 0787bf615ece6807694c44e1e14eb3eb977271dc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 May 2019 09:33:49 -0700 Subject: [PATCH 86/99] Use undefined instead of null | undefined for commands --- .../editor/browser/controller/coreCommands.ts | 102 +++++++++--------- src/vs/editor/browser/editorExtensions.ts | 12 +-- .../bracketMatching/bracketMatching.ts | 4 +- src/vs/editor/contrib/clipboard/clipboard.ts | 16 +-- .../editor/contrib/contextmenu/contextmenu.ts | 2 +- .../editor/contrib/cursorUndo/cursorUndo.ts | 2 +- src/vs/editor/contrib/find/findController.ts | 22 ++-- src/vs/editor/contrib/folding/folding.ts | 20 ++-- src/vs/editor/contrib/fontZoom/fontZoom.ts | 6 +- .../goToDefinition/goToDefinitionMouse.ts | 2 +- src/vs/editor/contrib/hover/hover.ts | 2 +- .../editor/contrib/indentation/indentation.ts | 6 +- src/vs/editor/contrib/links/links.ts | 2 +- .../editor/contrib/multicursor/multicursor.ts | 20 ++-- .../editor/contrib/smartSelect/smartSelect.ts | 4 +- .../toggleTabFocusMode/toggleTabFocusMode.ts | 2 +- .../contrib/tokenization/tokenization.ts | 2 +- .../contrib/wordOperations/wordOperations.ts | 24 ++--- .../wordPartOperations/wordPartOperations.ts | 8 +- .../accessibilityHelp/accessibilityHelp.ts | 2 +- .../browser/inspectTokens/inspectTokens.ts | 2 +- .../standalone/browser/quickOpen/gotoLine.ts | 2 +- .../browser/quickOpen/quickCommand.ts | 2 +- .../toggleHighContrast/toggleHighContrast.ts | 2 +- .../browser/accessibility/accessibility.ts | 2 +- .../codeEditor/browser/inspectKeybindings.ts | 2 +- .../inspectTMScopes/inspectTMScopes.ts | 2 +- .../codeEditor/browser/toggleWordWrap.ts | 2 +- .../browser/commentsEditorContribution.ts | 2 +- .../debug/browser/debugEditorActions.ts | 10 +- .../quickopen/browser/commandsHandler.ts | 2 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 8 +- .../terminal/browser/terminal.contribution.ts | 2 +- 33 files changed, 150 insertions(+), 150 deletions(-) diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index f1abe319f58..e72acd359f3 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -301,13 +301,13 @@ export namespace CoreNavigationCommands { export const MoveTo: CoreEditorCommand = registerEditorCommand(new BaseMoveToCommand({ id: '_moveTo', inSelectionMode: false, - precondition: null + precondition: undefined })); export const MoveToSelect: CoreEditorCommand = registerEditorCommand(new BaseMoveToCommand({ id: '_moveToSelect', inSelectionMode: true, - precondition: null + precondition: undefined })); abstract class ColumnSelectCommand extends CoreEditorCommand { @@ -330,7 +330,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'columnSelect', - precondition: null + precondition: undefined }); } @@ -354,7 +354,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'cursorColumnSelectLeft', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -373,7 +373,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'cursorColumnSelectRight', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -405,7 +405,7 @@ export namespace CoreNavigationCommands { export const CursorColumnSelectUp: CoreEditorCommand = registerEditorCommand(new ColumnSelectUpCommand({ isPaged: false, id: 'cursorColumnSelectUp', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -417,7 +417,7 @@ export namespace CoreNavigationCommands { export const CursorColumnSelectPageUp: CoreEditorCommand = registerEditorCommand(new ColumnSelectUpCommand({ isPaged: true, id: 'cursorColumnSelectPageUp', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -443,7 +443,7 @@ export namespace CoreNavigationCommands { export const CursorColumnSelectDown: CoreEditorCommand = registerEditorCommand(new ColumnSelectDownCommand({ isPaged: false, id: 'cursorColumnSelectDown', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -455,7 +455,7 @@ export namespace CoreNavigationCommands { export const CursorColumnSelectPageDown: CoreEditorCommand = registerEditorCommand(new ColumnSelectDownCommand({ isPaged: true, id: 'cursorColumnSelectPageDown', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -468,7 +468,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'cursorMove', - precondition: null, + precondition: undefined, description: CursorMove_.description }); } @@ -531,7 +531,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorLeft', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -548,7 +548,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorLeftSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -564,7 +564,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorRight', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -581,7 +581,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorRightSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -597,7 +597,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorUp', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -614,7 +614,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorUpSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -633,7 +633,7 @@ export namespace CoreNavigationCommands { value: Constants.PAGE_SIZE_MARKER }, id: 'cursorPageUp', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -649,7 +649,7 @@ export namespace CoreNavigationCommands { value: Constants.PAGE_SIZE_MARKER }, id: 'cursorPageUpSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -665,7 +665,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorDown', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -682,7 +682,7 @@ export namespace CoreNavigationCommands { value: 1 }, id: 'cursorDownSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -701,7 +701,7 @@ export namespace CoreNavigationCommands { value: Constants.PAGE_SIZE_MARKER }, id: 'cursorPageDown', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -717,7 +717,7 @@ export namespace CoreNavigationCommands { value: Constants.PAGE_SIZE_MARKER }, id: 'cursorPageDownSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -729,7 +729,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'createCursor', - precondition: null + precondition: undefined }); } @@ -790,7 +790,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: '_lastCursorMoveToSelect', - precondition: null + precondition: undefined }); } @@ -835,7 +835,7 @@ export namespace CoreNavigationCommands { export const CursorHome: CoreEditorCommand = registerEditorCommand(new HomeCommand({ inSelectionMode: false, id: 'cursorHome', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -847,7 +847,7 @@ export namespace CoreNavigationCommands { export const CursorHomeSelect: CoreEditorCommand = registerEditorCommand(new HomeCommand({ inSelectionMode: true, id: 'cursorHomeSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -860,7 +860,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'cursorLineStart', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -914,7 +914,7 @@ export namespace CoreNavigationCommands { export const CursorEnd: CoreEditorCommand = registerEditorCommand(new EndCommand({ inSelectionMode: false, id: 'cursorEnd', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -926,7 +926,7 @@ export namespace CoreNavigationCommands { export const CursorEndSelect: CoreEditorCommand = registerEditorCommand(new EndCommand({ inSelectionMode: true, id: 'cursorEndSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -939,7 +939,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'cursorLineEnd', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -994,7 +994,7 @@ export namespace CoreNavigationCommands { export const CursorTop: CoreEditorCommand = registerEditorCommand(new TopCommand({ inSelectionMode: false, id: 'cursorTop', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1006,7 +1006,7 @@ export namespace CoreNavigationCommands { export const CursorTopSelect: CoreEditorCommand = registerEditorCommand(new TopCommand({ inSelectionMode: true, id: 'cursorTopSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1038,7 +1038,7 @@ export namespace CoreNavigationCommands { export const CursorBottom: CoreEditorCommand = registerEditorCommand(new BottomCommand({ inSelectionMode: false, id: 'cursorBottom', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1050,7 +1050,7 @@ export namespace CoreNavigationCommands { export const CursorBottomSelect: CoreEditorCommand = registerEditorCommand(new BottomCommand({ inSelectionMode: true, id: 'cursorBottomSelect', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1063,7 +1063,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'editorScroll', - precondition: null, + precondition: undefined, description: EditorScroll_.description }); } @@ -1134,7 +1134,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'scrollLineUp', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1159,7 +1159,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'scrollPageUp', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1185,7 +1185,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'scrollLineDown', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1210,7 +1210,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'scrollPageDown', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1257,20 +1257,20 @@ export namespace CoreNavigationCommands { export const WordSelect: CoreEditorCommand = registerEditorCommand(new WordCommand({ inSelectionMode: false, id: '_wordSelect', - precondition: null + precondition: undefined })); export const WordSelectDrag: CoreEditorCommand = registerEditorCommand(new WordCommand({ inSelectionMode: true, id: '_wordSelectDrag', - precondition: null + precondition: undefined })); export const LastCursorWordSelect: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { constructor() { super({ id: 'lastCursorWordSelect', - precondition: null + precondition: undefined }); } @@ -1317,13 +1317,13 @@ export namespace CoreNavigationCommands { export const LineSelect: CoreEditorCommand = registerEditorCommand(new LineCommand({ inSelectionMode: false, id: '_lineSelect', - precondition: null + precondition: undefined })); export const LineSelectDrag: CoreEditorCommand = registerEditorCommand(new LineCommand({ inSelectionMode: true, id: '_lineSelectDrag', - precondition: null + precondition: undefined })); class LastCursorLineCommand extends CoreEditorCommand { @@ -1353,20 +1353,20 @@ export namespace CoreNavigationCommands { export const LastCursorLineSelect: CoreEditorCommand = registerEditorCommand(new LastCursorLineCommand({ inSelectionMode: false, id: 'lastCursorLineSelect', - precondition: null + precondition: undefined })); export const LastCursorLineSelectDrag: CoreEditorCommand = registerEditorCommand(new LastCursorLineCommand({ inSelectionMode: true, id: 'lastCursorLineSelectDrag', - precondition: null + precondition: undefined })); export const ExpandLineSelection: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { constructor() { super({ id: 'expandLineSelection', - precondition: null, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1445,7 +1445,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'revealLine', - precondition: null, + precondition: undefined, description: RevealLine_.description }); } @@ -1493,7 +1493,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'selectAll', - precondition: null + precondition: undefined }); } @@ -1513,7 +1513,7 @@ export namespace CoreNavigationCommands { constructor() { super({ id: 'setSelection', - precondition: null + precondition: undefined }); } @@ -1728,7 +1728,7 @@ class EditorHandlerCommand extends Command { constructor(id: string, handlerId: string, description?: ICommandHandlerDescription) { super({ id: id, - precondition: null, + precondition: undefined, description: description }); this._handlerId = handlerId; diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index ac0a7331d21..640002cbd26 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -40,17 +40,17 @@ export interface ICommandMenubarOptions { } export interface ICommandOptions { id: string; - precondition: ContextKeyExpr | null; - kbOpts?: ICommandKeybindingsOptions | null; + precondition: ContextKeyExpr | undefined; + kbOpts?: ICommandKeybindingsOptions; description?: ICommandHandlerDescription; menubarOpts?: ICommandMenubarOptions; } export abstract class Command { public readonly id: string; - public readonly precondition: ContextKeyExpr | null; - private readonly _kbOpts: ICommandKeybindingsOptions | null | undefined; - private readonly _menubarOpts: ICommandMenubarOptions | null | undefined; - private readonly _description: ICommandHandlerDescription | null | undefined; + public readonly precondition: ContextKeyExpr | undefined; + private readonly _kbOpts: ICommandKeybindingsOptions | undefined; + private readonly _menubarOpts: ICommandMenubarOptions | undefined; + private readonly _description: ICommandHandlerDescription | undefined; constructor(opts: ICommandOptions) { this.id = opts.id; diff --git a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index 672b156378a..675fa71bd8c 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -31,7 +31,7 @@ class JumpToBracketAction extends EditorAction { id: 'editor.action.jumpToBracket', label: nls.localize('smartSelect.jumpBracket', "Go to Bracket"), alias: 'Go to Bracket', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH, @@ -55,7 +55,7 @@ class SelectToBracketAction extends EditorAction { id: 'editor.action.selectToBracket', label: nls.localize('smartSelect.selectToBracket', "Select to Bracket"), alias: 'Select to Bracket', - precondition: null + precondition: undefined }); } diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index 990be3a2987..102f1b019dc 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -59,7 +59,7 @@ abstract class ExecCommandAction extends EditorAction { class ExecCommandCutAction extends ExecCommandAction { constructor() { - let kbOpts: ICommandKeybindingsOptions | null = { + let kbOpts: ICommandKeybindingsOptions | undefined = { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_X, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }, @@ -68,7 +68,7 @@ class ExecCommandCutAction extends ExecCommandAction { // Do not bind cut keybindings in the browser, // since browsers do that for us and it avoids security prompts if (!platform.isNative) { - kbOpts = null; + kbOpts = undefined; } super('cut', { id: 'editor.action.clipboardCutAction', @@ -107,7 +107,7 @@ class ExecCommandCutAction extends ExecCommandAction { class ExecCommandCopyAction extends ExecCommandAction { constructor() { - let kbOpts: ICommandKeybindingsOptions | null = { + let kbOpts: ICommandKeybindingsOptions | undefined = { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_C, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }, @@ -116,14 +116,14 @@ class ExecCommandCopyAction extends ExecCommandAction { // Do not bind copy keybindings in the browser, // since browsers do that for us and it avoids security prompts if (!platform.isNative) { - kbOpts = null; + kbOpts = undefined; } super('copy', { id: 'editor.action.clipboardCopyAction', label: nls.localize('actions.clipboard.copyLabel', "Copy"), alias: 'Copy', - precondition: null, + precondition: undefined, kbOpts: kbOpts, menuOpts: { group: CLIPBOARD_CONTEXT_MENU_GROUP, @@ -162,7 +162,7 @@ class ExecCommandCopyAction extends ExecCommandAction { class ExecCommandPasteAction extends ExecCommandAction { constructor() { - let kbOpts: ICommandKeybindingsOptions | null = { + let kbOpts: ICommandKeybindingsOptions | undefined = { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_V, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, @@ -171,7 +171,7 @@ class ExecCommandPasteAction extends ExecCommandAction { // Do not bind paste keybindings in the browser, // since browsers do that for us and it avoids security prompts if (!platform.isNative) { - kbOpts = null; + kbOpts = undefined; } super('paste', { @@ -201,7 +201,7 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction', label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"), alias: 'Copy With Syntax Highlighting', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: 0, diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index 89af0a637a6..3267cf0c4cd 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -228,7 +228,7 @@ class ShowContextMenu extends EditorAction { id: 'editor.action.showContextMenu', label: nls.localize('action.showContextMenu.label', "Show Editor Context Menu"), alias: 'Show Editor Context Menu', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Shift | KeyCode.F10, diff --git a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts index 4b37afc51aa..d543253b099 100644 --- a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts +++ b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts @@ -119,7 +119,7 @@ export class CursorUndo extends EditorAction { id: 'cursorUndo', label: nls.localize('cursor.undo', "Soft Undo"), alias: 'Soft Undo', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_U, diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index d51962cdfef..3b12e6e1c5d 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -422,7 +422,7 @@ export class StartFindAction extends EditorAction { id: FIND_IDS.StartFindAction, label: nls.localize('startFindAction', "Find"), alias: 'Find', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_F, @@ -459,7 +459,7 @@ export class StartFindWithSelectionAction extends EditorAction { id: FIND_IDS.StartFindWithSelection, label: nls.localize('startFindWithSelectionAction', "Find With Selection"), alias: 'Find With Selection', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: null, primary: 0, @@ -513,7 +513,7 @@ export class NextMatchFindAction extends MatchFindAction { id: FIND_IDS.NextMatchFindAction, label: nls.localize('findNextMatchAction', "Find Next"), alias: 'Find Next', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyCode.F3, @@ -535,7 +535,7 @@ export class PreviousMatchFindAction extends MatchFindAction { id: FIND_IDS.PreviousMatchFindAction, label: nls.localize('findPreviousMatchAction', "Find Previous"), alias: 'Find Previous', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Shift | KeyCode.F3, @@ -583,7 +583,7 @@ export class NextSelectionMatchFindAction extends SelectionMatchFindAction { id: FIND_IDS.NextSelectionMatchFindAction, label: nls.localize('nextSelectionMatchFindAction', "Find Next Selection"), alias: 'Find Next Selection', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyCode.F3, @@ -604,7 +604,7 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction { id: FIND_IDS.PreviousSelectionMatchFindAction, label: nls.localize('previousSelectionMatchFindAction', "Find Previous Selection"), alias: 'Find Previous Selection', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F3, @@ -625,7 +625,7 @@ export class StartFindReplaceAction extends EditorAction { id: FIND_IDS.StartFindReplaceAction, label: nls.localize('startReplace', "Replace"), alias: 'Replace', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_H, @@ -704,7 +704,7 @@ registerEditorCommand(new FindCommand({ registerEditorCommand(new FindCommand({ id: FIND_IDS.ToggleCaseSensitiveCommand, - precondition: null, + precondition: undefined, handler: x => x.toggleCaseSensitive(), kbOpts: { weight: KeybindingWeight.EditorContrib + 5, @@ -718,7 +718,7 @@ registerEditorCommand(new FindCommand({ registerEditorCommand(new FindCommand({ id: FIND_IDS.ToggleWholeWordCommand, - precondition: null, + precondition: undefined, handler: x => x.toggleWholeWords(), kbOpts: { weight: KeybindingWeight.EditorContrib + 5, @@ -732,7 +732,7 @@ registerEditorCommand(new FindCommand({ registerEditorCommand(new FindCommand({ id: FIND_IDS.ToggleRegexCommand, - precondition: null, + precondition: undefined, handler: x => x.toggleRegex(), kbOpts: { weight: KeybindingWeight.EditorContrib + 5, @@ -746,7 +746,7 @@ registerEditorCommand(new FindCommand({ registerEditorCommand(new FindCommand({ id: FIND_IDS.ToggleSearchScopeCommand, - precondition: null, + precondition: undefined, handler: x => x.toggleSearchScope(), kbOpts: { weight: KeybindingWeight.EditorContrib + 5, diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 24d64b50225..389ef232a1b 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -503,7 +503,7 @@ class UnfoldAction extends FoldingAction { id: 'editor.unfold', label: nls.localize('unfoldAction.label', "Unfold"), alias: 'Unfold', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, @@ -567,7 +567,7 @@ class UnFoldRecursivelyAction extends FoldingAction { id: 'editor.unfoldRecursively', label: nls.localize('unFoldRecursivelyAction.label', "Unfold Recursively"), alias: 'Unfold Recursively', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET), @@ -588,7 +588,7 @@ class FoldAction extends FoldingAction { id: 'editor.fold', label: nls.localize('foldAction.label', "Fold"), alias: 'Fold', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, @@ -652,7 +652,7 @@ class FoldRecursivelyAction extends FoldingAction { id: 'editor.foldRecursively', label: nls.localize('foldRecursivelyAction.label', "Fold Recursively"), alias: 'Fold Recursively', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET), @@ -674,7 +674,7 @@ class FoldAllBlockCommentsAction extends FoldingAction { id: 'editor.foldAllBlockComments', label: nls.localize('foldAllBlockComments.label', "Fold All Block Comments"), alias: 'Fold All Block Comments', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_SLASH), @@ -707,7 +707,7 @@ class FoldAllRegionsAction extends FoldingAction { id: 'editor.foldAllMarkerRegions', label: nls.localize('foldAllMarkerRegions.label', "Fold All Regions"), alias: 'Fold All Regions', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_8), @@ -740,7 +740,7 @@ class UnfoldAllRegionsAction extends FoldingAction { id: 'editor.unfoldAllMarkerRegions', label: nls.localize('unfoldAllMarkerRegions.label', "Unfold All Regions"), alias: 'Unfold All Regions', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_9), @@ -773,7 +773,7 @@ class FoldAllAction extends FoldingAction { id: 'editor.foldAll', label: nls.localize('foldAllAction.label', "Fold All"), alias: 'Fold All', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_0), @@ -794,7 +794,7 @@ class UnfoldAllAction extends FoldingAction { id: 'editor.unfoldAll', label: nls.localize('unfoldAllAction.label', "Unfold All"), alias: 'Unfold All', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_J), @@ -838,7 +838,7 @@ for (let i = 1; i <= 7; i++) { id: FoldLevelAction.ID(i), label: nls.localize('foldLevelAction.label', "Fold Level {0}", i), alias: `Fold Level ${i}`, - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | (KeyCode.KEY_0 + i)), diff --git a/src/vs/editor/contrib/fontZoom/fontZoom.ts b/src/vs/editor/contrib/fontZoom/fontZoom.ts index b0c9716132f..b7eefcb8c3f 100644 --- a/src/vs/editor/contrib/fontZoom/fontZoom.ts +++ b/src/vs/editor/contrib/fontZoom/fontZoom.ts @@ -15,7 +15,7 @@ class EditorFontZoomIn extends EditorAction { id: 'editor.action.fontZoomIn', label: nls.localize('EditorFontZoomIn.label', "Editor Font Zoom In"), alias: 'Editor Font Zoom In', - precondition: null + precondition: undefined }); } @@ -31,7 +31,7 @@ class EditorFontZoomOut extends EditorAction { id: 'editor.action.fontZoomOut', label: nls.localize('EditorFontZoomOut.label', "Editor Font Zoom Out"), alias: 'Editor Font Zoom Out', - precondition: null + precondition: undefined }); } @@ -47,7 +47,7 @@ class EditorFontZoomReset extends EditorAction { id: 'editor.action.fontZoomReset', label: nls.localize('EditorFontZoomReset.label', "Editor Font Zoom Reset"), alias: 'Editor Font Zoom Reset', - precondition: null + precondition: undefined }); } diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 737f6c9ebb9..2f36e372857 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -294,7 +294,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private gotoDefinition(target: IMouseTarget, sideBySide: boolean): Promise { this.editor.setPosition(target.position!); - const action = new DefinitionAction(new DefinitionActionConfig(sideBySide, false, true, false), { alias: '', label: '', id: '', precondition: null }); + const action = new DefinitionAction(new DefinitionActionConfig(sideBySide, false, true, false), { alias: '', label: '', id: '', precondition: undefined }); return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor)); } diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 8c7f8bd5250..b977abf6cdf 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -251,7 +251,7 @@ class ShowHoverAction extends EditorAction { ] }, "Show Hover"), alias: 'Show Hover', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I), diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index 54edce87b07..051a5378cf0 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -253,7 +253,7 @@ export class IndentUsingTabs extends ChangeIndentationSizeAction { id: IndentUsingTabs.ID, label: nls.localize('indentUsingTabs', "Indent Using Tabs"), alias: 'Indent Using Tabs', - precondition: null + precondition: undefined }); } } @@ -267,7 +267,7 @@ export class IndentUsingSpaces extends ChangeIndentationSizeAction { id: IndentUsingSpaces.ID, label: nls.localize('indentUsingSpaces', "Indent Using Spaces"), alias: 'Indent Using Spaces', - precondition: null + precondition: undefined }); } } @@ -281,7 +281,7 @@ export class DetectIndentation extends EditorAction { id: DetectIndentation.ID, label: nls.localize('detectIndentation', "Detect Indentation from Content"), alias: 'Detect Indentation from Content', - precondition: null + precondition: undefined }); } diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 3b94a6906ab..f44dbff1b0b 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -410,7 +410,7 @@ class OpenLinkAction extends EditorAction { id: 'editor.action.openLink', label: nls.localize('label', "Open Link"), alias: 'Open Link', - precondition: null + precondition: undefined }); } diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 330d6811342..9a699df3229 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -34,7 +34,7 @@ export class InsertCursorAbove extends EditorAction { id: 'editor.action.insertCursorAbove', label: nls.localize('mutlicursor.insertAbove', "Add Cursor Above"), alias: 'Add Cursor Above', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.UpArrow, @@ -83,7 +83,7 @@ export class InsertCursorBelow extends EditorAction { id: 'editor.action.insertCursorBelow', label: nls.localize('mutlicursor.insertBelow', "Add Cursor Below"), alias: 'Add Cursor Below', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.DownArrow, @@ -132,7 +132,7 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { id: 'editor.action.insertCursorAtEndOfEachLineSelected', label: nls.localize('mutlicursor.insertAtEndOfEachLineSelected', "Add Cursors to Line Ends"), alias: 'Add Cursors to Line Ends', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_I, @@ -184,7 +184,7 @@ class InsertCursorAtEndOfLineSelected extends EditorAction { id: 'editor.action.addCursorsToBottom', label: nls.localize('mutlicursor.addCursorsToBottom', "Add Cursors To Bottom"), alias: 'Add Cursors To Bottom', - precondition: null + precondition: undefined }); } @@ -214,7 +214,7 @@ class InsertCursorAtTopOfLineSelected extends EditorAction { id: 'editor.action.addCursorsToTop', label: nls.localize('mutlicursor.addCursorsToTop', "Add Cursors To Top"), alias: 'Add Cursors To Top', - precondition: null + precondition: undefined }); } @@ -650,7 +650,7 @@ export class AddSelectionToNextFindMatchAction extends MultiCursorSelectionContr id: 'editor.action.addSelectionToNextFindMatch', label: nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), alias: 'Add Selection To Next Find Match', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyCode.KEY_D, @@ -675,7 +675,7 @@ export class AddSelectionToPreviousFindMatchAction extends MultiCursorSelectionC id: 'editor.action.addSelectionToPreviousFindMatch', label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), alias: 'Add Selection To Previous Find Match', - precondition: null, + precondition: undefined, menubarOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', @@ -695,7 +695,7 @@ export class MoveSelectionToNextFindMatchAction extends MultiCursorSelectionCont id: 'editor.action.moveSelectionToNextFindMatch', label: nls.localize('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"), alias: 'Move Last Selection To Next Find Match', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D), @@ -714,7 +714,7 @@ export class MoveSelectionToPreviousFindMatchAction extends MultiCursorSelection id: 'editor.action.moveSelectionToPreviousFindMatch', label: nls.localize('moveSelectionToPreviousFindMatch', "Move Last Selection To Previous Find Match"), alias: 'Move Last Selection To Previous Find Match', - precondition: null + precondition: undefined }); } protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { @@ -728,7 +728,7 @@ export class SelectHighlightsAction extends MultiCursorSelectionControllerAction id: 'editor.action.selectHighlights', label: nls.localize('selectAllOccurrencesOfFindMatch', "Select All Occurrences of Find Match"), alias: 'Select All Occurrences of Find Match', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index f8edd2156ee..164723bb251 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -161,7 +161,7 @@ class GrowSelectionAction extends AbstractSmartSelect { id: 'editor.action.smartSelect.expand', label: nls.localize('smartSelect.expand', "Expand Selection"), alias: 'Expand Selection', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.RightArrow, @@ -187,7 +187,7 @@ class ShrinkSelectionAction extends AbstractSmartSelect { id: 'editor.action.smartSelect.shrink', label: nls.localize('smartSelect.shrink', "Shrink Selection"), alias: 'Shrink Selection', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.LeftArrow, diff --git a/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts b/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts index 22dabb97eb2..37e74657713 100644 --- a/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts +++ b/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts @@ -20,7 +20,7 @@ export class ToggleTabFocusModeAction extends EditorAction { id: ToggleTabFocusModeAction.ID, label: nls.localize({ key: 'toggle.tabMovesFocus', comment: ['Turn on/off use of tab key for moving focus around VS Code'] }, "Toggle Tab Key Moves Focus"), alias: 'Toggle Tab Key Moves Focus', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_M, diff --git a/src/vs/editor/contrib/tokenization/tokenization.ts b/src/vs/editor/contrib/tokenization/tokenization.ts index aeb5b996e1a..96e8d80e052 100644 --- a/src/vs/editor/contrib/tokenization/tokenization.ts +++ b/src/vs/editor/contrib/tokenization/tokenization.ts @@ -14,7 +14,7 @@ class ForceRetokenizeAction extends EditorAction { id: 'editor.action.forceRetokenize', label: nls.localize('forceRetokenize', "Developer: Force Retokenize"), alias: 'Developer: Force Retokenize', - precondition: null + precondition: undefined }); } diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index b39269c30a7..282a4e05ac1 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -98,7 +98,7 @@ export class CursorWordStartLeft extends WordLeftCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordStartLeft', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, @@ -115,7 +115,7 @@ export class CursorWordEndLeft extends WordLeftCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordEndLeft', - precondition: null + precondition: undefined }); } } @@ -126,7 +126,7 @@ export class CursorWordLeft extends WordLeftCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordStartFast, id: 'cursorWordLeft', - precondition: null + precondition: undefined }); } } @@ -137,7 +137,7 @@ export class CursorWordStartLeftSelect extends WordLeftCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordStartLeftSelect', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow, @@ -154,7 +154,7 @@ export class CursorWordEndLeftSelect extends WordLeftCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordEndLeftSelect', - precondition: null + precondition: undefined }); } } @@ -165,7 +165,7 @@ export class CursorWordLeftSelect extends WordLeftCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordLeftSelect', - precondition: null + precondition: undefined }); } } @@ -176,7 +176,7 @@ export class CursorWordStartRight extends WordRightCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordStartRight', - precondition: null + precondition: undefined }); } } @@ -187,7 +187,7 @@ export class CursorWordEndRight extends WordRightCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordEndRight', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.RightArrow, @@ -204,7 +204,7 @@ export class CursorWordRight extends WordRightCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordRight', - precondition: null + precondition: undefined }); } } @@ -215,7 +215,7 @@ export class CursorWordStartRightSelect extends WordRightCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordStartRightSelect', - precondition: null + precondition: undefined }); } } @@ -226,7 +226,7 @@ export class CursorWordEndRightSelect extends WordRightCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordEndRightSelect', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow, @@ -243,7 +243,7 @@ export class CursorWordRightSelect extends WordRightCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordRightSelect', - precondition: null + precondition: undefined }); } } diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts index 0c20a58b790..5887bd84a21 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -79,7 +79,7 @@ export class CursorWordPartLeft extends WordPartLeftCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordPartLeft', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: 0, @@ -98,7 +98,7 @@ export class CursorWordPartLeftSelect extends WordPartLeftCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordStart, id: 'cursorWordPartLeftSelect', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: 0, @@ -122,7 +122,7 @@ export class CursorWordPartRight extends WordPartRightCommand { inSelectionMode: false, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordPartRight', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: 0, @@ -138,7 +138,7 @@ export class CursorWordPartRightSelect extends WordPartRightCommand { inSelectionMode: true, wordNavigationType: WordNavigationType.WordEnd, id: 'cursorWordPartRightSelect', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: 0, diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index 4bde524d545..73dfa8d3ac3 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -330,7 +330,7 @@ class ShowAccessibilityHelpAction extends EditorAction { id: 'editor.action.showAccessibilityHelp', label: AccessibilityHelpNLS.showAccessibilityHelpAction, alias: 'Show Accessibility Help', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: (browser.isIE ? KeyMod.CtrlCmd | KeyCode.F1 : KeyMod.Alt | KeyCode.F1), diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index e60dd1925c8..47a2da412ef 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -84,7 +84,7 @@ class InspectTokens extends EditorAction { id: 'editor.action.inspectTokens', label: InspectTokensNLS.inspectTokensAction, alias: 'Developer: Inspect Tokens', - precondition: null + precondition: undefined }); } diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 2b89cf276d9..5d2960a5497 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -149,7 +149,7 @@ export class GotoLineAction extends BaseEditorQuickOpenAction { id: 'editor.action.gotoLine', label: GoToLineNLS.gotoLineActionLabel, alias: 'Go to Line...', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyCode.KEY_G, diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index e50716e3c37..b5d2715c808 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -82,7 +82,7 @@ export class QuickCommandAction extends BaseEditorQuickOpenAction { id: 'editor.action.quickCommand', label: QuickCommandNLS.quickCommandActionLabel, alias: 'Command Palette', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: (browser.isIE ? KeyMod.Alt | KeyCode.F1 : KeyCode.F1), diff --git a/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts index 9f6261900aa..22cb5063a94 100644 --- a/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts +++ b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts @@ -17,7 +17,7 @@ class ToggleHighContrast extends EditorAction { id: 'editor.action.toggleHighContrast', label: ToggleHighContrastNLS.toggleHighContrast, alias: 'Toggle High Contrast Theme', - precondition: null + precondition: undefined }); this._originalThemeName = null; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index 787c9ef03f8..32d2eeaaa2d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -282,7 +282,7 @@ class ShowAccessibilityHelpAction extends EditorAction { id: 'editor.action.showAccessibilityHelp', label: nls.localize('ShowAccessibilityHelpAction', "Show Accessibility Help"), alias: 'Show Accessibility Help', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Alt | KeyCode.F1, diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts index 516640bd086..b66f6d7b093 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts @@ -17,7 +17,7 @@ class InspectKeyMap extends EditorAction { id: 'workbench.action.inspectKeyMappings', label: nls.localize('workbench.action.inspectKeyMap', "Developer: Inspect Key Mappings"), alias: 'Developer: Inspect Key Mappings', - precondition: null + precondition: undefined }); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts index bf47a0c870d..c01b75c3cbb 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts @@ -102,7 +102,7 @@ class InspectTMScopes extends EditorAction { id: 'editor.action.inspectTMScopes', label: nls.localize('inspectTMScopes', "Developer: Inspect TM Scopes"), alias: 'Developer: Inspect TM Scopes', - precondition: null + precondition: undefined }); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index e192d5cf4cc..d2a7bd7f4c0 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -121,7 +121,7 @@ class ToggleWordWrapAction extends EditorAction { id: TOGGLE_WORD_WRAP_ID, label: nls.localize('toggle.wordwrap', "View: Toggle Word Wrap"), alias: 'View: Toggle Word Wrap', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: null, primary: KeyMod.Alt | KeyCode.KEY_Z, diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index d91b88891df..866bd19ca5a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -840,7 +840,7 @@ export class NextCommentThreadAction extends EditorAction { id: 'editor.action.nextCommentThreadAction', label: nls.localize('nextCommentThreadAction', "Go to Next Comment Thread"), alias: 'Go to Next Comment Thread', - precondition: null, + precondition: undefined, }); } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 15edca38b8f..a55cb493a1e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -26,7 +26,7 @@ class ToggleBreakpointAction extends EditorAction { id: TOGGLE_BREAKPOINT_ID, label: nls.localize('toggleBreakpointAction', "Debug: Toggle Breakpoint"), alias: 'Debug: Toggle Breakpoint', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyCode.F9, @@ -63,7 +63,7 @@ class ConditionalBreakpointAction extends EditorAction { id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, label: nls.localize('conditionalBreakpointEditorAction', "Debug: Add Conditional Breakpoint..."), alias: 'Debug: Add Conditional Breakpoint...', - precondition: null + precondition: undefined }); } @@ -85,7 +85,7 @@ class LogPointAction extends EditorAction { id: TOGGLE_LOG_POINT_ID, label: nls.localize('logPointEditorAction', "Debug: Add Logpoint..."), alias: 'Debug: Add Logpoint...', - precondition: null + precondition: undefined }); } @@ -288,7 +288,7 @@ class GoToNextBreakpointAction extends GoToBreakpointAction { id: 'editor.debug.action.goToNextBreakpoint', label: nls.localize('goToNextBreakpoint', "Debug: Go To Next Breakpoint"), alias: 'Debug: Go To Next Breakpoint', - precondition: null + precondition: undefined }); } } @@ -299,7 +299,7 @@ class GoToPreviousBreakpointAction extends GoToBreakpointAction { id: 'editor.debug.action.goToPreviousBreakpoint', label: nls.localize('goToPreviousBreakpoint', "Debug: Go To Previous Breakpoint"), alias: 'Debug: Go To Previous Breakpoint', - precondition: null + precondition: undefined }); } } diff --git a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts index 4226adc935a..5ee73f788d1 100644 --- a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts @@ -192,7 +192,7 @@ class CommandPaletteEditorAction extends EditorAction { id: ShowAllCommandsAction.ID, label: nls.localize('showCommands.label', "Command Palette..."), alias: 'Command Palette', - precondition: null, + precondition: undefined, menuOpts: { group: 'z_commands', order: 1 diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index d1531247ed7..2a22287760a 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -367,7 +367,7 @@ export class ShowPreviousChangeAction extends EditorAction { id: 'editor.action.dirtydiff.previous', label: nls.localize('show previous change', "Show Previous Change"), alias: 'Show Previous Change', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } }); } @@ -401,7 +401,7 @@ export class ShowNextChangeAction extends EditorAction { id: 'editor.action.dirtydiff.next', label: nls.localize('show next change', "Show Next Change"), alias: 'Show Next Change', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } }); } @@ -454,7 +454,7 @@ export class MoveToPreviousChangeAction extends EditorAction { id: 'workbench.action.editor.previousChange', label: nls.localize('move to previous change', "Move to Previous Change"), alias: 'Move to Previous Change', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } }); } @@ -496,7 +496,7 @@ export class MoveToNextChangeAction extends EditorAction { id: 'workbench.action.editor.nextChange', label: nls.localize('move to next change', "Move to Next Change"), alias: 'Move to Next Change', - precondition: null, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index d467ac1b6b3..0466fb5a593 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -502,7 +502,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({ id: SendSequenceTerminalCommand.ID, - precondition: null, + precondition: undefined, description: { description: `Send Custom Sequence To Terminal`, args: [{ From a65adc6fda7d96d54d3448c287b831838b18f051 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 May 2019 17:17:04 -0700 Subject: [PATCH 87/99] Use vscode.d.ts we ship instead of version from vscode package --- extensions/vscode-colorize-tests/package.json | 3 +-- extensions/vscode-colorize-tests/src/typings/ref.d.ts | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index 763513a590a..517429bb6d7 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -8,8 +8,7 @@ "vscode": "*" }, "scripts": { - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json", - "postinstall": "node ./node_modules/vscode/bin/install" + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json" }, "devDependencies": { "@types/node": "^10.12.21", diff --git a/extensions/vscode-colorize-tests/src/typings/ref.d.ts b/extensions/vscode-colorize-tests/src/typings/ref.d.ts index 8ea9f802a47..a45a0c6353f 100644 --- a/extensions/vscode-colorize-tests/src/typings/ref.d.ts +++ b/extensions/vscode-colorize-tests/src/typings/ref.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// +/// From 6685f2d2bdcf59429c88f171e375fa426055a3c0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 May 2019 17:28:31 -0700 Subject: [PATCH 88/99] Convert `toResource` to return undefined instead of null --- src/vs/workbench/browser/labels.ts | 2 +- src/vs/workbench/browser/parts/editor/editorPicker.ts | 2 +- src/vs/workbench/browser/parts/editor/titleControl.ts | 6 +++--- src/vs/workbench/common/editor.ts | 10 +++++----- src/vs/workbench/contrib/files/browser/fileCommands.ts | 7 ++++--- src/vs/workbench/contrib/files/browser/files.ts | 4 ++-- .../contrib/files/browser/views/openEditorsView.ts | 4 ++-- src/vs/workbench/contrib/files/common/files.ts | 2 +- src/vs/workbench/services/history/browser/history.ts | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 8eaff3f7e37..daafe77c133 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -350,7 +350,7 @@ class ResourceLabelWidget extends IconLabel { setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { this.setResource({ - resource: withNullAsUndefined(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })), + resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }), name: withNullAsUndefined(editor.getName()), description: withNullAsUndefined(editor.getDescription()) }, options); diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index 5844f8eaebe..6b30c32d092 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -51,7 +51,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { } getResource() { - return withNullAsUndefined(toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER })); + return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER }); } getAriaLabel(): string { diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 164c7ad43ea..4d32086b4e1 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -39,7 +39,7 @@ import { Themable } from 'vs/workbench/common/theme'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IFileService } from 'vs/platform/files/common/files'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; export interface IToolbarActions { @@ -221,7 +221,7 @@ export abstract class TitleControl extends Themable { this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables); // Update the resource context - this.resourceContext.set(this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null); + this.resourceContext.set(this.group.activeEditor ? withUndefinedAsNull(toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.MASTER })) : null); // Editor actions require the editor control to be there, so we retrieve it via service const activeControl = this.group.activeControl; @@ -288,7 +288,7 @@ export abstract class TitleControl extends Themable { // Update the resource context const currentContext = this.resourceContext.get(); - this.resourceContext.set(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })); + this.resourceContext.set(withUndefinedAsNull(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }))); // Find target anchor let anchor: HTMLElement | { x: number, y: number } = node; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index dd7224c579d..73b8e872b92 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -5,7 +5,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; -import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types'; +import { isUndefinedOrNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; @@ -1002,9 +1002,9 @@ export interface IResourceOptions { filterByScheme?: string | string[]; } -export function toResource(editor: IEditorInput | null | undefined, options?: IResourceOptions): URI | null { +export function toResource(editor: IEditorInput | undefined, options?: IResourceOptions): URI | undefined { if (!editor) { - return null; + return undefined; } if (options && options.supportSideBySide && editor instanceof SideBySideEditorInput) { @@ -1013,7 +1013,7 @@ export function toResource(editor: IEditorInput | null | undefined, options?: IR const resource = editor.getResource(); if (!resource || !options || !options.filterByScheme) { - return withUndefinedAsNull(resource); + return resource; } if (Array.isArray(options.filterByScheme) && options.filterByScheme.some(scheme => resource.scheme === scheme)) { @@ -1024,7 +1024,7 @@ export function toResource(editor: IEditorInput | null | undefined, options?: IR return resource; } - return null; + return undefined; } export const enum CloseDirection { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 747a78e70b8..be91bc74ffe 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -43,6 +43,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { UNTITLED_WORKSPACE_NAME } from 'vs/platform/workspaces/common/workspaces'; +import { withUndefinedAsNull } from 'vs/base/common/types'; // Commands @@ -349,7 +350,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -let globalResourceToCompare: URI | null; +let globalResourceToCompare: URI | undefined; let resourceSelectedForCompareContext: IContextKey; CommandsRegistry.registerCommand({ id: SELECT_FOR_COMPARE_COMMAND_ID, @@ -524,9 +525,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const editorService = accessor.get(IEditorService); let resource: URI | null = null; if (resourceOrObject && 'from' in resourceOrObject && resourceOrObject.from === 'menu') { - resource = toResource(editorService.activeEditor); + resource = withUndefinedAsNull(toResource(editorService.activeEditor)); } else { - resource = getResourceForCommand(resourceOrObject, accessor.get(IListService), editorService); + resource = withUndefinedAsNull(getResourceForCommand(resourceOrObject, accessor.get(IListService), editorService)); } return save(resource, true, undefined, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IWorkbenchEnvironmentService)); diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index 5f23f110a1a..85147dc5a43 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -14,7 +14,7 @@ import { coalesce } from 'vs/base/common/arrays'; // Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding // To cover all these cases we need to properly compute the resource on which the command is being executed -export function getResourceForCommand(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): URI | null { +export function getResourceForCommand(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): URI | undefined { if (URI.isUri(resource)) { return resource; } @@ -41,7 +41,7 @@ export function getResourceForCommand(resource: URI | object | undefined, listSe } } - return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null; + return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : undefined; } export function getMultiSelectedResources(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): Array { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index ba4b39603fe..80704ef56f2 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -41,7 +41,7 @@ import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; import { memoize } from 'vs/base/common/decorators'; import { ElementsDragAndDropData, DesktopDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { URI } from 'vs/base/common/uri'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; const $ = dom.$; @@ -245,7 +245,7 @@ export class OpenEditorsView extends ViewletPanel { const element = e.elements.length ? e.elements[0] : undefined; if (element instanceof OpenEditor) { this.dirtyEditorFocusedContext.set(this.textFileService.isDirty(withNullAsUndefined(element.getResource()))); - this.resourceContext.set(element.getResource()); + this.resourceContext.set(withUndefinedAsNull(element.getResource())); } else if (!!element) { this.groupFocusedContext.set(true); } diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 19f1c4c83d5..9b1d56da125 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -258,7 +258,7 @@ export class OpenEditor implements IEditorIdentifier { return this.editor.isDirty(); } - public getResource(): URI | null { + public getResource(): URI | undefined { return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER }); } } diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 74ef7b96867..3a1c48cc97e 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -958,7 +958,7 @@ export class HistoryService extends Disposable implements IHistoryService { getLastActiveFile(filterByScheme: string): URI | undefined { const history = this.getHistory(); for (const input of history) { - let resource: URI | null; + let resource: URI | undefined; if (input instanceof EditorInput) { resource = toResource(input, { filterByScheme }); } else { From 9bd21e3f2b406ca2f5c4f47e45514ceb803872ac Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 May 2019 17:28:47 -0700 Subject: [PATCH 89/99] Pick up latest ts@next --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 1c7d369b234..51de041b453 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.5.0-dev.20190514" + "typescript": "3.5.0-dev.20190515" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 33a730f9993..19e80f8a8fe 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.5.0-dev.20190514: - version "3.5.0-dev.20190514" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.0-dev.20190514.tgz#f791a398f2b57cc514434726f690da68144ee73c" - integrity sha512-XFEpXv7n2nnYek4SaL09QcN8JnxwVplaPKPquPO12qc9okhN7Afmiu54LT1WP/6RmcXRFma1QKrr8Kfj1nLxYA== +typescript@3.5.0-dev.20190515: + version "3.5.0-dev.20190515" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.0-dev.20190515.tgz#e34d9f4ae5352ecbb7bbc2f67db6cef3d5f7d58e" + integrity sha512-pE5yxFLdt5gT8PEX1Ra7vWU6HCdo4039be182oeWk8PTBWCWqr/XxlTjRxWfYgycNrI1vMjOux+n2y0aFJ9eUg== From a579bb364d0aa4d81faa093ed93c3be9c86ffb2d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 3 May 2019 15:05:07 +0200 Subject: [PATCH 90/99] Use same code path in accept for trying to update --- .../dialogs/browser/remoteFileDialog.ts | 67 +++++++------------ 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 662f6e5c232..8d30f12cb1b 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -350,54 +350,34 @@ export class RemoteFileDialog { } private async onDidAccept(): Promise { + let updateString: string; + let updateUri: URI; + if (this.filePickBox.activeItems.length === 1) { + updateUri = this.filePickBox.selectedItems[0].uri; + updateString = this.pathFromUri(updateUri); + } else { + updateString = this.filePickBox.value; + updateUri = this.filePickBoxValue(); + } + // If the items have updated, don't try to resolve + if ((await this.tryUpdateItems(updateString, updateUri)) !== UpdateResult.NotUpdated) { + return; + } + this.filePickBox.busy = true; let resolveValue: URI | undefined; - let navigateValue: URI | undefined; - let inputUri: URI | undefined; - let inputUriDirname: URI | undefined; - let stat: IFileStat | undefined; - let statDirname: IFileStat | undefined; - try { - inputUri = resources.removeTrailingPathSeparator(this.filePickBoxValue()); - inputUriDirname = resources.dirname(inputUri); - statDirname = await this.fileService.resolve(inputUriDirname); - stat = await this.fileService.resolve(inputUri); - } catch (e) { - // do nothing - } - // Find resolve value if (this.filePickBox.activeItems.length === 0) { - if (!this.requiresTrailing && resources.isEqual(this.currentFolder, inputUri, true)) { - resolveValue = inputUri; - } else if (statDirname && statDirname.isDirectory) { - resolveValue = inputUri; - } else if (stat && stat.isDirectory) { - navigateValue = inputUri; - } + resolveValue = this.filePickBoxValue(); } else if (this.filePickBox.activeItems.length === 1) { - const item = this.filePickBox.selectedItems[0]; - if (item) { - if (!item.isFolder) { - resolveValue = item.uri; - } else { - navigateValue = item.uri; - } - } + resolveValue = this.filePickBox.selectedItems[0].uri; } - - - if (navigateValue) { - // Try to navigate into the folder. - await this.updateItems(navigateValue, true, this.trailing); - } else { - if (resolveValue) { - resolveValue = this.addPostfix(resolveValue); - } - if (await this.validate(resolveValue)) { - this.filePickBox.busy = false; - return resolveValue; - } + if (resolveValue) { + resolveValue = this.addPostfix(resolveValue); + } + if (await this.validate(resolveValue)) { + this.filePickBox.busy = false; + return resolveValue; } this.filePickBox.busy = false; return undefined; @@ -771,7 +751,7 @@ export class RemoteFileDialog { private createBackItem(currFolder: URI): FileQuickPickItem | null { const parentFolder = resources.dirname(currFolder)!; if (!resources.isEqual(currFolder, parentFolder, true)) { - return { label: '..', uri: resources.dirname(currFolder), isFolder: true }; + return { label: '..', uri: this.ensureTrailingSeparator(parentFolder), isFolder: true }; } return null; } @@ -829,6 +809,7 @@ export class RemoteFileDialog { const stat = await this.fileService.resolve(fullPath); if (stat.isDirectory) { filename = this.basenameWithTrailingSlash(fullPath); + fullPath = this.ensureTrailingSeparator(fullPath); return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; } else if (!stat.isDirectory && this.allowFileSelection && this.filterFile(fullPath)) { return { label: filename, uri: fullPath, isFolder: false, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined) }; From 0c9dd09e787e5e4fad169e97de458e7bc941ae54 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 6 May 2019 10:47:15 +0200 Subject: [PATCH 91/99] Fix file picker backtracking when holding delete key Fixes https://github.com/Microsoft/vscode-remote-release/issues/4 --- .../dialogs/browser/remoteFileDialog.ts | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 8d30f12cb1b..fb7e1b0040f 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -22,11 +22,11 @@ import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; -import { equalsIgnoreCase, format } from 'vs/base/common/strings'; +import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; interface FileQuickPickItem extends IQuickPickItem { uri: URI; @@ -63,6 +63,7 @@ export class RemoteFileDialog { private userHome: URI; private badPath: string | undefined; private remoteAgentEnvironment: IRemoteAgentEnvironment | null; + private separator: string; constructor( @IFileService private readonly fileService: IFileService, @@ -159,6 +160,7 @@ export class RemoteFileDialog { private async pickResource(isSave: boolean = false): Promise { this.allowFolderSelection = !!this.options.canSelectFolders; this.allowFileSelection = !!this.options.canSelectFiles; + this.separator = this.labelService.getSeparator(this.scheme, this.remoteAuthority); this.hidden = false; let homedir: URI = this.options.defaultUri ? this.options.defaultUri : this.workspaceContextService.getWorkspace().folders[0].uri; let stat: IFileStat | undefined; @@ -306,7 +308,7 @@ export class RemoteFileDialog { this.filePickBox.show(); this.contextKey.set(true); - await this.updateItems(homedir, false, this.trailing); + await this.updateItems(homedir, true, this.trailing); if (this.trailing) { this.filePickBox.valueSelection = [this.filePickBox.value.length - this.trailing.length, this.filePickBox.value.length - ext.length]; } else { @@ -350,21 +352,36 @@ export class RemoteFileDialog { } private async onDidAccept(): Promise { - let updateString: string; - let updateUri: URI; + this.filePickBox.busy = true; if (this.filePickBox.activeItems.length === 1) { - updateUri = this.filePickBox.selectedItems[0].uri; - updateString = this.pathFromUri(updateUri); + const item = this.filePickBox.selectedItems[0]; + if (item.isFolder) { + if (this.trailing) { + await this.updateItems(item.uri, true, this.trailing); + } else { + // When possible, cause the update to happen by modifying the input box. + // This allows all input box updates to happen first, and uses the same code path as the user typing. + const newPath = this.pathFromUri(item.uri); + if (startsWithIgnoreCase(newPath, this.filePickBox.value)) { + const insertValue = newPath.substring(this.filePickBox.value.length, newPath.length); + this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; + this.insertText(newPath, insertValue); + } else if ((item.label === '..') && startsWithIgnoreCase(this.filePickBox.value, newPath)) { + this.filePickBox.valueSelection = [newPath.length, this.filePickBox.value.length]; + this.insertText(newPath, ''); + } else { + await this.updateItems(item.uri, true); + } + } + return; + } } else { - updateString = this.filePickBox.value; - updateUri = this.filePickBoxValue(); - } - // If the items have updated, don't try to resolve - if ((await this.tryUpdateItems(updateString, updateUri)) !== UpdateResult.NotUpdated) { - return; + // If the items have updated, don't try to resolve + if ((await this.tryUpdateItems(this.filePickBox.value, this.filePickBoxValue())) !== UpdateResult.NotUpdated) { + return; + } } - this.filePickBox.busy = true; let resolveValue: URI | undefined; // Find resolve value if (this.filePickBox.activeItems.length === 0) { @@ -384,10 +401,7 @@ export class RemoteFileDialog { } private async tryUpdateItems(value: string, valueUri: URI): Promise { - if (this.filePickBox.busy) { - this.badPath = undefined; - return UpdateResult.Updating; - } else if ((value.length > 0) && ((value[value.length - 1] === '~') || (value[0] === '~'))) { + if ((value.length > 0) && ((value[value.length - 1] === '~') || (value[0] === '~'))) { let newDir = this.userHome; if ((value[0] === '~') && (value.length > 1)) { newDir = resources.joinPath(newDir, value.substring(1)); @@ -636,7 +650,7 @@ export class RemoteFileDialog { return uri; } else { const dir = resources.dirname(uri); - const base = resources.basename(uri) + this.labelService.getSeparator(uri.scheme, uri.authority); + const base = resources.basename(uri) + this.separator; return resources.joinPath(dir, base); } } @@ -646,24 +660,16 @@ export class RemoteFileDialog { this.userEnteredPathSegment = trailing ? trailing : ''; this.autoCompletePathSegment = ''; const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true); - const oldFolder = this.currentFolder; - const newFolderPath = this.pathFromUri(newFolder, true); this.currentFolder = this.ensureTrailingSeparator(newFolder); return this.createItems(this.currentFolder).then(items => { this.filePickBox.items = items; if (this.allowFolderSelection) { this.filePickBox.activeItems = []; } - if (!equalsIgnoreCase(this.filePickBox.value, newValue)) { - // the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory. - if (!equalsIgnoreCase(this.filePickBox.value.substring(0, newValue.length), newValue)) { - this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; - this.insertText(newValue, newValue); - } else if (force || equalsIgnoreCase(this.pathFromUri(resources.dirname(oldFolder), true), newFolderPath)) { - // This is the case where the user went up one dir or is clicking on dirs. We need to make sure that we remove the final dir. - this.filePickBox.valueSelection = [newFolderPath.length, this.filePickBox.value.length]; - this.insertText(newValue, ''); - } + // the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory. + if (!equalsIgnoreCase(this.filePickBox.value, newValue) && force) { + this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; + this.insertText(newValue, newValue); } if (force && trailing) { // Keep the cursor position in front of the save as name. @@ -676,15 +682,14 @@ export class RemoteFileDialog { } private pathFromUri(uri: URI, endWithSeparator: boolean = false): string { - const sep = this.labelService.getSeparator(uri.scheme, uri.authority); let result: string = uri.fsPath.replace(/\n/g, ''); - if (sep === '/') { - result = result.replace(/\\/g, sep); + if (this.separator === '/') { + result = result.replace(/\\/g, this.separator); } else { - result = result.replace(/\//g, sep); + result = result.replace(/\//g, this.separator); } if (endWithSeparator && !this.endsWithSlash(result)) { - result = result + sep; + result = result + this.separator; } return result; } @@ -692,7 +697,7 @@ export class RemoteFileDialog { private pathAppend(uri: URI, additional: string): string { if ((additional === '..') || (additional === '.')) { const basePath = this.pathFromUri(uri); - return basePath + (this.endsWithSlash(basePath) ? '' : this.labelService.getSeparator(uri.scheme, uri.authority)) + additional; + return basePath + (this.endsWithSlash(basePath) ? '' : this.separator) + additional; } else { return this.pathFromUri(resources.joinPath(uri, additional)); } From b57b98761d15bd3576ea96b2afc6d70686238a5a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 May 2019 11:29:00 -0700 Subject: [PATCH 92/99] Re-use isValidBasename in file picker (#73332) Fixes #72866 --- src/vs/base/common/extpath.ts | 19 +++++---- .../dialogs/browser/remoteFileDialog.ts | 39 +------------------ 2 files changed, 13 insertions(+), 45 deletions(-) diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts index 8d664f3a650..8f47ac2cc5e 100644 --- a/src/vs/base/common/extpath.ts +++ b/src/vs/base/common/extpath.ts @@ -139,19 +139,22 @@ export function isUNC(path: string): boolean { } // Reference: https://en.wikipedia.org/wiki/Filename -const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g; +const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g; +const UNIX_INVALID_FILE_CHARS = /[\\/]/g; const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; -export function isValidBasename(name: string | null | undefined): boolean { +export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean { + const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS; + if (!name || name.length === 0 || /^\s+$/.test(name)) { return false; // require a name that is not just whitespace } - INVALID_FILE_CHARS.lastIndex = 0; // the holy grail of software development - if (INVALID_FILE_CHARS.test(name)) { + invalidFileChars.lastIndex = 0; // the holy grail of software development + if (invalidFileChars.test(name)) { return false; // check for certain invalid file characters } - if (isWindows && WINDOWS_FORBIDDEN_NAMES.test(name)) { + if (isWindowsOS && WINDOWS_FORBIDDEN_NAMES.test(name)) { return false; // check for certain invalid file names } @@ -159,16 +162,16 @@ export function isValidBasename(name: string | null | undefined): boolean { return false; // check for reserved values } - if (isWindows && name[name.length - 1] === '.') { + if (isWindowsOS && name[name.length - 1] === '.') { return false; // Windows: file cannot end with a "." } - if (isWindows && name.length !== name.trim().length) { + if (isWindowsOS && name.length !== name.trim().length) { return false; // Windows: file cannot end with a whitespace } if (name.length > 255) { - return false; // most file systems do not allow files > 255 lenth + return false; // most file systems do not allow files > 255 length } return true; diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index fb7e1b0040f..c9fec099a49 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -26,6 +26,7 @@ import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/s import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { isValidBasename } from 'vs/base/common/extpath'; import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; interface FileQuickPickItem extends IQuickPickItem { @@ -40,11 +41,6 @@ enum UpdateResult { InvalidPath } -// Reference: https://en.wikipedia.org/wiki/Filename -const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g; -const UNIX_INVALID_FILE_CHARS = /[\\/]/g; -const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; - export class RemoteFileDialog { private options: IOpenDialogOptions; private currentFolder: URI; @@ -618,7 +614,7 @@ export class RemoteFileDialog { // Show a yes/no prompt const message = nls.localize('remoteFileDialog.validateExisting', '{0} already exists. Are you sure you want to overwrite it?', resources.basename(uri)); return this.yesNoPrompt(uri, message); - } else if (!(await this.isValidBaseName(resources.basename(uri)))) { + } else if (!(isValidBasename(resources.basename(uri), await this.isWindowsOS()))) { // Filename not allowed this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateBadFilename', 'Please enter a valid file name.'); return Promise.resolve(false); @@ -712,37 +708,6 @@ export class RemoteFileDialog { return isWindowsOS; } - private async isValidBaseName(name: string): Promise { - if (!name || name.length === 0 || /^\s+$/.test(name)) { - return false; // require a name that is not just whitespace - } - - const isWindowsOS = await this.isWindowsOS(); - const INVALID_FILE_CHARS = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS; - INVALID_FILE_CHARS.lastIndex = 0; // the holy grail of software development - if (INVALID_FILE_CHARS.test(name)) { - return false; // check for certain invalid file characters - } - - if (isWindowsOS && WINDOWS_FORBIDDEN_NAMES.test(name)) { - return false; // check for certain invalid file names - } - - if (name === '.' || name === '..') { - return false; // check for reserved values - } - - if (isWindowsOS && name[name.length - 1] === '.') { - return false; // Windows: file cannot end with a "." - } - - if (isWindowsOS && name.length !== name.trim().length) { - return false; // Windows: file cannot end with a whitespace - } - - return true; - } - private endsWithSlash(s: string) { return /[\/\\]$/.test(s); } From 2cf9beda589f0a928217cf7fb536a8312e9b73a7 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 May 2019 11:30:28 -0700 Subject: [PATCH 93/99] Fix hasTrailingPathSeparator for Windows drives (#73229) * Fix hasTrailingPathSeparator for Windows drives * Add trailing slash utility and tests --- src/vs/base/common/resources.ts | 30 ++++++++++++++++++----- src/vs/base/test/common/resources.test.ts | 22 +++++++++++++++-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 40095c3aaa5..521a02a4b57 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -176,28 +176,46 @@ export function isAbsolutePath(resource: URI): boolean { /** * Returns true if the URI path has a trailing path separator */ -export function hasTrailingPathSeparator(resource: URI): boolean { +export function hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean { if (resource.scheme === Schemas.file) { const fsp = originalFSPath(resource); - return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === paths.sep; + return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep; } else { const p = resource.path; return p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; // ignore the slash at offset 0 } } - /** - * Removes a trailing path seperator, if theres one. + * Removes a trailing path separator, if there's one. * Important: Doesn't remove the first slash, it would make the URI invalid */ -export function removeTrailingPathSeparator(resource: URI): URI { - if (hasTrailingPathSeparator(resource)) { +export function removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + if (hasTrailingPathSeparator(resource, sep)) { return resource.with({ path: resource.path.substr(0, resource.path.length - 1) }); } return resource; } +/** + * Adds a trailing path separator to the URI if there isn't one already. + * For example, c:\ would be unchanged, but c:\users would become c:\users\ + */ +export function addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + let isRootSep: boolean = false; + if (resource.scheme === Schemas.file) { + const fsp = originalFSPath(resource); + isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep)); + } else { + sep = '/'; + const p = resource.path; + isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; + } + if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) { + return resource.with({ path: resource.path + '/' }); + } + return resource; +} /** * Returns a relative path between two URIs. If the URIs don't have the same schema or authority, `undefined` is returned. diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 22d576dd3f1..b9d1a99d539 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath } from 'vs/base/common/resources'; +import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath, addTrailingPathSeparator } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { toSlashes } from 'vs/base/common/extpath'; @@ -178,6 +178,9 @@ suite('Resources', () => { assertEqualURI(removeTrailingPathSeparator(u1), expected, u1.toString()); } + function assertAddTrailingSeparator(u1: URI, expected: URI) { + assertEqualURI(addTrailingPathSeparator(u1), expected, u1.toString()); + } test('trailingPathSeparator', () => { assertTrailingSeparator(URI.parse('foo://a/foo'), false); @@ -190,6 +193,11 @@ suite('Resources', () => { assertRemoveTrailingSeparator(URI.parse('foo://a/'), URI.parse('foo://a/')); assertRemoveTrailingSeparator(URI.parse('foo://a'), URI.parse('foo://a')); + assertAddTrailingSeparator(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/')); + assertAddTrailingSeparator(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo/')); + assertAddTrailingSeparator(URI.parse('foo://a/'), URI.parse('foo://a/')); + assertAddTrailingSeparator(URI.parse('foo://a'), URI.parse('foo://a/')); + if (isWindows) { assertTrailingSeparator(URI.file('c:\\a\\foo'), false); assertTrailingSeparator(URI.file('c:\\a\\foo\\'), true); @@ -202,6 +210,12 @@ suite('Resources', () => { assertRemoveTrailingSeparator(URI.file('c:\\'), URI.file('c:\\')); assertRemoveTrailingSeparator(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some')); assertRemoveTrailingSeparator(URI.file('\\\\server\\share\\'), URI.file('\\\\server\\share\\')); + + assertAddTrailingSeparator(URI.file('c:\\a\\foo'), URI.file('c:\\a\\foo\\')); + assertAddTrailingSeparator(URI.file('c:\\a\\foo\\'), URI.file('c:\\a\\foo\\')); + assertAddTrailingSeparator(URI.file('c:\\'), URI.file('c:\\')); + assertAddTrailingSeparator(URI.file('\\\\server\\share\\some'), URI.file('\\\\server\\share\\some\\')); + assertAddTrailingSeparator(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some\\')); } else { assertTrailingSeparator(URI.file('/foo/bar'), false); assertTrailingSeparator(URI.file('/foo/bar/'), true); @@ -210,12 +224,16 @@ suite('Resources', () => { assertRemoveTrailingSeparator(URI.file('/foo/bar'), URI.file('/foo/bar')); assertRemoveTrailingSeparator(URI.file('/foo/bar/'), URI.file('/foo/bar')); assertRemoveTrailingSeparator(URI.file('/'), URI.file('/')); + + assertAddTrailingSeparator(URI.file('/foo/bar'), URI.file('/foo/bar/')); + assertAddTrailingSeparator(URI.file('/foo/bar/'), URI.file('/foo/bar/')); + assertAddTrailingSeparator(URI.file('/'), URI.file('/')); } }); function assertEqualURI(actual: URI, expected: URI, message?: string) { if (!isEqual(expected, actual)) { - assert.equal(expected.toString(), actual.toString(), message); + assert.equal(actual.toString(), expected.toString(), message); } } From 07ec42fb0070bd629c1a7fa8f17a8183d7ac4958 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 May 2019 11:41:55 -0700 Subject: [PATCH 94/99] Fix open folder in simple file picker --- src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index c9fec099a49..4c9137dfa6c 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -404,7 +404,7 @@ export class RemoteFileDialog { } await this.updateItems(newDir, true); return UpdateResult.Updated; - } else if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) { + } else if (!resources.isEqual(this.currentFolder, valueUri, true) && (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true)))) { let stat: IFileStat | undefined; try { stat = await this.fileService.resolve(valueUri); From e0917ec45ee96bf98c9e0c30cca4ed0e52b73539 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 16 May 2019 14:22:47 -0700 Subject: [PATCH 95/99] update distro: allow badge.buildkite.com for badges --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 57b9aca4fb5..e18d98df0c6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.35.0", - "distro": "64154bcc07e6621913c55f0daa25bef184b758a6", + "distro": "a80429a5120c955df4bd2dd95b1cff71b72ab556", "author": { "name": "Microsoft Corporation" }, @@ -155,4 +155,4 @@ "windows-mutex": "0.2.1", "windows-process-tree": "0.2.3" } -} +} \ No newline at end of file From 2e0e111d4745199b4492c9e7ae9d5e574b08c0eb Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 May 2019 15:17:53 -0700 Subject: [PATCH 96/99] Update name of simple file dialog setting --- src/vs/workbench/browser/workbench.contribution.ts | 5 ----- src/vs/workbench/contrib/files/browser/files.contribution.ts | 5 +++++ .../workbench/services/dialogs/browser/fileDialogService.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index e9cf5a91312..696f351bc09 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -239,11 +239,6 @@ import { isMacintosh } from 'vs/base/common/platform'; 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), 'default': false, 'scope': ConfigurationScope.APPLICATION - }, - 'workbench.simpleFileDialog.enable': { - 'type': 'boolean', - 'description': nls.localize('workbench.simpleFileDialog.enable', "Enables the simple file dialog. The simple file dialog replaces the system file dialog when enabled."), - 'default': false, } } }); diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index ef84bbdaf89..65c3e55c4a3 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -327,6 +327,11 @@ configurationRegistry.registerConfiguration({ 'type': 'number', 'default': 4096, 'markdownDescription': nls.localize('maxMemoryForLargeFilesMB', "Controls the memory available to VS Code after restart when trying to open large files. Same effect as specifying `--max-memory=NEWSIZE` on the command line.") + }, + 'files.simpleDialog.enable': { + 'type': 'boolean', + 'description': nls.localize('files.simpleDialog.enable', "Enables the simple file dialog. The simple file dialog replaces the system file dialog when enabled."), + 'default': false, } } }); diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index b54e64c6e21..9ff5db4854a 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -85,7 +85,7 @@ export class FileDialogService implements IFileDialogService { } private shouldUseSimplified(schema: string): boolean { - const setting = this.configurationService.getValue('workbench.simpleFileDialog.enable'); + const setting = this.configurationService.getValue('files.simpleDialog.enable'); return (schema !== Schemas.file) || (setting === true); } From 8600294d5f2beab240b618f54a1b770facfea923 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 16 May 2019 15:34:44 -0700 Subject: [PATCH 97/99] debt - flaky test on windows --- .../services/files/test/node/diskFileService.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/services/files/test/node/diskFileService.test.ts b/src/vs/workbench/services/files/test/node/diskFileService.test.ts index a992b0fcce9..8068df82ed6 100644 --- a/src/vs/workbench/services/files/test/node/diskFileService.test.ts +++ b/src/vs/workbench/services/files/test/node/diskFileService.test.ts @@ -1523,6 +1523,10 @@ suite('Disk File Service', () => { }); test('watch - file - rename file', done => { + if (isWindows) { + return done(); // watch tests are flaky on other platforms + } + const toWatch = URI.file(join(testDir, 'index-watch1.html')); const toWatchRenamed = URI.file(join(testDir, 'index-watch1-renamed.html')); writeFileSync(toWatch.fsPath, 'Init'); From 09b738cacf6793f2c1b9063fac7a56ead937395a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 May 2019 15:37:05 -0700 Subject: [PATCH 98/99] Fix trailing slash in file picker --- .../services/dialogs/browser/remoteFileDialog.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 4c9137dfa6c..4ade8743b8b 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -641,22 +641,12 @@ export class RemoteFileDialog { return Promise.resolve(true); } - private ensureTrailingSeparator(uri: URI): URI { - if (resources.hasTrailingPathSeparator(uri)) { - return uri; - } else { - const dir = resources.dirname(uri); - const base = resources.basename(uri) + this.separator; - return resources.joinPath(dir, base); - } - } - private async updateItems(newFolder: URI, force: boolean = false, trailing?: string) { this.filePickBox.busy = true; this.userEnteredPathSegment = trailing ? trailing : ''; this.autoCompletePathSegment = ''; const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true); - this.currentFolder = this.ensureTrailingSeparator(newFolder); + this.currentFolder = resources.addTrailingPathSeparator(newFolder, this.separator); return this.createItems(this.currentFolder).then(items => { this.filePickBox.items = items; if (this.allowFolderSelection) { @@ -721,7 +711,7 @@ export class RemoteFileDialog { private createBackItem(currFolder: URI): FileQuickPickItem | null { const parentFolder = resources.dirname(currFolder)!; if (!resources.isEqual(currFolder, parentFolder, true)) { - return { label: '..', uri: this.ensureTrailingSeparator(parentFolder), isFolder: true }; + return { label: '..', uri: resources.addTrailingPathSeparator(parentFolder, this.separator), isFolder: true }; } return null; } @@ -779,7 +769,7 @@ export class RemoteFileDialog { const stat = await this.fileService.resolve(fullPath); if (stat.isDirectory) { filename = this.basenameWithTrailingSlash(fullPath); - fullPath = this.ensureTrailingSeparator(fullPath); + fullPath = resources.addTrailingPathSeparator(fullPath, this.separator); return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; } else if (!stat.isDirectory && this.allowFileSelection && this.filterFile(fullPath)) { return { label: filename, uri: fullPath, isFolder: false, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined) }; From 7fc0c65b91b57a13d01bf5637d4fd7aad2daf624 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 May 2019 15:52:07 -0700 Subject: [PATCH 99/99] Terminate all tasks polish Fixes #73417 --- .../tasks/electron-browser/task.contribution.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts index 5947afebd05..2a9a0b42a15 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts @@ -1973,7 +1973,7 @@ class TaskService extends Disposable implements ITaskService { } private showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, additionalEntries?: TaskQuickPickEntry[]): Promise { - let _createEntries = (): Promise => { + let _createEntries = (): Promise[]> => { if (Array.isArray(tasks)) { return Promise.resolve(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); } else { @@ -1983,8 +1983,8 @@ class TaskService extends Disposable implements ITaskService { return this.quickInputService.pick(_createEntries().then((entries) => { if ((entries.length === 0) && defaultEntry) { entries.push(defaultEntry); - } - else if (entries.length > 1 && additionalEntries && additionalEntries.length > 0) { + } else if (entries.length > 1 && additionalEntries && additionalEntries.length > 0) { + entries.push({ type: 'separator', label: '' }); entries.push(additionalEntries[0]); } return entries; @@ -2218,7 +2218,7 @@ class TaskService extends Disposable implements ITaskService { } let runQuickPick = (promise?: Promise) => { this.showQuickPick(promise || this.getActiveTasks(), - nls.localize('TaskService.taskToTerminate', 'Select task to terminate'), + nls.localize('TaskService.taskToTerminate', 'Select a task to terminate'), { label: nls.localize('TaskService.noTaskRunning', 'No task is currently running'), task: undefined @@ -2226,7 +2226,7 @@ class TaskService extends Disposable implements ITaskService { false, true, undefined, [{ - label: nls.localize('TaskService.terminateAllRunningTasks', 'All running tasks'), + label: nls.localize('TaskService.terminateAllRunningTasks', 'All Running Tasks'), id: 'terminateAll', task: undefined }]