Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

files - adopt fileService.stat method in more places #143491

Merged
merged 2 commits into from Feb 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/vs/platform/files/common/fileService.ts
Expand Up @@ -18,7 +18,7 @@ import { extUri, extUriIgnorePathCase, IExtUri, isAbsolutePath } from 'vs/base/c
import { consumeStream, isReadableBufferedStream, isReadableStream, listenStream, newWriteableStream, peekReadable, peekStream, transform } from 'vs/base/common/stream';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ensureFileSystemProviderError, etag, ETAG_DISABLED, FileChangesEvent, FileDeleteOptions, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FilePermission, FileSystemProviderCapabilities, FileSystemProviderErrorCode, FileType, hasFileAtomicReadCapability, hasFileFolderCopyCapability, hasFileReadStreamCapability, hasOpenReadWriteCloseCapability, hasReadWriteCapability, ICreateFileOptions, IFileContent, IFileService, IFileStat, IFileStatWithMetadata, IFileStreamContent, IFileSystemProvider, IFileSystemProviderActivationEvent, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemProviderRegistrationEvent, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileReadStreamCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, IReadFileOptions, IReadFileStreamOptions, IResolveFileOptions, IResolveFileResult, IResolveFileResultWithMetadata, IResolveMetadataFileOptions, IStat, IWatchOptions, IWriteFileOptions, NotModifiedSinceFileOperationError, toFileOperationResult, toFileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { ensureFileSystemProviderError, etag, ETAG_DISABLED, FileChangesEvent, FileDeleteOptions, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FilePermission, FileSystemProviderCapabilities, FileSystemProviderErrorCode, FileType, hasFileAtomicReadCapability, hasFileFolderCopyCapability, hasFileReadStreamCapability, hasOpenReadWriteCloseCapability, hasReadWriteCapability, ICreateFileOptions, IFileContent, IFileService, IFileStat, IFileStatWithMetadata, IFileStreamContent, IFileSystemProvider, IFileSystemProviderActivationEvent, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemProviderRegistrationEvent, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileReadStreamCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, IReadFileOptions, IReadFileStreamOptions, IResolveFileOptions, IFileStatResult, IFileStatResultWithMetadata, IResolveMetadataFileOptions, IStat, IFileStatWithPartialMetadata, IWatchOptions, IWriteFileOptions, NotModifiedSinceFileOperationError, toFileOperationResult, toFileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { readFileIntoStream } from 'vs/platform/files/common/io';
import { ILogService } from 'vs/platform/log/common/log';

Expand Down Expand Up @@ -249,7 +249,8 @@ export class FileService extends Disposable implements IFileService {
ctime: stat.ctime,
size: stat.size,
readonly: Boolean((stat.permissions ?? 0) & FilePermission.Readonly) || Boolean(provider.capabilities & FileSystemProviderCapabilities.Readonly),
etag: etag({ mtime: stat.mtime, size: stat.size })
etag: etag({ mtime: stat.mtime, size: stat.size }),
children: undefined
};

// check to recurse for directories
Expand Down Expand Up @@ -283,9 +284,9 @@ export class FileService extends Disposable implements IFileService {
return fileStat;
}

async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
async resolveAll(toResolve: { resource: URI; options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResultWithMetadata[]>;
async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> {
async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]>;
async resolveAll(toResolve: { resource: URI; options: IResolveMetadataFileOptions }[]): Promise<IFileStatResultWithMetadata[]>;
async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]> {
return Promises.settled(toResolve.map(async entry => {
try {
return { stat: await this.doResolveFile(entry.resource, entry.options), success: true };
Expand All @@ -297,7 +298,7 @@ export class FileService extends Disposable implements IFileService {
}));
}

async stat(resource: URI): Promise<IFileStatWithMetadata> {
async stat(resource: URI): Promise<IFileStatWithPartialMetadata> {
const provider = await this.withProvider(resource);

const stat = await provider.stat(resource);
Expand Down
30 changes: 16 additions & 14 deletions src/vs/platform/files/common/files.ts
Expand Up @@ -118,14 +118,14 @@ export interface IFileService {
* If one of the resolve targets fails to resolve returns a fake `IFileStat` instead of
* making the whole call fail.
*/
resolveAll(toResolve: { resource: URI; options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResult[]>;
resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
resolveAll(toResolve: { resource: URI; options: IResolveMetadataFileOptions }[]): Promise<IFileStatResult[]>;
resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]>;

/**
* Same as `resolve()` but without resolving the children of a folder if the
* resource is pointing to a folder.
*/
stat(resource: URI): Promise<IFileStatWithMetadata>;
stat(resource: URI): Promise<IFileStatWithPartialMetadata>;

/**
* Finds out if a file/folder identified by the resource exists.
Expand Down Expand Up @@ -889,7 +889,7 @@ export function isParent(path: string, candidate: string, ignoreCase?: boolean):
return path.indexOf(candidate) === 0;
}

interface IBaseStat {
interface IBaseFileStat {

/**
* The unified resource identifier of this file or folder.
Expand Down Expand Up @@ -941,12 +941,12 @@ interface IBaseStat {
readonly readonly?: boolean;
}

export interface IBaseStatWithMetadata extends Required<IBaseStat> { }
export interface IBaseFileStatWithMetadata extends Required<IBaseFileStat> { }

/**
* A file resource with meta information.
* A file resource with meta information and resolved children if any.
*/
export interface IFileStat extends IBaseStat {
export interface IFileStat extends IBaseFileStat {

/**
* The resource is a file.
Expand All @@ -969,36 +969,38 @@ export interface IFileStat extends IBaseStat {
/**
* The children of the file stat or undefined if none.
*/
children?: IFileStat[];
children: IFileStat[] | undefined;
}

export interface IFileStatWithMetadata extends IFileStat, IBaseStatWithMetadata {
export interface IFileStatWithMetadata extends IFileStat, IBaseFileStatWithMetadata {
readonly mtime: number;
readonly ctime: number;
readonly etag: string;
readonly size: number;
readonly readonly: boolean;
readonly children?: IFileStatWithMetadata[];
readonly children: IFileStatWithMetadata[] | undefined;
}

export interface IResolveFileResult {
export interface IFileStatResult {
readonly stat?: IFileStat;
readonly success: boolean;
}

export interface IResolveFileResultWithMetadata extends IResolveFileResult {
export interface IFileStatResultWithMetadata extends IFileStatResult {
readonly stat?: IFileStatWithMetadata;
}

export interface IFileContent extends IBaseStatWithMetadata {
export interface IFileStatWithPartialMetadata extends Omit<IFileStatWithMetadata, 'children'> { }

export interface IFileContent extends IBaseFileStatWithMetadata {

/**
* The content of a file as buffer.
*/
readonly value: VSBuffer;
}

export interface IFileStreamContent extends IBaseStatWithMetadata {
export interface IFileStreamContent extends IBaseFileStatWithMetadata {

/**
* The content of a file as stream.
Expand Down
2 changes: 0 additions & 2 deletions src/vs/platform/files/test/node/diskFileService.test.ts
Expand Up @@ -468,7 +468,6 @@ flakySuite('Disk File Service', function () {
assert.strictEqual(resolved.readonly, false);
assert.strictEqual(resolved.isSymbolicLink, false);
assert.strictEqual(resolved.resource.toString(), resource.toString());
assert.strictEqual(resolved.children, undefined);
assert.ok(resolved.mtime! > 0);
assert.ok(resolved.ctime! > 0);
assert.ok(resolved.size! > 0);
Expand All @@ -481,7 +480,6 @@ flakySuite('Disk File Service', function () {
assert.ok(result);
assert.strictEqual(result.resource.toString(), resource.toString());
assert.strictEqual(result.name, 'resolver');
assert.strictEqual(result.children, undefined);
assert.ok(result.isDirectory);
assert.strictEqual(result.readonly, false);
assert.ok(result.mtime! > 0);
Expand Down
9 changes: 5 additions & 4 deletions src/vs/workbench/api/browser/mainThreadDocuments.ts
Expand Up @@ -264,10 +264,11 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen

private _handleUntitledScheme(uri: URI): Promise<URI> {
const asLocalUri = toLocalResource(uri, this._environmentService.remoteAuthority, this._pathService.defaultUriScheme);
return this._fileService.resolve(asLocalUri).then(stats => {
// don't create a new file ontop of an existing file
return Promise.reject(new Error('file already exists'));
}, err => {
return this._fileService.exists(asLocalUri).then(exists => {
if (exists) {
// don't create a new file ontop of an existing file
return Promise.reject(new Error('file already exists'));
}
return this._doCreateUntitled(Boolean(uri.path) ? uri : undefined);
});
}
Expand Down
6 changes: 3 additions & 3 deletions src/vs/workbench/api/browser/mainThreadFileSystem.ts
Expand Up @@ -6,7 +6,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, IFileStat, FileOperationError, FileOperationResult, FileSystemProviderErrorCode, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileFolderCopyCapability, FilePermission, toFileSystemProviderErrorCode, IFilesConfiguration } from 'vs/platform/files/common/files';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, FileOperationError, FileOperationResult, FileSystemProviderErrorCode, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileFolderCopyCapability, FilePermission, toFileSystemProviderErrorCode, IFilesConfiguration, IFileStatWithPartialMetadata, IFileStat } from 'vs/platform/files/common/files';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { ExtHostContext, ExtHostFileSystemShape, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../common/extHost.protocol';
import { VSBuffer } from 'vs/base/common/buffer';
Expand Down Expand Up @@ -69,7 +69,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
// --- consumer fs, vscode.workspace.fs

$stat(uri: UriComponents): Promise<IStat> {
return this._fileService.resolve(URI.revive(uri), { resolveMetadata: true }).then(stat => {
return this._fileService.stat(URI.revive(uri)).then(stat => {
return {
ctime: stat.ctime,
mtime: stat.mtime,
Expand All @@ -91,7 +91,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
}).catch(MainThreadFileSystem._handleError);
}

private static _asFileType(stat: IFileStat): FileType {
private static _asFileType(stat: IFileStat | IFileStatWithPartialMetadata): FileType {
let res = 0;
if (stat.isFile) {
res += FileType.File;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/dnd.ts
Expand Up @@ -298,7 +298,7 @@ export class ResourcesDropHandler {

// Check for Folder
try {
const stat = await this.fileService.resolve(resource);
const stat = await this.fileService.stat(resource);
if (stat.isDirectory) {
toOpen.push({ folderUri: stat.resource });
folderURIs.push({ uri: stat.resource });
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/common/editor.ts
Expand Up @@ -1277,7 +1277,7 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService
let type = path.type;
if (typeof exists !== 'boolean' || typeof type !== 'number') {
try {
type = (await fileService.resolve(resource)).isDirectory ? FileType.Directory : FileType.Unknown;
type = (await fileService.stat(resource)).isDirectory ? FileType.Directory : FileType.Unknown;
exists = true;
} catch {
exists = false;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/common/editor/binaryEditorModel.ts
Expand Up @@ -58,7 +58,7 @@ export class BinaryEditorModel extends EditorModel {

// Make sure to resolve up to date stat for file resources
if (this.fileService.hasProvider(this.resource)) {
const stat = await this.fileService.resolve(this.resource, { resolveMetadata: true });
const stat = await this.fileService.stat(this.resource);
this.etag = stat.etag;
if (typeof stat.size === 'number') {
this.size = stat.size;
Expand Down
9 changes: 5 additions & 4 deletions src/vs/workbench/contrib/debug/browser/linkDetector.ts
Expand Up @@ -113,12 +113,13 @@ export class LinkDetector {
const path = await this.pathService.path;
const fileUrl = osPath.normalize(((path.sep === osPath.posix.sep) && platform.isWindows) ? fsPath.replace(/\\/g, osPath.posix.sep) : fsPath);

const resolvedLink = await this.fileService.resolve(URI.parse(fileUrl));
if (!resolvedLink) {
const fileUri = URI.parse(fileUrl);
const exists = await this.fileService.exists(fileUri);
if (!exists) {
return;
}

await this.editorService.openEditor({ resource: resolvedLink.resource, options: { pinned: true } });
await this.editorService.openEditor({ resource: fileUri, options: { pinned: true } });
return;
}

Expand Down Expand Up @@ -155,7 +156,7 @@ export class LinkDetector {
const link = this.createLink(text);
link.tabIndex = 0;
const uri = URI.file(osPath.normalize(path));
this.fileService.resolve(uri).then(stat => {
this.fileService.stat(uri).then(stat => {
if (stat.isDirectory) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/contrib/files/browser/fileActions.ts
Expand Up @@ -1010,7 +1010,7 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => {
if (element.resource.toString() !== fileToPaste.toString() && resources.isEqualOrParent(element.resource, fileToPaste)) {
throw new Error(nls.localize('fileIsAncestor', "File to paste is an ancestor of the destination folder"));
}
const fileToPasteStat = await fileService.resolve(fileToPaste);
const fileToPasteStat = await fileService.stat(fileToPaste);

// Find target
let target: ExplorerItem;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/contrib/files/browser/fileCommands.ts
Expand Up @@ -144,7 +144,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
return item;
}

return await fileService.resolve(resource);
return await fileService.stat(resource);
}));
const files = items.filter(i => !i.isDirectory);
const editors = files.map(f => ({
Expand Down
Expand Up @@ -17,7 +17,7 @@ import { IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities, NO_TYPE_ID,
import { CancellationToken } from 'vs/base/common/cancellation';
import { IResolvedWorkingCopyBackup, IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup';
import { Schemas } from 'vs/base/common/network';
import { IFileStatWithMetadata, IFileService, FileChangeType, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { IFileService, FileChangeType, FileSystemProviderCapabilities, IFileStatWithPartialMetadata } from 'vs/platform/files/common/files';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILogService } from 'vs/platform/log/common/log';
Expand Down Expand Up @@ -47,7 +47,7 @@ export class ComplexNotebookEditorModel extends EditorModel implements INotebook
readonly onDidChangeOrphaned = Event.None;
readonly onDidChangeReadonly = Event.None;

private _lastResolvedFileStat?: IFileStatWithMetadata;
private _lastResolvedFileStat?: IFileStatWithPartialMetadata;

private readonly _name: string;
private readonly _workingCopyIdentifier: IWorkingCopyIdentifier;
Expand Down Expand Up @@ -428,7 +428,7 @@ export class ComplexNotebookEditorModel extends EditorModel implements INotebook

try {
this._logService.debug(`[notebook editor model] _resolveStats`, this.resource.toString(true));
const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
const newStats = await this._fileService.stat(this.resource);
this._logService.debug(`[notebook editor model] _resolveStats - latest file stats: ${JSON.stringify(newStats)}`, this.resource.toString(true));
return newStats;
} catch (e) {
Expand Down
Expand Up @@ -65,7 +65,7 @@ class OutputFileListener extends Disposable {
}

private async doWatch(): Promise<void> {
const stat = await this.fileService.resolve(this.file, { resolveMetadata: true });
const stat = await this.fileService.stat(this.file);
if (stat.etag !== this.etag) {
this.etag = stat.etag;
this._onDidContentChange.fire(stat.size);
Expand Down
Expand Up @@ -170,7 +170,7 @@ export class KeyboardLayoutPickerAction extends Action {
if (pick === configureKeyboardLayout) {
const file = this.environmentService.keyboardLayoutResource;

await this.fileService.resolve(file).then(undefined, (error) => {
await this.fileService.stat(file).then(undefined, () => {
return this.fileService.createFile(file, VSBuffer.fromString(KeyboardLayoutPickerAction.DEFAULT_CONTENT));
}).then((stat): Promise<IEditorPane | undefined> | undefined => {
if (!stat) {
Expand Down
Expand Up @@ -683,7 +683,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
}

try {
if ((await this.fileService.resolve(resource)).isFile) {
if ((await this.fileService.stat(resource)).isFile) {
return resource;
}
} catch (error) {
Expand Down Expand Up @@ -716,7 +716,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
);

try {
if ((await this.fileService.resolve(resource)).isFile) {
if ((await this.fileService.stat(resource)).isFile) {
resources.push(resource);
}
} catch (error) {
Expand Down
10 changes: 5 additions & 5 deletions src/vs/workbench/contrib/search/common/searchModel.ts
Expand Up @@ -29,7 +29,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
import { memoize } from 'vs/base/common/decorators';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { compareFileNames, compareFileExtensions, comparePaths } from 'vs/base/common/comparers';
import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { IFileService, IFileStatWithPartialMetadata } from 'vs/platform/files/common/files';
import { Schemas } from 'vs/base/common/network';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';

Expand Down Expand Up @@ -202,7 +202,7 @@ export class FileMatch extends Disposable implements IFileMatch {
readonly onDispose: Event<void> = this._onDispose.event;

private _resource: URI;
private _fileStat?: IFileStatWithMetadata;
private _fileStat?: IFileStatWithPartialMetadata;
private _model: ITextModel | null = null;
private _modelListener: IDisposable | null = null;
private _matches: Map<string, Match>;
Expand Down Expand Up @@ -430,14 +430,14 @@ export class FileMatch extends Disposable implements IFileMatch {
}

async resolveFileStat(fileService: IFileService): Promise<void> {
this._fileStat = await fileService.resolve(this.resource, { resolveMetadata: true }).catch(() => undefined);
this._fileStat = await fileService.stat(this.resource).catch(() => undefined);
}

public get fileStat(): IFileStatWithMetadata | undefined {
public get fileStat(): IFileStatWithPartialMetadata | undefined {
return this._fileStat;
}

public set fileStat(stat: IFileStatWithMetadata | undefined) {
public set fileStat(stat: IFileStatWithPartialMetadata | undefined) {
this._fileStat = stat;
}

Expand Down