diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index b53ed1a7a29..170eca5c10c 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -47,7 +47,7 @@ export interface IFileService { * Same as resolveFile but supports resolving mulitple resources in parallel. * 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?: IResolveFileOptions }[]): TPromise; + resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): TPromise; /** *Finds out if a file identified by the resource exists. @@ -390,6 +390,11 @@ export interface IFileStat extends IBaseStat { size?: number; } +export interface IResolveFileResult { + stat: IFileStat; + success: boolean; +} + /** * Content and meta information of a file. */ diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index db143477b6d..9668d4a2d3f 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -717,9 +717,22 @@ export class ExplorerView extends CollapsibleView { } // Load Root Stat with given target path configured - const promise = this.fileService.resolveFiles(targetsToResolve).then(stats => { + const promise = this.fileService.resolveFiles(targetsToResolve).then(results => { // Convert to model - const modelStats = stats.map((stat, index) => FileStat.create(stat, targetsToResolve[index].root, targetsToResolve[index].options.resolveTo)); + const modelStats = results.map((result, index) => { + if (result.success) { + return FileStat.create(result.stat, targetsToResolve[index].root, targetsToResolve[index].options.resolveTo); + } + + return FileStat.create({ + resource: targetsToResolve[index].resource, + name: paths.basename(targetsToResolve[index].resource.fsPath), + mtime: 0, + etag: undefined, + isDirectory: true, + hasChildren: false + }, targetsToResolve[index].root); + }); // Subsequent refresh: Merge stat into our local model and refresh tree modelStats.forEach((modelStat, index) => FileStat.mergeLocalWithDisk(modelStat, this.model.roots[index])); diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index a0a398e7855..8f3af22b457 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -12,7 +12,7 @@ import encoding = require('vs/base/node/encoding'); import errors = require('vs/base/common/errors'); import uri from 'vs/base/common/uri'; import { toResource } from 'vs/workbench/common/editor'; -import { FileOperation, FileOperationEvent, IFileService, IFilesConfiguration, IResolveFileOptions, IFileStat, IContent, IStreamContent, IImportResult, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent } from 'vs/platform/files/common/files'; +import { FileOperation, FileOperationEvent, IFileService, IFilesConfiguration, IResolveFileOptions, IFileStat, IResolveFileResult, IContent, IStreamContent, IImportResult, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent } from 'vs/platform/files/common/files'; import { FileService as NodeFileService, IFileServiceOptions, IEncodingOverride } from 'vs/workbench/services/files/node/fileService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -196,7 +196,7 @@ export class FileService implements IFileService { return this.raw.resolveFile(resource, options); } - public resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): TPromise { + public resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): TPromise { return this.raw.resolveFiles(toResolve); } diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index 53971b2e3e0..b616e972ef5 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -11,7 +11,7 @@ import os = require('os'); import crypto = require('crypto'); import assert = require('assert'); -import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, IImportResult, MAX_FILE_SIZE, FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; +import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, IImportResult, MAX_FILE_SIZE, FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; import { isEqualOrParent } from 'vs/base/common/paths'; import { ResourceMap } from 'vs/base/common/map'; import arrays = require('vs/base/common/arrays'); @@ -196,15 +196,9 @@ export class FileService implements IFileService { return this.resolve(resource, options); } - public resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): TPromise { - return TPromise.join(toResolve.map(resourceAndOptions => this.resolve(resourceAndOptions.resource, resourceAndOptions.options).then(undefined, error => ({ - resource: resourceAndOptions.resource, - name: paths.basename(resourceAndOptions.resource.fsPath), - mtime: 0, - etag: undefined, - isDirectory: true, - hasChildren: false - })))); + public resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): TPromise { + return TPromise.join(toResolve.map(resourceAndOptions => this.resolve(resourceAndOptions.resource, resourceAndOptions.options) + .then(stat => ({ stat, success: true }), error => ({ stat: undefined, success: false })))); } public existsFile(resource: uri): TPromise { diff --git a/src/vs/workbench/services/files/test/node/fileService.test.ts b/src/vs/workbench/services/files/test/node/fileService.test.ts index 09778ad2701..9e2610f9b42 100644 --- a/src/vs/workbench/services/files/test/node/fileService.test.ts +++ b/src/vs/workbench/services/files/test/node/fileService.test.ts @@ -513,14 +513,14 @@ suite('FileService', () => { { resource: uri.file(testDir), options: { resolveTo: [uri.file(path.join(testDir, 'deep'))] } }, { resource: uri.file(path.join(testDir, 'deep')) } ]).then(res => { - const r1 = res[0]; + const r1 = res[0].stat; assert.equal(r1.children.length, 6); let deep = utils.getByName(r1, 'deep'); assert.equal(deep.children.length, 4); - const r2 = res[1]; + const r2 = res[1].stat; assert.equal(r2.children.length, 4); assert.equal(r2.name, 'deep'); diff --git a/src/vs/workbench/services/telemetry/common/workspaceStats.ts b/src/vs/workbench/services/telemetry/common/workspaceStats.ts index 9324a54fb93..c2ff4ed1eb9 100644 --- a/src/vs/workbench/services/telemetry/common/workspaceStats.ts +++ b/src/vs/workbench/services/telemetry/common/workspaceStats.ts @@ -107,8 +107,8 @@ export class WorkspaceStats { const folders = workspace ? workspace.roots : this.environmentService.appQuality !== 'stable' && this.findFolders(workbenchOptions); if (folders && folders.length && this.fileService) { - return this.fileService.resolveFiles(folders.map(resource => ({ resource }))).then(statses => { - const names = ([]).concat(...statses.map(stats => stats.children || [])).map(c => c.name); + return this.fileService.resolveFiles(folders.map(resource => ({ resource }))).then(results => { + const names = ([]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); tags['workspace.language.cs'] = this.searchArray(names, /^.+\.cs$/i); tags['workspace.language.js'] = this.searchArray(names, /^.+\.js$/i); @@ -229,8 +229,8 @@ export class WorkspaceStats { return workspaceUri.with({ path: `${path !== '/' ? path : ''}/node_modules` }); }); return this.fileService.resolveFiles(uris.map(resource => ({ resource }))).then( - statses => { - const names = ([]).concat(...statses.map(stats => stats.children || [])).map(c => c.name); + results => { + const names = ([]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); const referencesAzure = this.searchArray(names, /azure/i); if (referencesAzure) { tags['node'] = true; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0f6a48998aa..f4ff2845a92 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -34,7 +34,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEditorGroupService, GroupArrangement, GroupOrientation, ITabOptions, IMoveOptions } from 'vs/workbench/services/group/common/groupService'; import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; -import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IImportResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent } from 'vs/platform/files/common/files'; +import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IResolveFileResult, IImportResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; @@ -665,8 +665,8 @@ export class TestFileService implements IFileService { }); } - resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): TPromise { - return TPromise.join(toResolve.map(resourceAndOption => this.resolveFile(resourceAndOption.resource, resourceAndOption.options))); + resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): TPromise { + return TPromise.join(toResolve.map(resourceAndOption => this.resolveFile(resourceAndOption.resource, resourceAndOption.options))).then(stats => stats.map(stat => ({ stat, success: true }))); } existsFile(resource: URI): TPromise {