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

@@ -126,8 +126,8 @@ export class ConfigWatcher<T> extends Disposable implements IConfigWatcher<T> {
}
private async handleSymbolicLink(): Promise<void> {
const { stat, isSymbolicLink } = await statLink(this._path);
if (isSymbolicLink && !stat.isDirectory()) {
const { stat, symbolicLink } = await statLink(this._path);
if (symbolicLink && !stat.isDirectory()) {
const realPath = await realpath(this._path);
this.watch(realPath, false);

View File

@@ -14,7 +14,18 @@ import { promisify } from 'util';
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
import { generateUuid } from 'vs/base/common/uuid';
import { normalizeNFC } from 'vs/base/common/normalization';
import { encode, encodeStream } from 'vs/base/node/encoding';
import { encode } from 'vs/base/node/encoding';
// See https://github.com/Microsoft/vscode/issues/30180
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149
const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB
const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;
export enum RimRafMode {
@@ -178,30 +189,52 @@ export function stat(path: string): Promise<fs.Stats> {
}
export interface IStatAndLink {
// The stats of the file. If the file is a symbolic
// link, the stats will be of that target file and
// not the link itself.
// If the file is a symbolic link pointing to a non
// existing file, the stat will be of the link and
// the `dangling` flag will indicate this.
stat: fs.Stats;
isSymbolicLink: boolean;
// Will be provided if the resource is a symbolic link
// on disk. Use the `dangling` flag to find out if it
// points to a resource that does not exist on disk.
symbolicLink?: { dangling: boolean };
}
export async function statLink(path: string): Promise<IStatAndLink> {
// First stat the link
let linkStat: fs.Stats | undefined;
let linkStatError: NodeJS.ErrnoException | undefined;
let lstats: fs.Stats | undefined;
try {
linkStat = await lstat(path);
lstats = await lstat(path);
// Return early if the stat is not a symbolic link at all
if (!lstats.isSymbolicLink()) {
return { stat: lstats };
}
} catch (error) {
linkStatError = error;
/* ignore - use stat() instead */
}
// Then stat the target and return that
const isLink = !!(linkStat && linkStat.isSymbolicLink());
if (linkStatError || isLink) {
const fileStat = await stat(path);
// If the stat is a symbolic link or failed to stat, use fs.stat()
// which for symbolic links will stat the target they point to
try {
const stats = await stat(path);
return { stat: fileStat, isSymbolicLink: isLink };
return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined };
} catch (error) {
// If the link points to a non-existing file we still want
// to return it as result while setting dangling: true flag
if (error.code === 'ENOENT' && lstats) {
return { stat: lstats, symbolicLink: { dangling: true } };
}
throw error;
}
return { stat: linkStat!, isSymbolicLink: false };
}
export function lstat(path: string): Promise<fs.Stats> {
@@ -213,9 +246,7 @@ export function rename(oldPath: string, newPath: string): Promise<void> {
}
export function renameIgnoreError(oldPath: string, newPath: string): Promise<void> {
return new Promise(resolve => {
fs.rename(oldPath, newPath, () => resolve());
});
return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve()));
}
export function unlink(path: string): Promise<void> {
@@ -236,6 +267,10 @@ export function readFile(path: string, encoding?: string): Promise<Buffer | stri
return promisify(fs.readFile)(path, encoding);
}
export async function mkdirp(path: string, mode?: number): Promise<void> {
return promisify(fs.mkdir)(path, { mode, recursive: true });
}
// According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
// it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
@@ -244,12 +279,15 @@ const writeFilePathQueues: Map<string, Queue<void>> = new Map();
export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: NodeJS.ReadableStream, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise<void> {
export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void>;
export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void> {
const queueKey = toQueueKey(path);
return ensureWriteFileQueue(queueKey).queue(() => writeFileAndFlush(path, data, options));
return ensureWriteFileQueue(queueKey).queue(() => {
const ensuredOptions = ensureWriteOptions(options);
return new Promise((resolve, reject) => doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve()));
});
}
function toQueueKey(path: string): string {
@@ -294,103 +332,6 @@ interface IEnsuredWriteFileOptions extends IWriteFileOptions {
}
let canFlush = true;
function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options: IWriteFileOptions | undefined): Promise<void> {
const ensuredOptions = ensureWriteOptions(options);
return new Promise((resolve, reject) => {
if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) {
doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve());
} else {
doWriteFileStreamAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve());
}
});
}
function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void {
// finish only once
let finished = false;
const finish = (error?: Error) => {
if (!finished) {
finished = true;
// in error cases we need to manually close streams
// if the write stream was successfully opened
if (error) {
if (isOpen) {
writer.once('close', () => callback(error));
writer.destroy();
} else {
callback(error);
}
}
// otherwise just return without error
else {
callback();
}
}
};
// create writer to target. we set autoClose: false because we want to use the streams
// file descriptor to call fs.fdatasync to ensure the data is flushed to disk
const writer = fs.createWriteStream(path, { mode: options.mode, flags: options.flag, autoClose: false });
// Event: 'open'
// Purpose: save the fd for later use and start piping
// Notes: will not be called when there is an error opening the file descriptor!
let fd: number;
let isOpen: boolean;
writer.once('open', descriptor => {
fd = descriptor;
isOpen = true;
// if an encoding is provided, we need to pipe the stream through
// an encoder stream and forward the encoding related options
if (options.encoding) {
reader = reader.pipe(encodeStream(options.encoding.charset, { addBOM: options.encoding.addBOM }));
}
// start data piping only when we got a successful open. this ensures that we do
// not consume the stream when an error happens and helps to fix this issue:
// https://github.com/Microsoft/vscode/issues/42542
reader.pipe(writer);
});
// Event: 'error'
// Purpose: to return the error to the outside and to close the write stream (does not happen automatically)
reader.once('error', error => finish(error));
writer.once('error', error => finish(error));
// Event: 'finish'
// Purpose: use fs.fdatasync to flush the contents to disk
// Notes: event is called when the writer has finished writing to the underlying resource. we must call writer.close()
// because we have created the WriteStream with autoClose: false
writer.once('finish', () => {
// flush to disk
if (canFlush && isOpen) {
fs.fdatasync(fd, (syncError: Error) => {
// In some exotic setups it is well possible that node fails to sync
// In that case we disable flushing and warn to the console
if (syncError) {
console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError);
canFlush = false;
}
writer.destroy();
});
} else {
writer.destroy();
}
});
// Event: 'close'
// Purpose: signal we are done to the outside
// Notes: event is called when the writer's filedescriptor is closed
writer.once('close', () => finish());
}
// Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk
// We do this in cases where we want to make sure the data is really on disk and
@@ -631,18 +572,3 @@ async function doCopyFile(source: string, target: string, mode: number): Promise
reader.pipe(writer);
});
}
export async function mkdirp(path: string, mode?: number): Promise<void> {
return promisify(fs.mkdir)(path, { mode, recursive: true });
}
// See https://github.com/Microsoft/vscode/issues/30180
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149
const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB
const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;