Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5

This commit is contained in:
ADS Merger
2020-02-08 04:50:58 +00:00
parent 8c61538a27
commit 2af13c18d2
752 changed files with 16458 additions and 10063 deletions

View File

@@ -752,7 +752,22 @@ export class FileService extends Disposable implements IFileService {
// Create directories as needed
for (let i = directoriesToCreate.length - 1; i >= 0; i--) {
directory = joinPath(directory, directoriesToCreate[i]);
await provider.mkdir(directory);
try {
await provider.mkdir(directory);
} catch (error) {
if (toFileSystemProviderErrorCode(error) !== FileSystemProviderErrorCode.FileExists) {
// For mkdirp() we tolerate that the mkdir() call fails
// in case the folder already exists. This follows node.js
// own implementation of fs.mkdir({ recursive: true }) and
// reduces the chances of race conditions leading to errors
// if multiple calls try to create the same folders
// As such, we only throw an error here if it is other than
// the fact that the file already exists.
// (see also https://github.com/microsoft/vscode/issues/89834)
throw error;
}
}
}
}

View File

@@ -804,7 +804,7 @@ export interface IFilesConfiguration {
eol: string;
enableTrash: boolean;
hotExit: string;
preventSaveConflicts: boolean;
saveConflictResolution: 'askUser' | 'overwriteFileOnDisk';
};
}

View File

@@ -76,10 +76,10 @@ export class DiskFileSystemProvider extends Disposable implements
async stat(resource: URI): Promise<IStat> {
try {
const { stat, isSymbolicLink } = await statLink(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly
const { stat, symbolicLink } = await statLink(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly
return {
type: this.toType(stat, isSymbolicLink),
type: this.toType(stat, symbolicLink),
ctime: stat.birthtime.getTime(), // intentionally not using ctime here, we want the creation time
mtime: stat.mtime.getTime(),
size: stat.size
@@ -115,12 +115,28 @@ export class DiskFileSystemProvider extends Disposable implements
}
}
private toType(entry: Stats | Dirent, isSymbolicLink = entry.isSymbolicLink()): FileType {
if (isSymbolicLink) {
return FileType.SymbolicLink | (entry.isDirectory() ? FileType.Directory : FileType.File);
private toType(entry: Stats | Dirent, symbolicLink?: { dangling: boolean }): FileType {
// Signal file type by checking for file / directory, except:
// - symbolic links pointing to non-existing files are FileType.Unknown
// - files that are neither file nor directory are FileType.Unknown
let type: FileType;
if (symbolicLink?.dangling) {
type = FileType.Unknown;
} else if (entry.isFile()) {
type = FileType.File;
} else if (entry.isDirectory()) {
type = FileType.Directory;
} else {
type = FileType.Unknown;
}
return entry.isFile() ? FileType.File : entry.isDirectory() ? FileType.Directory : FileType.Unknown;
// Always signal symbolic link as file type additionally
if (symbolicLink) {
type |= FileType.SymbolicLink;
}
return type;
}
//#endregion

View File

@@ -35,14 +35,14 @@ export class FileWatcher extends Disposable {
private async startWatching(): Promise<void> {
try {
const { stat, isSymbolicLink } = await statLink(this.path);
const { stat, symbolicLink } = await statLink(this.path);
if (this.isDisposed) {
return;
}
let pathToWatch = this.path;
if (isSymbolicLink) {
if (symbolicLink) {
try {
pathToWatch = await realpath(pathToWatch);
} catch (error) {

View File

@@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { isEqual, isEqualOrParent } from 'vs/base/common/extpath';
import { FileChangeType, FileChangesEvent, isParent } from 'vs/platform/files/common/files';
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
// eslint-disable-next-line code-import-patterns
import { toResource } from 'vs/base/test/common/utils';
suite('Files', () => {

View File

@@ -19,7 +19,7 @@ import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, File
import { NullLogService } from 'vs/platform/log/common/log';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isEqual } from 'vs/base/common/resources';
import { isEqual, joinPath } from 'vs/base/common/resources';
import { VSBuffer, VSBufferReadable, streamToBufferReadableStream, VSBufferReadableStream, bufferToReadable, bufferToStream, streamToBuffer } from 'vs/base/common/buffer';
import { find } from 'vs/base/common/arrays';
@@ -440,17 +440,22 @@ suite('Disk File Service', function () {
assert.equal(resolved.isSymbolicLink, true);
});
test('resolve - invalid symbolic link does not break', async () => {
test('resolve - symbolic link pointing to non-existing file does not break', async () => {
if (isWindows) {
return; // not reliable on windows
}
const link = URI.file(join(testDir, 'foo'));
await symlink(link.fsPath, join(testDir, 'bar'));
await symlink(join(testDir, 'foo'), join(testDir, 'bar'));
const resolved = await service.resolve(URI.file(testDir));
assert.equal(resolved.isDirectory, true);
assert.equal(resolved.children!.length, 8);
assert.equal(resolved.children!.length, 9);
const resolvedLink = resolved.children?.filter(child => child.name === 'bar' && child.isSymbolicLink)[0];
assert.ok(resolvedLink);
assert.ok(!resolvedLink?.isDirectory);
assert.ok(!resolvedLink?.isFile);
});
test('deleteFile', async () => {
@@ -479,6 +484,52 @@ suite('Disk File Service', function () {
assert.equal((<FileOperationError>error).fileOperationResult, FileOperationResult.FILE_NOT_FOUND);
});
test('deleteFile - symbolic link (exists)', async () => {
if (isWindows) {
return; // not reliable on windows
}
const target = URI.file(join(testDir, 'lorem.txt'));
const link = URI.file(join(testDir, 'lorem.txt-linked'));
await symlink(target.fsPath, link.fsPath);
const source = await service.resolve(link);
let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e));
await service.del(source.resource);
assert.equal(existsSync(source.resource.fsPath), false);
assert.ok(event!);
assert.equal(event!.resource.fsPath, link.fsPath);
assert.equal(event!.operation, FileOperation.DELETE);
assert.equal(existsSync(target.fsPath), true); // target the link pointed to is never deleted
});
test('deleteFile - symbolic link (pointing to non-existing file)', async () => {
if (isWindows) {
return; // not reliable on windows
}
const target = URI.file(join(testDir, 'foo'));
const link = URI.file(join(testDir, 'bar'));
await symlink(target.fsPath, link.fsPath);
let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e));
await service.del(link);
assert.equal(existsSync(link.fsPath), false);
assert.ok(event!);
assert.equal(event!.resource.fsPath, link.fsPath);
assert.equal(event!.operation, FileOperation.DELETE);
});
test('deleteFolder (recursive)', async () => {
let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e));
@@ -1867,6 +1918,47 @@ suite('Disk File Service', function () {
assert.ok(!error);
});
test('writeFile - no error when writing to same non-existing folder multiple times different new files', async () => {
const newFolder = URI.file(join(testDir, 'some', 'new', 'folder'));
const file1 = joinPath(newFolder, 'file-1');
const file2 = joinPath(newFolder, 'file-2');
const file3 = joinPath(newFolder, 'file-3');
// this essentially verifies that the mkdirp logic implemented
// in the file service is able to receive multiple requests for
// the same folder and will not throw errors if another racing
// call succeeded first.
const newContent = 'Updates to the small file';
await Promise.all([
service.writeFile(file1, VSBuffer.fromString(newContent)),
service.writeFile(file2, VSBuffer.fromString(newContent)),
service.writeFile(file3, VSBuffer.fromString(newContent))
]);
assert.ok(service.exists(file1));
assert.ok(service.exists(file2));
assert.ok(service.exists(file3));
});
test('writeFile - error when writing to folder that is a file', async () => {
const existingFile = URI.file(join(testDir, 'my-file'));
await service.createFile(existingFile);
const newFile = joinPath(existingFile, 'file-1');
let error;
const newContent = 'Updates to the small file';
try {
await service.writeFile(newFile, VSBuffer.fromString(newContent));
} catch (e) {
error = e;
}
assert.ok(error);
});
const runWatchTests = isLinux;
(runWatchTests ? test : test.skip)('watch - file', done => {