Merge from vscode b8c2e7108b3cae7aa2782112da654bedd8bb3a52 (#4808)

This commit is contained in:
Karl Burtram
2019-04-02 14:35:06 -07:00
committed by GitHub
parent e83a6f9c2e
commit f8706abebe
53 changed files with 495 additions and 584 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"description": "Dependencies shared by all extensions", "description": "Dependencies shared by all extensions",
"dependencies": { "dependencies": {
"typescript": "3.4.0-rc" "typescript": "3.4.1"
}, },
"scripts": { "scripts": {
"postinstall": "node ./postinstall" "postinstall": "node ./postinstall"
+4 -4
View File
@@ -2,7 +2,7 @@
# yarn lockfile v1 # yarn lockfile v1
typescript@3.4.0-rc: typescript@3.4.1:
version "3.4.0-rc" version "3.4.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.0-rc.tgz#bc4026c3c517a30fa514b896f2684c6ba92caadb" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6"
integrity sha512-wtm2GwuV0Yy2zvmpFaMDtWHnRbzfERi9FLpL+K+JqGkHoclgEQ6TmD1AMxMIjs80NWRIq0Xp1veQyuZbGsUYsg== integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==
-16
View File
@@ -11,7 +11,6 @@ const lp = require('./vs/base/node/languagePacks');
perf.mark('main:started'); perf.mark('main:started');
const fs = require('fs');
const path = require('path'); const path = require('path');
const bootstrap = require('./bootstrap'); const bootstrap = require('./bootstrap');
const paths = require('./paths'); const paths = require('./paths');
@@ -29,21 +28,6 @@ bootstrap.enableASARSupport();
// Set userData path before app 'ready' event and call to process.chdir // Set userData path before app 'ready' event and call to process.chdir
const args = parseCLIArgs(); const args = parseCLIArgs();
const userDataPath = getUserDataPath(args); const userDataPath = getUserDataPath(args);
// global storage migration needs to happen very early before app.on("ready")
// TODO@Ben remove after a while
try {
const globalStorageHome = path.join(userDataPath, 'User', 'globalStorage', 'state.vscdb');
const localStorageHome = path.join(userDataPath, 'Local Storage');
const localStorageDB = path.join(localStorageHome, 'file__0.localstorage');
const localStorageDBBackup = path.join(localStorageHome, 'file__0.vscmig');
if (!fs.existsSync(globalStorageHome) && fs.existsSync(localStorageDB)) {
fs.renameSync(localStorageDB, localStorageDBBackup);
}
} catch (error) {
console.error(error);
}
app.setPath('userData', userDataPath); app.setPath('userData', userDataPath);
// Update cwd based on environment and platform // Update cwd based on environment and platform
+22 -23
View File
@@ -366,18 +366,23 @@ export interface IWriteFileOptions {
}; };
} }
interface IEnsuredWriteFileOptions extends IWriteFileOptions {
mode: number;
flag: string;
}
let canFlush = true; let canFlush = true;
export function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options: IWriteFileOptions, callback: (error?: Error) => void): void { export function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options: IWriteFileOptions, callback: (error?: Error) => void): void {
options = ensureOptions(options); const ensuredOptions = ensureWriteOptions(options);
if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) { if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) {
doWriteFileAndFlush(path, data, options, callback); doWriteFileAndFlush(path, data, ensuredOptions, callback);
} else { } else {
doWriteFileStreamAndFlush(path, data, options, callback); doWriteFileStreamAndFlush(path, data, ensuredOptions, callback);
} }
} }
function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void { function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void {
// finish only once // finish only once
let finished = false; let finished = false;
@@ -468,7 +473,7 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream,
// not in some cache. // not in some cache.
// //
// See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194 // See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194
function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, options: IWriteFileOptions, callback: (error?: Error) => void): void { function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void {
if (options.encoding) { if (options.encoding) {
data = encode(data instanceof Uint8Array ? Buffer.from(data) : data, options.encoding.charset, { addBOM: options.encoding.addBOM }); data = encode(data instanceof Uint8Array ? Buffer.from(data) : data, options.encoding.charset, { addBOM: options.encoding.addBOM });
} }
@@ -478,7 +483,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o
} }
// Open the file with same flags and mode as fs.writeFile() // Open the file with same flags and mode as fs.writeFile()
fs.open(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode, (openError, fd) => { fs.open(path, options.flag, options.mode, (openError, fd) => {
if (openError) { if (openError) {
return callback(openError); return callback(openError);
} }
@@ -506,18 +511,18 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o
} }
export function writeFileAndFlushSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void { export function writeFileAndFlushSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void {
options = ensureOptions(options); const ensuredOptions = ensureWriteOptions(options);
if (options.encoding) { if (ensuredOptions.encoding) {
data = encode(data, options.encoding.charset, { addBOM: options.encoding.addBOM }); data = encode(data, ensuredOptions.encoding.charset, { addBOM: ensuredOptions.encoding.addBOM });
} }
if (!canFlush) { if (!canFlush) {
return fs.writeFileSync(path, data, { mode: options.mode, flag: options.flag }); return fs.writeFileSync(path, data, { mode: ensuredOptions.mode, flag: ensuredOptions.flag });
} }
// Open the file with same flags and mode as fs.writeFile() // Open the file with same flags and mode as fs.writeFile()
const fd = fs.openSync(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode); const fd = fs.openSync(path, ensuredOptions.flag, ensuredOptions.mode);
try { try {
@@ -536,22 +541,16 @@ export function writeFileAndFlushSync(path: string, data: string | Buffer, optio
} }
} }
function ensureOptions(options?: IWriteFileOptions): IWriteFileOptions { function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptions {
if (!options) { if (!options) {
return { mode: 0o666, flag: 'w' }; return { mode: 0o666, flag: 'w' };
} }
const ensuredOptions: IWriteFileOptions = { mode: options.mode, flag: options.flag, encoding: options.encoding }; return {
mode: typeof options.mode === 'number' ? options.mode : 0o666,
if (typeof ensuredOptions.mode !== 'number') { flag: typeof options.flag === 'string' ? options.flag : 'w',
ensuredOptions.mode = 0o666; encoding: options.encoding
} };
if (typeof ensuredOptions.flag !== 'string') {
ensuredOptions.flag = 'w';
}
return ensuredOptions;
} }
/** /**
@@ -70,8 +70,8 @@ export class FileServiceBasedUserConfiguration extends Disposable {
this._register(fileService.onFileChanges(e => this.handleFileEvents(e))); this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50)); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
this.fileService.watchFileChanges(this.configurationResource); this.fileService.watch(this.configurationResource);
this._register(toDisposable(() => this.fileService.unwatchFileChanges(this.configurationResource))); this._register(toDisposable(() => this.fileService.unwatch(this.configurationResource)));
} }
initialize(): Promise<ConfigurationModel> { initialize(): Promise<ConfigurationModel> {
+25 -30
View File
@@ -70,7 +70,7 @@ export interface IFileService {
/** /**
* Allows to listen for file changes. The event will fire for every file within the opened workspace * Allows to listen for file changes. The event will fire for every file within the opened workspace
* (if any) as well as all files that have been watched explicitly using the #watchFileChanges() API. * (if any) as well as all files that have been watched explicitly using the #watch() API.
*/ */
readonly onFileChanges: Event<FileChangesEvent>; readonly onFileChanges: Event<FileChangesEvent>;
@@ -80,7 +80,7 @@ export interface IFileService {
readonly onAfterOperation: Event<FileOperationEvent>; readonly onAfterOperation: Event<FileOperationEvent>;
/** /**
* Resolve the properties of a file identified by the resource. * Resolve the properties of a file/folder identified by the resource.
* *
* If the optional parameter "resolveTo" is specified in options, the stat service is asked * If the optional parameter "resolveTo" is specified in options, the stat service is asked
* to provide a stat object that should contain the full graph of folders up to all of the * to provide a stat object that should contain the full graph of folders up to all of the
@@ -93,20 +93,20 @@ export interface IFileService {
* If the optional parameter "resolveMetadata" is specified in options, * If the optional parameter "resolveMetadata" is specified in options,
* the stat will contain metadata information such as size, mtime and etag. * the stat will contain metadata information such as size, mtime and etag.
*/ */
resolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>; resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>; resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
/** /**
* Same as resolveFile but supports resolving multiple resources in parallel. * Same as resolve() but supports resolving multiple resources in parallel.
* If one of the resolve targets fails to resolve returns a fake IFileStat instead of making the whole call fail. * If one of the resolve targets fails to resolve returns a fake IFileStat instead of making the whole call fail.
*/ */
resolveFiles(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResult[]>; resolveAll(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResult[]>;
resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>; resolveAll(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
/** /**
* Finds out if a file identified by the resource exists. * Finds out if a file/folder identified by the resource exists.
*/ */
existsFile(resource: URI): Promise<boolean>; exists(resource: URI): Promise<boolean>;
/** /**
* Resolve the contents of a file identified by the resource. * Resolve the contents of a file identified by the resource.
@@ -128,18 +128,18 @@ export interface IFileService {
updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStatWithMetadata>; updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStatWithMetadata>;
/** /**
* Moves the file to a new path identified by the resource. * Moves the file/folder to a new path identified by the resource.
* *
* The optional parameter overwrite can be set to replace an existing file at the location. * The optional parameter overwrite can be set to replace an existing file at the location.
*/ */
moveFile(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>; move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
/** /**
* Copies the file to a path identified by the resource. * Copies the file/folder to a path identified by the resource.
* *
* The optional parameter overwrite can be set to replace an existing file at the location. * The optional parameter overwrite can be set to replace an existing file at the location.
*/ */
copyFile(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>; copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
/** /**
* Creates a new file with the given path. The returned promise * Creates a new file with the given path. The returned promise
@@ -165,12 +165,12 @@ export interface IFileService {
/** /**
* Allows to start a watcher that reports file change events on the provided resource. * Allows to start a watcher that reports file change events on the provided resource.
*/ */
watchFileChanges(resource: URI): void; watch(resource: URI): void;
/** /**
* Allows to stop a watcher on the provided resource or absolute fs path. * Allows to stop a watcher on the provided resource or absolute fs path.
*/ */
unwatchFileChanges(resource: URI): void; unwatch(resource: URI): void;
/** /**
* Frees up any resources occupied by this service. * Frees up any resources occupied by this service.
@@ -371,19 +371,14 @@ export const enum FileOperation {
export class FileOperationEvent { export class FileOperationEvent {
constructor(private _resource: URI, private _operation: FileOperation, private _target?: IFileStatWithMetadata) { constructor(resource: URI, operation: FileOperation.DELETE);
} constructor(resource: URI, operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY, target: IFileStatWithMetadata);
constructor(public readonly resource: URI, public readonly operation: FileOperation, public readonly target?: IFileStatWithMetadata) { }
get resource(): URI { isOperation(operation: FileOperation.DELETE): boolean;
return this._resource; isOperation(operation: FileOperation.MOVE | FileOperation.COPY | FileOperation.CREATE): this is { readonly target: IFileStatWithMetadata };
} isOperation(operation: FileOperation): boolean {
return this.operation === operation;
get target(): IFileStat | undefined {
return this._target;
}
get operation(): FileOperation {
return this._operation;
} }
} }
@@ -1140,7 +1135,7 @@ export interface ILegacyFileService {
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStat>; createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStat>;
watchFileChanges(resource: URI): void; watch(resource: URI): void;
unwatchFileChanges(resource: URI): void; unwatch(resource: URI): void;
} }
@@ -10,9 +10,7 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IStorage, Storage, SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions, InMemoryStorageDatabase } from 'vs/base/node/storage'; import { IStorage, Storage, SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions, InMemoryStorageDatabase } from 'vs/base/node/storage';
import { join } from 'vs/base/common/path'; import { join } from 'vs/base/common/path';
import { exists, readdir } from 'vs/base/node/pfs'; import { exists } from 'vs/base/node/pfs';
import { Database } from 'vscode-sqlite3';
import { endsWith, startsWith } from 'vs/base/common/strings';
export const IStorageMainService = createDecorator<IStorageMainService>('storageMainService'); export const IStorageMainService = createDecorator<IStorageMainService>('storageMainService');
@@ -140,213 +138,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic
this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key })));
return this.storage.init().then(() => { return this.storage.init();
// Migrate storage if this is the first start and we are not using in-memory
let migrationPromise: Promise<void>;
if (!useInMemoryStorage && !exists) {
// TODO@Ben remove global storage migration and move Storage creation back to ctor
migrationPromise = this.migrateGlobalStorage().then(() => this.logService.info('[storage] migrated global storage'), error => this.logService.error(`[storage] migration error ${error}`));
} else {
migrationPromise = Promise.resolve();
}
return migrationPromise;
});
});
}
private migrateGlobalStorage(): Promise<void> {
this.logService.info('[storage] migrating global storage from localStorage into SQLite');
const localStorageDBBackup = join(this.environmentService.userDataPath, 'Local Storage', 'file__0.vscmig');
return exists(localStorageDBBackup).then(exists => {
if (!exists) {
return Promise.resolve(); // return if there is no DB to migrate from
}
return readdir(this.environmentService.extensionsPath).then(extensions => {
const supportedKeys = new Map<string, string>();
[
'editorFontInfo',
'peekViewLayout',
'expandSuggestionDocs',
'extensionsIdentifiers/disabled',
'integrityService',
'telemetry.lastSessionDate',
'telemetry.instanceId',
'telemetry.firstSessionDate',
'workbench.sidebar.width',
'workbench.panel.width',
'workbench.panel.height',
'workbench.panel.sizeBeforeMaximized',
'workbench.activity.placeholderViewlets',
'colorThemeData',
'iconThemeData',
'workbench.telemetryOptOutShown',
'workbench.hide.welcome',
'releaseNotes/lastVersion',
'debug.actionswidgetposition',
'debug.actionswidgety',
'editor.neverPromptForLargeFiles',
'menubar/electronFixRecommended',
'learnMoreDirtyWriteError',
'extensions.ignoredAutoUpdateExtension',
'askToInstallRemoteServerExtension',
'hasNotifiedOfSettingsAutosave',
'commandPalette.mru.cache',
'commandPalette.mru.counter',
'parts-splash-data',
'terminal.integrated.neverMeasureRenderTime',
'terminal.integrated.neverSuggestSelectWindowsShell',
'memento/workbench.parts.editor',
'memento/workbench.view.search',
'langugage.update.donotask',
'extensionsAssistant/languagePackSuggestionIgnore',
'workbench.panel.pinnedPanels',
'workbench.activity.pinnedViewlets',
'extensionsAssistant/ignored_recommendations',
'extensionsAssistant/recommendations',
'extensionsAssistant/importantRecommendationsIgnore',
'extensionsAssistant/fileExtensionsSuggestionIgnore',
'nps/skipVersion',
'nps/lastSessionDate',
'nps/sessionCount',
'nps/isCandidate',
'allExperiments',
'currentOrPreviouslyRunExperiments',
'update/win32-64bits',
'update/win32-fast-updates',
'update/lastKnownVersion',
'update/updateNotificationTime'
].forEach(key => supportedKeys.set(key.toLowerCase(), key));
// https://github.com/Microsoft/vscode/issues/68468
const wellKnownPublishers = ['Microsoft', 'GitHub'];
const wellKnownExtensions = ['ms-vscode.Go', 'WallabyJs.quokka-vscode', 'Telerik.nativescript', 'Shan.code-settings-sync', 'ritwickdey.LiveServer', 'PKief.material-icon-theme', 'PeterJausovec.vscode-docker', 'ms-vscode.PowerShell', 'LaurentTreguier.vscode-simple-icons', 'KnisterPeter.vscode-github', 'DotJoshJohnson.xml', 'Dart-Code.dart-code', 'alefragnani.Bookmarks'];
// Support extension storage as well (always the ID of the extension)
extensions.forEach(extension => {
let extensionId: string;
if (extension.indexOf('-') >= 0) {
extensionId = extension.substring(0, extension.lastIndexOf('-')); // convert "author.extension-0.2.5" => "author.extension"
} else {
extensionId = extension;
}
if (extensionId) {
for (let i = 0; i < wellKnownPublishers.length; i++) {
const publisher = wellKnownPublishers[i];
if (startsWith(extensionId, `${publisher.toLowerCase()}.`)) {
extensionId = `${publisher}${extensionId.substr(publisher.length)}`;
break;
}
}
for (let j = 0; j < wellKnownExtensions.length; j++) {
const wellKnownExtension = wellKnownExtensions[j];
if (extensionId === wellKnownExtension.toLowerCase()) {
extensionId = wellKnownExtension;
break;
}
}
supportedKeys.set(extensionId.toLowerCase(), extensionId);
}
});
return import('vscode-sqlite3').then(sqlite3 => {
return new Promise<void>((resolve, reject) => {
const handleSuffixKey = (row: any, key: string, suffix: string) => {
if (endsWith(key, suffix.toLowerCase())) {
const value: string = row.value.toString('utf16le');
const normalizedKey = key.substring(0, key.length - suffix.length) + suffix;
this.store(normalizedKey, value);
return true;
}
return false;
};
const db: Database = new (sqlite3.Database)(localStorageDBBackup, error => {
if (error) {
if (db) {
db.close();
}
return reject(error);
}
db.all('SELECT key, value FROM ItemTable', (error, rows) => {
if (error) {
db.close();
return reject(error);
}
try {
rows.forEach(row => {
let key: string = row.key;
if (key.indexOf('storage://global/') !== 0) {
return; // not a global key
}
// convert storage://global/colorthemedata => colorthemedata
key = key.substr('storage://global/'.length);
const supportedKey = supportedKeys.get(key);
if (supportedKey) {
const value: string = row.value.toString('utf16le');
this.store(supportedKey, value);
}
// dynamic values
else if (
endsWith(key, '.hidden') ||
startsWith(key, 'experiments.')
) {
const value: string = row.value.toString('utf16le');
this.store(key, value);
}
// fix lowercased ".sessionCount"
else if (handleSuffixKey(row, key, '.sessionCount')) { }
// fix lowercased ".lastSessionDate"
else if (handleSuffixKey(row, key, '.lastSessionDate')) { }
// fix lowercased ".skipVersion"
else if (handleSuffixKey(row, key, '.skipVersion')) { }
// fix lowercased ".isCandidate"
else if (handleSuffixKey(row, key, '.isCandidate')) { }
// fix lowercased ".editedCount"
else if (handleSuffixKey(row, key, '.editedCount')) { }
// fix lowercased ".editedDate"
else if (handleSuffixKey(row, key, '.editedDate')) { }
});
db.close();
} catch (error) {
db.close();
return reject(error);
}
resolve();
});
});
});
});
});
}); });
} }
@@ -215,7 +215,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private _handleUntitledScheme(uri: URI): Promise<boolean> { private _handleUntitledScheme(uri: URI): Promise<boolean> {
const asFileUri = uri.with({ scheme: Schemas.file }); const asFileUri = uri.with({ scheme: Schemas.file });
return this._fileService.resolveFile(asFileUri).then(stats => { return this._fileService.resolve(asFileUri).then(stats => {
// don't create a new file ontop of an existing file // don't create a new file ontop of an existing file
return Promise.reject(new Error('file already exists on disk')); return Promise.reject(new Error('file already exists on disk'));
}, err => { }, err => {
@@ -51,8 +51,8 @@ export class MainThreadFileSystemEventService {
// file operation events - (changes the editor makes) // file operation events - (changes the editor makes)
fileService.onAfterOperation(e => { fileService.onAfterOperation(e => {
if (e.operation === FileOperation.MOVE) { if (e.isOperation(FileOperation.MOVE)) {
proxy.$onFileRename(e.resource, e.target!.resource); proxy.$onFileRename(e.resource, e.target.resource);
} }
}, undefined, this._listener); }, undefined, this._listener);
+1 -1
View File
@@ -269,7 +269,7 @@ export class ResourcesDropHandler {
} }
// Check for Folder // Check for Folder
return this.fileService.resolveFile(fileOnDiskResource).then(stat => { return this.fileService.resolve(fileOnDiskResource).then(stat => {
if (stat.isDirectory) { if (stat.isDirectory) {
workspaceResources.folders.push({ uri: stat.resource, typeHint: 'folder' }); workspaceResources.folders.push({ uri: stat.resource, typeHint: 'folder' });
} }
@@ -695,16 +695,16 @@ export class SimpleRemoteFileService implements IFileService {
readonly onDidChangeFileSystemProviderRegistrations = Event.None; readonly onDidChangeFileSystemProviderRegistrations = Event.None;
readonly onWillActivateFileSystemProvider = Event.None; readonly onWillActivateFileSystemProvider = Event.None;
resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStatWithMetadata> { resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStatWithMetadata> {
// @ts-ignore // @ts-ignore
return Promise.resolve(fileMap.get(resource)); return Promise.resolve(fileMap.get(resource));
} }
resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> { resolveAll(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> {
return Promise.all(toResolve.map(resourceAndOption => this.resolveFile(resourceAndOption.resource, resourceAndOption.options))).then(stats => stats.map(stat => ({ stat, success: true }))); return Promise.all(toResolve.map(resourceAndOption => this.resolve(resourceAndOption.resource, resourceAndOption.options))).then(stats => stats.map(stat => ({ stat, success: true })));
} }
existsFile(resource: URI): Promise<boolean> { exists(resource: URI): Promise<boolean> {
return Promise.resolve(fileMap.has(resource)); return Promise.resolve(fileMap.has(resource));
} }
@@ -761,9 +761,9 @@ export class SimpleRemoteFileService implements IFileService {
}); });
} }
moveFile(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); } move(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }
copyFile(_source: URI, _target: URI, _overwrite?: boolean): Promise<any> { copy(_source: URI, _target: URI, _overwrite?: boolean): Promise<any> {
const parent = fileMap.get(dirname(_target)); const parent = fileMap.get(dirname(_target));
if (!parent) { if (!parent) {
return Promise.resolve(undefined); return Promise.resolve(undefined);
@@ -802,9 +802,9 @@ export class SimpleRemoteFileService implements IFileService {
del(_resource: URI, _options?: { useTrash?: boolean, recursive?: boolean }): Promise<void> { return Promise.resolve(); } del(_resource: URI, _options?: { useTrash?: boolean, recursive?: boolean }): Promise<void> { return Promise.resolve(); }
watchFileChanges(_resource: URI): void { } watch(_resource: URI): void { }
unwatchFileChanges(_resource: URI): void { } unwatch(_resource: URI): void { }
getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; } getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; }
@@ -228,7 +228,7 @@ class FileDataSource implements IAsyncDataSource<IWorkspace | URI, IWorkspaceFol
} else { } else {
uri = element.resource; uri = element.resource;
} }
return this._fileService.resolveFile(uri).then(stat => { return this._fileService.resolve(uri).then(stat => {
for (const child of stat.children || []) { for (const child of stat.children || []) {
this._parents.set(stat, child); this._parents.set(stat, child);
} }
@@ -76,7 +76,7 @@ export class BinaryEditorModel extends EditorModel {
// Make sure to resolve up to date stat for file resources // Make sure to resolve up to date stat for file resources
if (this.fileService.canHandleResource(this.resource)) { if (this.fileService.canHandleResource(this.resource)) {
return this.fileService.resolveFile(this.resource, { resolveMetadata: true }).then(stat => { return this.fileService.resolve(this.resource, { resolveMetadata: true }).then(stat => {
this.etag = stat.etag; this.etag = stat.etag;
if (typeof stat.size === 'number') { if (typeof stat.size === 'number') {
this.size = stat.size; this.size = stat.size;
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
import { RGBA, Color } from 'vs/base/common/color';
/** /**
* @param text The content to stylize. * @param text The content to stylize.
@@ -15,6 +16,8 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTML
const textLength: number = text.length; const textLength: number = text.length;
let styleNames: string[] = []; let styleNames: string[] = [];
let customFgColor: RGBA | undefined;
let customBgColor: RGBA | undefined;
let currentPos: number = 0; let currentPos: number = 0;
let buffer: string = ''; let buffer: string = '';
@@ -48,45 +51,33 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTML
if (sequenceFound) { if (sequenceFound) {
// Flush buffer with previous styles. // Flush buffer with previous styles.
appendStylizedStringToContainer(root, buffer, styleNames, linkDetector); appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, customFgColor, customBgColor);
buffer = ''; buffer = '';
/* /*
* Certain ranges that are matched here do not contain real graphics rendition sequences. For * Certain ranges that are matched here do not contain real graphics rendition sequences. For
* the sake of having a simpler expression, they have been included anyway. * the sake of having a simpler expression, they have been included anyway.
*/ */
if (ansiSequence.match(/^(?:[349][0-7]|10[0-7]|[013]|4|[34]9)(?:;(?:[349][0-7]|10[0-7]|[013]|4|[34]9))*;?m$/)) { if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[013]|4|[34]9)(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[012]?[0-9]?[0-9])*;?m$/)) {
const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character. const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character.
.split(';') // Separate style codes. .split(';') // Separate style codes.
.filter(elem => elem !== '') // Filter empty elems as '34;m' -> ['34', '']. .filter(elem => elem !== '') // Filter empty elems as '34;m' -> ['34', ''].
.map(elem => parseInt(elem, 10)); // Convert to numbers. .map(elem => parseInt(elem, 10)); // Convert to numbers.
for (let code of styleCodes) { if (styleCodes[0] === 38 || styleCodes[0] === 48) {
if (code === 0) { // Advanced color code - can't be combined with formatting codes like simple colors can
styleNames = []; // Ignores invalid colors and additional info beyond what is necessary
} else if (code === 1) { const colorType = (styleCodes[0] === 38) ? 'foreground' : 'background';
styleNames.push('code-bold');
} else if (code === 3) {
styleNames.push('code-italic');
} else if (code === 4) {
styleNames.push('code-underline');
} else if (code === 39 || (code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {
// Remove all previous foreground colour codes
styleNames = styleNames.filter(style => !style.match(/^code-foreground-\d+$/));
if (code !== 39) { if (styleCodes[1] === 5) {
styleNames.push('code-foreground-' + code); set8BitColor(styleCodes, colorType);
} } else if (styleCodes[1] === 2) {
} else if (code === 49 || (code >= 40 && code <= 47) || (code >= 100 && code <= 107)) { set24BitColor(styleCodes, colorType);
// Remove all previous background colour codes
styleNames = styleNames.filter(style => !style.match(/^code-background-\d+$/));
if (code !== 49) {
styleNames.push('code-background-' + code);
}
} }
} else {
setBasicFormatters(styleCodes);
} }
} else { } else {
@@ -96,23 +87,121 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTML
} else { } else {
currentPos = startPos; currentPos = startPos;
} }
} }
if (sequenceFound === false) { if (sequenceFound === false) {
buffer += text.charAt(currentPos); buffer += text.charAt(currentPos);
currentPos++; currentPos++;
} }
} }
// Flush remaining text buffer if not empty. // Flush remaining text buffer if not empty.
if (buffer) { if (buffer) {
appendStylizedStringToContainer(root, buffer, styleNames, linkDetector); appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, customFgColor, customBgColor);
} }
return root; return root;
/**
* Change the foreground or background color by clearing the current color
* and adding the new one.
* @param newClass If string or number, new class will be
* `code-(foreground or background)-newClass`. If `undefined`, no new class
* will be added.
* @param colorType If `'foreground'`, will change the foreground color, if
* `'background'`, will change the background color.
* @param customColor If provided, this custom color will be used instead of
* a class-defined color.
*/
function changeColor(newClass: string | number | undefined, colorType: 'foreground' | 'background', customColor?: RGBA): void {
styleNames = styleNames.filter(style => !style.match(new RegExp(`^code-${colorType}-(\\d+|custom)$`)));
if (newClass) {
styleNames.push(`code-${colorType}-${newClass}`);
}
if (colorType === 'foreground') {
customFgColor = customColor;
} else {
customBgColor = customColor;
}
}
/**
* Calculate and set basic ANSI formatting. Supports bold, italic, underline,
* normal foreground and background colors, and bright foreground and
* background colors. Not to be used for codes containing advanced colors.
* Will ignore invalid codes.
* @param styleCodes Array of ANSI basic styling numbers, which will be
* applied in order. New colors and backgrounds clear old ones; new formatting
* does not.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code }
*/
function setBasicFormatters(styleCodes: number[]): void {
for (let code of styleCodes) {
if (code === 0) {
styleNames = [];
} else if (code === 1) {
styleNames.push('code-bold');
} else if (code === 3) {
styleNames.push('code-italic');
} else if (code === 4) {
styleNames.push('code-underline');
} else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {
changeColor(code, 'foreground');
} else if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) {
changeColor(code, 'background');
} else if (code === 39) {
changeColor(undefined, 'foreground');
} else if (code === 49) {
changeColor(undefined, 'background');
}
}
}
/**
* Calculate and set styling for complicated 24-bit ANSI color codes.
* @param styleCodes Full list of integer codes that make up the full ANSI
* sequence, including the two defining codes and the three RGB codes.
* @param colorType If `'foreground'`, will set foreground color, if
* `'background'`, will set background color.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit }
*/
function set24BitColor(styleCodes: number[], colorType: 'foreground' | 'background'): void {
if (styleCodes.length >= 5 &&
styleCodes[2] >= 0 && styleCodes[2] <= 255 &&
styleCodes[3] >= 0 && styleCodes[3] <= 255 &&
styleCodes[4] >= 0 && styleCodes[4] <= 255) {
const customColor = new RGBA(styleCodes[2], styleCodes[3], styleCodes[4]);
changeColor('custom', colorType, customColor);
}
}
/**
* Calculate and set styling for advanced 8-bit ANSI color codes.
* @param styleCodes Full list of integer codes that make up the ANSI
* sequence, including the two defining codes and the one color code.
* @param colorType If `'foreground'`, will set foreground color, if
* `'background'`, will set background color.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit }
*/
function set8BitColor(styleCodes: number[], colorType: 'foreground' | 'background'): void {
let colorNumber = styleCodes[2];
const color = calcANSI8bitColor(colorNumber);
if (color) {
changeColor('custom', colorType, color);
} else if (colorNumber >= 0 && colorNumber <= 15) {
// Need to map to one of the four basic color ranges (30-37, 90-97, 40-47, 100-107)
colorNumber += 30;
if (colorNumber >= 38) {
// Bright colors
colorNumber += 52;
}
if (colorType === 'background') {
colorNumber += 10;
}
changeColor(colorNumber, colorType);
}
}
} }
/** /**
@@ -120,8 +209,17 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTML
* @param stringContent The text content to be appended. * @param stringContent The text content to be appended.
* @param cssClasses The list of CSS styles to apply to the text content. * @param cssClasses The list of CSS styles to apply to the text content.
* @param linkDetector The {@link LinkDetector} responsible for generating links from {@param stringContent}. * @param linkDetector The {@link LinkDetector} responsible for generating links from {@param stringContent}.
* @param customTextColor If provided, will apply custom color with inline style.
* @param customBackgroundColor If provided, will apply custom color with inline style.
*/ */
export function appendStylizedStringToContainer(root: HTMLElement, stringContent: string, cssClasses: string[], linkDetector: LinkDetector): void { export function appendStylizedStringToContainer(
root: HTMLElement,
stringContent: string,
cssClasses: string[],
linkDetector: LinkDetector,
customTextColor?: RGBA,
customBackgroundColor?: RGBA
): void {
if (!root || !stringContent) { if (!root || !stringContent) {
return; return;
} }
@@ -129,5 +227,55 @@ export function appendStylizedStringToContainer(root: HTMLElement, stringContent
const container = linkDetector.handleLinks(stringContent); const container = linkDetector.handleLinks(stringContent);
container.className = cssClasses.join(' '); container.className = cssClasses.join(' ');
if (customTextColor) {
container.style.color =
Color.Format.CSS.formatRGB(new Color(customTextColor));
}
if (customBackgroundColor) {
container.style.backgroundColor =
Color.Format.CSS.formatRGB(new Color(customBackgroundColor));
}
root.appendChild(container); root.appendChild(container);
} }
/**
* Calculate the color from the color set defined in the ANSI 8-bit standard.
* Standard and high intensity colors are not defined in the standard as specific
* colors, so these and invalid colors return `undefined`.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit } for info.
* @param colorNumber The number (ranging from 16 to 255) referring to the color
* desired.
*/
export function calcANSI8bitColor(colorNumber: number): RGBA | undefined {
if (colorNumber % 1 !== 0) {
// Should be integer
// {{SQL CARBON EDIT}} @todo anthonydresser this is necessary because we don't use strict null checks
return undefined;
} if (colorNumber >= 16 && colorNumber <= 231) {
// Converts to one of 216 RGB colors
colorNumber -= 16;
let blue: number = colorNumber % 6;
colorNumber = (colorNumber - blue) / 6;
let green: number = colorNumber % 6;
colorNumber = (colorNumber - green) / 6;
let red: number = colorNumber;
// red, green, blue now range on [0, 5], need to map to [0,255]
const convFactor: number = 255 / 5;
blue = Math.round(blue * convFactor);
green = Math.round(green * convFactor);
red = Math.round(red * convFactor);
return new RGBA(red, green, blue);
} else if (colorNumber >= 232 && colorNumber <= 255) {
// Converts to a grayscale value
colorNumber -= 232;
const colorLevel: number = Math.round(colorNumber / 23 * 255);
return new RGBA(colorLevel, colorLevel, colorLevel);
} else {
// {{SQL CARBON EDIT}} @todo anthonydresser this is necessary because we don't use strict null checks
return undefined;
}
}
@@ -6,10 +6,11 @@
import * as assert from 'assert'; import * as assert from 'assert';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import { appendStylizedStringToContainer, handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling'; import { appendStylizedStringToContainer, handleANSIOutput, calcANSI8bitColor } from 'vs/workbench/contrib/debug/browser/debugANSIHandling';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
import { Color, RGBA } from 'vs/base/common/color';
suite('Debug - ANSI Handling', () => { suite('Debug - ANSI Handling', () => {
test('appendStylizedStringToContainer', () => { test('appendStylizedStringToContainer', () => {
@@ -349,7 +349,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
private resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise<IExtensionsConfigContent | null> { private resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise<IExtensionsConfigContent | null> {
const extensionsJsonUri = workspaceFolder.toResource(EXTENSIONS_CONFIG); const extensionsJsonUri = workspaceFolder.toResource(EXTENSIONS_CONFIG);
return Promise.resolve(this.fileService.resolveFile(extensionsJsonUri) return Promise.resolve(this.fileService.resolve(extensionsJsonUri)
.then(() => this.fileService.resolveContent(extensionsJsonUri)) .then(() => this.fileService.resolveContent(extensionsJsonUri))
.then(content => <IExtensionsConfigContent>json.parse(content.value), err => null)); .then(content => <IExtensionsConfigContent>json.parse(content.value), err => null));
} }
@@ -2555,7 +2555,7 @@ export class OpenExtensionsFolderAction extends Action {
run(): Promise<void> { run(): Promise<void> {
const extensionsHome = URI.file(this.environmentService.extensionsPath); const extensionsHome = URI.file(this.environmentService.extensionsPath);
return Promise.resolve(this.fileService.resolveFile(extensionsHome)).then(file => { return Promise.resolve(this.fileService.resolve(extensionsHome)).then(file => {
let itemToShow: URI; let itemToShow: URI;
if (file.children && file.children.length > 0) { if (file.children && file.children.length > 0) {
itemToShow = file.children[0].resource; itemToShow = file.children[0].resource;
@@ -90,7 +90,7 @@ CommandsRegistry.registerCommand({
const terminalService = accessor.get(IExternalTerminalService); const terminalService = accessor.get(IExternalTerminalService);
const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService); const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService);
return fileService.resolveFiles(resources.map(r => ({ resource: r }))).then(stats => { return fileService.resolveAll(resources.map(r => ({ resource: r }))).then(stats => {
const directoriesToOpen = distinct(stats.filter(data => data.success).map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : paths.dirname(stat!.resource.fsPath))); const directoriesToOpen = distinct(stats.filter(data => data.success).map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : paths.dirname(stat!.resource.fsPath)));
return directoriesToOpen.map(dir => { return directoriesToOpen.map(dir => {
if (configurationService.getValue<IExternalTerminalConfiguration>().terminal.explorerKind === 'integrated') { if (configurationService.getValue<IExternalTerminalConfiguration>().terminal.explorerKind === 'integrated') {
@@ -114,12 +114,12 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
private onFileOperation(e: FileOperationEvent): void { private onFileOperation(e: FileOperationEvent): void {
// Handle moves specially when file is opened // Handle moves specially when file is opened
if (e.operation === FileOperation.MOVE && e.target) { if (e.isOperation(FileOperation.MOVE)) {
this.handleMovedFileInOpenedEditors(e.resource, e.target.resource); this.handleMovedFileInOpenedEditors(e.resource, e.target.resource);
} }
// Handle deletes // Handle deletes
if (e.operation === FileOperation.DELETE || e.operation === FileOperation.MOVE) { if (e.isOperation(FileOperation.DELETE) || e.isOperation(FileOperation.MOVE)) {
this.handleDeletes(e.resource, false, e.target ? e.target.resource : undefined); this.handleDeletes(e.resource, false, e.target ? e.target.resource : undefined);
} }
} }
@@ -176,7 +176,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
// flag. // flag.
let checkExists: Promise<boolean>; let checkExists: Promise<boolean>;
if (isExternal) { if (isExternal) {
checkExists = timeout(100).then(() => this.fileService.existsFile(resource)); checkExists = timeout(100).then(() => this.fileService.exists(resource));
} else { } else {
checkExists = Promise.resolve(false); checkExists = Promise.resolve(false);
} }
@@ -360,7 +360,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
// Handle no longer visible out of workspace resources // Handle no longer visible out of workspace resources
this.activeOutOfWorkspaceWatchers.forEach(resource => { this.activeOutOfWorkspaceWatchers.forEach(resource => {
if (!visibleOutOfWorkspacePaths.get(resource)) { if (!visibleOutOfWorkspacePaths.get(resource)) {
this.fileService.unwatchFileChanges(resource); this.fileService.unwatch(resource);
this.activeOutOfWorkspaceWatchers.delete(resource); this.activeOutOfWorkspaceWatchers.delete(resource);
} }
}); });
@@ -368,7 +368,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
// Handle newly visible out of workspace resources // Handle newly visible out of workspace resources
visibleOutOfWorkspacePaths.forEach(resource => { visibleOutOfWorkspacePaths.forEach(resource => {
if (!this.activeOutOfWorkspaceWatchers.get(resource)) { if (!this.activeOutOfWorkspaceWatchers.get(resource)) {
this.fileService.watchFileChanges(resource); this.fileService.watch(resource);
this.activeOutOfWorkspaceWatchers.set(resource, resource); this.activeOutOfWorkspaceWatchers.set(resource, resource);
} }
}); });
@@ -378,7 +378,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
super.dispose(); super.dispose();
// Dispose watchers if any // Dispose watchers if any
this.activeOutOfWorkspaceWatchers.forEach(resource => this.fileService.unwatchFileChanges(resource)); this.activeOutOfWorkspaceWatchers.forEach(resource => this.fileService.unwatch(resource));
this.activeOutOfWorkspaceWatchers.clear(); this.activeOutOfWorkspaceWatchers.clear();
} }
} }
@@ -503,7 +503,7 @@ class PasteFileAction extends BaseErrorReportingAction {
throw new Error(nls.localize('fileIsAncestor', "File to paste is an ancestor of the destination folder")); throw new Error(nls.localize('fileIsAncestor', "File to paste is an ancestor of the destination folder"));
} }
return this.fileService.resolveFile(fileToPaste).then(fileToPasteStat => { return this.fileService.resolve(fileToPaste).then(fileToPasteStat => {
// Find target // Find target
let target: ExplorerItem; let target: ExplorerItem;
@@ -516,7 +516,7 @@ class PasteFileAction extends BaseErrorReportingAction {
const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwirte: pasteShouldMove }); const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwirte: pasteShouldMove });
// Copy File // Copy File
const promise = pasteShouldMove ? this.fileService.moveFile(fileToPaste, targetFile) : this.fileService.copyFile(fileToPaste, targetFile); const promise = pasteShouldMove ? this.fileService.move(fileToPaste, targetFile) : this.fileService.copy(fileToPaste, targetFile);
return promise.then<ITextEditor | undefined>(stat => { return promise.then<ITextEditor | undefined>(stat => {
if (pasteShouldMove) { if (pasteShouldMove) {
// Cut is done. Make sure to clear cut state. // Cut is done. Make sure to clear cut state.
@@ -300,7 +300,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
// Set side input // Set side input
if (resources.length) { if (resources.length) {
return fileService.resolveFiles(resources.map(resource => ({ resource }))).then(resolved => { return fileService.resolveAll(resources.map(resource => ({ resource }))).then(resolved => {
const editors = resolved.filter(r => r.stat && r.success && !r.stat.isDirectory).map(r => ({ const editors = resolved.filter(r => r.stat && r.success && !r.stat.isDirectory).map(r => ({
resource: r.stat!.resource resource: r.stat!.resource
})); }));
@@ -608,7 +608,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
const droppedResources = extractResources(originalEvent, true); const droppedResources = extractResources(originalEvent, true);
// Check for dropped external files to be folders // Check for dropped external files to be folders
return this.fileService.resolveFiles(droppedResources).then(result => { return this.fileService.resolveAll(droppedResources).then(result => {
// Pass focus to window // Pass focus to window
this.windowService.focusWindow(); this.windowService.focusWindow();
@@ -649,7 +649,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
if (resources && resources.length > 0) { if (resources && resources.length > 0) {
// Resolve target to check for name collisions and ask user // Resolve target to check for name collisions and ask user
return this.fileService.resolveFile(target.resource).then(targetStat => { return this.fileService.resolve(target.resource).then(targetStat => {
// Check for name collisions // Check for name collisions
const targetNames = new Set<string>(); const targetNames = new Set<string>();
@@ -695,7 +695,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
return revertPromise.then(() => { return revertPromise.then(() => {
const copyTarget = joinPath(target.resource, basename(sourceFile)); const copyTarget = joinPath(target.resource, basename(sourceFile));
return this.fileService.copyFile(sourceFile, copyTarget, true).then(stat => { return this.fileService.copy(sourceFile, copyTarget, true).then(stat => {
// if we only add one file, just open it directly // if we only add one file, just open it directly
if (resources.length === 1) { if (resources.length === 1) {
@@ -794,7 +794,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
// Reuse duplicate action if user copies // Reuse duplicate action if user copies
if (isCopy) { if (isCopy) {
return this.fileService.copyFile(source.resource, findValidPasteFileTarget(target, { resource: source.resource, isDirectory: source.isDirectory, allowOverwirte: false })).then(stat => { return this.fileService.copy(source.resource, findValidPasteFileTarget(target, { resource: source.resource, isDirectory: source.isDirectory, allowOverwirte: false })).then(stat => {
if (!stat.isDirectory) { if (!stat.isDirectory) {
return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }).then(() => undefined); return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }).then(() => undefined);
} }
@@ -249,7 +249,7 @@ export class ExplorerItem {
// Resolve metadata only when the mtime is needed since this can be expensive // Resolve metadata only when the mtime is needed since this can be expensive
// Mtime is only used when the sort order is 'modified' // Mtime is only used when the sort order is 'modified'
const resolveMetadata = explorerService.sortOrder === 'modified'; const resolveMetadata = explorerService.sortOrder === 'modified';
promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true, resolveMetadata }).then(stat => { promise = fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata }).then(stat => {
const resolved = ExplorerItem.create(stat, this); const resolved = ExplorerItem.create(stat, this);
ExplorerItem.mergeLocalWithDisk(resolved, this); ExplorerItem.mergeLocalWithDisk(resolved, this);
this._isDirectoryResolved = true; this._isDirectoryResolved = true;
@@ -40,7 +40,6 @@ export class ExplorerService implements IExplorerService {
private editable: { stat: ExplorerItem, data: IEditableData } | undefined; private editable: { stat: ExplorerItem, data: IEditableData } | undefined;
private _sortOrder: SortOrder; private _sortOrder: SortOrder;
private cutItems: ExplorerItem[] | undefined; private cutItems: ExplorerItem[] | undefined;
private fileSystemProviderSchemes = new Set<string>();
constructor( constructor(
@IFileService private fileService: IFileService, @IFileService private fileService: IFileService,
@@ -99,14 +98,7 @@ export class ExplorerService implements IExplorerService {
this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e)));
this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>()))); this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>())));
this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(e => { this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire(undefined)));
if (e.added && this.fileSystemProviderSchemes.has(e.scheme)) {
// A file system provider got re-registered, we should update all file stats since they might change (got read-only)
this._onDidChangeItem.fire(undefined);
} else {
this.fileSystemProviderSchemes.add(e.scheme);
}
}));
this.disposables.push(model.onDidChangeRoots(() => this._onDidChangeRoots.fire())); this.disposables.push(model.onDidChangeRoots(() => this._onDidChangeRoots.fire()));
return model; return model;
@@ -159,7 +151,7 @@ export class ExplorerService implements IExplorerService {
const workspaceFolder = this.contextService.getWorkspaceFolder(resource); const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource;
const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!; const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!;
return this.fileService.resolveFile(rootUri, options).then(stat => { return this.fileService.resolve(rootUri, options).then(stat => {
// Convert to model // Convert to model
const modelStat = ExplorerItem.create(stat, undefined, options.resolveTo); const modelStat = ExplorerItem.create(stat, undefined, options.resolveTo);
@@ -190,8 +182,8 @@ export class ExplorerService implements IExplorerService {
private onFileOperation(e: FileOperationEvent): void { private onFileOperation(e: FileOperationEvent): void {
// Add // Add
if (e.operation === FileOperation.CREATE || e.operation === FileOperation.COPY) { if (e.isOperation(FileOperation.CREATE) || e.isOperation(FileOperation.COPY)) {
const addedElement = e.target!; const addedElement = e.target;
const parentResource = dirname(addedElement.resource)!; const parentResource = dirname(addedElement.resource)!;
const parents = this.model.findAll(parentResource); const parents = this.model.findAll(parentResource);
@@ -201,7 +193,7 @@ export class ExplorerService implements IExplorerService {
parents.forEach(p => { parents.forEach(p => {
// We have to check if the parent is resolved #29177 // We have to check if the parent is resolved #29177
const resolveMetadata = this.sortOrder === `modified`; const resolveMetadata = this.sortOrder === `modified`;
const thenable: Promise<IFileStat | undefined> = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource, { resolveMetadata }); const thenable: Promise<IFileStat | undefined> = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolve(p.resource, { resolveMetadata });
thenable.then(stat => { thenable.then(stat => {
if (stat) { if (stat) {
const modelStat = ExplorerItem.create(stat, p.parent); const modelStat = ExplorerItem.create(stat, p.parent);
@@ -220,9 +212,9 @@ export class ExplorerService implements IExplorerService {
} }
// Move (including Rename) // Move (including Rename)
else if (e.operation === FileOperation.MOVE) { else if (e.isOperation(FileOperation.MOVE)) {
const oldResource = e.resource; const oldResource = e.resource;
const newElement = e.target!; const newElement = e.target;
const oldParentResource = dirname(oldResource); const oldParentResource = dirname(oldResource);
const newParentResource = dirname(newElement.resource); const newParentResource = dirname(newElement.resource);
@@ -254,7 +246,7 @@ export class ExplorerService implements IExplorerService {
} }
// Delete // Delete
else if (e.operation === FileOperation.DELETE) { else if (e.isOperation(FileOperation.DELETE)) {
const modelElements = this.model.findAll(e.resource); const modelElements = this.model.findAll(e.resource);
modelElements.forEach(element => { modelElements.forEach(element => {
if (element.parent) { if (element.parent) {
@@ -191,7 +191,7 @@ export class OpenFileHandler extends QuickOpenHandler {
workspaceFolders[0].uri.with({ path: detildifiedQuery }) : workspaceFolders[0].uri.with({ path: detildifiedQuery }) :
URI.file(detildifiedQuery); URI.file(detildifiedQuery);
return this.fileService.resolveFile(resource).then( return this.fileService.resolve(resource).then(
stat => stat.isDirectory ? undefined : resource, stat => stat.isDirectory ? undefined : resource,
error => undefined); error => undefined);
} }
@@ -365,7 +365,7 @@ const searchInFolderCommand: ICommandHandler = (accessor, resource?: URI) => {
return openSearchView(viewletService, panelService, configurationService, true).then(searchView => { return openSearchView(viewletService, panelService, configurationService, true).then(searchView => {
if (resources && resources.length && searchView) { if (resources && resources.length && searchView) {
return fileService.resolveFiles(resources.map(resource => ({ resource }))).then(results => { return fileService.resolveAll(resources.map(resource => ({ resource }))).then(results => {
const folders: URI[] = []; const folders: URI[] = [];
results.forEach(result => { results.forEach(result => {
@@ -1200,7 +1200,7 @@ export class SearchView extends ViewletPanel {
// Validate folderQueries // Validate folderQueries
const folderQueriesExistP = const folderQueriesExistP =
query.folderQueries.map(fq => { query.folderQueries.map(fq => {
return this.fileService.existsFile(fq.folder); return this.fileService.exists(fq.folder);
}); });
return Promise.resolve(folderQueriesExistP).then(existResults => { return Promise.resolve(folderQueriesExistP).then(existResults => {
@@ -164,7 +164,7 @@ async function createSnippetFile(scope: string, defaultPath: URI, windowService:
} }
async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileService) { async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileService) {
if (await fileService.existsFile(URI.file(pick.filepath))) { if (await fileService.exists(URI.file(pick.filepath))) {
return; return;
} }
const contents = [ const contents = [
@@ -121,11 +121,11 @@ function watch(service: IFileService, resource: URI, callback: (type: FileChange
} }
} }
}); });
service.watchFileChanges(resource); service.watch(resource);
return { return {
dispose() { dispose() {
listener.dispose(); listener.dispose();
service.unwatchFileChanges(resource); service.unwatch(resource);
} }
}; };
} }
@@ -277,7 +277,7 @@ class SnippetsService implements ISnippetsService {
private _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: IDisposable[]): Promise<any> { private _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: IDisposable[]): Promise<any> {
let promises = workspace.folders.map(folder => { let promises = workspace.folders.map(folder => {
const snippetFolder = folder.toResource('.vscode'); const snippetFolder = folder.toResource('.vscode');
return this._fileService.existsFile(snippetFolder).then(value => { return this._fileService.exists(snippetFolder).then(value => {
if (value) { if (value) {
this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket);
} else { } else {
@@ -305,7 +305,7 @@ class SnippetsService implements ISnippetsService {
if (type === FileChangeType.DELETED) { if (type === FileChangeType.DELETED) {
return Promise.resolve(); return Promise.resolve();
} }
return this._fileService.resolveFile(folder).then(stat => { return this._fileService.resolve(folder).then(stat => {
for (const entry of stat.children || []) { for (const entry of stat.children || []) {
disposables.push(this._addSnippetFile(entry.resource, source)); disposables.push(this._addSnippetFile(entry.resource, source));
} }
@@ -196,7 +196,7 @@ export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: bool
export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, stripEndingDotGit: boolean = false): Promise<string[]> { export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, stripEndingDotGit: boolean = false): Promise<string[]> {
const path = workspaceUri.path; const path = workspaceUri.path;
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
return fileService.existsFile(uri).then(exists => { return fileService.exists(uri).then(exists => {
if (!exists) { if (!exists) {
return []; return [];
} }
@@ -364,7 +364,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
return Promise.resolve(tags); return Promise.resolve(tags);
} }
return this.fileService.resolveFiles(folders.map(resource => ({ resource }))).then((files: IResolveFileResult[]) => { return this.fileService.resolveAll(folders.map(resource => ({ resource }))).then((files: IResolveFileResult[]) => {
const names = (<IFileStat[]>[]).concat(...files.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name); const names = (<IFileStat[]>[]).concat(...files.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name);
const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set()); const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set());
@@ -437,7 +437,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
function getFilePromises(filename: string, fileService: IFileService, contentHandler: (content: IContent) => void): Promise<void>[] { function getFilePromises(filename: string, fileService: IFileService, contentHandler: (content: IContent) => void): Promise<void>[] {
return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => { return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => {
const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` }); const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` });
return fileService.existsFile(uri).then(exists => { return fileService.exists(uri).then(exists => {
if (!exists) { if (!exists) {
return undefined; return undefined;
} }
@@ -620,7 +620,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
Promise.all<string[]>(workspaceUris.map(workspaceUri => { Promise.all<string[]>(workspaceUris.map(workspaceUri => {
const path = workspaceUri.path; const path = workspaceUri.path;
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
return this.fileService.existsFile(uri).then(exists => { return this.fileService.exists(uri).then(exists => {
if (!exists) { if (!exists) {
return []; return [];
} }
@@ -666,7 +666,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
const path = workspaceUri.path; const path = workspaceUri.path;
return workspaceUri.with({ path: `${path !== '/' ? path : ''}/node_modules` }); return workspaceUri.with({ path: `${path !== '/' ? path : ''}/node_modules` });
}); });
return this.fileService.resolveFiles(uris.map(resource => ({ resource }))).then( return this.fileService.resolveAll(uris.map(resource => ({ resource }))).then(
results => { results => {
const names = (<IFileStat[]>[]).concat(...results.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name); const names = (<IFileStat[]>[]).concat(...results.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name);
const referencesAzure = WorkspaceStats.searchArray(names, /azure/i); const referencesAzure = WorkspaceStats.searchArray(names, /azure/i);
@@ -689,7 +689,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
return Promise.all(workspaceUris.map(workspaceUri => { return Promise.all(workspaceUris.map(workspaceUri => {
const path = workspaceUri.path; const path = workspaceUri.path;
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/pom.xml` }); const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/pom.xml` });
return this.fileService.existsFile(uri).then(exists => { return this.fileService.exists(uri).then(exists => {
if (!exists) { if (!exists) {
return false; return false;
} }
@@ -2318,7 +2318,7 @@ class TaskService extends Disposable implements ITaskService {
let openTaskFile = (workspaceFolder: IWorkspaceFolder): void => { let openTaskFile = (workspaceFolder: IWorkspaceFolder): void => {
let resource = workspaceFolder.toResource('.vscode/tasks.json'); let resource = workspaceFolder.toResource('.vscode/tasks.json');
let configFileCreated = false; let configFileCreated = false;
this.fileService.resolveFile(resource).then((stat) => stat, () => undefined).then((stat) => { this.fileService.resolve(resource).then((stat) => stat, () => undefined).then((stat) => {
if (stat) { if (stat) {
return stat.resource; return stat.resource;
} }
@@ -2375,7 +2375,7 @@ class TaskService extends Disposable implements ITaskService {
} }
let stats = this.contextService.getWorkspace().folders.map<Promise<IFileStat | undefined>>((folder) => { let stats = this.contextService.getWorkspace().folders.map<Promise<IFileStat | undefined>>((folder) => {
return this.fileService.resolveFile(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined); return this.fileService.resolve(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined);
}); });
let createLabel = nls.localize('TaskService.createJsonFile', 'Create tasks.json file from template'); let createLabel = nls.localize('TaskService.createJsonFile', 'Create tasks.json file from template');
@@ -230,7 +230,7 @@ export class ProcessRunnerDetector {
} }
private tryDetectGulp(workspaceFolder: IWorkspaceFolder, list: boolean): Promise<DetectorResult> { private tryDetectGulp(workspaceFolder: IWorkspaceFolder, list: boolean): Promise<DetectorResult> {
return Promise.resolve(this.fileService.resolveFile(workspaceFolder.toResource('gulpfile.js'))).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return Promise.resolve(this.fileService.resolve(workspaceFolder.toResource('gulpfile.js'))).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454)
let config = ProcessRunnerDetector.detectorConfig('gulp'); let config = ProcessRunnerDetector.detectorConfig('gulp');
let process = new LineProcess('gulp', [config.arg, '--no-color'], true, { cwd: this._cwd }); let process = new LineProcess('gulp', [config.arg, '--no-color'], true, { cwd: this._cwd });
return this.runDetection(process, 'gulp', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); return this.runDetection(process, 'gulp', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list);
@@ -240,7 +240,7 @@ export class ProcessRunnerDetector {
} }
private tryDetectGrunt(workspaceFolder: IWorkspaceFolder, list: boolean): Promise<DetectorResult> { private tryDetectGrunt(workspaceFolder: IWorkspaceFolder, list: boolean): Promise<DetectorResult> {
return Promise.resolve(this.fileService.resolveFile(workspaceFolder.toResource('Gruntfile.js'))).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return Promise.resolve(this.fileService.resolve(workspaceFolder.toResource('Gruntfile.js'))).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454)
let config = ProcessRunnerDetector.detectorConfig('grunt'); let config = ProcessRunnerDetector.detectorConfig('grunt');
let process = new LineProcess('grunt', [config.arg, '--no-color'], true, { cwd: this._cwd }); let process = new LineProcess('grunt', [config.arg, '--no-color'], true, { cwd: this._cwd });
return this.runDetection(process, 'grunt', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); return this.runDetection(process, 'grunt', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list);
@@ -255,10 +255,10 @@ export class ProcessRunnerDetector {
let process = new LineProcess('jake', [config.arg], true, { cwd: this._cwd }); let process = new LineProcess('jake', [config.arg], true, { cwd: this._cwd });
return this.runDetection(process, 'jake', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); return this.runDetection(process, 'jake', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list);
}; };
return Promise.resolve(this.fileService.resolveFile(workspaceFolder.toResource('Jakefile'))).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return Promise.resolve(this.fileService.resolve(workspaceFolder.toResource('Jakefile'))).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454)
return run(); return run();
}, (err: any) => { }, (err: any) => {
return this.fileService.resolveFile(workspaceFolder.toResource('Jakefile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return this.fileService.resolve(workspaceFolder.toResource('Jakefile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454)
return run(); return run();
}, (err: any) => { }, (err: any) => {
return null; return null;
@@ -305,7 +305,7 @@ export class TerminalLinkHandler {
uri = URI.file(linkUrl); uri = URI.file(linkUrl);
} }
return this._fileService.resolveFile(uri).then(stat => { return this._fileService.resolve(uri).then(stat => {
if (stat.isDirectory) { if (stat.isDirectory) {
return null; return null;
} }
@@ -416,7 +416,7 @@ export abstract class TerminalService implements ITerminalService {
return Promise.resolve(null); return Promise.resolve(null);
} }
const current = potentialPaths.shift(); const current = potentialPaths.shift();
return this._fileService.existsFile(URI.file(current!)).then(exists => { return this._fileService.exists(URI.file(current!)).then(exists => {
if (!exists) { if (!exists) {
return this._validateShellPaths(label, potentialPaths); return this._validateShellPaths(label, potentialPaths);
} }
@@ -82,7 +82,7 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
const interval = setInterval(() => { const interval = setInterval(() => {
if (!running) { if (!running) {
running = true; running = true;
this._fileService.existsFile(path).then(exists => { this._fileService.exists(path).then(exists => {
running = false; running = false;
if (!exists) { if (!exists) {
@@ -69,7 +69,7 @@ export class WelcomePageContribution implements IWorkbenchContribution {
if (openWithReadme) { if (openWithReadme) {
return Promise.all(contextService.getWorkspace().folders.map(folder => { return Promise.all(contextService.getWorkspace().folders.map(folder => {
const folderUri = folder.uri; const folderUri = folder.uri;
return fileService.resolveFile(folderUri) return fileService.resolve(folderUri)
.then(folder => { .then(folder => {
const files = folder.children ? folder.children.map(child => child.name) : []; const files = folder.children ? folder.children.map(child => child.name) : [];
@@ -318,14 +318,14 @@ export class BulkEdit {
if (edit.newUri && edit.oldUri) { if (edit.newUri && edit.oldUri) {
// rename // rename
if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.existsFile(edit.newUri)) { if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.exists(edit.newUri)) {
continue; // not overwriting, but ignoring, and the target file exists continue; // not overwriting, but ignoring, and the target file exists
} }
await this._textFileService.move(edit.oldUri, edit.newUri, options.overwrite); await this._textFileService.move(edit.oldUri, edit.newUri, options.overwrite);
} else if (!edit.newUri && edit.oldUri) { } else if (!edit.newUri && edit.oldUri) {
// delete file // delete file
if (await this._fileService.existsFile(edit.oldUri)) { if (await this._fileService.exists(edit.oldUri)) {
let useTrash = this._configurationService.getValue<boolean>('files.enableTrash'); let useTrash = this._configurationService.getValue<boolean>('files.enableTrash');
if (useTrash && !(await this._fileService.hasCapability(edit.oldUri, FileSystemProviderCapabilities.Trash))) { if (useTrash && !(await this._fileService.hasCapability(edit.oldUri, FileSystemProviderCapabilities.Trash))) {
useTrash = false; // not supported by provider useTrash = false; // not supported by provider
@@ -336,7 +336,7 @@ export class BulkEdit {
} }
} else if (edit.newUri && !edit.oldUri) { } else if (edit.newUri && !edit.oldUri) {
// create file // create file
if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.existsFile(edit.newUri)) { if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.exists(edit.newUri)) {
continue; // not overwriting, but ignoring, and the target file exists continue; // not overwriting, but ignoring, and the target file exists
} }
await this._textFileService.create(edit.newUri, undefined, { overwrite: options.overwrite }); await this._textFileService.create(edit.newUri, undefined, { overwrite: options.overwrite });
@@ -373,7 +373,7 @@ export class ConfigurationEditingService {
} }
private async resolveModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> { private async resolveModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
const exists = await this.fileService.existsFile(resource); const exists = await this.fileService.exists(resource);
if (!exists) { if (!exists) {
await this.fileService.updateContent(resource, '{}', { encoding: 'utf8' }); await this.fileService.updateContent(resource, '{}', { encoding: 'utf8' });
} }
@@ -84,7 +84,7 @@ export class JSONEditingService implements IJSONEditingService {
} }
private async resolveModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> { private async resolveModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
const exists = await this.fileService.existsFile(resource); const exists = await this.fileService.exists(resource);
if (!exists) { if (!exists) {
await this.fileService.updateContent(resource, '{}', { encoding: 'utf8' }); await this.fileService.updateContent(resource, '{}', { encoding: 'utf8' });
} }
@@ -369,13 +369,13 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat
private watchWorkspaceConfigurationFile(): void { private watchWorkspaceConfigurationFile(): void {
if (this.workspaceConfig) { if (this.workspaceConfig) {
this.fileService.watchFileChanges(this.workspaceConfig); this.fileService.watch(this.workspaceConfig);
} }
} }
private unWatchWorkspaceConfigurtionFile(): void { private unWatchWorkspaceConfigurtionFile(): void {
if (this.workspaceConfig) { if (this.workspaceConfig) {
this.fileService.unwatchFileChanges(this.workspaceConfig); this.fileService.unwatch(this.workspaceConfig);
} }
} }
@@ -638,7 +638,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
private doLoadFolderConfigurationContents(): Promise<Array<{ resource: URI, value: string }>> { private doLoadFolderConfigurationContents(): Promise<Array<{ resource: URI, value: string }>> {
const workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: Promise<IContent | undefined> } = Object.create(null); const workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: Promise<IContent | undefined> } = Object.create(null);
const bulkContentFetchromise = Promise.resolve(this.fileService.resolveFile(this.folderConfigurationPath)) const bulkContentFetchromise = Promise.resolve(this.fileService.resolve(this.folderConfigurationPath))
.then(stat => { .then(stat => {
if (stat.isDirectory && stat.children) { if (stat.isDirectory && stat.children) {
stat.children stat.children
@@ -87,7 +87,7 @@ export class RemoteFileDialog {
return this.pickResource().then(async fileFolderUri => { return this.pickResource().then(async fileFolderUri => {
if (fileFolderUri) { if (fileFolderUri) {
const stat = await this.fileService.resolveFile(fileFolderUri); const stat = await this.fileService.resolve(fileFolderUri);
return <IURIToOpen[]>[{ uri: fileFolderUri, typeHint: stat.isDirectory ? 'folder' : 'file' }]; return <IURIToOpen[]>[{ uri: fileFolderUri, typeHint: stat.isDirectory ? 'folder' : 'file' }];
} }
@@ -98,7 +98,7 @@ export class RemoteFileDialog {
public async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> { public async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {
this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems); this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems);
this.requiresTrailing = true; this.requiresTrailing = true;
const newOptions = await this.getOptions(options); const newOptions = await this.getOptions(options, true);
if (!newOptions) { if (!newOptions) {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }
@@ -114,15 +114,19 @@ export class RemoteFileDialog {
}); });
} }
private async getOptions(options: ISaveDialogOptions | IOpenDialogOptions): Promise<IOpenDialogOptions | undefined> { private async getOptions(options: ISaveDialogOptions | IOpenDialogOptions, isSave: boolean = false): Promise<IOpenDialogOptions | undefined> {
let defaultUri = options.defaultUri; let defaultUri = options.defaultUri;
if (!defaultUri) { const filename = (defaultUri && isSave && (resources.dirname(defaultUri).path === '/')) ? resources.basename(defaultUri) : undefined;
if (!defaultUri || filename) {
const env = await this.remoteAgentService.getEnvironment(); const env = await this.remoteAgentService.getEnvironment();
if (env) { if (env) {
defaultUri = env.userHome; defaultUri = env.userHome;
} else { } else {
defaultUri = URI.from({ scheme: this.scheme, path: this.environmentService.userHome }); defaultUri = URI.from({ scheme: this.scheme, path: this.environmentService.userHome });
} }
if (filename) {
defaultUri = resources.joinPath(defaultUri, filename);
}
} }
if ((this.scheme !== Schemas.file) && !this.fileService.canHandleResource(defaultUri)) { if ((this.scheme !== Schemas.file) && !this.fileService.canHandleResource(defaultUri)) {
this.notificationService.info(nls.localize('remoteFileDialog.notConnectedToRemote', 'File system provider for {0} is not available.', defaultUri.toString())); this.notificationService.info(nls.localize('remoteFileDialog.notConnectedToRemote', 'File system provider for {0} is not available.', defaultUri.toString()));
@@ -159,7 +163,7 @@ export class RemoteFileDialog {
let ext: string = resources.extname(homedir); let ext: string = resources.extname(homedir);
if (this.options.defaultUri) { if (this.options.defaultUri) {
try { try {
stat = await this.fileService.resolveFile(this.options.defaultUri); stat = await this.fileService.resolve(this.options.defaultUri);
} catch (e) { } catch (e) {
// The file or folder doesn't exist // The file or folder doesn't exist
} }
@@ -292,8 +296,8 @@ export class RemoteFileDialog {
let stat: IFileStat | undefined; let stat: IFileStat | undefined;
let statDirname: IFileStat | undefined; let statDirname: IFileStat | undefined;
try { try {
statDirname = await this.fileService.resolveFile(inputUriDirname); statDirname = await this.fileService.resolve(inputUriDirname);
stat = await this.fileService.resolveFile(inputUri); stat = await this.fileService.resolve(inputUri);
} catch (e) { } catch (e) {
// do nothing // do nothing
} }
@@ -335,7 +339,7 @@ export class RemoteFileDialog {
if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) { if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) {
let stat: IFileStat | undefined; let stat: IFileStat | undefined;
try { try {
stat = await this.fileService.resolveFile(valueUri); stat = await this.fileService.resolve(valueUri);
} catch (e) { } catch (e) {
// do nothing // do nothing
} }
@@ -346,7 +350,7 @@ export class RemoteFileDialog {
if (!resources.isEqual(this.currentFolder, inputUriDirname, true)) { if (!resources.isEqual(this.currentFolder, inputUriDirname, true)) {
let statWithoutTrailing: IFileStat | undefined; let statWithoutTrailing: IFileStat | undefined;
try { try {
statWithoutTrailing = await this.fileService.resolveFile(inputUriDirname); statWithoutTrailing = await this.fileService.resolve(inputUriDirname);
} catch (e) { } catch (e) {
// do nothing // do nothing
} }
@@ -387,8 +391,8 @@ export class RemoteFileDialog {
let stat: IFileStat | undefined; let stat: IFileStat | undefined;
let statDirname: IFileStat | undefined; let statDirname: IFileStat | undefined;
try { try {
statDirname = await this.fileService.resolveFile(resources.dirname(uri)); statDirname = await this.fileService.resolve(resources.dirname(uri));
stat = await this.fileService.resolveFile(uri); stat = await this.fileService.resolve(uri);
} catch (e) { } catch (e) {
// do nothing // do nothing
} }
@@ -509,7 +513,7 @@ export class RemoteFileDialog {
const backDir = this.createBackItem(currentFolder); const backDir = this.createBackItem(currentFolder);
try { try {
const folder = await this.fileService.resolveFile(currentFolder); const folder = await this.fileService.resolve(currentFolder);
const fileNames = folder.children ? folder.children.map(child => child.name) : []; const fileNames = folder.children ? folder.children.map(child => child.name) : [];
const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder))); const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder)));
for (let item of items) { for (let item of items) {
@@ -558,7 +562,7 @@ export class RemoteFileDialog {
private async createItem(filename: string, parent: URI): Promise<FileQuickPickItem | undefined> { private async createItem(filename: string, parent: URI): Promise<FileQuickPickItem | undefined> {
let fullPath = resources.joinPath(parent, filename); let fullPath = resources.joinPath(parent, filename);
try { try {
const stat = await this.fileService.resolveFile(fullPath); const stat = await this.fileService.resolve(fullPath);
if (stat.isDirectory) { if (stat.isDirectory) {
filename = this.basenameWithTrailingSlash(fullPath); filename = this.basenameWithTrailingSlash(fullPath);
return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) };
@@ -280,7 +280,7 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
return Promise.reject(error); return Promise.reject(error);
}; };
const statsPromise = this.resolveFile(resource).then(stat => { const statsPromise = this.resolve(resource).then(stat => {
result.resource = stat.resource; result.resource = stat.resource;
result.name = stat.name; result.name = stat.name;
result.mtime = stat.mtime; result.mtime = stat.mtime;
@@ -812,11 +812,11 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
)); ));
} }
moveFile(source: uri, target: uri, overwrite?: boolean): Promise<IFileStatWithMetadata> { move(source: uri, target: uri, overwrite?: boolean): Promise<IFileStatWithMetadata> {
return this.moveOrCopyFile(source, target, false, !!overwrite); return this.moveOrCopyFile(source, target, false, !!overwrite);
} }
copyFile(source: uri, target: uri, overwrite?: boolean): Promise<IFileStatWithMetadata> { copy(source: uri, target: uri, overwrite?: boolean): Promise<IFileStatWithMetadata> {
return this.moveOrCopyFile(source, target, true, !!overwrite); return this.moveOrCopyFile(source, target, true, !!overwrite);
} }
@@ -828,7 +828,7 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
return this.doMoveOrCopyFile(sourcePath, targetPath, keepCopy, overwrite).then(() => { return this.doMoveOrCopyFile(sourcePath, targetPath, keepCopy, overwrite).then(() => {
// 2.) resolve // 2.) resolve
return this.resolve(target, { resolveMetadata: true }).then(result => { return this.doResolve(target, { resolveMetadata: true }).then(result => {
// Events (unless it was a no-op because paths are identical) // Events (unless it was a no-op because paths are identical)
if (sourcePath !== targetPath) { if (sourcePath !== targetPath) {
@@ -952,9 +952,9 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
return paths.normalize(resource.fsPath); return paths.normalize(resource.fsPath);
} }
private resolve(resource: uri, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>; private doResolve(resource: uri, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
private resolve(resource: uri, options?: IResolveFileOptions): Promise<IFileStat>; private doResolve(resource: uri, options?: IResolveFileOptions): Promise<IFileStat>;
private resolve(resource: uri, options: IResolveFileOptions = Object.create(null)): Promise<IFileStat> { private doResolve(resource: uri, options: IResolveFileOptions = Object.create(null)): Promise<IFileStat> {
return this.toStatResolver(resource).then(model => model.resolve(options)); return this.toStatResolver(resource).then(model => model.resolve(options));
} }
@@ -966,7 +966,7 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
}); });
} }
watchFileChanges(resource: uri): void { watch(resource: uri): void {
assert.ok(resource && resource.scheme === Schemas.file, `Invalid resource for watching: ${resource}`); assert.ok(resource && resource.scheme === Schemas.file, `Invalid resource for watching: ${resource}`);
// Check for existing watcher first // Check for existing watcher first
@@ -1000,11 +1000,11 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
// Wait a bit and try to install watcher again, assuming that the file was renamed quickly ("Atomic Save") // Wait a bit and try to install watcher again, assuming that the file was renamed quickly ("Atomic Save")
setTimeout(() => { setTimeout(() => {
this.existsFile(resource).then(exists => { this.exists(resource).then(exists => {
// File still exists, so reapply the watcher // File still exists, so reapply the watcher
if (exists) { if (exists) {
this.watchFileChanges(resource); this.watch(resource);
} }
// File seems to be really gone, so emit a deleted event // File seems to be really gone, so emit a deleted event
@@ -1063,7 +1063,7 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
}); });
} }
unwatchFileChanges(resource: uri): void { unwatch(resource: uri): void {
const watcher = this.activeFileChangesWatchers.get(resource); const watcher = this.activeFileChangesWatchers.get(resource);
if (watcher && --watcher.count === 0) { if (watcher && --watcher.count === 0) {
watcher.unwatch(); watcher.unwatch();
@@ -1092,14 +1092,14 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
// Tests only // Tests only
resolveFile(resource: uri, options?: IResolveFileOptions): Promise<IFileStat>; resolve(resource: uri, options?: IResolveFileOptions): Promise<IFileStat>;
resolveFile(resource: uri, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>; resolve(resource: uri, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
resolveFile(resource: uri, options?: IResolveFileOptions): Promise<IFileStat> { resolve(resource: uri, options?: IResolveFileOptions): Promise<IFileStat> {
return this.resolve(resource, options); return this.doResolve(resource, options);
} }
resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> { resolveAll(toResolve: { resource: uri, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> {
return Promise.all(toResolve.map(resourceAndOptions => this.resolve(resourceAndOptions.resource, resourceAndOptions.options) return Promise.all(toResolve.map(resourceAndOptions => this.doResolve(resourceAndOptions.resource, resourceAndOptions.options)
.then(stat => ({ stat, success: true }), error => ({ stat: undefined, success: false })))); .then(stat => ({ stat, success: true }), error => ({ stat: undefined, success: false }))));
} }
@@ -1110,7 +1110,7 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
return pfs.mkdirp(absolutePath).then(() => { return pfs.mkdirp(absolutePath).then(() => {
// 2.) Resolve // 2.) Resolve
return this.resolve(resource, { resolveMetadata: true }).then(result => { return this.doResolve(resource, { resolveMetadata: true }).then(result => {
// Events // Events
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result)); this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result));
@@ -1120,8 +1120,8 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
}); });
} }
existsFile(resource: uri): Promise<boolean> { exists(resource: uri): Promise<boolean> {
return this.resolveFile(resource).then(() => true, () => false); return this.resolve(resource).then(() => true, () => false);
} }
} }
@@ -140,18 +140,18 @@ class WorkspaceWatchLogic extends Disposable {
} }
} }
this._watches.set(resource.toString(), resource); this._watches.set(resource.toString(), resource);
this._fileService.watchFileChanges(resource, { recursive: true, excludes }); this._fileService.watch(resource, { recursive: true, excludes });
} }
private _unwatchWorkspace(resource: URI) { private _unwatchWorkspace(resource: URI) {
if (this._watches.has(resource.toString())) { if (this._watches.has(resource.toString())) {
this._fileService.unwatchFileChanges(resource); this._fileService.unwatch(resource);
this._watches.delete(resource.toString()); this._watches.delete(resource.toString());
} }
} }
private _unwatchWorkspaces() { private _unwatchWorkspaces() {
this._watches.forEach(uri => this._fileService.unwatchFileChanges(uri)); this._watches.forEach(uri => this._fileService.unwatch(uri));
this._watches.clear(); this._watches.clear();
} }
} }
@@ -223,11 +223,11 @@ export class RemoteFileService extends FileService {
}); });
} }
resolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>; resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>; resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> { resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> {
if (resource.scheme === Schemas.file) { if (resource.scheme === Schemas.file) {
return super.resolveFile(resource, options); return super.resolve(resource, options);
} else { } else {
return this._doResolveFiles([{ resource, options }]).then(data => { return this._doResolveFiles([{ resource, options }]).then(data => {
if (data.length !== 1 || !data[0].success) { if (data.length !== 1 || !data[0].success) {
@@ -279,7 +279,7 @@ export class RemoteFileService extends FileService {
private _readFile(resource: URI, options: IResolveContentOptions = Object.create(null)): Promise<IStreamContent> { private _readFile(resource: URI, options: IResolveContentOptions = Object.create(null)): Promise<IStreamContent> {
return this._withProvider(resource).then(provider => { return this._withProvider(resource).then(provider => {
return this.resolveFile(resource).then(fileStat => { return this.resolve(resource).then(fileStat => {
if (fileStat.isDirectory) { if (fileStat.isDirectory) {
// todo@joh cannot copy a folder // todo@joh cannot copy a folder
@@ -409,7 +409,7 @@ export class RemoteFileService extends FileService {
target.once('error', err => reject(err)); target.once('error', err => reject(err));
target.once('finish', (_: unknown) => resolve(undefined)); target.once('finish', (_: unknown) => resolve(undefined));
}).then(_ => { }).then(_ => {
return this.resolveFile(resource, { resolveMetadata: true }) as Promise<IFileStatWithMetadata>; return this.resolve(resource, { resolveMetadata: true }) as Promise<IFileStatWithMetadata>;
}); });
} }
@@ -433,9 +433,9 @@ export class RemoteFileService extends FileService {
private _activeWatches = new Map<string, { unwatch: Promise<IDisposable>, count: number }>(); private _activeWatches = new Map<string, { unwatch: Promise<IDisposable>, count: number }>();
watchFileChanges(resource: URI, opts: IWatchOptions = { recursive: false, excludes: [] }): void { watch(resource: URI, opts: IWatchOptions = { recursive: false, excludes: [] }): void {
if (resource.scheme === Schemas.file) { if (resource.scheme === Schemas.file) {
return super.watchFileChanges(resource); return super.watch(resource);
} }
const key = resource.toString(); const key = resource.toString();
@@ -455,9 +455,9 @@ export class RemoteFileService extends FileService {
}); });
} }
unwatchFileChanges(resource: URI): void { unwatch(resource: URI): void {
if (resource.scheme === Schemas.file) { if (resource.scheme === Schemas.file) {
return super.unwatchFileChanges(resource); return super.unwatch(resource);
} }
let entry = this._activeWatches.get(resource.toString()); let entry = this._activeWatches.get(resource.toString());
if (entry && --entry.count === 0) { if (entry && --entry.count === 0) {
@@ -348,15 +348,15 @@ suite('FileService', () => {
}); });
}); });
test('watchFileChanges', function (done) { test('watch', function (done) {
const toWatch = uri.file(path.join(testDir, 'index.html')); const toWatch = uri.file(path.join(testDir, 'index.html'));
service.watchFileChanges(toWatch); service.watch(toWatch);
service.onFileChanges((e: FileChangesEvent) => { service.onFileChanges((e: FileChangesEvent) => {
assert.ok(e); assert.ok(e);
service.unwatchFileChanges(toWatch); service.unwatch(toWatch);
done(); done();
}); });
@@ -365,15 +365,15 @@ suite('FileService', () => {
}, 100); }, 100);
}); });
// test('watchFileChanges - support atomic save', function (done) { // test('watch - support atomic save', function (done) {
// const toWatch = uri.file(path.join(testDir, 'index.html')); // const toWatch = uri.file(path.join(testDir, 'index.html'));
// service.watchFileChanges(toWatch); // service.watch(toWatch);
// service.onFileChanges((e: FileChangesEvent) => { // service.onFileChanges((e: FileChangesEvent) => {
// assert.ok(e); // assert.ok(e);
// service.unwatchFileChanges(toWatch); // service.unwatch(toWatch);
// done(); // done();
// }); // });
@@ -152,9 +152,9 @@ export class FileService2 extends Disposable implements IFileService {
//#region File Metadata Resolving //#region File Metadata Resolving
async resolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>; async resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
async resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>; async resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
async resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> { async resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> {
try { try {
return await this.doResolveFile(resource, options); return await this.doResolveFile(resource, options);
} catch (error) { } catch (error) {
@@ -251,9 +251,9 @@ export class FileService2 extends Disposable implements IFileService {
return Promise.resolve(fileStat); return Promise.resolve(fileStat);
} }
async resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>; async resolveAll(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
async resolveFiles(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResultWithMetadata[]>; async resolveAll(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResultWithMetadata[]>;
async resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise<IResolveFileResult[]> { async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise<IResolveFileResult[]> {
return Promise.all(toResolve.map(async entry => { return Promise.all(toResolve.map(async entry => {
try { try {
return { stat: await this.doResolveFile(entry.resource, entry.options), success: true }; return { stat: await this.doResolveFile(entry.resource, entry.options), success: true };
@@ -265,9 +265,9 @@ export class FileService2 extends Disposable implements IFileService {
})); }));
} }
async existsFile(resource: URI): Promise<boolean> { async exists(resource: URI): Promise<boolean> {
try { try {
return !!(await this.resolveFile(resource)); return !!(await this.resolve(resource));
} catch (error) { } catch (error) {
return false; return false;
} }
@@ -295,7 +295,7 @@ export class FileService2 extends Disposable implements IFileService {
// validate overwrite // validate overwrite
const overwrite = !!(options && options.overwrite); const overwrite = !!(options && options.overwrite);
if (await this.existsFile(resource)) { if (await this.exists(resource)) {
if (!overwrite) { if (!overwrite) {
throw new FileOperationError(localize('fileExists', "File to create already exists ({0})", resource.toString(true)), FileOperationResult.FILE_MODIFIED_SINCE, options); throw new FileOperationError(localize('fileExists', "File to create already exists ({0})", resource.toString(true)), FileOperationResult.FILE_MODIFIED_SINCE, options);
} }
@@ -328,7 +328,7 @@ export class FileService2 extends Disposable implements IFileService {
} }
// events // events
const fileStat = await this.resolveFile(resource, { resolveMetadata: true }); const fileStat = await this.resolve(resource, { resolveMetadata: true });
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat));
return fileStat; return fileStat;
@@ -350,7 +350,7 @@ export class FileService2 extends Disposable implements IFileService {
//#region Move/Copy/Delete/Create Folder //#region Move/Copy/Delete/Create Folder
async moveFile(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> { async move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> {
const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withProvider(source)); const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withProvider(source));
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withProvider(target)); const targetProvider = this.throwIfFileSystemIsReadonly(await this.withProvider(target));
@@ -358,13 +358,13 @@ export class FileService2 extends Disposable implements IFileService {
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'move', overwrite); const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'move', overwrite);
// resolve and send events // resolve and send events
const fileStat = await this.resolveFile(target, { resolveMetadata: true }); const fileStat = await this.resolve(target, { resolveMetadata: true });
this._onAfterOperation.fire(new FileOperationEvent(source, mode === 'move' ? FileOperation.MOVE : FileOperation.COPY, fileStat)); this._onAfterOperation.fire(new FileOperationEvent(source, mode === 'move' ? FileOperation.MOVE : FileOperation.COPY, fileStat));
return fileStat; return fileStat;
} }
async copyFile(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> { async copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> {
const sourceProvider = await this.withProvider(source); const sourceProvider = await this.withProvider(source);
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withProvider(target)); const targetProvider = this.throwIfFileSystemIsReadonly(await this.withProvider(target));
@@ -372,7 +372,7 @@ export class FileService2 extends Disposable implements IFileService {
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'copy', overwrite); const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'copy', overwrite);
// resolve and send events // resolve and send events
const fileStat = await this.resolveFile(target, { resolveMetadata: true }); const fileStat = await this.resolve(target, { resolveMetadata: true });
this._onAfterOperation.fire(new FileOperationEvent(source, mode === 'copy' ? FileOperation.COPY : FileOperation.MOVE, fileStat)); this._onAfterOperation.fire(new FileOperationEvent(source, mode === 'copy' ? FileOperation.COPY : FileOperation.MOVE, fileStat));
return fileStat; return fileStat;
@@ -409,7 +409,7 @@ export class FileService2 extends Disposable implements IFileService {
// when copying via buffer/unbuffered, we have to manually // when copying via buffer/unbuffered, we have to manually
// traverse the source if it is a folder and not a file // traverse the source if it is a folder and not a file
const sourceFile = await this.resolveFile(source); const sourceFile = await this.resolve(source);
if (sourceFile.isDirectory) { if (sourceFile.isDirectory) {
return this.doCopyFolder(sourceProvider, sourceFile, targetProvider, target, overwrite).then(() => mode); return this.doCopyFolder(sourceProvider, sourceFile, targetProvider, target, overwrite).then(() => mode);
} else { } else {
@@ -467,7 +467,7 @@ export class FileService2 extends Disposable implements IFileService {
await Promise.all(sourceFolder.children.map(async sourceChild => { await Promise.all(sourceFolder.children.map(async sourceChild => {
const targetChild = joinPath(targetFolder, sourceChild.name); const targetChild = joinPath(targetFolder, sourceChild.name);
if (sourceChild.isDirectory) { if (sourceChild.isDirectory) {
return this.doCopyFolder(sourceProvider, await this.resolveFile(sourceChild.resource), targetProvider, targetChild, overwrite); return this.doCopyFolder(sourceProvider, await this.resolve(sourceChild.resource), targetProvider, targetChild, overwrite);
} else { } else {
return this.doCopyFile(sourceProvider, sourceChild.resource, targetProvider, targetChild, overwrite); return this.doCopyFile(sourceProvider, sourceChild.resource, targetProvider, targetChild, overwrite);
} }
@@ -489,7 +489,7 @@ export class FileService2 extends Disposable implements IFileService {
} }
// Extra checks if target exists and this is not a rename // Extra checks if target exists and this is not a rename
const exists = await this.existsFile(target); const exists = await this.exists(target);
if (exists && !isCaseChange) { if (exists && !isCaseChange) {
// Bail out if target exists and we are not about to overwrite // Bail out if target exists and we are not about to overwrite
@@ -514,7 +514,7 @@ export class FileService2 extends Disposable implements IFileService {
await this.mkdirp(provider, resource); await this.mkdirp(provider, resource);
// events // events
const fileStat = await this.resolveFile(resource, { resolveMetadata: true }); const fileStat = await this.resolve(resource, { resolveMetadata: true });
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat));
return fileStat; return fileStat;
@@ -565,8 +565,8 @@ export class FileService2 extends Disposable implements IFileService {
// Validate recursive // Validate recursive
const recursive = !!(options && options.recursive); const recursive = !!(options && options.recursive);
if (!recursive && await this.existsFile(resource)) { if (!recursive && await this.exists(resource)) {
const stat = await this.resolveFile(resource); const stat = await this.resolve(resource);
if (stat.isDirectory && Array.isArray(stat.children) && stat.children.length > 0) { if (stat.isDirectory && Array.isArray(stat.children) && stat.children.length > 0) {
throw new Error(localize('deleteFailed', "Failed to delete non-empty folder '{0}'.", resource.toString())); throw new Error(localize('deleteFailed', "Failed to delete non-empty folder '{0}'.", resource.toString()));
} }
@@ -586,12 +586,12 @@ export class FileService2 extends Disposable implements IFileService {
private _onFileChanges: Emitter<FileChangesEvent> = this._register(new Emitter<FileChangesEvent>()); private _onFileChanges: Emitter<FileChangesEvent> = this._register(new Emitter<FileChangesEvent>());
get onFileChanges(): Event<FileChangesEvent> { return this._onFileChanges.event; } get onFileChanges(): Event<FileChangesEvent> { return this._onFileChanges.event; }
watchFileChanges(resource: URI): void { watch(resource: URI): void {
this.joinOnLegacy.then(legacy => legacy.watchFileChanges(resource)); this.joinOnLegacy.then(legacy => legacy.watch(resource));
} }
unwatchFileChanges(resource: URI): void { unwatch(resource: URI): void {
this.joinOnLegacy.then(legacy => legacy.unwatchFileChanges(resource)); this.joinOnLegacy.then(legacy => legacy.unwatch(resource));
} }
//#endregion //#endregion
@@ -690,7 +690,7 @@ export class FileService2 extends Disposable implements IFileService {
private async doPipeBufferedToUnbuffered(sourceProvider: IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithFileReadWriteCapability, target: URI, overwrite: boolean): Promise<void> { private async doPipeBufferedToUnbuffered(sourceProvider: IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithFileReadWriteCapability, target: URI, overwrite: boolean): Promise<void> {
// Determine file size // Determine file size
const size = (await this.resolveFile(source, { resolveMetadata: true })).size; const size = (await this.resolve(source, { resolveMetadata: true })).size;
// Open handle // Open handle
const sourceHandle = await sourceProvider.open(source, { create: false }); const sourceHandle = await sourceProvider.open(source, { create: false });
@@ -225,10 +225,6 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
await this.doDelete(filePath, opts); await this.doDelete(filePath, opts);
} catch (error) { } catch (error) {
if (error.code === 'ENOENT') {
return Promise.resolve(); // tolerate that the file might not exist
}
throw this.toFileSystemProviderError(error); throw this.toFileSystemProviderError(error);
} }
} }
@@ -100,7 +100,7 @@ suite('Disk File Service', () => {
let event: FileOperationEvent | undefined; let event: FileOperationEvent | undefined;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const parent = await service.resolveFile(URI.file(testDir)); const parent = await service.resolve(URI.file(testDir));
const newFolderResource = URI.file(join(parent.resource.fsPath, 'newFolder')); const newFolderResource = URI.file(join(parent.resource.fsPath, 'newFolder'));
@@ -121,7 +121,7 @@ suite('Disk File Service', () => {
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const multiFolderPaths = ['a', 'couple', 'of', 'folders']; const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
const parent = await service.resolveFile(URI.file(testDir)); const parent = await service.resolve(URI.file(testDir));
const newFolderResource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths)); const newFolderResource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths));
@@ -138,26 +138,26 @@ suite('Disk File Service', () => {
assert.equal(event!.target!.isDirectory, true); assert.equal(event!.target!.isDirectory, true);
}); });
test('existsFile', async () => { test('exists', async () => {
let exists = await service.existsFile(URI.file(testDir)); let exists = await service.exists(URI.file(testDir));
assert.equal(exists, true); assert.equal(exists, true);
exists = await service.existsFile(URI.file(testDir + 'something')); exists = await service.exists(URI.file(testDir + 'something'));
assert.equal(exists, false); assert.equal(exists, false);
}); });
test('resolveFile', async () => { test('resolve', async () => {
const resolved = await service.resolveFile(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] }); const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });
assert.equal(resolved.children!.length, 8); assert.equal(resolved.children!.length, 8);
const deep = (getByName(resolved, 'deep')!); const deep = (getByName(resolved, 'deep')!);
assert.equal(deep.children!.length, 4); assert.equal(deep.children!.length, 4);
}); });
test('resolveFile - directory', async () => { test('resolve - directory', async () => {
const testsElements = ['examples', 'other', 'index.html', 'site.css']; const testsElements = ['examples', 'other', 'index.html', 'site.css'];
const result = await service.resolveFile(URI.file(getPathFromAmdModule(require, './fixtures/resolver'))); const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')));
assert.ok(result); assert.ok(result);
assert.ok(result.children); assert.ok(result.children);
@@ -187,10 +187,10 @@ suite('Disk File Service', () => {
}); });
}); });
test('resolveFile - directory - with metadata', async () => { test('resolve - directory - with metadata', async () => {
const testsElements = ['examples', 'other', 'index.html', 'site.css']; const testsElements = ['examples', 'other', 'index.html', 'site.css'];
const result = await service.resolveFile(URI.file(getPathFromAmdModule(require, './fixtures/resolver')), { resolveMetadata: true }); const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')), { resolveMetadata: true });
assert.ok(result); assert.ok(result);
assert.ok(result.children); assert.ok(result.children);
@@ -222,9 +222,9 @@ suite('Disk File Service', () => {
}); });
}); });
test('resolveFile - directory - resolveTo single directory', async () => { test('resolve - directory - resolveTo single directory', async () => {
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver'); const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
const result = await service.resolveFile(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] }); const result = await service.resolve(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] });
assert.ok(result); assert.ok(result);
assert.ok(result.children); assert.ok(result.children);
@@ -246,7 +246,7 @@ suite('Disk File Service', () => {
test('resolve directory - resolveTo multiple directories', async () => { test('resolve directory - resolveTo multiple directories', async () => {
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver'); const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
const result = await service.resolveFile(URI.file(resolverFixturesPath), { const result = await service.resolve(URI.file(resolverFixturesPath), {
resolveTo: [ resolveTo: [
URI.file(join(resolverFixturesPath, 'other/deep')), URI.file(join(resolverFixturesPath, 'other/deep')),
URI.file(join(resolverFixturesPath, 'examples')) URI.file(join(resolverFixturesPath, 'examples'))
@@ -278,7 +278,7 @@ suite('Disk File Service', () => {
test('resolve directory - resolveSingleChildFolders', async () => { test('resolve directory - resolveSingleChildFolders', async () => {
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver/other'); const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver/other');
const result = await service.resolveFile(URI.file(resolverFixturesPath), { resolveSingleChildDescendants: true }); const result = await service.resolve(URI.file(resolverFixturesPath), { resolveSingleChildDescendants: true });
assert.ok(result); assert.ok(result);
assert.ok(result.children); assert.ok(result.children);
@@ -294,8 +294,8 @@ suite('Disk File Service', () => {
assert.equal(deep!.children!.length, 4); assert.equal(deep!.children!.length, 4);
}); });
test('resolveFiles', async () => { test('resolves', async () => {
const res = await service.resolveFiles([ const res = await service.resolveAll([
{ resource: URI.file(testDir), options: { resolveTo: [URI.file(join(testDir, 'deep'))] } }, { resource: URI.file(testDir), options: { resolveTo: [URI.file(join(testDir, 'deep'))] } },
{ resource: URI.file(join(testDir, 'deep')) } { resource: URI.file(join(testDir, 'deep')) }
]); ]);
@@ -311,7 +311,7 @@ suite('Disk File Service', () => {
assert.equal(r2.name, 'deep'); assert.equal(r2.name, 'deep');
}); });
test('resolveFile - folder symbolic link', async () => { test('resolve - folder symbolic link', async () => {
if (isWindows) { if (isWindows) {
return; // not happy return; // not happy
} }
@@ -319,13 +319,13 @@ suite('Disk File Service', () => {
const link = URI.file(join(testDir, 'deep-link')); const link = URI.file(join(testDir, 'deep-link'));
await symlink(join(testDir, 'deep'), link.fsPath); await symlink(join(testDir, 'deep'), link.fsPath);
const resolved = await service.resolveFile(link); const resolved = await service.resolve(link);
assert.equal(resolved.children!.length, 4); assert.equal(resolved.children!.length, 4);
assert.equal(resolved.isDirectory, true); assert.equal(resolved.isDirectory, true);
assert.equal(resolved.isSymbolicLink, true); assert.equal(resolved.isSymbolicLink, true);
}); });
test('resolveFile - file symbolic link', async () => { test('resolve - file symbolic link', async () => {
if (isWindows) { if (isWindows) {
return; // not happy return; // not happy
} }
@@ -333,12 +333,12 @@ suite('Disk File Service', () => {
const link = URI.file(join(testDir, 'lorem.txt-linked')); const link = URI.file(join(testDir, 'lorem.txt-linked'));
await symlink(join(testDir, 'lorem.txt'), link.fsPath); await symlink(join(testDir, 'lorem.txt'), link.fsPath);
const resolved = await service.resolveFile(link); const resolved = await service.resolve(link);
assert.equal(resolved.isDirectory, false); assert.equal(resolved.isDirectory, false);
assert.equal(resolved.isSymbolicLink, true); assert.equal(resolved.isSymbolicLink, true);
}); });
test('resolveFile - invalid symbolic link does not break', async () => { test('resolve - invalid symbolic link does not break', async () => {
if (isWindows) { if (isWindows) {
return; // not happy return; // not happy
} }
@@ -346,7 +346,7 @@ suite('Disk File Service', () => {
const link = URI.file(join(testDir, 'foo')); const link = URI.file(join(testDir, 'foo'));
await symlink(link.fsPath, join(testDir, 'bar')); await symlink(link.fsPath, join(testDir, 'bar'));
const resolved = await service.resolveFile(URI.file(testDir)); const resolved = await service.resolve(URI.file(testDir));
assert.equal(resolved.isDirectory, true); assert.equal(resolved.isDirectory, true);
assert.equal(resolved.children!.length, 8); assert.equal(resolved.children!.length, 8);
}); });
@@ -356,7 +356,7 @@ suite('Disk File Service', () => {
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const resource = URI.file(join(testDir, 'deep', 'conway.js')); const resource = URI.file(join(testDir, 'deep', 'conway.js'));
const source = await service.resolveFile(resource); const source = await service.resolve(resource);
await service.del(source.resource); await service.del(source.resource);
@@ -371,7 +371,7 @@ suite('Disk File Service', () => {
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const resource = URI.file(join(testDir, 'deep')); const resource = URI.file(join(testDir, 'deep'));
const source = await service.resolveFile(resource); const source = await service.resolve(resource);
await service.del(source.resource, { recursive: true }); await service.del(source.resource, { recursive: true });
@@ -383,7 +383,7 @@ suite('Disk File Service', () => {
test('deleteFolder (non recursive)', async () => { test('deleteFolder (non recursive)', async () => {
const resource = URI.file(join(testDir, 'deep')); const resource = URI.file(join(testDir, 'deep'));
const source = await service.resolveFile(resource); const source = await service.resolve(resource);
try { try {
await service.del(source.resource); await service.del(source.resource);
@@ -394,7 +394,7 @@ suite('Disk File Service', () => {
} }
}); });
test('moveFile', async () => { test('move', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
@@ -403,7 +403,7 @@ suite('Disk File Service', () => {
const target = URI.file(join(dirname(source.fsPath), 'other.html')); const target = URI.file(join(dirname(source.fsPath), 'other.html'));
const renamed = await service.moveFile(source, target); const renamed = await service.move(source, target);
assert.equal(existsSync(renamed.resource.fsPath), true); assert.equal(existsSync(renamed.resource.fsPath), true);
assert.equal(existsSync(source.fsPath), false); assert.equal(existsSync(source.fsPath), false);
@@ -418,56 +418,56 @@ suite('Disk File Service', () => {
assert.equal(sourceContents.toString(), targetContents.toString()); assert.equal(sourceContents.toString(), targetContents.toString());
}); });
test('moveFile - across providers (buffered => buffered)', async () => { test('move - across providers (buffered => buffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
await testMoveAcrossProviders(); await testMoveAcrossProviders();
}); });
test('moveFile - across providers (unbuffered => unbuffered)', async () => { test('move - across providers (unbuffered => unbuffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
await testMoveAcrossProviders(); await testMoveAcrossProviders();
}); });
test('moveFile - across providers (buffered => unbuffered)', async () => { test('move - across providers (buffered => unbuffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
await testMoveAcrossProviders(); await testMoveAcrossProviders();
}); });
test('moveFile - across providers (unbuffered => buffered)', async () => { test('move - across providers (unbuffered => buffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
await testMoveAcrossProviders(); await testMoveAcrossProviders();
}); });
test('moveFile - across providers - large (buffered => buffered)', async () => { test('move - across providers - large (buffered => buffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
await testMoveAcrossProviders('lorem.txt'); await testMoveAcrossProviders('lorem.txt');
}); });
test('moveFile - across providers - large (unbuffered => unbuffered)', async () => { test('move - across providers - large (unbuffered => unbuffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
await testMoveAcrossProviders('lorem.txt'); await testMoveAcrossProviders('lorem.txt');
}); });
test('moveFile - across providers - large (buffered => unbuffered)', async () => { test('move - across providers - large (buffered => unbuffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
await testMoveAcrossProviders('lorem.txt'); await testMoveAcrossProviders('lorem.txt');
}); });
test('moveFile - across providers - large (unbuffered => buffered)', async () => { test('move - across providers - large (unbuffered => buffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
@@ -483,7 +483,7 @@ suite('Disk File Service', () => {
const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema }); const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema });
const renamed = await service.moveFile(source, target); const renamed = await service.move(source, target);
assert.equal(existsSync(renamed.resource.fsPath), true); assert.equal(existsSync(renamed.resource.fsPath), true);
assert.equal(existsSync(source.fsPath), false); assert.equal(existsSync(source.fsPath), false);
@@ -498,7 +498,7 @@ suite('Disk File Service', () => {
assert.equal(sourceContents.toString(), targetContents.toString()); assert.equal(sourceContents.toString(), targetContents.toString());
} }
test('moveFile - multi folder', async () => { test('move - multi folder', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
@@ -507,7 +507,7 @@ suite('Disk File Service', () => {
const source = URI.file(join(testDir, 'index.html')); const source = URI.file(join(testDir, 'index.html'));
const renamed = await service.moveFile(source, URI.file(join(dirname(source.fsPath), renameToPath))); const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), renameToPath)));
assert.equal(existsSync(renamed.resource.fsPath), true); assert.equal(existsSync(renamed.resource.fsPath), true);
assert.equal(existsSync(source.fsPath), false); assert.equal(existsSync(source.fsPath), false);
@@ -517,13 +517,13 @@ suite('Disk File Service', () => {
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
}); });
test('moveFile - directory', async () => { test('move - directory', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const source = URI.file(join(testDir, 'deep')); const source = URI.file(join(testDir, 'deep'));
const renamed = await service.moveFile(source, URI.file(join(dirname(source.fsPath), 'deeper'))); const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'deeper')));
assert.equal(existsSync(renamed.resource.fsPath), true); assert.equal(existsSync(renamed.resource.fsPath), true);
assert.equal(existsSync(source.fsPath), false); assert.equal(existsSync(source.fsPath), false);
@@ -533,28 +533,28 @@ suite('Disk File Service', () => {
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
}); });
test('moveFile - directory - across providers (buffered => buffered)', async () => { test('move - directory - across providers (buffered => buffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
await testMoveFolderAcrossProviders(); await testMoveFolderAcrossProviders();
}); });
test('moveFile - directory - across providers (unbuffered => unbuffered)', async () => { test('move - directory - across providers (unbuffered => unbuffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
await testMoveFolderAcrossProviders(); await testMoveFolderAcrossProviders();
}); });
test('moveFile - directory - across providers (buffered => unbuffered)', async () => { test('move - directory - across providers (buffered => unbuffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
await testMoveFolderAcrossProviders(); await testMoveFolderAcrossProviders();
}); });
test('moveFile - directory - across providers (unbuffered => buffered)', async () => { test('move - directory - across providers (unbuffered => buffered)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
@@ -570,7 +570,7 @@ suite('Disk File Service', () => {
const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema }); const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema });
const renamed = await service.moveFile(source, target); const renamed = await service.move(source, target);
assert.equal(existsSync(renamed.resource.fsPath), true); assert.equal(existsSync(renamed.resource.fsPath), true);
assert.equal(existsSync(source.fsPath), false); assert.equal(existsSync(source.fsPath), false);
@@ -586,14 +586,14 @@ suite('Disk File Service', () => {
} }
} }
test('moveFile - MIX CASE', async () => { test('move - MIX CASE', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const source = URI.file(join(testDir, 'index.html')); const source = URI.file(join(testDir, 'index.html'));
await service.resolveFile(source); await service.resolve(source);
const renamed = await service.moveFile(source, URI.file(join(dirname(source.fsPath), 'INDEX.html'))); const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'INDEX.html')));
assert.equal(existsSync(renamed.resource.fsPath), true); assert.equal(existsSync(renamed.resource.fsPath), true);
assert.equal(basename(renamed.resource.fsPath), 'INDEX.html'); assert.equal(basename(renamed.resource.fsPath), 'INDEX.html');
@@ -603,33 +603,33 @@ suite('Disk File Service', () => {
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
}); });
test('moveFile - source parent of target', async () => { test('move - source parent of target', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
await service.resolveFile(URI.file(join(testDir, 'index.html'))); await service.resolve(URI.file(join(testDir, 'index.html')));
try { try {
await service.moveFile(URI.file(testDir), URI.file(join(testDir, 'binary.txt'))); await service.move(URI.file(testDir), URI.file(join(testDir, 'binary.txt')));
} catch (e) { } catch (e) {
assert.ok(e); assert.ok(e);
assert.ok(!event!); assert.ok(!event!);
} }
}); });
test('moveFile - FILE_MOVE_CONFLICT', async () => { test('move - FILE_MOVE_CONFLICT', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const source = await service.resolveFile(URI.file(join(testDir, 'index.html'))); const source = await service.resolve(URI.file(join(testDir, 'index.html')));
try { try {
await service.moveFile(source.resource, URI.file(join(testDir, 'binary.txt'))); await service.move(source.resource, URI.file(join(testDir, 'binary.txt')));
} catch (e) { } catch (e) {
assert.equal(e.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT); assert.equal(e.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT);
assert.ok(!event!); assert.ok(!event!);
} }
}); });
test('moveFile - overwrite folder with file', async () => { test('move - overwrite folder with file', async () => {
let createEvent: FileOperationEvent; let createEvent: FileOperationEvent;
let moveEvent: FileOperationEvent; let moveEvent: FileOperationEvent;
let deleteEvent: FileOperationEvent; let deleteEvent: FileOperationEvent;
@@ -643,12 +643,12 @@ suite('Disk File Service', () => {
} }
})); }));
const parent = await service.resolveFile(URI.file(testDir)); const parent = await service.resolve(URI.file(testDir));
const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js')); const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
const f = await service.createFolder(folderResource); const f = await service.createFolder(folderResource);
const source = URI.file(join(testDir, 'deep', 'conway.js')); const source = URI.file(join(testDir, 'deep', 'conway.js'));
const moved = await service.moveFile(source, f.resource, true); const moved = await service.move(source, f.resource, true);
assert.equal(existsSync(moved.resource.fsPath), true); assert.equal(existsSync(moved.resource.fsPath), true);
assert.ok(statSync(moved.resource.fsPath).isFile); assert.ok(statSync(moved.resource.fsPath).isFile);
@@ -660,32 +660,32 @@ suite('Disk File Service', () => {
assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath); assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath);
}); });
test('copyFile', async () => { test('copy', async () => {
await doTestCopyFile(); await doTestCopy();
}); });
test('copyFile - unbuffered (FileSystemProviderCapabilities.FileReadWrite)', async () => { test('copy - unbuffered (FileSystemProviderCapabilities.FileReadWrite)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
await doTestCopyFile(); await doTestCopy();
}); });
test('copyFile - unbuffered large (FileSystemProviderCapabilities.FileReadWrite)', async () => { test('copy - unbuffered large (FileSystemProviderCapabilities.FileReadWrite)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
await doTestCopyFile('lorem.txt'); await doTestCopy('lorem.txt');
}); });
test('copyFile - buffered (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => { test('copy - buffered (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
await doTestCopyFile(); await doTestCopy();
}); });
test('copyFile - buffered large (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => { test('copy - buffered large (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
await doTestCopyFile('lorem.txt'); await doTestCopy('lorem.txt');
}); });
function setCapabilities(provider: TestDiskFileSystemProvider, capabilities: FileSystemProviderCapabilities): void { function setCapabilities(provider: TestDiskFileSystemProvider, capabilities: FileSystemProviderCapabilities): void {
@@ -695,14 +695,14 @@ suite('Disk File Service', () => {
} }
} }
async function doTestCopyFile(sourceName: string = 'index.html') { async function doTestCopy(sourceName: string = 'index.html') {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.push(service.onAfterOperation(e => event = e)); disposables.push(service.onAfterOperation(e => event = e));
const source = await service.resolveFile(URI.file(join(testDir, sourceName))); const source = await service.resolve(URI.file(join(testDir, sourceName)));
const target = URI.file(join(testDir, 'other.html')); const target = URI.file(join(testDir, 'other.html'));
const copied = await service.copyFile(source.resource, target); const copied = await service.copy(source.resource, target);
assert.equal(existsSync(copied.resource.fsPath), true); assert.equal(existsSync(copied.resource.fsPath), true);
assert.equal(existsSync(source.resource.fsPath), true); assert.equal(existsSync(source.resource.fsPath), true);
@@ -718,7 +718,7 @@ suite('Disk File Service', () => {
assert.equal(sourceContents.toString(), targetContents.toString()); assert.equal(sourceContents.toString(), targetContents.toString());
} }
test('copyFile - overwrite folder with file', async () => { test('copy - overwrite folder with file', async () => {
let createEvent: FileOperationEvent; let createEvent: FileOperationEvent;
let copyEvent: FileOperationEvent; let copyEvent: FileOperationEvent;
let deleteEvent: FileOperationEvent; let deleteEvent: FileOperationEvent;
@@ -732,12 +732,12 @@ suite('Disk File Service', () => {
} }
})); }));
const parent = await service.resolveFile(URI.file(testDir)); const parent = await service.resolve(URI.file(testDir));
const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js')); const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
const f = await service.createFolder(folderResource); const f = await service.createFolder(folderResource);
const source = URI.file(join(testDir, 'deep', 'conway.js')); const source = URI.file(join(testDir, 'deep', 'conway.js'));
const copied = await service.copyFile(source, f.resource, true); const copied = await service.copy(source, f.resource, true);
assert.equal(existsSync(copied.resource.fsPath), true); assert.equal(existsSync(copied.resource.fsPath), true);
assert.ok(statSync(copied.resource.fsPath).isFile); assert.ok(statSync(copied.resource.fsPath).isFile);
@@ -749,28 +749,28 @@ suite('Disk File Service', () => {
assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath); assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath);
}); });
test('copyFile - MIX CASE', async () => { test('copy - MIX CASE', async () => {
const source = await service.resolveFile(URI.file(join(testDir, 'index.html'))); const source = await service.resolve(URI.file(join(testDir, 'index.html')));
const renamed = await service.moveFile(source.resource, URI.file(join(dirname(source.resource.fsPath), 'CONWAY.js'))); const renamed = await service.move(source.resource, URI.file(join(dirname(source.resource.fsPath), 'CONWAY.js')));
assert.equal(existsSync(renamed.resource.fsPath), true); assert.equal(existsSync(renamed.resource.fsPath), true);
assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js')); assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js'));
const source_1 = await service.resolveFile(URI.file(join(testDir, 'deep', 'conway.js'))); const source_1 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')));
const targetParent = URI.file(testDir); const targetParent = URI.file(testDir);
const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source_1.resource.path)) }); const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source_1.resource.path)) });
const res = await service.copyFile(source_1.resource, target, true); const res = await service.copy(source_1.resource, target, true);
assert.equal(existsSync(res.resource.fsPath), true); assert.equal(existsSync(res.resource.fsPath), true);
assert.ok(readdirSync(testDir).some(f => f === 'conway.js')); assert.ok(readdirSync(testDir).some(f => f === 'conway.js'));
}); });
test('copyFile - same file should throw', async () => { test('copy - same file should throw', async () => {
const source = await service.resolveFile(URI.file(join(testDir, 'index.html'))); const source = await service.resolve(URI.file(join(testDir, 'index.html')));
const targetParent = URI.file(dirname(source.resource.fsPath)); const targetParent = URI.file(dirname(source.resource.fsPath));
const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) }); const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });
try { try {
await service.copyFile(source.resource, target, true); await service.copy(source.resource, target, true);
} catch (error) { } catch (error) {
assert.ok(error); assert.ok(error);
} }
@@ -208,7 +208,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding
private resolveModelReference(): Promise<IReference<IResolvedTextEditorModel>> { private resolveModelReference(): Promise<IReference<IResolvedTextEditorModel>> {
return this.fileService.existsFile(this.resource) return this.fileService.exists(this.resource)
.then(exists => { .then(exists => {
const EOL = this.configurationService.getValue('files', { overrideIdentifier: 'json' })['eol']; const EOL = this.configurationService.getValue('files', { overrideIdentifier: 'json' })['eol'];
const result: Promise<any> = exists ? Promise.resolve(null) : this.fileService.updateContent(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' }); const result: Promise<any> = exists ? Promise.resolve(null) : this.fileService.updateContent(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' });
@@ -160,7 +160,7 @@ class OutputFileListener extends Disposable {
} }
private doWatch(): Promise<void> { private doWatch(): Promise<void> {
return this.fileService.resolveFile(this.file, { resolveMetadata: true }) return this.fileService.resolve(this.file, { resolveMetadata: true })
.then(stat => { .then(stat => {
if (stat.etag !== this.etag) { if (stat.etag !== this.etag) {
this.etag = stat.etag; this.etag = stat.etag;
@@ -161,7 +161,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return true; return true;
} }
return this.fileService.existsFile(this.resource).then(exists => !exists); return this.fileService.exists(this.resource).then(exists => !exists);
}); });
} else { } else {
checkOrphanedPromise = Promise.resolve(false); checkOrphanedPromise = Promise.resolve(false);
@@ -718,7 +718,7 @@ export class TextFileService extends Disposable implements ITextFileService {
} }
// Otherwise we can only copy // Otherwise we can only copy
return this.fileService.copyFile(resource, target).then(() => true); return this.fileService.copy(resource, target).then(() => true);
}).then(result => { }).then(result => {
// Return early if the operation was not running // Return early if the operation was not running
@@ -748,7 +748,7 @@ export class TextFileService extends Disposable implements ITextFileService {
// Otherwise create the target file empty if it does not exist already and resolve it from there // Otherwise create the target file empty if it does not exist already and resolve it from there
else { else {
targetModelResolver = this.fileService.existsFile(target).then<any>(exists => { targetModelResolver = this.fileService.exists(target).then<any>(exists => {
targetExists = exists; targetExists = exists;
// create target model adhoc if file does not exist yet // create target model adhoc if file does not exist yet
@@ -964,7 +964,7 @@ export class TextFileService extends Disposable implements ITextFileService {
return this.revertAll(dirtySourceModels.map(dirtySourceModel => dirtySourceModel.getResource()), { soft: true }).then(() => { return this.revertAll(dirtySourceModels.map(dirtySourceModel => dirtySourceModel.getResource()), { soft: true }).then(() => {
// Rename to target // Rename to target
return this.fileService.moveFile(source, target, overwrite).then(() => { return this.fileService.move(source, target, overwrite).then(() => {
// Load models that were dirty before // Load models that were dirty before
return Promise.all(dirtyTargetModels.map(dirtyTargetModel => this.models.loadOrCreate(dirtyTargetModel))).then(() => undefined); return Promise.all(dirtyTargetModels.map(dirtyTargetModel => this.models.loadOrCreate(dirtyTargetModel))).then(() => undefined);
@@ -396,12 +396,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) { if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) {
if (this.watchedColorThemeLocation) { if (this.watchedColorThemeLocation) {
this.fileService.unwatchFileChanges(this.watchedColorThemeLocation); this.fileService.unwatch(this.watchedColorThemeLocation);
this.watchedColorThemeLocation = undefined; this.watchedColorThemeLocation = undefined;
} }
if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) {
this.watchedColorThemeLocation = newTheme.location; this.watchedColorThemeLocation = newTheme.location;
this.fileService.watchFileChanges(this.watchedColorThemeLocation); this.fileService.watch(this.watchedColorThemeLocation);
} }
} }
@@ -514,12 +514,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) { if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) {
if (this.watchedIconThemeLocation) { if (this.watchedIconThemeLocation) {
this.fileService.unwatchFileChanges(this.watchedIconThemeLocation); this.fileService.unwatch(this.watchedIconThemeLocation);
this.watchedIconThemeLocation = undefined; this.watchedIconThemeLocation = undefined;
} }
if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) {
this.watchedIconThemeLocation = iconThemeData.location; this.watchedIconThemeLocation = iconThemeData.location;
this.fileService.watchFileChanges(this.watchedIconThemeLocation); this.fileService.watch(this.watchedIconThemeLocation);
} }
} }
+10 -10
View File
@@ -929,9 +929,9 @@ export class TestFileService implements IFileService {
this._onAfterOperation.fire(event); this._onAfterOperation.fire(event);
} }
resolveFile(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat>; resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat>;
resolveFile(resource: URI, _options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>; resolve(resource: URI, _options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
resolveFile(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat> { resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat> {
return Promise.resolve({ return Promise.resolve({
resource, resource,
etag: Date.now().toString(), etag: Date.now().toString(),
@@ -943,11 +943,11 @@ export class TestFileService implements IFileService {
}); });
} }
resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> { resolveAll(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> {
return Promise.all(toResolve.map(resourceAndOption => this.resolveFile(resourceAndOption.resource, resourceAndOption.options))).then(stats => stats.map(stat => ({ stat, success: true }))); return Promise.all(toResolve.map(resourceAndOption => this.resolve(resourceAndOption.resource, resourceAndOption.options))).then(stats => stats.map(stat => ({ stat, success: true })));
} }
existsFile(_resource: URI): Promise<boolean> { exists(_resource: URI): Promise<boolean> {
return Promise.resolve(true); return Promise.resolve(true);
} }
@@ -996,11 +996,11 @@ export class TestFileService implements IFileService {
})); }));
} }
moveFile(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { move(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> {
return Promise.resolve(null!); return Promise.resolve(null!);
} }
copyFile(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { copy(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> {
throw new Error('not implemented'); throw new Error('not implemented');
} }
@@ -1032,10 +1032,10 @@ export class TestFileService implements IFileService {
return Promise.resolve(); return Promise.resolve();
} }
watchFileChanges(_resource: URI): void { watch(_resource: URI): void {
} }
unwatchFileChanges(_resource: URI): void { unwatch(_resource: URI): void {
} }
getWriteEncoding(_resource: URI): IResourceEncoding { getWriteEncoding(_resource: URI): IResourceEncoding {