提交 5ae52b61 编写于 作者: B Benjamin Pasero

files - implement ctime properly as btime (fix #84525)

上级 d5ff86c8
......@@ -23,6 +23,8 @@ suite('workspace-fs', () => {
assert.equal(typeof stat.mtime, 'number');
assert.equal(typeof stat.ctime, 'number');
assert.ok(stat.mtime > 0);
assert.ok(stat.ctime > 0);
const entries = await vscode.workspace.fs.readDirectory(root);
assert.ok(entries.length > 0);
......
......@@ -209,6 +209,8 @@ export class FileService extends Disposable implements IFileService {
});
}
private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat | { type: FileType } & Partial<IStat>, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStat>;
private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat, siblings: number | undefined, resolveMetadata: true, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStatWithMetadata>;
private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat | { type: FileType } & Partial<IStat>, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStat> {
// convert to file stat
......@@ -219,6 +221,7 @@ export class FileService extends Disposable implements IFileService {
isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0,
isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly),
mtime: stat.mtime,
ctime: stat.ctime,
size: stat.size,
etag: etag({ mtime: stat.mtime, size: stat.size })
};
......
......@@ -207,8 +207,17 @@ export enum FileType {
export interface IStat {
type: FileType;
/**
* The last modification date represented as millis from unix epoch.
*/
mtime: number;
/**
* The creation date represented as millis from unix epoch.
*/
ctime: number;
size: number;
}
......@@ -583,14 +592,21 @@ interface IBaseStat {
size?: number;
/**
* The last modification date represented
* as millis from unix epoch.
* The last modification date represented as millis from unix epoch.
*
* The value may or may not be resolved as
* it is optional.
*/
mtime?: number;
/**
* The creation date represented as millis from unix epoch.
*
* The value may or may not be resolved as
* it is optional.
*/
ctime?: number;
/**
* A unique identifier thet represents the
* current state of the file or directory.
......@@ -608,6 +624,7 @@ interface IBaseStat {
export interface IBaseStatWithMetadata extends IBaseStat {
mtime: number;
ctime: number;
etag: string;
size: number;
}
......@@ -635,6 +652,7 @@ export interface IFileStat extends IBaseStat {
export interface IFileStatWithMetadata extends IFileStat, IBaseStatWithMetadata {
mtime: number;
ctime: number;
etag: string;
size: number;
children?: IFileStatWithMetadata[];
......@@ -703,7 +721,7 @@ export interface IResolveFileOptions {
readonly resolveSingleChildDescendants?: boolean;
/**
* Will resolve mtime, size and etag of files if enabled. This can have a negative impact
* Will resolve mtime, ctime, size and etag of files if enabled. This can have a negative impact
* on performance and thus should only be used when these values are required.
*/
readonly resolveMetadata?: boolean;
......
......@@ -80,7 +80,7 @@ export class DiskFileSystemProvider extends Disposable implements
return {
type: this.toType(stat, isSymbolicLink),
ctime: stat.ctime.getTime(),
ctime: stat.birthtime.getTime(), // intentionally not using ctime here, we want the creation time
mtime: stat.mtime.getTime(),
size: stat.size
};
......
......@@ -213,23 +213,32 @@ suite('Disk File Service', function () {
assert.equal(exists, false);
});
test('resolve', async () => {
const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });
assert.equal(resolved.children!.length, 8);
test('resolve - file', async () => {
const resource = URI.file(getPathFromAmdModule(require, './fixtures/resolver/index.html'));
const resolved = await service.resolve(resource);
const deep = (getByName(resolved, 'deep')!);
assert.equal(deep.children!.length, 4);
assert.equal(resolved.name, 'index.html');
assert.equal(resolved.resource.toString(), resource.toString());
assert.equal(resolved.children, undefined);
assert.ok(resolved.mtime! > 0);
assert.ok(resolved.ctime! > 0);
assert.ok(resolved.size! > 0);
});
test('resolve - directory', async () => {
const testsElements = ['examples', 'other', 'index.html', 'site.css'];
const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')));
const resource = URI.file(getPathFromAmdModule(require, './fixtures/resolver'));
const result = await service.resolve(resource);
assert.ok(result);
assert.equal(result.resource.toString(), resource.toString());
assert.equal(result.name, 'resolver');
assert.ok(result.children);
assert.ok(result.children!.length > 0);
assert.ok(result!.isDirectory);
assert.ok(result.mtime! > 0);
assert.ok(result.ctime! > 0);
assert.equal(result.children!.length, testsElements.length);
assert.ok(result.children!.every(entry => {
......@@ -242,12 +251,18 @@ suite('Disk File Service', function () {
assert.ok(basename(value.resource.fsPath));
if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
assert.ok(value.isDirectory);
assert.equal(value.mtime, undefined);
assert.equal(value.ctime, undefined);
} else if (basename(value.resource.fsPath) === 'index.html') {
assert.ok(!value.isDirectory);
assert.ok(!value.children);
assert.equal(value.mtime, undefined);
assert.equal(value.ctime, undefined);
} else if (basename(value.resource.fsPath) === 'site.css') {
assert.ok(!value.isDirectory);
assert.ok(!value.children);
assert.equal(value.mtime, undefined);
assert.equal(value.ctime, undefined);
} else {
assert.ok(!'Unexpected value ' + basename(value.resource.fsPath));
}
......@@ -260,9 +275,12 @@ suite('Disk File Service', function () {
const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')), { resolveMetadata: true });
assert.ok(result);
assert.equal(result.name, 'resolver');
assert.ok(result.children);
assert.ok(result.children!.length > 0);
assert.ok(result!.isDirectory);
assert.ok(result.mtime! > 0);
assert.ok(result.ctime! > 0);
assert.equal(result.children!.length, testsElements.length);
assert.ok(result.children!.every(entry => {
......@@ -277,18 +295,32 @@ suite('Disk File Service', function () {
assert.ok(basename(value.resource.fsPath));
if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
assert.ok(value.isDirectory);
assert.ok(value.mtime! > 0);
assert.ok(value.ctime! > 0);
} else if (basename(value.resource.fsPath) === 'index.html') {
assert.ok(!value.isDirectory);
assert.ok(!value.children);
assert.ok(value.mtime! > 0);
assert.ok(value.ctime! > 0);
} else if (basename(value.resource.fsPath) === 'site.css') {
assert.ok(!value.isDirectory);
assert.ok(!value.children);
assert.ok(value.mtime! > 0);
assert.ok(value.ctime! > 0);
} else {
assert.ok(!'Unexpected value ' + basename(value.resource.fsPath));
}
});
});
test('resolve - directory with resolveTo', async () => {
const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });
assert.equal(resolved.children!.length, 8);
const deep = (getByName(resolved, 'deep')!);
assert.equal(deep.children!.length, 4);
});
test('resolve - directory - resolveTo single directory', async () => {
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
const result = await service.resolve(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] });
......
......@@ -52,7 +52,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
$stat(uri: UriComponents): Promise<IStat> {
return this._fileService.resolve(URI.revive(uri), { resolveMetadata: true }).then(stat => {
return {
ctime: 0,
ctime: stat.ctime,
mtime: stat.mtime,
size: stat.size,
type: MainThreadFileSystem._getFileType(stat)
......
......@@ -32,6 +32,7 @@ import { Schemas } from 'vs/base/common/network';
export interface IBackupMetaData {
mtime: number;
ctime: number;
size: number;
etag: string;
orphaned: boolean;
......@@ -224,6 +225,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
if (isEqual(target, this.resource) && this.lastResolvedFileStat) {
meta = {
mtime: this.lastResolvedFileStat.mtime,
ctime: this.lastResolvedFileStat.ctime,
size: this.lastResolvedFileStat.size,
etag: this.lastResolvedFileStat.etag,
orphaned: this.inOrphanMode
......@@ -313,6 +315,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
resource: this.resource,
name: basename(this.resource),
mtime: resolvedBackup.meta ? resolvedBackup.meta.mtime : Date.now(),
ctime: resolvedBackup.meta ? resolvedBackup.meta.ctime : Date.now(),
size: resolvedBackup.meta ? resolvedBackup.meta.size : 0,
etag: resolvedBackup.meta ? resolvedBackup.meta.etag : ETAG_DISABLED, // etag disabled if unknown!
value: resolvedBackup.value,
......@@ -397,6 +400,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
resource: this.resource,
name: content.name,
mtime: content.mtime,
ctime: content.ctime,
size: content.size,
etag: content.etag,
isDirectory: false,
......
......@@ -262,6 +262,7 @@ export class TestTextFileService extends NativeTextFileService {
resource: content.resource,
name: content.name,
mtime: content.mtime,
ctime: content.ctime,
etag: content.etag,
encoding: 'utf8',
value: await createTextBufferFactoryFromStream(content.value),
......@@ -996,6 +997,7 @@ export class TestFileService implements IFileService {
etag: 'index.txt',
encoding: 'utf8',
mtime: Date.now(),
ctime: Date.now(),
name: resources.basename(resource),
size: 1
});
......@@ -1022,6 +1024,7 @@ export class TestFileService implements IFileService {
etag: 'index.txt',
encoding: 'utf8',
mtime: Date.now(),
ctime: Date.now(),
size: 1,
name: resources.basename(resource)
});
......@@ -1033,6 +1036,7 @@ export class TestFileService implements IFileService {
etag: 'index.txt',
encoding: 'utf8',
mtime: Date.now(),
ctime: Date.now(),
size: 42,
isDirectory: false,
name: resources.basename(resource)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册