diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index a43f44887f5..2eaafd63aa9 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -155,12 +155,6 @@ export interface IFileService { dispose(): void; } -export enum FileType2 { - File = 1, - Directory = 2, - SymbolicLink = 4, -} - export interface FileOptions { /** * Create a file when it doesn't exists. @@ -184,11 +178,17 @@ export interface FileOptions { write?: boolean; } +export enum FileType { + Unknown = 0, + File = 1, + Directory = 2, + SymbolicLink = 64 +} + export interface IStat { - isFile?: boolean; - isDirectory?: boolean; - isSymbolicLink?: boolean; + type: FileType; mtime: number; + ctime: number; size: number; } @@ -214,7 +214,7 @@ export interface IFileSystemProvider { stat(resource: URI): TPromise; mkdir(resource: URI): TPromise; - readdir(resource: URI): TPromise<[string, IStat][]>; + readdir(resource: URI): TPromise<[string, FileType][]>; delete(resource: URI): TPromise; rename(from: URI, to: URI, opts: FileOptions): TPromise; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index efe3eb07a9b..9a89f22ec55 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4823,24 +4823,42 @@ declare module 'vscode' { resolveTask(task: Task, token?: CancellationToken): ProviderResult; } + /** + * Enumeration of file types. + */ + export enum FileType { + /** + * The file type is unknown. + */ + Unknown = 0, + /** + * A regular file. + */ + File = 1, + /** + * A directory. + */ + Directory = 2, + /** + * A symbolic link to a file. + */ + SymbolicLink = 64 + } + /** * The `FileStat`-type represents metadata about a file. */ export interface FileStat { /** - * The file is a regular file. + * The type of the file, e.g. is a regular file, a directory, or symbolic link + * to a file. */ - isFile?: boolean; + type: FileType; /** - * The file is a directory. + * The creation timestamp in milliseconds. */ - isDirectory?: boolean; - - /** - * The file is symbolic link to another file. - */ - isSymbolicLink?: boolean; + ctime: number; /** * The modification timestamp in milliseconds. @@ -5001,7 +5019,7 @@ declare module 'vscode' { * @param token A cancellation token. * @return A thenable that resolves to an array of tuples of file names and files stats. */ - readDirectory(uri: Uri, options: { /*future: onlyType?*/ }, token: CancellationToken): [string, FileStat][] | Thenable<[string, FileStat][]>; + readDirectory(uri: Uri, options: { /*future: onlyType?*/ }, token: CancellationToken): [string, FileType][] | Thenable<[string, FileType][]>; /** * Create a new directory. *Note* that new files are created via `write`-calls. diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 2f632a363e8..39e5599f3c0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { FileOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions } from 'vs/platform/files/common/files'; +import { FileOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType } from 'vs/platform/files/common/files'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../node/extHost.protocol'; @@ -115,7 +115,7 @@ class RemoteFileSystemProvider implements IFileSystemProvider { return this._proxy.$mkdir(this._handle, resource); } - readdir(resource: URI): TPromise<[string, IStat][], any> { + readdir(resource: URI): TPromise<[string, FileType][], any> { return this._proxy.$readdir(this._handle, resource); } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index ee6e7186c07..1599bef1be7 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -45,6 +45,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as vscode from 'vscode'; import * as paths from 'vs/base/common/paths'; +import * as files from 'vs/platform/files/common/files'; import { MainContext, ExtHostContext, IInitData, IExtHostContext } from './extHost.protocol'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; @@ -700,6 +701,7 @@ export function createApiFactory( DeprecatedFileChangeType: extHostTypes.DeprecatedFileChangeType, DeprecatedFileType: extHostTypes.DeprecatedFileType, FileChangeType: extHostTypes.FileChangeType, + FileType: files.FileType, FileSystemError: extHostTypes.FileSystemError, FoldingRange: extHostTypes.FoldingRange, FoldingRangeKind: extHostTypes.FoldingRangeKind diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index dc7aba45b18..dd0d72b1ba0 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -42,7 +42,7 @@ import { ITreeItem } from 'vs/workbench/common/views'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { SerializedError } from 'vs/base/common/errors'; -import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileOptions } from 'vs/platform/files/common/files'; +import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileOptions, FileType } from 'vs/platform/files/common/files'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration'; import { ISingleEditOperation } from 'vs/editor/common/model'; @@ -582,12 +582,12 @@ export interface ExtHostWorkspaceShape { export interface ExtHostFileSystemShape { $stat(handle: number, resource: UriComponents): TPromise; + $readdir(handle: number, resource: UriComponents): TPromise<[string, FileType][]>; $readFile(handle: number, resource: UriComponents, opts: FileOptions): TPromise; $writeFile(handle: number, resource: UriComponents, base64Encoded: string, opts: FileOptions): TPromise; $rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOptions): TPromise; $copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOptions): TPromise; $mkdir(handle: number, resource: UriComponents): TPromise; - $readdir(handle: number, resource: UriComponents): TPromise<[string, IStat][]>; $delete(handle: number, resource: UriComponents): TPromise; $watch(handle: number, session: number, resource: UriComponents, opts: IWatchOptions): void; $unwatch(handle: number, session: number): void; diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index fbf7724536a..330628c339b 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -81,31 +81,29 @@ class FileSystemProviderShim implements vscode.FileSystemProvider { rename(oldUri: vscode.Uri, newUri: vscode.Uri): Thenable { return this._delegate.move(oldUri, newUri).then(stat => FileSystemProviderShim._modernizeFileStat(stat)); } - readDirectory(resource: vscode.Uri): Thenable<[string, vscode.FileStat][]> { + readDirectory(resource: vscode.Uri): Thenable<[string, vscode.FileType][]> { return this._delegate.readdir(resource).then(tuples => { - return tuples.map(tuple => <[string, vscode.FileStat]>[path.posix.basename(tuple[0].path), FileSystemProviderShim._modernizeFileStat(tuple[1])]); + return tuples.map(tuple => <[string, vscode.FileType]>[path.posix.basename(tuple[0].path), FileSystemProviderShim._modernizeFileStat(tuple[1]).type]); }); } private static _modernizeFileStat(stat: vscode.DeprecatedFileStat): vscode.FileStat { let { mtime, size, type } = stat; - let isFile = false; - let isDirectory = false; - let isSymbolicLink = false; + let newType: files.FileType; // no support for bitmask, effectively no support for symlinks switch (type) { case DeprecatedFileType.Dir: - isDirectory = true; + newType = files.FileType.Directory; break; case DeprecatedFileType.File: - isFile = true; + newType = files.FileType.File; break; case DeprecatedFileType.Symlink: - isSymbolicLink = true; + newType = files.FileType.File & files.FileType.SymbolicLink; break; } - return { mtime, size, isFile, isDirectory, isSymbolicLink }; + return { type: newType, ctime: 0, mtime, size }; } private static _modernizeFileChange(e: vscode.DeprecatedFileChange): vscode.FileChangeEvent { @@ -246,18 +244,16 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } private static _asIStat(stat: vscode.FileStat): files.IStat { - const { isFile, isDirectory, isSymbolicLink, mtime, size } = stat; - return { isFile, isDirectory, isSymbolicLink, mtime, size }; + const { type, ctime, mtime, size } = stat; + return { type, ctime, mtime, size }; } $stat(handle: number, resource: UriComponents): TPromise { return asWinJsPromise(token => this._fsProvider.get(handle).stat(URI.revive(resource), {}, token)).then(ExtHostFileSystem._asIStat); } - $readdir(handle: number, resource: UriComponents): TPromise<[string, files.IStat][], any> { - return asWinJsPromise(token => this._fsProvider.get(handle).readDirectory(URI.revive(resource), {}, token)).then(tuples => { - return tuples.map(([name, stat]) => <[string, files.IStat]>[name, ExtHostFileSystem._asIStat(stat)]); - }); + $readdir(handle: number, resource: UriComponents): TPromise<[string, files.FileType][], any> { + return asWinJsPromise(token => this._fsProvider.get(handle).readDirectory(URI.revive(resource), {}, token)); } $readFile(handle: number, resource: UriComponents, opts: files.FileOptions): TPromise { diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 0d898666576..530188a07a4 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -16,7 +16,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { FileChangesEvent, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileStat, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IResolveFileOptions, IResolveFileResult, IStat, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, IWatchOptions } from 'vs/platform/files/common/files'; +import { FileChangesEvent, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileStat, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IResolveFileOptions, IResolveFileResult, IStat, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, IWatchOptions, FileType } from 'vs/platform/files/common/files'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -25,13 +25,26 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; import { createReadableOfProvider, createReadableOfSnapshot, createWritableOfProvider } from 'vs/workbench/services/files/electron-browser/streams'; +class TypeOnlyStat implements IStat { + + constructor(readonly type: FileType) { + // + } + + // todo@remote -> make a getter and warn when + // being used in development. + mtime: number = 0; + ctime: number = 0; + size: number = 0; +} + function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse?: (tuple: [URI, IStat]) => boolean): TPromise { const [resource, stat] = tuple; const fileStat: IFileStat = { resource, name: posix.basename(resource.path), - isDirectory: stat.isDirectory, - isSymbolicLink: stat.isSymbolicLink, + isDirectory: (stat.type & FileType.Directory) !== 0, + isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0, mtime: stat.mtime, size: stat.size, etag: stat.mtime.toString(29) + stat.size.toString(31), @@ -43,9 +56,9 @@ function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse return provider.readdir(resource).then(entries => { // resolve children if requested return TPromise.join(entries.map(tuple => { - const [name, stat] = tuple; + const [name, type] = tuple; const childResource = resource.with({ path: posix.join(resource.path, name) }); - return toIFileStat(provider, [childResource, stat], recurse); + return toIFileStat(provider, [childResource, new TypeOnlyStat(type)], recurse); })).then(children => { fileStat.children = children; return fileStat;