Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)

* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998

* fix pipelines

* fix strict-null-checks

* add missing files
This commit is contained in:
Anthony Dresser
2019-10-21 22:12:22 -07:00
committed by GitHub
parent 7c9be74970
commit 1e22f47304
913 changed files with 18898 additions and 16536 deletions

View File

@@ -163,15 +163,15 @@ export class FileService extends Disposable implements IFileService {
private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> {
const provider = await this.withProvider(resource);
const resolveTo = options && options.resolveTo;
const resolveSingleChildDescendants = !!(options && options.resolveSingleChildDescendants);
const resolveMetadata = !!(options && options.resolveMetadata);
const resolveTo = options?.resolveTo;
const resolveSingleChildDescendants = options?.resolveSingleChildDescendants;
const resolveMetadata = options?.resolveMetadata;
const stat = await provider.stat(resource);
let trie: TernarySearchTree<boolean> | undefined;
return this.toFileStat(provider, resource, stat, undefined, resolveMetadata, (stat, siblings) => {
return this.toFileStat(provider, resource, stat, undefined, !!resolveMetadata, (stat, siblings) => {
// lazy trie to check for recursive resolving
if (!trie) {
@@ -274,8 +274,7 @@ export class FileService extends Disposable implements IFileService {
async createFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream = VSBuffer.fromString(''), options?: ICreateFileOptions): Promise<IFileStatWithMetadata> {
// validate overwrite
const overwrite = !!(options && options.overwrite);
if (!overwrite && await this.exists(resource)) {
if (!options?.overwrite && await this.exists(resource)) {
throw new FileOperationError(localize('fileExists', "File to create already exists ({0})", this.resourceForError(resource)), FileOperationResult.FILE_MODIFIED_SINCE, options);
}
@@ -507,7 +506,7 @@ export class FileService extends Disposable implements IFileService {
}
// Return early if file is too large to load
if (options && options.limits) {
if (options?.limits) {
if (typeof options.limits.memory === 'number' && stat.size > options.limits.memory) {
throw new FileOperationError(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow it to use more memory"), FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
}
@@ -529,7 +528,7 @@ export class FileService extends Disposable implements IFileService {
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(target));
// move
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'move', overwrite);
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'move', !!overwrite);
// resolve and send events
const fileStat = await this.resolve(target, { resolveMetadata: true });
@@ -543,7 +542,7 @@ export class FileService extends Disposable implements IFileService {
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(target));
// copy
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'copy', overwrite);
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'copy', !!overwrite);
// resolve and send events
const fileStat = await this.resolve(target, { resolveMetadata: true });
@@ -552,7 +551,7 @@ export class FileService extends Disposable implements IFileService {
return fileStat;
}
private async doMoveCopy(sourceProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, target: URI, mode: 'move' | 'copy', overwrite?: boolean): Promise<'move' | 'copy'> {
private async doMoveCopy(sourceProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, target: URI, mode: 'move' | 'copy', overwrite: boolean): Promise<'move' | 'copy'> {
if (source.toString() === target.toString()) {
return mode; // simulate node.js behaviour here and do a no-op if paths match
}
@@ -573,7 +572,7 @@ export class FileService extends Disposable implements IFileService {
// same provider with fast copy: leverage copy() functionality
if (sourceProvider === targetProvider && hasFileFolderCopyCapability(sourceProvider)) {
await sourceProvider.copy(source, target, { overwrite: !!overwrite });
await sourceProvider.copy(source, target, { overwrite });
}
// when copying via buffer/unbuffered, we have to manually
@@ -595,7 +594,7 @@ export class FileService extends Disposable implements IFileService {
// same provider: leverage rename() functionality
if (sourceProvider === targetProvider) {
await sourceProvider.rename(source, target, { overwrite: !!overwrite });
await sourceProvider.rename(source, target, { overwrite });
return mode;
}
@@ -744,13 +743,13 @@ export class FileService extends Disposable implements IFileService {
const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource));
// Validate trash support
const useTrash = !!(options && options.useTrash);
const useTrash = !!options?.useTrash;
if (useTrash && !(provider.capabilities & FileSystemProviderCapabilities.Trash)) {
throw new Error(localize('err.trash', "Provider does not support trash."));
}
// Validate recursive
const recursive = !!(options && options.recursive);
const recursive = !!options?.recursive;
if (!recursive && await this.exists(resource)) {
const stat = await this.resolve(resource);
if (stat.isDirectory && Array.isArray(stat.children) && stat.children.length > 0) {
@@ -1062,7 +1061,7 @@ export class FileService extends Disposable implements IFileService {
private throwIfTooLarge(totalBytesRead: number, options?: IReadFileOptions): boolean {
// Return early if file is too large to load
if (options && options.limits) {
if (options?.limits) {
if (typeof options.limits.memory === 'number' && totalBytesRead > options.limits.memory) {
throw new FileOperationError(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow it to use more memory"), FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
}

View File

@@ -3,14 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { mkdir, open, close, read, write, fdatasync } from 'fs';
import { mkdir, open, close, read, write, fdatasync, Dirent, Stats } from 'fs';
import { promisify } from 'util';
import { IDisposable, Disposable, toDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, FileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { statLink, readdir, unlink, move, copy, readFile, truncate, rimraf, RimRafMode, exists } from 'vs/base/node/pfs';
import { statLink, unlink, move, copy, readFile, truncate, rimraf, RimRafMode, exists, readdirWithFileTypes } from 'vs/base/node/pfs';
import { normalize, basename, dirname } from 'vs/base/common/path';
import { joinPath } from 'vs/base/common/resources';
import { isEqual } from 'vs/base/common/extpath';
@@ -62,15 +62,8 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
try {
const { stat, isSymbolicLink } = await statLink(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly
let type: number;
if (isSymbolicLink) {
type = FileType.SymbolicLink | (stat.isDirectory() ? FileType.Directory : FileType.File);
} else {
type = stat.isFile() ? FileType.File : stat.isDirectory() ? FileType.Directory : FileType.Unknown;
}
return {
type,
type: this.toType(stat, isSymbolicLink),
ctime: stat.ctime.getTime(),
mtime: stat.mtime.getTime(),
size: stat.size
@@ -82,13 +75,19 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
async readdir(resource: URI): Promise<[string, FileType][]> {
try {
const children = await readdir(this.toFilePath(resource));
const children = await readdirWithFileTypes(this.toFilePath(resource));
const result: [string, FileType][] = [];
await Promise.all(children.map(async child => {
try {
const stat = await this.stat(joinPath(resource, child));
result.push([child, stat.type]);
let type: FileType;
if (child.isSymbolicLink()) {
type = (await this.stat(joinPath(resource, child.name))).type; // always resolve target the link points to if any
} else {
type = this.toType(child);
}
result.push([child.name, type]);
} catch (error) {
this.logService.trace(error); // ignore errors for individual entries that can arise from permission denied
}
@@ -100,6 +99,14 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
}
}
private toType(entry: Stats | Dirent, isSymbolicLink = entry.isSymbolicLink()): FileType {
if (isSymbolicLink) {
return FileType.SymbolicLink | (entry.isDirectory() ? FileType.Directory : FileType.File);
}
return entry.isFile() ? FileType.File : entry.isDirectory() ? FileType.Directory : FileType.Unknown;
}
//#endregion
//#region File Reading/Writing
@@ -373,7 +380,7 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
try {
// Ensure target does not exist
await this.validateTargetDeleted(from, to, 'move', opts && opts.overwrite);
await this.validateTargetDeleted(from, to, 'move', opts.overwrite);
// Move
await move(fromFilePath, toFilePath);
@@ -400,7 +407,7 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
try {
// Ensure target does not exist
await this.validateTargetDeleted(from, to, 'copy', opts && opts.overwrite);
await this.validateTargetDeleted(from, to, 'copy', opts.overwrite);
// Copy
await copy(fromFilePath, toFilePath);

View File

@@ -17,6 +17,9 @@ import { isMacintosh, isLinux } from 'vs/base/common/platform';
import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
import { IWatcherRequest, IWatcherService, IWatcherOptions } from 'vs/platform/files/node/watcher/unix/watcher';
import { Emitter, Event } from 'vs/base/common/event';
import { equals } from 'vs/base/common/arrays';
process.noAsar = true; // disable ASAR support in watcher process
interface IWatcher {
requests: ExtendedWatcherRequest[];
@@ -32,8 +35,8 @@ export class ChokidarWatcherService implements IWatcherService {
private static readonly FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms)
private static readonly EVENT_SPAM_WARNING_THRESHOLD = 60 * 1000; // warn after certain time span of event spam
private _watchers: { [watchPath: string]: IWatcher };
private _watcherCount: number;
private _watchers: { [watchPath: string]: IWatcher } = Object.create(null);
private _watcherCount = 0;
private _pollingInterval?: number;
private _usePolling?: boolean;
@@ -49,29 +52,30 @@ export class ChokidarWatcherService implements IWatcherService {
private readonly _onLogMessage = new Emitter<ILogMessage>();
readonly onLogMessage: Event<ILogMessage> = this._onLogMessage.event;
public watch(options: IWatcherOptions): Event<IDiskFileChange[]> {
watch(options: IWatcherOptions): Event<IDiskFileChange[]> {
this._pollingInterval = options.pollingInterval;
this._usePolling = options.usePolling;
this._watchers = Object.create(null);
this._watcherCount = 0;
return this.onWatchEvent;
}
public setVerboseLogging(enabled: boolean): Promise<void> {
setVerboseLogging(enabled: boolean): Promise<void> {
this._verboseLogging = enabled;
return Promise.resolve();
}
public setRoots(requests: IWatcherRequest[]): Promise<void> {
setRoots(requests: IWatcherRequest[]): Promise<void> {
const watchers = Object.create(null);
const newRequests: string[] = [];
const requestsByBasePath = normalizeRoots(requests);
// evaluate new & remaining watchers
for (let basePath in requestsByBasePath) {
let watcher = this._watchers[basePath];
for (const basePath in requestsByBasePath) {
const watcher = this._watchers[basePath];
if (watcher && isEqualRequests(watcher.requests, requestsByBasePath[basePath])) {
watchers[basePath] = watcher;
delete this._watchers[basePath];
@@ -79,13 +83,15 @@ export class ChokidarWatcherService implements IWatcherService {
newRequests.push(basePath);
}
}
// stop all old watchers
for (let path in this._watchers) {
for (const path in this._watchers) {
this._watchers[path].stop();
}
// start all new watchers
for (let basePath of newRequests) {
let requests = requestsByBasePath[basePath];
for (const basePath of newRequests) {
const requests = requestsByBasePath[basePath];
watchers[basePath] = this._watch(basePath, requests);
}
@@ -94,7 +100,7 @@ export class ChokidarWatcherService implements IWatcherService {
}
// for test purposes
public get wacherCount() {
get wacherCount() {
return this._watcherCount;
}
@@ -120,9 +126,10 @@ export class ChokidarWatcherService implements IWatcherService {
};
const excludes: string[] = [];
// if there's only one request, use the built-in ignore-filterering
const isSingleFolder = requests.length === 1;
if (isSingleFolder) {
// if there's only one request, use the built-in ignore-filterering
excludes.push(...requests[0].excludes);
}
@@ -132,6 +139,9 @@ export class ChokidarWatcherService implements IWatcherService {
excludes.push('/proc/**', '/sys/**');
}
}
excludes.push('**/*.asar'); // Ensure we never recurse into ASAR archives
watcherOpts.ignored = excludes;
// Chokidar fails when the basePath does not match case-identical to the path on disk
@@ -219,7 +229,7 @@ export class ChokidarWatcherService implements IWatcherService {
}
}
let event = { type: eventType, path };
const event = { type: eventType, path };
// Logging
if (this._verboseLogging) {
@@ -283,12 +293,14 @@ export class ChokidarWatcherService implements IWatcherService {
return watcher;
}
public stop(): Promise<void> {
for (let path in this._watchers) {
let watcher = this._watchers[path];
stop(): Promise<void> {
for (const path in this._watchers) {
const watcher = this._watchers[path];
watcher.stop();
}
this._watchers = Object.create(null);
return Promise.resolve();
}
@@ -306,25 +318,28 @@ export class ChokidarWatcherService implements IWatcherService {
}
function isIgnored(path: string, requests: ExtendedWatcherRequest[]): boolean {
for (let request of requests) {
for (const request of requests) {
if (request.path === path) {
return false;
}
if (extpath.isEqualOrParent(path, request.path)) {
if (!request.parsedPattern) {
if (request.excludes && request.excludes.length > 0) {
let pattern = `{${request.excludes.join(',')}}`;
const pattern = `{${request.excludes.join(',')}}`;
request.parsedPattern = glob.parse(pattern);
} else {
request.parsedPattern = () => false;
}
}
const relPath = path.substr(request.path.length + 1);
if (!request.parsedPattern(relPath)) {
return false;
}
}
}
return true;
}
@@ -334,11 +349,12 @@ function isIgnored(path: string, requests: ExtendedWatcherRequest[]): boolean {
*/
export function normalizeRoots(requests: IWatcherRequest[]): { [basePath: string]: IWatcherRequest[] } {
requests = requests.sort((r1, r2) => r1.path.localeCompare(r2.path));
let prevRequest: IWatcherRequest | null = null;
let result: { [basePath: string]: IWatcherRequest[] } = Object.create(null);
for (let request of requests) {
let basePath = request.path;
let ignored = (request.excludes || []).sort();
const result: { [basePath: string]: IWatcherRequest[] } = Object.create(null);
for (const request of requests) {
const basePath = request.path;
const ignored = (request.excludes || []).sort();
if (prevRequest && (extpath.isEqualOrParent(basePath, prevRequest.path))) {
if (!isEqualIgnore(ignored, prevRequest.excludes)) {
result[prevRequest.path].push({ path: basePath, excludes: ignored });
@@ -348,29 +364,14 @@ export function normalizeRoots(requests: IWatcherRequest[]): { [basePath: string
result[basePath] = [prevRequest];
}
}
return result;
}
function isEqualRequests(r1: IWatcherRequest[], r2: IWatcherRequest[]) {
if (r1.length !== r2.length) {
return false;
}
for (let k = 0; k < r1.length; k++) {
if (r1[k].path !== r2[k].path || !isEqualIgnore(r1[k].excludes, r2[k].excludes)) {
return false;
}
}
return true;
function isEqualRequests(r1: readonly IWatcherRequest[], r2: readonly IWatcherRequest[]) {
return equals(r1, r2, (a, b) => a.path === b.path && isEqualIgnore(a.excludes, b.excludes));
}
function isEqualIgnore(i1: string[], i2: string[]) {
if (i1.length !== i2.length) {
return false;
}
for (let k = 0; k < i1.length; k++) {
if (i1[k] !== i2[k]) {
return false;
}
}
return true;
function isEqualIgnore(i1: readonly string[], i2: readonly string[]) {
return equals(i1, i2);
}

View File

@@ -21,19 +21,13 @@ import { isLinux, isWindows } from 'vs/base/common/platform';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isEqual } from 'vs/base/common/resources';
import { VSBuffer, VSBufferReadable, toVSBufferReadableStream, VSBufferReadableStream, bufferToReadable, bufferToStream } from 'vs/base/common/buffer';
import { find } from 'vs/base/common/arrays';
function getByName(root: IFileStat, name: string): IFileStat | null {
function getByName(root: IFileStat, name: string): IFileStat | undefined {
if (root.children === undefined) {
return null;
return undefined;
}
for (const child of root.children) {
if (child.name === name) {
return child;
}
}
return null;
return find(root.children, child => child.name === name);
}
function toLineByLineReadable(content: string): VSBufferReadable {