mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Merge branch 'master' into sandy081/smoketests/remote
This commit is contained in:
@@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop"
|
||||
|
||||
$CertBytes = [System.Convert]::FromBase64String($CertBase64)
|
||||
$CertCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
|
||||
$CertCollection.Import($CertBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
|
||||
$CertCollection.Import($CertBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet)
|
||||
|
||||
$CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
|
||||
$CertStore.Open("ReadWrite")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"editor.minimap.enabled": false // see https://github.com/microsoft/vscode/issues/115747
|
||||
}
|
||||
@@ -37,6 +37,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"views": {
|
||||
"remote": [
|
||||
{
|
||||
"id": "test.treeId",
|
||||
"when": "never"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configurationDefaults": {
|
||||
"[abcLang]": {
|
||||
"editor.lineNumbers": "off",
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/files-exclude/**": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor.minimap.enabled": false // see https://github.com/microsoft/vscode/issues/115747
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"editor.minimap.enabled": false // see https://github.com/microsoft/vscode/issues/115747
|
||||
}
|
||||
@@ -231,11 +231,13 @@ export class IconLabel extends Disposable {
|
||||
}
|
||||
tokenSource = new CancellationTokenSource();
|
||||
function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): any {
|
||||
isHovering = false;
|
||||
hoverOptions = undefined;
|
||||
tokenSource.dispose(true);
|
||||
mouseLeaveDisposable.dispose();
|
||||
mouseDownDisposable.dispose();
|
||||
if ((<any>e).fromElement === htmlElement) {
|
||||
isHovering = false;
|
||||
hoverOptions = undefined;
|
||||
tokenSource.dispose(true);
|
||||
mouseLeaveDisposable.dispose();
|
||||
mouseDownDisposable.dispose();
|
||||
}
|
||||
}
|
||||
const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeaveOrDown.bind(htmlElement));
|
||||
const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement));
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "vs/base",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "vs",
|
||||
"internal": false
|
||||
}
|
||||
],
|
||||
"libs": [
|
||||
"lib.core.d.ts"
|
||||
],
|
||||
"sources": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"declares": []
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import * as fs from 'fs';
|
||||
import { rtrim } from 'vs/base/common/strings';
|
||||
import { sep, join, normalize, dirname, basename } from 'vs/base/common/path';
|
||||
import { readdirSync } from 'vs/base/node/pfs';
|
||||
import { promisify } from 'util';
|
||||
|
||||
/**
|
||||
* Copied from: https://github.com/microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83
|
||||
@@ -53,7 +52,7 @@ export function realcaseSync(path: string): string | null {
|
||||
|
||||
export async function realpath(path: string): Promise<string> {
|
||||
try {
|
||||
return await promisify(fs.realpath)(path);
|
||||
return await fs.promises.realpath(path);
|
||||
} catch (error) {
|
||||
|
||||
// We hit an error calling fs.realpath(). Since fs.realpath() is doing some path normalization
|
||||
@@ -63,7 +62,7 @@ export async function realpath(path: string): Promise<string> {
|
||||
// to not resolve links but to simply see if the path is read accessible or not.
|
||||
const normalizedPath = normalizePath(path);
|
||||
|
||||
await promisify(fs.access)(normalizedPath, fs.constants.R_OK);
|
||||
await fs.promises.access(normalizedPath, fs.constants.R_OK);
|
||||
|
||||
return normalizedPath;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
//@ts-check
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {NodeRequire} nodeRequire
|
||||
@@ -30,22 +30,6 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
function lstat(file) {
|
||||
return new Promise((c, e) => fs.lstat(file, (err, stats) => err ? e(err) : c(stats)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
function readdir(dir) {
|
||||
return new Promise((c, e) => fs.readdir(dir, (err, files) => err ? e(err) : c(files)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string>}
|
||||
@@ -54,53 +38,20 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rmdir(dir) {
|
||||
return new Promise((c, e) => fs.rmdir(dir, err => err ? e(err) : c(undefined)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function unlink(file) {
|
||||
return new Promise((c, e) => fs.unlink(file, err => err ? e(err) : c(undefined)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} location
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rimraf(location) {
|
||||
return lstat(location).then(stat => {
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
return readdir(location)
|
||||
.then(children => Promise.all(children.map(child => rimraf(path.join(location, child)))))
|
||||
.then(() => rmdir(location));
|
||||
} else {
|
||||
return unlink(location);
|
||||
}
|
||||
}, err => {
|
||||
if (err.code === 'ENOENT') {
|
||||
return undefined;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function readFile(file) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.readFile(file, 'utf8', function (err, data) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,18 +60,9 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function writeFile(file, content) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.writeFile(file, content, 'utf8', function (err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} userDataPath
|
||||
* @returns {object}
|
||||
@@ -301,8 +243,10 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
}
|
||||
|
||||
|
||||
// @ts-ignore
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
// @ts-ignore
|
||||
define(['path', 'fs', 'vs/base/common/performance'], function (path, fs, perf) { return factory(require.__$__nodeRequire, path, fs, perf); });
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const path = require('path');
|
||||
|
||||
+393
-315
@@ -6,14 +6,15 @@
|
||||
import * as fs from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { Promises, Queue } from 'vs/base/common/async';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { promisify } from 'util';
|
||||
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
import { isEqualOrParent, isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { normalizeNFC } from 'vs/base/common/normalization';
|
||||
|
||||
//#region Constants
|
||||
|
||||
// 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
|
||||
@@ -25,6 +26,10 @@ 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;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region rimraf
|
||||
|
||||
export enum RimRafMode {
|
||||
|
||||
/**
|
||||
@@ -40,12 +45,19 @@ export enum RimRafMode {
|
||||
MOVE
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to delete the provied path (either file or folder) recursively
|
||||
* with the options:
|
||||
* - `UNLINK`: direct removal from disk
|
||||
* - `MOVE`: faster variant that first moves the target to temp dir and then
|
||||
* deletes it in the background without waiting for that to finish.
|
||||
*/
|
||||
export async function rimraf(path: string, mode = RimRafMode.UNLINK): Promise<void> {
|
||||
if (isRootOrDriveLetter(path)) {
|
||||
throw new Error('rimraf - will refuse to recursively delete root');
|
||||
}
|
||||
|
||||
// delete: via unlink
|
||||
// delete: via rmDir
|
||||
if (mode === RimRafMode.UNLINK) {
|
||||
return rimrafUnlink(path);
|
||||
}
|
||||
@@ -54,32 +66,17 @@ export async function rimraf(path: string, mode = RimRafMode.UNLINK): Promise<vo
|
||||
return rimrafMove(path);
|
||||
}
|
||||
|
||||
async function rimrafUnlink(path: string): Promise<void> {
|
||||
async function rimrafMove(path: string): Promise<void> {
|
||||
try {
|
||||
const stat = await lstat(path);
|
||||
|
||||
// Folder delete (recursive) - NOT for symbolic links though!
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
|
||||
// Children
|
||||
const children = await readdir(path);
|
||||
await Promises.settled(children.map(child => rimrafUnlink(join(path, child))));
|
||||
|
||||
// Folder
|
||||
await promisify(fs.rmdir)(path);
|
||||
const pathInTemp = join(tmpdir(), generateUuid());
|
||||
try {
|
||||
await fs.promises.rename(path, pathInTemp);
|
||||
} catch (error) {
|
||||
return rimrafUnlink(path); // if rename fails, delete without tmp dir
|
||||
}
|
||||
|
||||
// Single file delete
|
||||
else {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
await chmod(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return unlink(path);
|
||||
}
|
||||
// Delete but do not return as promise
|
||||
rimrafUnlink(pathInTemp).catch(error => {/* ignore */ });
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
@@ -87,22 +84,8 @@ async function rimrafUnlink(path: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function rimrafMove(path: string): Promise<void> {
|
||||
try {
|
||||
const pathInTemp = join(tmpdir(), generateUuid());
|
||||
try {
|
||||
await rename(path, pathInTemp);
|
||||
} catch (error) {
|
||||
return rimrafUnlink(path); // if rename fails, delete without tmp dir
|
||||
}
|
||||
|
||||
// Delete but do not return as promise
|
||||
rimrafUnlink(pathInTemp);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function rimrafUnlink(path: string): Promise<void> {
|
||||
return fs.promises.rmdir(path, { recursive: true, maxRetries: 3 });
|
||||
}
|
||||
|
||||
export function rimrafSync(path: string): void {
|
||||
@@ -110,193 +93,272 @@ export function rimrafSync(path: string): void {
|
||||
throw new Error('rimraf - will refuse to recursively delete root');
|
||||
}
|
||||
|
||||
fs.rmdirSync(path, { recursive: true });
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region readdir with NFC support (macos)
|
||||
|
||||
export interface IDirent {
|
||||
name: string;
|
||||
|
||||
isFile(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isSymbolicLink(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-in replacement of `fs.readdir` with support
|
||||
* for converting from macOS NFD unicon form to NFC
|
||||
* (https://github.com/nodejs/node/issues/2165)
|
||||
*/
|
||||
export async function readdir(path: string): Promise<string[]>;
|
||||
export async function readdir(path: string, options: { withFileTypes: true }): Promise<IDirent[]>;
|
||||
export async function readdir(path: string, options?: { withFileTypes: true }): Promise<(string | IDirent)[]> {
|
||||
return handleDirectoryChildren(await (options ? safeReaddirWithFileTypes(path) : fs.promises.readdir(path)));
|
||||
}
|
||||
|
||||
async function safeReaddirWithFileTypes(path: string): Promise<IDirent[]> {
|
||||
try {
|
||||
const stat = fs.lstatSync(path);
|
||||
|
||||
// Folder delete (recursive) - NOT for symbolic links though!
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
|
||||
// Children
|
||||
const children = readdirSync(path);
|
||||
children.map(child => rimrafSync(join(path, child)));
|
||||
|
||||
// Folder
|
||||
fs.rmdirSync(path);
|
||||
}
|
||||
|
||||
// Single file delete
|
||||
else {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
fs.chmodSync(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return fs.unlinkSync(path);
|
||||
}
|
||||
return await fs.promises.readdir(path, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
console.warn('[node.js fs] readdir with filetypes failed with error: ', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function readdir(path: string): Promise<string[]> {
|
||||
return handleDirectoryChildren(await promisify(fs.readdir)(path));
|
||||
}
|
||||
|
||||
export async function readdirWithFileTypes(path: string): Promise<fs.Dirent[]> {
|
||||
const children = await promisify(fs.readdir)(path, { withFileTypes: true });
|
||||
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (isMacintosh) {
|
||||
for (const child of children) {
|
||||
child.name = normalizeNFC(child.name);
|
||||
// Fallback to manually reading and resolving each
|
||||
// children of the folder in case we hit an error
|
||||
// previously.
|
||||
// This can only really happen on exotic file systems
|
||||
// such as explained in #115645 where we get entries
|
||||
// from `readdir` that we can later not `lstat`.
|
||||
const result: IDirent[] = [];
|
||||
const children = await readdir(path);
|
||||
for (const child of children) {
|
||||
try {
|
||||
const lstat = await fs.promises.lstat(join(path, child));
|
||||
result.push({
|
||||
name: child,
|
||||
isFile: () => lstat.isFile(),
|
||||
isDirectory: () => lstat.isDirectory(),
|
||||
isSymbolicLink: () => lstat.isSymbolicLink()
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('[node.js fs] unexpected error from lstat after readdir: ', error);
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-in replacement of `fs.readdirSync` with support
|
||||
* for converting from macOS NFD unicon form to NFC
|
||||
* (https://github.com/nodejs/node/issues/2165)
|
||||
*/
|
||||
export function readdirSync(path: string): string[] {
|
||||
return handleDirectoryChildren(fs.readdirSync(path));
|
||||
}
|
||||
|
||||
function handleDirectoryChildren(children: string[]): string[] {
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (isMacintosh) {
|
||||
return children.map(child => normalizeNFC(child));
|
||||
}
|
||||
function handleDirectoryChildren(children: string[]): string[];
|
||||
function handleDirectoryChildren(children: IDirent[]): IDirent[];
|
||||
function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[];
|
||||
function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[] {
|
||||
return children.map(child => {
|
||||
|
||||
return children;
|
||||
}
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
|
||||
export function exists(path: string): Promise<boolean> {
|
||||
return promisify(fs.exists)(path);
|
||||
}
|
||||
|
||||
export function chmod(path: string, mode: number): Promise<void> {
|
||||
return promisify(fs.chmod)(path, mode);
|
||||
}
|
||||
|
||||
export function stat(path: string): Promise<fs.Stats> {
|
||||
return promisify(fs.stat)(path);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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 lstats: fs.Stats | undefined;
|
||||
try {
|
||||
lstats = await lstat(path);
|
||||
|
||||
// Return early if the stat is not a symbolic link at all
|
||||
if (!lstats.isSymbolicLink()) {
|
||||
return { stat: lstats };
|
||||
}
|
||||
} catch (error) {
|
||||
/* ignore - use stat() instead */
|
||||
}
|
||||
|
||||
// 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: 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 } };
|
||||
if (typeof child === 'string') {
|
||||
return isMacintosh ? normalizeNFC(child) : child;
|
||||
}
|
||||
|
||||
// Windows: workaround a node.js bug where reparse points
|
||||
// are not supported (https://github.com/nodejs/node/issues/36790)
|
||||
if (isWindows && error.code === 'EACCES' && lstats) {
|
||||
try {
|
||||
const stats = await stat(await readlink(path));
|
||||
child.name = isMacintosh ? normalizeNFC(child.name) : child.name;
|
||||
|
||||
return { stat: stats, symbolicLink: lstats.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
return child;
|
||||
});
|
||||
}
|
||||
|
||||
// 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') {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
/**
|
||||
* A convinience method to read all children of a path that
|
||||
* are directories.
|
||||
*/
|
||||
export async function readDirsInDir(dirPath: string): Promise<string[]> {
|
||||
const children = await readdir(dirPath);
|
||||
const directories: string[] = [];
|
||||
|
||||
throw error;
|
||||
for (const child of children) {
|
||||
if (await SymlinkSupport.existsDirectory(join(dirPath, child))) {
|
||||
directories.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region whenDeleted()
|
||||
|
||||
/**
|
||||
* A `Promise` that resolves when the provided `path`
|
||||
* is deleted from disk.
|
||||
*/
|
||||
export function whenDeleted(path: string, intervalMs = 1000): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
let running = false;
|
||||
const interval = setInterval(() => {
|
||||
if (!running) {
|
||||
running = true;
|
||||
fs.access(path, err => {
|
||||
running = false;
|
||||
|
||||
if (err) {
|
||||
clearInterval(interval);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Methods with symbolic links support
|
||||
|
||||
export namespace SymlinkSupport {
|
||||
|
||||
export interface IStats {
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the `fs.Stats` of the provided path. If the path is a
|
||||
* symbolic link, the `fs.Stats` will be from the target it points
|
||||
* to. If the target does not exist, `dangling: true` will be returned
|
||||
* as `symbolicLink` value.
|
||||
*/
|
||||
export async function stat(path: string): Promise<IStats> {
|
||||
|
||||
// First stat the link
|
||||
let lstats: fs.Stats | undefined;
|
||||
try {
|
||||
lstats = await fs.promises.lstat(path);
|
||||
|
||||
// Return early if the stat is not a symbolic link at all
|
||||
if (!lstats.isSymbolicLink()) {
|
||||
return { stat: lstats };
|
||||
}
|
||||
} catch (error) {
|
||||
/* ignore - use stat() instead */
|
||||
}
|
||||
|
||||
throw error;
|
||||
// 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 fs.promises.stat(path);
|
||||
|
||||
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 } };
|
||||
}
|
||||
|
||||
// Windows: workaround a node.js bug where reparse points
|
||||
// are not supported (https://github.com/nodejs/node/issues/36790)
|
||||
if (isWindows && error.code === 'EACCES') {
|
||||
try {
|
||||
const stats = await fs.promises.stat(await fs.promises.readlink(path));
|
||||
|
||||
return { stat: stats, symbolicLink: { dangling: false } };
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out if the `path` exists and is a file with support
|
||||
* for symlinks.
|
||||
*
|
||||
* Note: this will return `false` for a symlink that exists on
|
||||
* disk but is dangling (pointing to a non-existing path).
|
||||
*
|
||||
* Use `exists` if you only care about the path existing on disk
|
||||
* or not without support for symbolic links.
|
||||
*/
|
||||
export async function existsFile(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(path);
|
||||
|
||||
return stat.isFile() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out if the `path` exists and is a directory with support for
|
||||
* symlinks.
|
||||
*
|
||||
* Note: this will return `false` for a symlink that exists on
|
||||
* disk but is dangling (pointing to a non-existing path).
|
||||
*
|
||||
* Use `exists` if you only care about the path existing on disk
|
||||
* or not without support for symbolic links.
|
||||
*/
|
||||
export async function existsDirectory(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(path);
|
||||
|
||||
return stat.isDirectory() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function lstat(path: string): Promise<fs.Stats> {
|
||||
return promisify(fs.lstat)(path);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
export function rename(oldPath: string, newPath: string): Promise<void> {
|
||||
return promisify(fs.rename)(oldPath, newPath);
|
||||
}
|
||||
|
||||
export function renameIgnoreError(oldPath: string, newPath: string): Promise<void> {
|
||||
return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve()));
|
||||
}
|
||||
|
||||
export function readlink(path: string): Promise<string> {
|
||||
return promisify(fs.readlink)(path);
|
||||
}
|
||||
|
||||
export function unlink(path: string): Promise<void> {
|
||||
return promisify(fs.unlink)(path);
|
||||
}
|
||||
|
||||
export function symlink(target: string, path: string, type?: string): Promise<void> {
|
||||
return promisify(fs.symlink)(target, path, type);
|
||||
}
|
||||
|
||||
export function truncate(path: string, len: number): Promise<void> {
|
||||
return promisify(fs.truncate)(path, len);
|
||||
}
|
||||
|
||||
export function readFile(path: string): Promise<Buffer>;
|
||||
export function readFile(path: string, encoding: string): Promise<string>;
|
||||
export function readFile(path: string, encoding?: string): Promise<Buffer | string> {
|
||||
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.
|
||||
const writeFilePathQueues: Map<string, Queue<void>> = new Map();
|
||||
//#region Write File
|
||||
|
||||
/**
|
||||
* Same as `fs.writeFile` but with an additional call to
|
||||
* `fs.fdatasync` after writing to ensure changes are
|
||||
* flushed to disk.
|
||||
*
|
||||
* In addition, multiple writes to the same path are queued.
|
||||
*/
|
||||
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>;
|
||||
@@ -311,6 +373,11 @@ export function writeFile(path: string, data: string | Buffer | Uint8Array, opti
|
||||
});
|
||||
}
|
||||
|
||||
// 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.
|
||||
const writeFilePathQueues: Map<string, Queue<void>> = new Map();
|
||||
|
||||
function toQueueKey(path: string): string {
|
||||
let queueKey = path;
|
||||
if (isWindows || isMacintosh) {
|
||||
@@ -388,6 +455,11 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `fs.writeFileSync` but with an additional call to
|
||||
* `fs.fdatasyncSync` after writing to ensure changes are
|
||||
* flushed to disk.
|
||||
*/
|
||||
export function writeFileSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void {
|
||||
const ensuredOptions = ensureWriteOptions(options);
|
||||
|
||||
@@ -426,87 +498,48 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio
|
||||
};
|
||||
}
|
||||
|
||||
export async function readDirsInDir(dirPath: string): Promise<string[]> {
|
||||
const children = await readdir(dirPath);
|
||||
const directories: string[] = [];
|
||||
//#endregion
|
||||
|
||||
for (const child of children) {
|
||||
if (await dirExists(join(dirPath, child))) {
|
||||
directories.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
export async function dirExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return stat.isDirectory() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function fileExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return stat.isFile() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function whenDeleted(path: string): Promise<void> {
|
||||
|
||||
// Complete when wait marker file is deleted
|
||||
return new Promise<void>(resolve => {
|
||||
let running = false;
|
||||
const interval = setInterval(() => {
|
||||
if (!running) {
|
||||
running = true;
|
||||
fs.exists(path, exists => {
|
||||
running = false;
|
||||
|
||||
if (!exists) {
|
||||
clearInterval(interval);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
//#region Move / Copy
|
||||
|
||||
/**
|
||||
* A drop-in replacement for `fs.rename` that:
|
||||
* - updates the `mtime` of the `source` after the operation
|
||||
* - allows to move across multiple disks
|
||||
*/
|
||||
export async function move(source: string, target: string): Promise<void> {
|
||||
if (source === target) {
|
||||
return;
|
||||
return; // simulate node.js behaviour here and do a no-op if paths match
|
||||
}
|
||||
|
||||
// We have been updating `mtime` for move operations for files since the
|
||||
// beginning for reasons that are no longer quite clear, but changing
|
||||
// this could be risky as well. As such, trying to reason about it:
|
||||
// It is very common as developer to have file watchers enabled that watch
|
||||
// the current workspace for changes. Updating the `mtime` might make it
|
||||
// easier for these watchers to recognize an actual change. Since changing
|
||||
// a source code file also updates the `mtime`, moving a file should do so
|
||||
// as well because conceptually it is a change of a similar category.
|
||||
async function updateMtime(path: string): Promise<void> {
|
||||
const stat = await lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
const fd = await promisify(fs.open)(path, 'a');
|
||||
try {
|
||||
await promisify(fs.futimes)(fd, stat.atime, new Date());
|
||||
} catch (error) {
|
||||
//ignore
|
||||
}
|
||||
const stat = await fs.promises.lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
return promisify(fs.close)(fd);
|
||||
const fh = await fs.promises.open(path, 'a');
|
||||
try {
|
||||
await fh.utimes(stat.atime, new Date());
|
||||
} finally {
|
||||
await fh.close();
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore any error
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await rename(source, target);
|
||||
await fs.promises.rename(source, target);
|
||||
await updateMtime(target);
|
||||
} catch (error) {
|
||||
|
||||
@@ -519,7 +552,7 @@ export async function move(source: string, target: string): Promise<void> {
|
||||
// 2.) The user tries to rename a file/folder that ends with a dot. This is not
|
||||
// really possible to move then, at least on UNC devices.
|
||||
if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || source.endsWith('.')) {
|
||||
await copy(source, target);
|
||||
await copy(source, target, { preserveSymlinks: false /* copying to another device */ });
|
||||
await rimraf(source, RimRafMode.MOVE);
|
||||
await updateMtime(target);
|
||||
} else {
|
||||
@@ -528,74 +561,119 @@ export async function move(source: string, target: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
interface ICopyPayload {
|
||||
readonly root: { source: string, target: string };
|
||||
readonly options: { preserveSymlinks: boolean };
|
||||
readonly handledSourcePaths: Set<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copies all of `source` to `target`.
|
||||
*
|
||||
* The options `preserveSymlinks` configures how symbolic
|
||||
* links should be handled when encountered. Set to
|
||||
* `false` to not preserve them and `true` otherwise.
|
||||
*/
|
||||
export async function copy(source: string, target: string, options: { preserveSymlinks: boolean }): Promise<void> {
|
||||
return doCopy(source, target, { root: { source, target }, options, handledSourcePaths: new Set<string>() });
|
||||
}
|
||||
|
||||
// When copying a file or folder, we want to preserve the mode
|
||||
// it had and as such provide it when creating. However, modes
|
||||
// can go beyond what we expect (see link below), so we mask it.
|
||||
// (https://github.com/nodejs/node-v0.x-archive/issues/3045#issuecomment-4862588)
|
||||
//
|
||||
// The `copy` method is very old so we should probably revisit
|
||||
// it's implementation and check wether this mask is still needed.
|
||||
const COPY_MODE_MASK = 0o777;
|
||||
|
||||
export async function copy(source: string, target: string, handledSourcesIn?: { [path: string]: boolean }): Promise<void> {
|
||||
async function doCopy(source: string, target: string, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Keep track of paths already copied to prevent
|
||||
// cycles from symbolic links to cause issues
|
||||
const handledSources = handledSourcesIn ?? Object.create(null);
|
||||
if (handledSources[source]) {
|
||||
if (payload.handledSourcePaths.has(source)) {
|
||||
return;
|
||||
} else {
|
||||
handledSources[source] = true;
|
||||
payload.handledSourcePaths.add(source);
|
||||
}
|
||||
|
||||
const { stat, symbolicLink } = await statLink(source);
|
||||
if (symbolicLink?.dangling) {
|
||||
return; // skip over dangling symbolic links (https://github.com/microsoft/vscode/issues/111621)
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(source);
|
||||
|
||||
// Symlink
|
||||
if (symbolicLink) {
|
||||
if (symbolicLink.dangling) {
|
||||
return; // do not copy dangling symbolic links (https://github.com/microsoft/vscode/issues/111621)
|
||||
}
|
||||
|
||||
// Try to re-create the symlink unless `preserveSymlinks: false`
|
||||
if (payload.options.preserveSymlinks) {
|
||||
try {
|
||||
return await doCopySymlink(source, target, payload);
|
||||
} catch (error) {
|
||||
// in any case of an error fallback to normal copy via dereferencing
|
||||
console.warn('[node.js fs] copy of symlink failed: ', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stat.isDirectory()) {
|
||||
// Folder
|
||||
if (stat.isDirectory()) {
|
||||
return doCopyDirectory(source, target, stat.mode & COPY_MODE_MASK, payload);
|
||||
}
|
||||
|
||||
// File or file-like
|
||||
else {
|
||||
return doCopyFile(source, target, stat.mode & COPY_MODE_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
async function doCopyDirectory(source: string, target: string, mode: number, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Create folder
|
||||
await mkdirp(target, stat.mode & COPY_MODE_MASK);
|
||||
await fs.promises.mkdir(target, { recursive: true, mode });
|
||||
|
||||
// Copy each file recursively
|
||||
const files = await readdir(source);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
await copy(join(source, file), join(target, file), handledSources);
|
||||
for (const file of files) {
|
||||
await doCopy(join(source, file), join(target, file), payload);
|
||||
}
|
||||
}
|
||||
|
||||
async function doCopyFile(source: string, target: string, mode: number): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = fs.createReadStream(source);
|
||||
const writer = fs.createWriteStream(target, { mode });
|
||||
|
||||
let finished = false;
|
||||
const finish = (error?: Error) => {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
// Copy file
|
||||
await fs.promises.copyFile(source, target);
|
||||
|
||||
// in error cases, pass to callback
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// we need to explicitly chmod because of https://github.com/nodejs/node/issues/1104
|
||||
fs.chmod(target, mode, error => error ? reject(error) : resolve());
|
||||
}
|
||||
};
|
||||
|
||||
// handle errors properly
|
||||
reader.once('error', error => finish(error));
|
||||
writer.once('error', error => finish(error));
|
||||
|
||||
// we are done (underlying fd has been closed)
|
||||
writer.once('close', () => finish());
|
||||
|
||||
// start piping
|
||||
reader.pipe(writer);
|
||||
});
|
||||
// restore mode (https://github.com/nodejs/node/issues/1104)
|
||||
await fs.promises.chmod(target, mode);
|
||||
}
|
||||
|
||||
async function doCopySymlink(source: string, target: string, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Figure out link target
|
||||
let linkTarget = await fs.promises.readlink(source);
|
||||
|
||||
// Special case: the symlink points to a target that is
|
||||
// actually within the path that is being copied. In that
|
||||
// case we want the symlink to point to the target and
|
||||
// not the source
|
||||
if (isEqualOrParent(linkTarget, payload.root.source, !isLinux)) {
|
||||
linkTarget = join(payload.root.target, linkTarget.substr(payload.root.source.length + 1));
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
await fs.promises.symlink(linkTarget, target);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Async FS Methods
|
||||
|
||||
export async function exists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.promises.access(path);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -38,7 +38,7 @@ class PossiblePowerShellExe implements IPossiblePowerShellExe {
|
||||
|
||||
public async exists(): Promise<boolean> {
|
||||
if (this.knownToExist === undefined) {
|
||||
this.knownToExist = await pfs.fileExists(this.exePath);
|
||||
this.knownToExist = await pfs.SymlinkSupport.existsFile(this.exePath);
|
||||
}
|
||||
return this.knownToExist;
|
||||
}
|
||||
@@ -100,7 +100,7 @@ async function findPSCoreWindowsInstallation(
|
||||
const powerShellInstallBaseDir = path.join(programFilesPath, 'PowerShell');
|
||||
|
||||
// Ensure the base directory exists
|
||||
if (!await pfs.dirExists(powerShellInstallBaseDir)) {
|
||||
if (!await pfs.SymlinkSupport.existsDirectory(powerShellInstallBaseDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ async function findPSCoreWindowsInstallation(
|
||||
|
||||
// Now look for the file
|
||||
const exePath = path.join(powerShellInstallBaseDir, item, 'pwsh.exe');
|
||||
if (!await pfs.fileExists(exePath)) {
|
||||
if (!await pfs.SymlinkSupport.existsFile(exePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ async function findPSCoreMsix({ findPreview }: { findPreview?: boolean } = {}):
|
||||
// Find the base directory for MSIX application exe shortcuts
|
||||
const msixAppDir = path.join(env.LOCALAPPDATA, 'Microsoft', 'WindowsApps');
|
||||
|
||||
if (!await pfs.dirExists(msixAppDir)) {
|
||||
if (!await pfs.SymlinkSupport.existsDirectory(msixAppDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as cp from 'child_process';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
@@ -456,8 +456,8 @@ export namespace win32 {
|
||||
}
|
||||
|
||||
async function fileExists(path: string): Promise<boolean> {
|
||||
if (await promisify(fs.exists)(path)) {
|
||||
return !((await promisify(fs.stat)(path)).isDirectory());
|
||||
if (await pfs.exists(path)) {
|
||||
return !((await fs.promises.stat(path)).isDirectory());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { createWriteStream, WriteStream } from 'fs';
|
||||
import { promises, createWriteStream, WriteStream } from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import { Sequencer, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { mkdirp, rimraf } from 'vs/base/node/pfs';
|
||||
import { rimraf } from 'vs/base/node/pfs';
|
||||
import { open as _openZip, Entry, ZipFile } from 'yauzl';
|
||||
import * as yazl from 'yazl';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
@@ -86,7 +86,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.resolve(mkdirp(targetDirName)).then(() => new Promise<void>((c, e) => {
|
||||
return Promise.resolve(promises.mkdir(targetDirName, { recursive: true })).then(() => new Promise<void>((c, e) => {
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok
|
||||
// directory file names end with '/'
|
||||
if (/\/$/.test(fileName)) {
|
||||
const targetFileName = path.join(targetPath, fileName);
|
||||
last = createCancelablePromise(token => mkdirp(targetFileName).then(() => readNextEntry(token)).then(undefined, e));
|
||||
last = createCancelablePromise(token => promises.mkdir(targetFileName, { recursive: true }).then(() => readNextEntry(token)).then(undefined, e));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { Database, Statement } from 'vscode-sqlite3';
|
||||
import { promises } from 'fs';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { mapToString, setToString } from 'vs/base/common/map';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs';
|
||||
import { copy } from 'vs/base/node/pfs';
|
||||
import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage';
|
||||
|
||||
interface IDatabaseConnection {
|
||||
@@ -186,7 +187,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
||||
// Delete the existing DB. If the path does not exist or fails to
|
||||
// be deleted, we do not try to recover anymore because we assume
|
||||
// that the path is no longer writeable for us.
|
||||
return unlink(this.path).then(() => {
|
||||
return promises.unlink(this.path).then(() => {
|
||||
|
||||
// Re-open the DB fresh
|
||||
return this.doConnect(this.path).then(recoveryConnection => {
|
||||
@@ -216,7 +217,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
||||
private backup(): Promise<void> {
|
||||
const backupPath = this.toBackupPath(this.path);
|
||||
|
||||
return copy(this.path, backupPath);
|
||||
return copy(this.path, backupPath, { preserveSymlinks: false });
|
||||
}
|
||||
|
||||
private toBackupPath(path: string): string {
|
||||
@@ -272,8 +273,12 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
||||
// folder is really not writeable for us.
|
||||
//
|
||||
try {
|
||||
await unlink(path);
|
||||
await renameIgnoreError(this.toBackupPath(path), path);
|
||||
await promises.unlink(path);
|
||||
try {
|
||||
await promises.rename(this.toBackupPath(path), path);
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return await this.doConnect(path);
|
||||
} catch (error) {
|
||||
|
||||
@@ -7,8 +7,9 @@ import { SQLiteStorageDatabase, ISQLiteStorageDatabaseOptions } from 'vs/base/pa
|
||||
import { Storage, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { tmpdir } from 'os';
|
||||
import { promises } from 'fs';
|
||||
import { strictEqual, ok } from 'assert';
|
||||
import { mkdirp, writeFile, exists, unlink, rimraf } from 'vs/base/node/pfs';
|
||||
import { writeFile, exists, rimraf } from 'vs/base/node/pfs';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
@@ -22,7 +23,7 @@ flakySuite('Storage Library', function () {
|
||||
setup(function () {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storagelibrary');
|
||||
|
||||
return mkdirp(testDir);
|
||||
return promises.mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
@@ -294,7 +295,7 @@ flakySuite('SQLite Storage Library', function () {
|
||||
setup(function () {
|
||||
testdir = getRandomTestPath(tmpdir(), 'vsctests', 'storagelibrary');
|
||||
|
||||
return mkdirp(testdir);
|
||||
return promises.mkdir(testdir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
@@ -475,7 +476,7 @@ flakySuite('SQLite Storage Library', function () {
|
||||
// on shutdown.
|
||||
await storage.checkIntegrity(true).then(null, error => { } /* error is expected here but we do not want to fail */);
|
||||
|
||||
await unlink(backupPath); // also test that the recovery DB is backed up properly
|
||||
await promises.unlink(backupPath); // also test that the recovery DB is backed up properly
|
||||
|
||||
let recoveryCalled = false;
|
||||
await storage.close(() => {
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import { checksum } from 'vs/base/node/crypto';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { tmpdir } from 'os';
|
||||
import { mkdirp, rimraf, writeFile } from 'vs/base/node/pfs';
|
||||
import { promises } from 'fs';
|
||||
import { rimraf, writeFile } from 'vs/base/node/pfs';
|
||||
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
|
||||
suite('Crypto', () => {
|
||||
@@ -16,7 +17,7 @@ suite('Crypto', () => {
|
||||
setup(function () {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'crypto');
|
||||
|
||||
return mkdirp(testDir);
|
||||
return promises.mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { tmpdir } from 'os';
|
||||
import { mkdirp, rimraf } from 'vs/base/node/pfs';
|
||||
import { promises } from 'fs';
|
||||
import { rimraf } from 'vs/base/node/pfs';
|
||||
import { realcaseSync, realpath, realpathSync } from 'vs/base/node/extpath';
|
||||
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
|
||||
@@ -15,7 +16,7 @@ flakySuite('Extpath', () => {
|
||||
setup(() => {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'extpath');
|
||||
|
||||
return mkdirp(testDir, 493);
|
||||
return promises.mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
||||
@@ -8,12 +8,13 @@ import * as fs from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { join, sep } from 'vs/base/common/path';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { copy, exists, mkdirp, move, readdir, readDirsInDir, readdirWithFileTypes, readFile, renameIgnoreError, rimraf, RimRafMode, rimrafSync, statLink, writeFile, writeFileSync } from 'vs/base/node/pfs';
|
||||
import { copy, exists, move, readdir, readDirsInDir, rimraf, RimRafMode, rimrafSync, SymlinkSupport, writeFile, writeFileSync } from 'vs/base/node/pfs';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { canNormalize } from 'vs/base/common/normalization';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
flakySuite('PFS', function () {
|
||||
|
||||
@@ -22,7 +23,7 @@ flakySuite('PFS', function () {
|
||||
setup(() => {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'pfs');
|
||||
|
||||
return mkdirp(testDir, 493);
|
||||
return fs.promises.mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
@@ -36,7 +37,7 @@ flakySuite('PFS', function () {
|
||||
|
||||
await writeFile(testFile, 'Hello World', (null!));
|
||||
|
||||
assert.strictEqual((await readFile(testFile)).toString(), 'Hello World');
|
||||
assert.strictEqual((await fs.promises.readFile(testFile)).toString(), 'Hello World');
|
||||
});
|
||||
|
||||
test('writeFile - parallel write on different files works', async () => {
|
||||
@@ -153,10 +154,6 @@ flakySuite('PFS', function () {
|
||||
assert.ok(!fs.existsSync(testDir));
|
||||
});
|
||||
|
||||
test('moveIgnoreError', () => {
|
||||
return renameIgnoreError(join(testDir, 'foo'), join(testDir, 'bar'));
|
||||
});
|
||||
|
||||
test('copy, move and delete', async () => {
|
||||
const id = generateUuid();
|
||||
const id2 = generateUuid();
|
||||
@@ -165,7 +162,7 @@ flakySuite('PFS', function () {
|
||||
const targetDir = join(parentDir, id);
|
||||
const targetDir2 = join(parentDir, id2);
|
||||
|
||||
await copy(sourceDir, targetDir);
|
||||
await copy(sourceDir, targetDir, { preserveSymlinks: true });
|
||||
|
||||
assert.ok(fs.existsSync(targetDir));
|
||||
assert.ok(fs.existsSync(join(targetDir, 'index.html')));
|
||||
@@ -194,33 +191,103 @@ flakySuite('PFS', function () {
|
||||
assert.ok(!fs.existsSync(parentDir));
|
||||
});
|
||||
|
||||
test('copy skips over dangling symbolic links', async () => {
|
||||
test('copy handles symbolic links', async () => {
|
||||
const id1 = generateUuid();
|
||||
const symbolicLinkTarget = join(testDir, id1);
|
||||
|
||||
const id2 = generateUuid();
|
||||
const symbolicLink = join(testDir, id2);
|
||||
const symLink = join(testDir, id2);
|
||||
|
||||
const id3 = generateUuid();
|
||||
const copyTarget = join(testDir, id3);
|
||||
|
||||
await mkdirp(symbolicLinkTarget, 493);
|
||||
await fs.promises.mkdir(symbolicLinkTarget, { recursive: true });
|
||||
|
||||
fs.symlinkSync(symbolicLinkTarget, symbolicLink, 'junction');
|
||||
fs.symlinkSync(symbolicLinkTarget, symLink, 'junction');
|
||||
|
||||
// Copy preserves symlinks if configured as such
|
||||
//
|
||||
// Windows: this test does not work because creating symlinks
|
||||
// requires priviledged permissions (admin).
|
||||
if (!isWindows) {
|
||||
await copy(symLink, copyTarget, { preserveSymlinks: true });
|
||||
|
||||
assert.ok(fs.existsSync(copyTarget));
|
||||
|
||||
const { symbolicLink } = await SymlinkSupport.stat(copyTarget);
|
||||
assert.ok(symbolicLink);
|
||||
assert.ok(!symbolicLink.dangling);
|
||||
|
||||
const target = await fs.promises.readlink(copyTarget);
|
||||
assert.strictEqual(target, symbolicLinkTarget);
|
||||
|
||||
// Copy does not preserve symlinks if configured as such
|
||||
|
||||
await rimraf(copyTarget);
|
||||
await copy(symLink, copyTarget, { preserveSymlinks: false });
|
||||
|
||||
assert.ok(fs.existsSync(copyTarget));
|
||||
|
||||
const { symbolicLink: symbolicLink2 } = await SymlinkSupport.stat(copyTarget);
|
||||
assert.ok(!symbolicLink2);
|
||||
}
|
||||
|
||||
// Copy ignores dangling symlinks
|
||||
|
||||
await rimraf(copyTarget);
|
||||
await rimraf(symbolicLinkTarget);
|
||||
|
||||
await copy(symbolicLink, copyTarget); // this should not throw
|
||||
await copy(symLink, copyTarget, { preserveSymlinks: true }); // this should not throw
|
||||
|
||||
assert.ok(!fs.existsSync(copyTarget));
|
||||
});
|
||||
|
||||
test('mkdirp', async () => {
|
||||
const newDir = join(testDir, generateUuid());
|
||||
test('copy handles symbolic links when the reference is inside source', async () => {
|
||||
|
||||
await mkdirp(newDir, 493);
|
||||
// Source Folder
|
||||
const sourceFolder = join(testDir, generateUuid(), 'copy-test'); // copy-test
|
||||
const sourceLinkTestFolder = join(sourceFolder, 'link-test'); // copy-test/link-test
|
||||
const sourceLinkMD5JSFolder = join(sourceLinkTestFolder, 'md5'); // copy-test/link-test/md5
|
||||
const sourceLinkMD5JSFile = join(sourceLinkMD5JSFolder, 'md5.js'); // copy-test/link-test/md5/md5.js
|
||||
await fs.promises.mkdir(sourceLinkMD5JSFolder, { recursive: true });
|
||||
await writeFile(sourceLinkMD5JSFile, 'Hello from MD5');
|
||||
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
const sourceLinkMD5JSFolderLinked = join(sourceLinkTestFolder, 'md5-linked'); // copy-test/link-test/md5-linked
|
||||
fs.symlinkSync(sourceLinkMD5JSFolder, sourceLinkMD5JSFolderLinked, 'junction');
|
||||
|
||||
// Target Folder
|
||||
const targetLinkTestFolder = join(sourceFolder, 'link-test copy'); // copy-test/link-test copy
|
||||
const targetLinkMD5JSFolder = join(targetLinkTestFolder, 'md5'); // copy-test/link-test copy/md5
|
||||
const targetLinkMD5JSFile = join(targetLinkMD5JSFolder, 'md5.js'); // copy-test/link-test copy/md5/md5.js
|
||||
const targetLinkMD5JSFolderLinked = join(targetLinkTestFolder, 'md5-linked'); // copy-test/link-test copy/md5-linked
|
||||
|
||||
// Copy with `preserveSymlinks: true` and verify result
|
||||
//
|
||||
// Windows: this test does not work because creating symlinks
|
||||
// requires priviledged permissions (admin).
|
||||
if (!isWindows) {
|
||||
await copy(sourceLinkTestFolder, targetLinkTestFolder, { preserveSymlinks: true });
|
||||
|
||||
assert.ok(fs.existsSync(targetLinkTestFolder));
|
||||
assert.ok(fs.existsSync(targetLinkMD5JSFolder));
|
||||
assert.ok(fs.existsSync(targetLinkMD5JSFile));
|
||||
assert.ok(fs.existsSync(targetLinkMD5JSFolderLinked));
|
||||
assert.ok(fs.lstatSync(targetLinkMD5JSFolderLinked).isSymbolicLink());
|
||||
|
||||
const linkTarget = await fs.promises.readlink(targetLinkMD5JSFolderLinked);
|
||||
assert.strictEqual(linkTarget, targetLinkMD5JSFolder);
|
||||
|
||||
await fs.promises.rmdir(targetLinkTestFolder, { recursive: true });
|
||||
}
|
||||
|
||||
// Copy with `preserveSymlinks: false` and verify result
|
||||
await copy(sourceLinkTestFolder, targetLinkTestFolder, { preserveSymlinks: false });
|
||||
|
||||
assert.ok(fs.existsSync(targetLinkTestFolder));
|
||||
assert.ok(fs.existsSync(targetLinkMD5JSFolder));
|
||||
assert.ok(fs.existsSync(targetLinkMD5JSFile));
|
||||
assert.ok(fs.existsSync(targetLinkMD5JSFolderLinked));
|
||||
assert.ok(fs.lstatSync(targetLinkMD5JSFolderLinked).isDirectory());
|
||||
});
|
||||
|
||||
test('readDirsInDir', async () => {
|
||||
@@ -244,14 +311,14 @@ flakySuite('PFS', function () {
|
||||
const id2 = generateUuid();
|
||||
const symbolicLink = join(testDir, id2);
|
||||
|
||||
await mkdirp(directory, 493);
|
||||
await fs.promises.mkdir(directory, { recursive: true });
|
||||
|
||||
fs.symlinkSync(directory, symbolicLink, 'junction');
|
||||
|
||||
let statAndIsLink = await statLink(directory);
|
||||
let statAndIsLink = await SymlinkSupport.stat(directory);
|
||||
assert.ok(!statAndIsLink?.symbolicLink);
|
||||
|
||||
statAndIsLink = await statLink(symbolicLink);
|
||||
statAndIsLink = await SymlinkSupport.stat(symbolicLink);
|
||||
assert.ok(statAndIsLink?.symbolicLink);
|
||||
assert.ok(!statAndIsLink?.symbolicLink?.dangling);
|
||||
});
|
||||
@@ -263,13 +330,13 @@ flakySuite('PFS', function () {
|
||||
const id2 = generateUuid();
|
||||
const symbolicLink = join(testDir, id2);
|
||||
|
||||
await mkdirp(directory, 493);
|
||||
await fs.promises.mkdir(directory, { recursive: true });
|
||||
|
||||
fs.symlinkSync(directory, symbolicLink, 'junction');
|
||||
|
||||
await rimraf(directory);
|
||||
|
||||
const statAndIsLink = await statLink(symbolicLink);
|
||||
const statAndIsLink = await SymlinkSupport.stat(symbolicLink);
|
||||
assert.ok(statAndIsLink?.symbolicLink);
|
||||
assert.ok(statAndIsLink?.symbolicLink?.dangling);
|
||||
});
|
||||
@@ -279,7 +346,7 @@ flakySuite('PFS', function () {
|
||||
const id = generateUuid();
|
||||
const newDir = join(testDir, 'pfs', id, 'öäü');
|
||||
|
||||
await mkdirp(newDir, 493);
|
||||
await fs.promises.mkdir(newDir, { recursive: true });
|
||||
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
|
||||
@@ -288,16 +355,16 @@ flakySuite('PFS', function () {
|
||||
}
|
||||
});
|
||||
|
||||
test('readdirWithFileTypes', async () => {
|
||||
test('readdir (with file types)', async () => {
|
||||
if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) {
|
||||
const newDir = join(testDir, 'öäü');
|
||||
await mkdirp(newDir, 493);
|
||||
await fs.promises.mkdir(newDir, { recursive: true });
|
||||
|
||||
await writeFile(join(testDir, 'somefile.txt'), 'contents');
|
||||
|
||||
assert.ok(fs.existsSync(newDir));
|
||||
|
||||
const children = await readdirWithFileTypes(testDir);
|
||||
const children = await readdir(testDir, { withFileTypes: true });
|
||||
|
||||
assert.strictEqual(children.some(n => n.name === 'öäü'), true); // Mac always converts to NFD, so
|
||||
assert.strictEqual(children.some(n => n.isDirectory()), true);
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { tmpdir } from 'os';
|
||||
import { promises } from 'fs';
|
||||
import { extract } from 'vs/base/node/zip';
|
||||
import { rimraf, exists, mkdirp } from 'vs/base/node/pfs';
|
||||
import { rimraf, exists } from 'vs/base/node/pfs';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { createCancelablePromise } from 'vs/base/common/async';
|
||||
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
@@ -19,7 +20,7 @@ suite('Zip', () => {
|
||||
setup(() => {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'zip');
|
||||
|
||||
return mkdirp(testDir);
|
||||
return promises.mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "vs",
|
||||
"dependencies": [
|
||||
],
|
||||
"libs": [
|
||||
"lib.core.d.ts"
|
||||
],
|
||||
"sources": [
|
||||
],
|
||||
"declares": [
|
||||
"vs/nls.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
@@ -52,7 +53,7 @@ export class LanguagePackCachedDataCleaner extends Disposable {
|
||||
: 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months
|
||||
try {
|
||||
const installed: IStringDictionary<boolean> = Object.create(null);
|
||||
const metaData: LanguagePackFile = JSON.parse(await pfs.readFile(path.join(this._environmentService.userDataPath, 'languagepacks.json'), 'utf8'));
|
||||
const metaData: LanguagePackFile = JSON.parse(await fs.promises.readFile(path.join(this._environmentService.userDataPath, 'languagepacks.json'), 'utf8'));
|
||||
for (let locale of Object.keys(metaData)) {
|
||||
const entry = metaData[locale];
|
||||
installed[`${entry.hash}.${locale}`] = true;
|
||||
@@ -80,7 +81,7 @@ export class LanguagePackCachedDataCleaner extends Disposable {
|
||||
continue;
|
||||
}
|
||||
const candidate = path.join(folder, entry);
|
||||
const stat = await pfs.stat(candidate);
|
||||
const stat = await fs.promises.stat(candidate);
|
||||
if (stat.isDirectory()) {
|
||||
const diff = now - stat.mtime.getTime();
|
||||
if (diff > maxAge) {
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises } from 'fs';
|
||||
import { basename, dirname, join } from 'vs/base/common/path';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { readdir, rimraf, stat } from 'vs/base/node/pfs';
|
||||
import { readdir, rimraf } from 'vs/base/node/pfs';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
|
||||
export class NodeCachedDataCleaner {
|
||||
@@ -54,7 +55,7 @@ export class NodeCachedDataCleaner {
|
||||
if (entry !== nodeCachedDataCurrent) {
|
||||
|
||||
const path = join(nodeCachedDataRootDir, entry);
|
||||
deletes.push(stat(path).then(stats => {
|
||||
deletes.push(promises.stat(path).then(stats => {
|
||||
// stat check
|
||||
// * only directories
|
||||
// * only when old enough
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises } from 'fs';
|
||||
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { readdir, readFile, rimraf } from 'vs/base/node/pfs';
|
||||
import { readdir, rimraf } from 'vs/base/node/pfs';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IBackupWorkspacesFormat } from 'vs/platform/backup/node/backup';
|
||||
@@ -32,7 +33,7 @@ export class StorageDataCleaner extends Disposable {
|
||||
try {
|
||||
// Leverage the backup workspace file to find out which empty workspace is currently in use to
|
||||
// determine which empty workspace storage can safely be deleted
|
||||
const contents = await readFile(this.backupWorkspacesPath, 'utf8');
|
||||
const contents = await promises.readFile(this.backupWorkspacesPath, 'utf8');
|
||||
|
||||
const workspaces = JSON.parse(contents) as IBackupWorkspacesFormat;
|
||||
const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder);
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
|
||||
import 'vs/platform/update/common/update.config.contribution';
|
||||
import { app, dialog } from 'electron';
|
||||
import { unlinkSync } from 'fs';
|
||||
import { promises, unlinkSync } from 'fs';
|
||||
import { localize } from 'vs/nls';
|
||||
import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { parseMainProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper';
|
||||
import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile';
|
||||
import { mkdirp } from 'vs/base/node/pfs';
|
||||
import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Server as NodeIPCServer, serve as nodeIPCServe, connect as nodeIPCConnect, XDG_RUNTIME_DIR } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
@@ -186,7 +185,7 @@ class CodeMain {
|
||||
environmentService.globalStorageHome.fsPath,
|
||||
environmentService.workspaceStorageHome.fsPath,
|
||||
environmentService.backupHome
|
||||
].map((path): undefined | Promise<void> => path ? mkdirp(path) : undefined));
|
||||
].map((path): undefined | Promise<void> => path ? promises.mkdir(path, { recursive: true }) : undefined));
|
||||
|
||||
// Configuration service
|
||||
const configurationServiceInitialization = configurationService.initialize();
|
||||
|
||||
@@ -28,7 +28,6 @@ import { RequestService } from 'vs/platform/request/node/requestService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
|
||||
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
|
||||
import { mkdirp, writeFile } from 'vs/base/node/pfs';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { StateService } from 'vs/platform/state/node/stateService';
|
||||
import { ILogService, getLogLevel, LogLevel, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
|
||||
@@ -46,6 +45,7 @@ import { LocalizationsService } from 'vs/platform/localizations/node/localizatio
|
||||
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
class CliMain extends Disposable {
|
||||
|
||||
@@ -73,6 +73,7 @@ class CliMain extends Disposable {
|
||||
|
||||
return instantiationService.invokeFunction(async accessor => {
|
||||
const logService = accessor.get(ILogService);
|
||||
const fileService = accessor.get(IFileService);
|
||||
const environmentService = accessor.get(INativeEnvironmentService);
|
||||
const extensionManagementCLIService = accessor.get(IExtensionManagementCLIService);
|
||||
|
||||
@@ -83,7 +84,7 @@ class CliMain extends Disposable {
|
||||
this.registerErrorHandler(logService);
|
||||
|
||||
// Run based on argv
|
||||
await this.doRun(environmentService, extensionManagementCLIService);
|
||||
await this.doRun(environmentService, extensionManagementCLIService, fileService);
|
||||
|
||||
// Flush the remaining data in AI adapter (with 1s timeout)
|
||||
return raceTimeout(combinedAppender(...appenders).flush(), 1000);
|
||||
@@ -99,7 +100,7 @@ class CliMain extends Disposable {
|
||||
services.set(INativeEnvironmentService, environmentService);
|
||||
|
||||
// Init folders
|
||||
await Promise.all([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath].map(path => path ? mkdirp(path) : undefined));
|
||||
await Promise.all([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath].map(path => path ? fs.promises.mkdir(path, { recursive: true }) : undefined));
|
||||
|
||||
// Log
|
||||
const logLevel = getLogLevel(environmentService);
|
||||
@@ -182,11 +183,11 @@ class CliMain extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private async doRun(environmentService: INativeEnvironmentService, extensionManagementCLIService: IExtensionManagementCLIService): Promise<void> {
|
||||
private async doRun(environmentService: INativeEnvironmentService, extensionManagementCLIService: IExtensionManagementCLIService, fileService: IFileService): Promise<void> {
|
||||
|
||||
// Install Source
|
||||
if (this.argv['install-source']) {
|
||||
return this.setInstallSource(environmentService, this.argv['install-source']);
|
||||
return this.setInstallSource(environmentService, fileService, this.argv['install-source']);
|
||||
}
|
||||
|
||||
// List Extensions
|
||||
@@ -219,8 +220,8 @@ class CliMain extends Disposable {
|
||||
return inputs.map(input => /\.vsix$/i.test(input) ? URI.file(isAbsolute(input) ? input : join(process.cwd(), input)) : input);
|
||||
}
|
||||
|
||||
private setInstallSource(environmentService: INativeEnvironmentService, installSource: string): Promise<void> {
|
||||
return writeFile(environmentService.installSourcePath, installSource.slice(0, 30));
|
||||
private async setInstallSource(environmentService: INativeEnvironmentService, fileService: IFileService, installSource: string): Promise<void> {
|
||||
await fileService.writeFile(URI.file(environmentService.installSourcePath), VSBuffer.fromString(installSource.slice(0, 30)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, RimRafMode } from 'vs/base/node/pfs';
|
||||
import { createHash } from 'crypto';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { writeFileSync, writeFile, readdir, exists, rimraf, RimRafMode } from 'vs/base/node/pfs';
|
||||
import { IBackupMainService, IWorkspaceBackupInfo, isWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
|
||||
import { IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
@@ -35,7 +35,7 @@ export class BackupMainService implements IBackupMainService {
|
||||
// - ignore path casing on Windows/macOS
|
||||
// - respect path casing on Linux
|
||||
private readonly backupUriComparer = extUriBiasedIgnorePathCase;
|
||||
private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !platform.isLinux) };
|
||||
private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !isLinux) };
|
||||
|
||||
constructor(
|
||||
@IEnvironmentMainService environmentService: IEnvironmentMainService,
|
||||
@@ -49,7 +49,7 @@ export class BackupMainService implements IBackupMainService {
|
||||
async initialize(): Promise<void> {
|
||||
let backups: IBackupWorkspacesFormat;
|
||||
try {
|
||||
backups = JSON.parse(await readFile(this.workspacesJsonPath, 'utf8')); // invalid JSON or permission issue can happen here
|
||||
backups = JSON.parse(await fs.promises.readFile(this.workspacesJsonPath, 'utf8')); // invalid JSON or permission issue can happen here
|
||||
} catch (error) {
|
||||
backups = Object.create(null);
|
||||
}
|
||||
@@ -151,8 +151,8 @@ export class BackupMainService implements IBackupMainService {
|
||||
if (fs.existsSync(moveFromPath)) {
|
||||
try {
|
||||
fs.renameSync(moveFromPath, backupPath);
|
||||
} catch (ex) {
|
||||
this.logService.error(`Backup: Could not move backup folder to new location: ${ex.toString()}`);
|
||||
} catch (error) {
|
||||
this.logService.error(`Backup: Could not move backup folder to new location: ${error.toString()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ export class BackupMainService implements IBackupMainService {
|
||||
}
|
||||
|
||||
private getBackupPath(oldFolderHash: string): string {
|
||||
return path.join(this.backupHome, oldFolderHash);
|
||||
return join(this.backupHome, oldFolderHash);
|
||||
}
|
||||
|
||||
private async validateWorkspaces(rootWorkspaces: IWorkspaceBackupInfo[]): Promise<IWorkspaceBackupInfo[]> {
|
||||
@@ -312,8 +312,8 @@ export class BackupMainService implements IBackupMainService {
|
||||
if (await exists(backupPath)) {
|
||||
await rimraf(backupPath, RimRafMode.MOVE);
|
||||
}
|
||||
} catch (ex) {
|
||||
this.logService.error(`Backup: Could not delete stale backup: ${ex.toString()}`);
|
||||
} catch (error) {
|
||||
this.logService.error(`Backup: Could not delete stale backup: ${error.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,9 +328,9 @@ export class BackupMainService implements IBackupMainService {
|
||||
// Rename backupPath to new empty window backup path
|
||||
const newEmptyWindowBackupPath = this.getBackupPath(newBackupFolder);
|
||||
try {
|
||||
await rename(backupPath, newEmptyWindowBackupPath);
|
||||
} catch (ex) {
|
||||
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
|
||||
await fs.promises.rename(backupPath, newEmptyWindowBackupPath);
|
||||
} catch (error) {
|
||||
this.logService.error(`Backup: Could not rename backup folder: ${error.toString()}`);
|
||||
return false;
|
||||
}
|
||||
this.emptyWindows.push({ backupFolder: newBackupFolder });
|
||||
@@ -350,8 +350,8 @@ export class BackupMainService implements IBackupMainService {
|
||||
const newEmptyWindowBackupPath = this.getBackupPath(newBackupFolder);
|
||||
try {
|
||||
fs.renameSync(backupPath, newEmptyWindowBackupPath);
|
||||
} catch (ex) {
|
||||
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
|
||||
} catch (error) {
|
||||
this.logService.error(`Backup: Could not rename backup folder: ${error.toString()}`);
|
||||
return false;
|
||||
}
|
||||
this.emptyWindows.push({ backupFolder: newBackupFolder });
|
||||
@@ -406,7 +406,7 @@ export class BackupMainService implements IBackupMainService {
|
||||
|
||||
for (const backupSchema of backupSchemas) {
|
||||
try {
|
||||
const backupSchemaChildren = await readdir(path.join(backupPath, backupSchema));
|
||||
const backupSchemaChildren = await readdir(join(backupPath, backupSchema));
|
||||
if (backupSchemaChildren.length > 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -424,16 +424,16 @@ export class BackupMainService implements IBackupMainService {
|
||||
private saveSync(): void {
|
||||
try {
|
||||
writeFileSync(this.workspacesJsonPath, JSON.stringify(this.serializeBackups()));
|
||||
} catch (ex) {
|
||||
this.logService.error(`Backup: Could not save workspaces.json: ${ex.toString()}`);
|
||||
} catch (error) {
|
||||
this.logService.error(`Backup: Could not save workspaces.json: ${error.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async save(): Promise<void> {
|
||||
try {
|
||||
await writeFile(this.workspacesJsonPath, JSON.stringify(this.serializeBackups()));
|
||||
} catch (ex) {
|
||||
this.logService.error(`Backup: Could not save workspaces.json: ${ex.toString()}`);
|
||||
} catch (error) {
|
||||
this.logService.error(`Backup: Could not save workspaces.json: ${error.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,11 +454,11 @@ export class BackupMainService implements IBackupMainService {
|
||||
|
||||
if (folderUri.scheme === Schemas.file) {
|
||||
// for backward compatibility, use the fspath as key
|
||||
key = platform.isLinux ? folderUri.fsPath : folderUri.fsPath.toLowerCase();
|
||||
key = isLinux ? folderUri.fsPath : folderUri.fsPath.toLowerCase();
|
||||
} else {
|
||||
key = folderUri.toString().toLowerCase();
|
||||
}
|
||||
|
||||
return crypto.createHash('md5').update(key).digest('hex');
|
||||
return createHash('md5').update(key).digest('hex');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ flakySuite('BackupMainService', () => {
|
||||
|
||||
environmentService = new EnvironmentMainService(parseArgs(process.argv, OPTIONS));
|
||||
|
||||
await pfs.mkdirp(backupHome);
|
||||
await fs.promises.mkdir(backupHome, { recursive: true });
|
||||
|
||||
configService = new TestConfigurationService();
|
||||
service = new class TestBackupMainService extends BackupMainService {
|
||||
@@ -445,7 +445,7 @@ flakySuite('BackupMainService', () => {
|
||||
await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson));
|
||||
await service.initialize();
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
|
||||
assert.deepStrictEqual(json.folderURIWorkspaces, [existingTestFolder1.toString()]);
|
||||
});
|
||||
@@ -461,7 +461,7 @@ flakySuite('BackupMainService', () => {
|
||||
};
|
||||
await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson));
|
||||
await service.initialize();
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
|
||||
assert.deepStrictEqual(json.folderURIWorkspaces, [existingTestFolder1.toString()]);
|
||||
});
|
||||
@@ -483,7 +483,7 @@ flakySuite('BackupMainService', () => {
|
||||
await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson));
|
||||
await service.initialize();
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
|
||||
assert.strictEqual(json.rootURIWorkspaces.length, platform.isLinux ? 3 : 1);
|
||||
if (platform.isLinux) {
|
||||
@@ -499,7 +499,7 @@ flakySuite('BackupMainService', () => {
|
||||
service.registerFolderBackupSync(fooFile);
|
||||
service.registerFolderBackupSync(barFile);
|
||||
assertEqualUris(service.getFolderBackupPaths(), [fooFile, barFile]);
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
|
||||
assert.deepStrictEqual(json.folderURIWorkspaces, [fooFile.toString(), barFile.toString()]);
|
||||
});
|
||||
@@ -514,7 +514,7 @@ flakySuite('BackupMainService', () => {
|
||||
assert.strictEqual(ws1.workspace.id, service.getWorkspaceBackups()[0].workspace.id);
|
||||
assert.strictEqual(ws2.workspace.id, service.getWorkspaceBackups()[1].workspace.id);
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
|
||||
|
||||
assert.deepStrictEqual(json.rootURIWorkspaces.map(b => b.configURIPath), [fooFile.toString(), barFile.toString()]);
|
||||
@@ -527,7 +527,7 @@ flakySuite('BackupMainService', () => {
|
||||
service.registerFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase()));
|
||||
assertEqualUris(service.getFolderBackupPaths(), [URI.file(fooFile.fsPath.toUpperCase())]);
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
|
||||
assert.deepStrictEqual(json.folderURIWorkspaces, [URI.file(fooFile.fsPath.toUpperCase()).toString()]);
|
||||
});
|
||||
@@ -537,7 +537,7 @@ flakySuite('BackupMainService', () => {
|
||||
service.registerWorkspaceBackupSync(toWorkspaceBackupInfo(upperFooPath));
|
||||
assertEqualUris(service.getWorkspaceBackups().map(b => b.workspace.configPath), [URI.file(upperFooPath)]);
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = (<IBackupWorkspacesFormat>JSON.parse(buffer));
|
||||
assert.deepStrictEqual(json.rootURIWorkspaces.map(b => b.configURIPath), [URI.file(upperFooPath).toString()]);
|
||||
});
|
||||
@@ -548,12 +548,12 @@ flakySuite('BackupMainService', () => {
|
||||
service.registerFolderBackupSync(barFile);
|
||||
service.unregisterFolderBackupSync(fooFile);
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = (<IBackupWorkspacesFormat>JSON.parse(buffer));
|
||||
assert.deepStrictEqual(json.folderURIWorkspaces, [barFile.toString()]);
|
||||
service.unregisterFolderBackupSync(barFile);
|
||||
|
||||
const content = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const content = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json2 = (<IBackupWorkspacesFormat>JSON.parse(content));
|
||||
assert.deepStrictEqual(json2.folderURIWorkspaces, []);
|
||||
});
|
||||
@@ -565,12 +565,12 @@ flakySuite('BackupMainService', () => {
|
||||
service.registerWorkspaceBackupSync(ws2);
|
||||
service.unregisterWorkspaceBackupSync(ws1.workspace);
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = (<IBackupWorkspacesFormat>JSON.parse(buffer));
|
||||
assert.deepStrictEqual(json.rootURIWorkspaces.map(r => r.configURIPath), [barFile.toString()]);
|
||||
service.unregisterWorkspaceBackupSync(ws2.workspace);
|
||||
|
||||
const content = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const content = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json2 = (<IBackupWorkspacesFormat>JSON.parse(content));
|
||||
assert.deepStrictEqual(json2.rootURIWorkspaces, []);
|
||||
});
|
||||
@@ -580,12 +580,12 @@ flakySuite('BackupMainService', () => {
|
||||
service.registerEmptyWindowBackupSync('bar');
|
||||
service.unregisterEmptyWindowBackupSync('foo');
|
||||
|
||||
const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const buffer = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = (<IBackupWorkspacesFormat>JSON.parse(buffer));
|
||||
assert.deepStrictEqual(json.emptyWorkspaceInfos, [{ backupFolder: 'bar' }]);
|
||||
service.unregisterEmptyWindowBackupSync('bar');
|
||||
|
||||
const content = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const content = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json2 = (<IBackupWorkspacesFormat>JSON.parse(content));
|
||||
assert.deepStrictEqual(json2.emptyWorkspaceInfos, []);
|
||||
});
|
||||
@@ -599,7 +599,7 @@ flakySuite('BackupMainService', () => {
|
||||
await service.initialize();
|
||||
service.unregisterFolderBackupSync(barFile);
|
||||
service.unregisterEmptyWindowBackupSync('test');
|
||||
const content = await pfs.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const content = await fs.promises.readFile(backupWorkspacesPath, 'utf-8');
|
||||
const json = (<IBackupWorkspacesFormat>JSON.parse(content));
|
||||
assert.deepStrictEqual(json.folderURIWorkspaces, [existingTestFolder1.toString()]);
|
||||
});
|
||||
@@ -669,8 +669,8 @@ flakySuite('BackupMainService', () => {
|
||||
assert.strictEqual(((await service.getDirtyWorkspaces()).length), 0);
|
||||
|
||||
try {
|
||||
await pfs.mkdirp(path.join(folderBackupPath, Schemas.file));
|
||||
await pfs.mkdirp(path.join(workspaceBackupPath, Schemas.untitled));
|
||||
await fs.promises.mkdir(path.join(folderBackupPath, Schemas.file), { recursive: true });
|
||||
await fs.promises.mkdir(path.join(workspaceBackupPath, Schemas.untitled), { recursive: true });
|
||||
} catch (error) {
|
||||
// ignore - folder might exist already
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import * as osLib from 'os';
|
||||
import { virtualMachineHint } from 'vs/base/node/id';
|
||||
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics';
|
||||
import { readdir, exists, readFile } from 'fs';
|
||||
import { exists, readFile } from 'fs';
|
||||
import { join, basename } from 'vs/base/common/path';
|
||||
import { parse, ParseError, getNodeType } from 'vs/base/common/json';
|
||||
import { listProcesses } from 'vs/base/node/ps';
|
||||
@@ -19,6 +19,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ByteSize } from 'vs/platform/files/common/files';
|
||||
import { IDirent, readdir } from 'vs/base/node/pfs';
|
||||
|
||||
export const ID = 'diagnosticsService';
|
||||
export const IDiagnosticsService = createDecorator<IDiagnosticsService>(ID);
|
||||
@@ -79,68 +80,69 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P
|
||||
function collect(root: string, dir: string, filter: string[], token: { count: number, maxReached: boolean }): Promise<void> {
|
||||
const relativePath = dir.substring(root.length + 1);
|
||||
|
||||
return new Promise(resolve => {
|
||||
readdir(dir, { withFileTypes: true }, async (err, files) => {
|
||||
return new Promise(async resolve => {
|
||||
let files: IDirent[];
|
||||
try {
|
||||
files = await readdir(dir, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
// Ignore folders that can't be read
|
||||
if (err) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.count >= MAX_FILES) {
|
||||
token.count += files.length;
|
||||
token.maxReached = true;
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let pending = files.length;
|
||||
if (pending === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let filesToRead = files;
|
||||
if (token.count + files.length > MAX_FILES) {
|
||||
token.maxReached = true;
|
||||
pending = MAX_FILES - token.count;
|
||||
filesToRead = files.slice(0, pending);
|
||||
}
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.count >= MAX_FILES) {
|
||||
token.count += files.length;
|
||||
token.maxReached = true;
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of filesToRead) {
|
||||
if (file.isDirectory()) {
|
||||
if (!filter.includes(file.name)) {
|
||||
await collect(root, join(dir, file.name), filter, token);
|
||||
}
|
||||
let pending = files.length;
|
||||
if (pending === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (--pending === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const index = file.name.lastIndexOf('.');
|
||||
if (index >= 0) {
|
||||
const fileType = file.name.substring(index + 1);
|
||||
if (fileType) {
|
||||
fileTypes.set(fileType, (fileTypes.get(fileType) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
let filesToRead = files;
|
||||
if (token.count + files.length > MAX_FILES) {
|
||||
token.maxReached = true;
|
||||
pending = MAX_FILES - token.count;
|
||||
filesToRead = files.slice(0, pending);
|
||||
}
|
||||
|
||||
for (const configFile of configFilePatterns) {
|
||||
if (configFile.relativePathPattern?.test(relativePath) !== false && configFile.filePattern.test(file.name)) {
|
||||
configFiles.set(configFile.tag, (configFiles.get(configFile.tag) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
token.count += files.length;
|
||||
|
||||
if (--pending === 0) {
|
||||
resolve();
|
||||
return;
|
||||
for (const file of filesToRead) {
|
||||
if (file.isDirectory()) {
|
||||
if (!filter.includes(file.name)) {
|
||||
await collect(root, join(dir, file.name), filter, token);
|
||||
}
|
||||
|
||||
if (--pending === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const index = file.name.lastIndexOf('.');
|
||||
if (index >= 0) {
|
||||
const fileType = file.name.substring(index + 1);
|
||||
if (fileType) {
|
||||
fileTypes.set(fileType, (fileTypes.get(fileType) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (const configFile of configFilePatterns) {
|
||||
if (configFile.relativePathPattern?.test(relativePath) !== false && configFile.filePattern.test(file.name)) {
|
||||
configFiles.set(configFile.tag, (configFiles.get(configFile.tag) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (--pending === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises } from 'fs';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { rename } from 'vs/base/node/pfs';
|
||||
import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
||||
import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
@@ -63,7 +63,7 @@ export class ExtensionsDownloader extends Disposable {
|
||||
|
||||
private async rename(from: URI, to: URI, retryUntil: number): Promise<void> {
|
||||
try {
|
||||
await rename(from.fsPath, to.fsPath);
|
||||
await promises.rename(from.fsPath, to.fsPath);
|
||||
} catch (error) {
|
||||
if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) {
|
||||
this.logService.info(`Failed renaming ${from} to ${to} with 'EPERM' error. Trying again...`);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
@@ -130,7 +131,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
const collectFilesFromDirectory = async (dir: string): Promise<string[]> => {
|
||||
let entries = await pfs.readdir(dir);
|
||||
entries = entries.map(e => path.join(dir, e));
|
||||
const stats = await Promise.all(entries.map(e => pfs.stat(e)));
|
||||
const stats = await Promise.all(entries.map(e => fs.promises.stat(e)));
|
||||
let promise: Promise<string[]> = Promise.resolve([]);
|
||||
stats.forEach((stat, index) => {
|
||||
const entry = entries[index];
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as semver from 'vs/base/common/semver/semver';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
@@ -138,7 +139,7 @@ export class ExtensionsScanner extends Disposable {
|
||||
metadata.isMachineScoped = metadata.isMachineScoped || undefined;
|
||||
metadata.isBuiltin = metadata.isBuiltin || undefined;
|
||||
const manifestPath = path.join(local.location.fsPath, 'package.json');
|
||||
const raw = await pfs.readFile(manifestPath, 'utf8');
|
||||
const raw = await fs.promises.readFile(manifestPath, 'utf8');
|
||||
const { manifest } = await this.parseManifest(raw);
|
||||
(manifest as ILocalExtensionManifest).__metadata = metadata;
|
||||
await pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t'));
|
||||
@@ -153,7 +154,7 @@ export class ExtensionsScanner extends Disposable {
|
||||
return this.uninstalledFileLimiter.queue(async () => {
|
||||
let raw: string | undefined;
|
||||
try {
|
||||
raw = await pfs.readFile(this.uninstalledPath, 'utf8');
|
||||
raw = await fs.promises.readFile(this.uninstalledPath, 'utf8');
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
@@ -211,7 +212,7 @@ export class ExtensionsScanner extends Disposable {
|
||||
|
||||
private async rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise<void> {
|
||||
try {
|
||||
await pfs.rename(extractPath, renamePath);
|
||||
await fs.promises.rename(extractPath, renamePath);
|
||||
} catch (error) {
|
||||
if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) {
|
||||
this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id);
|
||||
@@ -345,9 +346,9 @@ export class ExtensionsScanner extends Disposable {
|
||||
|
||||
private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IMetadata | null; }> {
|
||||
const promises = [
|
||||
pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8')
|
||||
fs.promises.readFile(path.join(extensionPath, 'package.json'), 'utf8')
|
||||
.then(raw => this.parseManifest(raw)),
|
||||
pfs.readFile(path.join(extensionPath, 'package.nls.json'), 'utf8')
|
||||
fs.promises.readFile(path.join(extensionPath, 'package.nls.json'), 'utf8')
|
||||
.then(undefined, err => err.code !== 'ENOENT' ? Promise.reject<string>(err) : '{}')
|
||||
.then(raw => JSON.parse(raw))
|
||||
];
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mkdir, open, close, read, write, fdatasync, Dirent, Stats } from 'fs';
|
||||
import { open, close, read, write, fdatasync, Stats, promises } from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import { IDisposable, Disposable, toDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, FileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileReadStreamCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, FileReadStreamOptions, IFileSystemProviderWithFileFolderCopyCapability } 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, unlink, move, copy, readFile, truncate, rimraf, RimRafMode, exists, readdirWithFileTypes } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport, move, copy, rimraf, RimRafMode, exists, readdir, IDirent } 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';
|
||||
@@ -80,7 +80,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
async stat(resource: URI): Promise<IStat> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly
|
||||
|
||||
return {
|
||||
type: this.toType(stat, symbolicLink),
|
||||
@@ -95,7 +95,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
async readdir(resource: URI): Promise<[string, FileType][]> {
|
||||
try {
|
||||
const children = await readdirWithFileTypes(this.toFilePath(resource));
|
||||
const children = await readdir(this.toFilePath(resource), { withFileTypes: true });
|
||||
|
||||
const result: [string, FileType][] = [];
|
||||
await Promise.all(children.map(async child => {
|
||||
@@ -119,7 +119,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
}
|
||||
}
|
||||
|
||||
private toType(entry: Stats | Dirent, symbolicLink?: { dangling: boolean }): FileType {
|
||||
private toType(entry: Stats | IDirent, symbolicLink?: { dangling: boolean }): FileType {
|
||||
|
||||
// Signal file type by checking for file / directory, except:
|
||||
// - symbolic links pointing to non-existing files are FileType.Unknown
|
||||
@@ -151,7 +151,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
try {
|
||||
const filePath = this.toFilePath(resource);
|
||||
|
||||
return await readFile(filePath);
|
||||
return await promises.readFile(filePath);
|
||||
} catch (error) {
|
||||
throw this.toFileSystemProviderError(error);
|
||||
}
|
||||
@@ -212,18 +212,20 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
let flags: string | undefined = undefined;
|
||||
if (opts.create) {
|
||||
if (isWindows && await exists(filePath)) {
|
||||
if (isWindows) {
|
||||
try {
|
||||
// On Windows and if the file exists, we use a different strategy of saving the file
|
||||
// by first truncating the file and then writing with r+ flag. This helps to save hidden files on Windows
|
||||
// (see https://github.com/microsoft/vscode/issues/931) and prevent removing alternate data streams
|
||||
// (see https://github.com/microsoft/vscode/issues/6363)
|
||||
await truncate(filePath, 0);
|
||||
await promises.truncate(filePath, 0);
|
||||
|
||||
// After a successful truncate() the flag can be set to 'r+' which will not truncate.
|
||||
flags = 'r+';
|
||||
} catch (error) {
|
||||
this.logService.trace(error);
|
||||
if (error.code !== 'ENOENT') {
|
||||
this.logService.trace(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +400,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
async mkdir(resource: URI): Promise<void> {
|
||||
try {
|
||||
await promisify(mkdir)(this.toFilePath(resource));
|
||||
await promises.mkdir(this.toFilePath(resource));
|
||||
} catch (error) {
|
||||
throw this.toFileSystemProviderError(error);
|
||||
}
|
||||
@@ -418,7 +420,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
if (opts.recursive) {
|
||||
await rimraf(filePath, RimRafMode.MOVE);
|
||||
} else {
|
||||
await unlink(filePath);
|
||||
await promises.unlink(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +465,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
await this.validateTargetDeleted(from, to, 'copy', opts.overwrite);
|
||||
|
||||
// Copy
|
||||
await copy(fromFilePath, toFilePath);
|
||||
await copy(fromFilePath, toFilePath, { preserveSymlinks: true });
|
||||
} catch (error) {
|
||||
|
||||
// rewrite some typical errors that can happen especially around symlinks
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { statLink } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport } from 'vs/base/node/pfs';
|
||||
import { realpath } from 'vs/base/node/extpath';
|
||||
import { watchFolder, watchFile, CHANGE_BUFFER_DELAY } from 'vs/base/node/watcher';
|
||||
import { FileChangeType } from 'vs/platform/files/common/files';
|
||||
@@ -35,7 +35,7 @@ export class FileWatcher extends Disposable {
|
||||
|
||||
private async startWatching(): Promise<void> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(this.path);
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(this.path);
|
||||
|
||||
if (this.isDisposed) {
|
||||
return;
|
||||
@@ -47,6 +47,10 @@ export class FileWatcher extends Disposable {
|
||||
pathToWatch = await realpath(pathToWatch);
|
||||
} catch (error) {
|
||||
this.onError(error);
|
||||
|
||||
if (symbolicLink.dangling) {
|
||||
return; // give up if symbolic link is dangling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +74,9 @@ export class FileWatcher extends Disposable {
|
||||
}, error => this.onError(error)));
|
||||
}
|
||||
} catch (error) {
|
||||
this.onError(error);
|
||||
if (error.code !== 'ENOENT') {
|
||||
this.onError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro
|
||||
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
import { join, basename, dirname, posix } from 'vs/base/common/path';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { copy, rimraf, symlink, rimrafSync } from 'vs/base/node/pfs';
|
||||
import { copy, rimraf, rimrafSync } from 'vs/base/node/pfs';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, createReadStream } from 'fs';
|
||||
import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, createReadStream, promises } from 'fs';
|
||||
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
@@ -147,7 +147,7 @@ flakySuite('Disk File Service', function () {
|
||||
|
||||
const sourceDir = getPathFromAmdModule(require, './fixtures/service');
|
||||
|
||||
await copy(sourceDir, testDir);
|
||||
await copy(sourceDir, testDir, { preserveSymlinks: false });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
@@ -408,7 +408,7 @@ flakySuite('Disk File Service', function () {
|
||||
|
||||
test('resolve - folder symbolic link', async () => {
|
||||
const link = URI.file(join(testDir, 'deep-link'));
|
||||
await symlink(join(testDir, 'deep'), link.fsPath, 'junction');
|
||||
await promises.symlink(join(testDir, 'deep'), link.fsPath, 'junction');
|
||||
|
||||
const resolved = await service.resolve(link);
|
||||
assert.strictEqual(resolved.children!.length, 4);
|
||||
@@ -418,7 +418,7 @@ flakySuite('Disk File Service', function () {
|
||||
|
||||
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('resolve - file symbolic link', async () => {
|
||||
const link = URI.file(join(testDir, 'lorem.txt-linked'));
|
||||
await symlink(join(testDir, 'lorem.txt'), link.fsPath);
|
||||
await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath);
|
||||
|
||||
const resolved = await service.resolve(link);
|
||||
assert.strictEqual(resolved.isDirectory, false);
|
||||
@@ -426,7 +426,7 @@ flakySuite('Disk File Service', function () {
|
||||
});
|
||||
|
||||
test('resolve - symbolic link pointing to non-existing file does not break', async () => {
|
||||
await symlink(join(testDir, 'foo'), join(testDir, 'bar'), 'junction');
|
||||
await promises.symlink(join(testDir, 'foo'), join(testDir, 'bar'), 'junction');
|
||||
|
||||
const resolved = await service.resolve(URI.file(testDir));
|
||||
assert.strictEqual(resolved.isDirectory, true);
|
||||
@@ -477,7 +477,7 @@ flakySuite('Disk File Service', function () {
|
||||
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (exists)', async () => {
|
||||
const target = URI.file(join(testDir, 'lorem.txt'));
|
||||
const link = URI.file(join(testDir, 'lorem.txt-linked'));
|
||||
await symlink(target.fsPath, link.fsPath);
|
||||
await promises.symlink(target.fsPath, link.fsPath);
|
||||
|
||||
const source = await service.resolve(link);
|
||||
|
||||
@@ -499,7 +499,7 @@ flakySuite('Disk File Service', function () {
|
||||
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (pointing to non-existing file)', async () => {
|
||||
const target = URI.file(join(testDir, 'foo'));
|
||||
const link = URI.file(join(testDir, 'bar'));
|
||||
await symlink(target.fsPath, link.fsPath);
|
||||
await promises.symlink(target.fsPath, link.fsPath);
|
||||
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
@@ -2010,7 +2010,7 @@ flakySuite('Disk File Service', function () {
|
||||
|
||||
(runWatchTests && !isWindows /* windows: cannot create file symbolic link without elevated context */ ? test : test.skip)('watch - file symbolic link', async () => {
|
||||
const toWatch = URI.file(join(testDir, 'lorem.txt-linked'));
|
||||
await symlink(join(testDir, 'lorem.txt'), toWatch.fsPath);
|
||||
await promises.symlink(join(testDir, 'lorem.txt'), toWatch.fsPath);
|
||||
|
||||
const promise = assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]]);
|
||||
setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes'), 50);
|
||||
@@ -2138,7 +2138,7 @@ flakySuite('Disk File Service', function () {
|
||||
|
||||
(runWatchTests ? test : test.skip)('watch - folder (non recursive) - symbolic link - change file', async () => {
|
||||
const watchDir = URI.file(join(testDir, 'deep-link'));
|
||||
await symlink(join(testDir, 'deep'), watchDir.fsPath, 'junction');
|
||||
await promises.symlink(join(testDir, 'deep'), watchDir.fsPath, 'junction');
|
||||
|
||||
const file = URI.file(join(watchDir.fsPath, 'index.html'));
|
||||
writeFileSync(file.fsPath, 'Init');
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { writeFile } from 'vs/base/node/pfs';
|
||||
import { promises } from 'fs';
|
||||
import { createHash } from 'crypto';
|
||||
import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -157,7 +158,7 @@ class LanguagePacksCache extends Disposable {
|
||||
private withLanguagePacks<T>(fn: (languagePacks: { [language: string]: ILanguagePack }) => T | null = () => null): Promise<T> {
|
||||
return this.languagePacksFileLimiter.queue(() => {
|
||||
let result: T | null = null;
|
||||
return pfs.readFile(this.languagePacksFilePath, 'utf8')
|
||||
return promises.readFile(this.languagePacksFilePath, 'utf8')
|
||||
.then(undefined, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err))
|
||||
.then<{ [language: string]: ILanguagePack }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } })
|
||||
.then(languagePacks => { result = fn(languagePacks); return languagePacks; })
|
||||
@@ -171,7 +172,7 @@ class LanguagePacksCache extends Disposable {
|
||||
this.initializedCache = true;
|
||||
const raw = JSON.stringify(this.languagePacks);
|
||||
this.logService.debug('Writing language packs', raw);
|
||||
return pfs.writeFile(this.languagePacksFilePath, raw);
|
||||
return writeFile(this.languagePacksFilePath, raw);
|
||||
})
|
||||
.then(() => result, error => this.logService.error(error));
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
import { AddFirstParameterToFunctions } from 'vs/base/common/types';
|
||||
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService';
|
||||
import { dirExists } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport } from 'vs/base/node/pfs';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -261,7 +261,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
|
||||
const paths = await this.dialogMainService.pickFileFolder(options);
|
||||
if (paths) {
|
||||
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFileFolder', options.telemetryExtraData);
|
||||
this.doOpenPicked(await Promise.all(paths.map(async path => (await dirExists(path)) ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) })), options, windowId);
|
||||
this.doOpenPicked(await Promise.all(paths.map(async path => (await SymlinkSupport.existsDirectory(path)) ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) })), options, windowId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as fs from 'fs';
|
||||
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { writeFileSync, readFile } from 'vs/base/node/pfs';
|
||||
import { writeFileSync } from 'vs/base/node/pfs';
|
||||
import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
@@ -58,7 +58,7 @@ export class FileStorage {
|
||||
|
||||
private async loadAsync(): Promise<StorageDatabase> {
|
||||
try {
|
||||
this.lastFlushedSerializedDatabase = (await readFile(this.dbPath)).toString();
|
||||
this.lastFlushedSerializedDatabase = (await fs.promises.readFile(this.dbPath)).toString();
|
||||
|
||||
return JSON.parse(this.lastFlushedSerializedDatabase);
|
||||
} catch (error) {
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { tmpdir } from 'os';
|
||||
import { promises } from 'fs';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
import { FileStorage } from 'vs/platform/state/node/stateService';
|
||||
import { mkdirp, rimraf, writeFileSync } from 'vs/base/node/pfs';
|
||||
import { rimraf, writeFileSync } from 'vs/base/node/pfs';
|
||||
|
||||
flakySuite('StateService', () => {
|
||||
|
||||
@@ -17,7 +18,7 @@ flakySuite('StateService', () => {
|
||||
setup(() => {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'stateservice');
|
||||
|
||||
return mkdirp(testDir);
|
||||
return promises.mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises } from 'fs';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { StorageScope, WillSaveStateReason, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage';
|
||||
@@ -10,7 +11,7 @@ import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/
|
||||
import { Storage, IStorageDatabase, IStorage, StorageHint } from 'vs/base/parts/storage/common/storage';
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { copy, exists, mkdirp, writeFile } from 'vs/base/node/pfs';
|
||||
import { copy, exists, writeFile } from 'vs/base/node/pfs';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
@@ -138,7 +139,7 @@ export class NativeStorageService extends AbstractStorageService {
|
||||
return { path: workspaceStorageFolderPath, wasCreated: false };
|
||||
}
|
||||
|
||||
await mkdirp(workspaceStorageFolderPath);
|
||||
await promises.mkdir(workspaceStorageFolderPath, { recursive: true });
|
||||
|
||||
// Write metadata into folder
|
||||
this.ensureWorkspaceStorageFolderMeta(payload);
|
||||
@@ -267,7 +268,7 @@ export class NativeStorageService extends AbstractStorageService {
|
||||
const newWorkspaceStoragePath = join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME);
|
||||
|
||||
// Copy current storage over to new workspace storage
|
||||
await copy(assertIsDefined(this.workspaceStoragePath), newWorkspaceStoragePath);
|
||||
await copy(assertIsDefined(this.workspaceStoragePath), newWorkspaceStoragePath, { preserveSymlinks: false });
|
||||
|
||||
// Recreate and init workspace storage
|
||||
return this.createWorkspaceStorage(newWorkspaceStoragePath).init();
|
||||
|
||||
@@ -7,7 +7,8 @@ import { strictEqual } from 'assert';
|
||||
import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
|
||||
import { tmpdir } from 'os';
|
||||
import { mkdirp, rimraf } from 'vs/base/node/pfs';
|
||||
import { promises } from 'fs';
|
||||
import { rimraf } from 'vs/base/node/pfs';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
|
||||
@@ -22,7 +23,7 @@ flakySuite('NativeStorageService', function () {
|
||||
setup(() => {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice');
|
||||
|
||||
return mkdirp(testDir);
|
||||
return promises.mkdir(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
||||
@@ -56,7 +56,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
||||
@memoize
|
||||
get cachePath(): Promise<string> {
|
||||
const result = path.join(tmpdir(), `vscode-update-${product.target}-${process.arch}`);
|
||||
return pfs.mkdirp(result).then(() => result);
|
||||
return fs.promises.mkdir(result, { recursive: true }).then(() => result);
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -146,7 +146,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
||||
return this.requestService.request({ url }, CancellationToken.None)
|
||||
.then(context => this.fileService.writeFile(URI.file(downloadPath), context.stream))
|
||||
.then(hash ? () => checksum(downloadPath, update.hash) : () => undefined)
|
||||
.then(() => pfs.rename(downloadPath, updatePackagePath))
|
||||
.then(() => fs.promises.rename(downloadPath, updatePackagePath))
|
||||
.then(() => updatePackagePath);
|
||||
});
|
||||
}).then(packagePath => {
|
||||
@@ -196,7 +196,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
||||
|
||||
const promises = versions.filter(filter).map(async one => {
|
||||
try {
|
||||
await pfs.unlink(path.join(cachePath, one));
|
||||
await fs.promises.unlink(path.join(cachePath, one));
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import { toWorkspaceFolders, IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder, IEnterWorkspaceResult, isUntitledWorkspace, isWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
import { join, dirname } from 'vs/base/common/path';
|
||||
import { mkdirp, writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs';
|
||||
import { readFileSync, existsSync, mkdirSync, statSync, Stats } from 'fs';
|
||||
import { writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs';
|
||||
import { promises, readFileSync, existsSync, mkdirSync, statSync, Stats } from 'fs';
|
||||
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
@@ -140,7 +140,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork
|
||||
const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority);
|
||||
const configPath = workspace.configPath.fsPath;
|
||||
|
||||
await mkdirp(dirname(configPath));
|
||||
await promises.mkdir(dirname(configPath), { recursive: true });
|
||||
await writeFile(configPath, JSON.stringify(storedWorkspace, null, '\t'));
|
||||
|
||||
return workspace;
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ suite('WorkspacesManagementMainService', () => {
|
||||
|
||||
service = new WorkspacesManagementMainService(environmentService, new NullLogService(), new TestBackupMainService(), new TestDialogMainService());
|
||||
|
||||
return pfs.mkdirp(untitledWorkspacesHomePath);
|
||||
return fs.promises.mkdir(untitledWorkspacesHomePath, { recursive: true });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
||||
@@ -9,7 +9,8 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender';
|
||||
import { toLocalISOString } from 'vs/base/common/date';
|
||||
import { dirExists, mkdirp } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport } from 'vs/base/node/pfs';
|
||||
import { promises } from 'fs';
|
||||
import { AbstractExtHostOutputChannel, ExtHostPushOutputChannel, ExtHostOutputService, LazyOutputChannel } from 'vs/workbench/api/common/extHostOutput';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
@@ -85,9 +86,9 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
private async _doCreateOutChannel(name: string): Promise<AbstractExtHostOutputChannel> {
|
||||
try {
|
||||
const outputDirPath = join(this._logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
|
||||
const exists = await dirExists(outputDirPath);
|
||||
const exists = await SymlinkSupport.existsDirectory(outputDirPath);
|
||||
if (!exists) {
|
||||
await mkdirp(outputDirPath);
|
||||
await promises.mkdir(outputDirPath, { recursive: true });
|
||||
}
|
||||
const fileName = `${this._namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}`;
|
||||
const file = URI.file(join(outputDirPath, `${fileName}.log`));
|
||||
|
||||
@@ -18,7 +18,6 @@ import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHos
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { promisify } from 'util';
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
@@ -267,8 +266,8 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
let tcp: string = '';
|
||||
let tcp6: string = '';
|
||||
try {
|
||||
tcp = await pfs.readFile('/proc/net/tcp', 'utf8');
|
||||
tcp6 = await pfs.readFile('/proc/net/tcp6', 'utf8');
|
||||
tcp = await fs.promises.readFile('/proc/net/tcp', 'utf8');
|
||||
tcp6 = await fs.promises.readFile('/proc/net/tcp6', 'utf8');
|
||||
} catch (e) {
|
||||
// File reading error. No additional handling needed.
|
||||
}
|
||||
@@ -286,10 +285,10 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
try {
|
||||
const pid: number = Number(childName);
|
||||
const childUri = resources.joinPath(URI.file('/proc'), childName);
|
||||
const childStat = await pfs.stat(childUri.fsPath);
|
||||
const childStat = await fs.promises.stat(childUri.fsPath);
|
||||
if (childStat.isDirectory() && !isNaN(pid)) {
|
||||
const cwd = await promisify(fs.readlink)(resources.joinPath(childUri, 'cwd').fsPath);
|
||||
const cmd = await pfs.readFile(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8');
|
||||
const cwd = await fs.promises.readlink(resources.joinPath(childUri, 'cwd').fsPath);
|
||||
const cmd = await fs.promises.readFile(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8');
|
||||
processes.push({ pid, cwd, cmd });
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
import * as assert from 'assert';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { tmpdir } from 'os';
|
||||
import { promises } from 'fs';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { mkdirp, rimraf, writeFile } from 'vs/base/node/pfs';
|
||||
import { rimraf, writeFile } from 'vs/base/node/pfs';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService';
|
||||
@@ -106,8 +107,8 @@ flakySuite('BackupTracker (native)', function () {
|
||||
|
||||
disposables.add(registerTestFileEditor());
|
||||
|
||||
await mkdirp(backupHome);
|
||||
await mkdirp(workspaceBackupPath);
|
||||
await promises.mkdir(backupHome, { recursive: true });
|
||||
await promises.mkdir(workspaceBackupPath, { recursive: true });
|
||||
|
||||
return writeFile(workspacesJsonPath, '');
|
||||
});
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as cp from 'child_process';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as extpath from 'vs/base/node/extpath';
|
||||
import { promisify } from 'util';
|
||||
@@ -74,9 +75,9 @@ class InstallAction extends Action2 {
|
||||
if (!isAvailable || isInstalled) {
|
||||
return Promise.resolve(null);
|
||||
} else {
|
||||
return pfs.unlink(target)
|
||||
return fs.promises.unlink(target)
|
||||
.then(undefined, ignore('ENOENT', null))
|
||||
.then(() => pfs.symlink(getSource(), target))
|
||||
.then(() => fs.promises.symlink(getSource(), target))
|
||||
.then(undefined, err => {
|
||||
if (err.code === 'EACCES' || err.code === 'ENOENT') {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
@@ -111,7 +112,7 @@ class InstallAction extends Action2 {
|
||||
}
|
||||
|
||||
private isInstalled(target: string): Promise<boolean> {
|
||||
return pfs.lstat(target)
|
||||
return fs.promises.lstat(target)
|
||||
.then(stat => stat.isSymbolicLink())
|
||||
.then(() => extpath.realpath(target))
|
||||
.then(link => link === getSource())
|
||||
@@ -149,7 +150,7 @@ class UninstallAction extends Action2 {
|
||||
}
|
||||
|
||||
const uninstall = () => {
|
||||
return pfs.unlink(target)
|
||||
return fs.promises.unlink(target)
|
||||
.then(undefined, ignore('ENOENT', null));
|
||||
};
|
||||
|
||||
|
||||
@@ -677,8 +677,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
|
||||
notificationService.prompt(
|
||||
Severity.Info,
|
||||
message,
|
||||
actions,
|
||||
{ sticky: true }
|
||||
actions
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { writeFile } from 'vs/base/node/pfs';
|
||||
import { IExtensionHostProfileService } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -22,6 +21,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
|
||||
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
@@ -36,7 +37,8 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@INativeWorkbenchEnvironmentService private readonly _environmentServie: INativeWorkbenchEnvironmentService
|
||||
@INativeWorkbenchEnvironmentService private readonly _environmentServie: INativeWorkbenchEnvironmentService,
|
||||
@IFileService private readonly _fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
this._register(_extensionService.onDidChangeResponsiveChange(this._onDidChangeResponsiveChange, this));
|
||||
@@ -139,8 +141,8 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
|
||||
|
||||
|
||||
// print message to log
|
||||
const path = joinPath(this._environmentServie.tmpDir, `exthost-${Math.random().toString(16).slice(2, 8)}.cpuprofile`).fsPath;
|
||||
await writeFile(path, JSON.stringify(profile.data));
|
||||
const path = joinPath(this._environmentServie.tmpDir, `exthost-${Math.random().toString(16).slice(2, 8)}.cpuprofile`);
|
||||
await this._fileService.writeFile(path, VSBuffer.fromString(JSON.stringify(profile.data)));
|
||||
this._logService.warn(`UNRESPONSIVE extension host, '${top.id}' took ${top!.percentage}% of ${duration / 1e3}ms, saved PROFILE here: '${path}'`, data);
|
||||
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { dirname, join, basename } from 'vs/base/common/path';
|
||||
import { exists, readdir, readFile, rimraf } from 'vs/base/node/pfs';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { promises } from 'fs';
|
||||
import { localize } from 'vs/nls';
|
||||
import { dirname, join, basename } from 'vs/base/common/path';
|
||||
import { exists, readdir, rimraf } from 'vs/base/node/pfs';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
@@ -52,7 +53,7 @@ export class StartupProfiler implements IWorkbenchContribution {
|
||||
const prefix = basename(profileFilenamePrefix);
|
||||
|
||||
const removeArgs: string[] = ['--prof-startup'];
|
||||
const markerFile = readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|')))
|
||||
const markerFile = promises.readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|')))
|
||||
.then(() => rimraf(profileFilenamePrefix)) // (1) delete the file to tell the main process to stop profiling
|
||||
.then(() => new Promise<void>(resolve => { // (2) wait for main that recreates the fail to signal profiling has stopped
|
||||
const check = () => {
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { appendFile } from 'fs';
|
||||
import { promises } from 'fs';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { promisify } from 'util';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
@@ -58,7 +57,7 @@ export class StartupTimings implements IWorkbenchContribution {
|
||||
this._timerService.whenReady(),
|
||||
timeout(15000), // wait: cached data creation, telemetry sending
|
||||
]).then(() => {
|
||||
return promisify(appendFile)(appendTo, `${this._timerService.startupMetrics.ellapsed}\t${this._productService.nameShort}\t${(this._productService.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${standardStartupError === undefined ? 'standard_start' : 'NO_standard_start : ' + standardStartupError}\n`);
|
||||
return promises.appendFile(appendTo, `${this._timerService.startupMetrics.ellapsed}\t${this._productService.nameShort}\t${(this._productService.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${standardStartupError === undefined ? 'standard_start' : 'NO_standard_start : ' + standardStartupError}\n`);
|
||||
}).then(() => {
|
||||
this._nativeHostService.quit();
|
||||
}).catch(err => {
|
||||
|
||||
@@ -106,7 +106,7 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
|
||||
}
|
||||
if (this.model.detected.size > 0) {
|
||||
groups.push({
|
||||
label: nls.localize('remote.tunnelsView.detected', "Existing Tunnels"),
|
||||
label: nls.localize('remote.tunnelsView.detected', "Static Ports"),
|
||||
tunnelType: TunnelType.Detected,
|
||||
items: this.detected
|
||||
});
|
||||
|
||||
@@ -172,7 +172,9 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
}
|
||||
},
|
||||
markdownDescription: localize('remote.portsAttributes', "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```"),
|
||||
defaultSnippets: [{ body: { '${1:3000}': { label: '${2:My Port}', onAutoForward: 'notify' } } }]
|
||||
defaultSnippets: [{ body: { '${1:3000}': { label: '${2:My Port}', onAutoForward: 'notify' } } }],
|
||||
errorMessage: localize('remote.portsAttributes.patternError', "Must be a port number or a range of port numbers"),
|
||||
additionalProperties: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { readFile, fileExists, stat, lstat } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport } from 'vs/base/node/pfs';
|
||||
import { LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { normalize, basename } from 'vs/base/common/path';
|
||||
@@ -14,11 +15,11 @@ import { enumeratePowerShellInstallations } from 'vs/base/node/powershell';
|
||||
let detectedDistro = LinuxDistro.Unknown;
|
||||
if (platform.isLinux) {
|
||||
const file = '/etc/os-release';
|
||||
fileExists(file).then(async exists => {
|
||||
SymlinkSupport.existsFile(file).then(async exists => {
|
||||
if (!exists) {
|
||||
return;
|
||||
}
|
||||
const buffer = await readFile(file);
|
||||
const buffer = await fs.promises.readFile(file);
|
||||
const contents = buffer.toString();
|
||||
if (/NAME="?Fedora"?/.test(contents)) {
|
||||
detectedDistro = LinuxDistro.Fedora;
|
||||
@@ -86,7 +87,7 @@ async function detectAvailableWindowsShells(): Promise<IShellDefinition[]> {
|
||||
}
|
||||
|
||||
async function detectAvailableUnixShells(): Promise<IShellDefinition[]> {
|
||||
const contents = await readFile('/etc/shells', 'utf8');
|
||||
const contents = await fs.promises.readFile('/etc/shells', 'utf8');
|
||||
const shells = contents.split('\n').filter(e => e.trim().indexOf('#') !== 0 && e.trim().length > 0);
|
||||
return shells.map(e => {
|
||||
return {
|
||||
@@ -105,7 +106,7 @@ async function validateShellPaths(label: string, potentialPaths: string[]): Prom
|
||||
return validateShellPaths(label, potentialPaths);
|
||||
}
|
||||
try {
|
||||
const result = await stat(normalize(current));
|
||||
const result = await fs.promises.stat(normalize(current));
|
||||
if (result.isFile() || result.isSymbolicLink()) {
|
||||
return {
|
||||
label,
|
||||
@@ -117,7 +118,7 @@ async function validateShellPaths(label: string, potentialPaths: string[]): Prom
|
||||
// throw 'permission denied' using 'stat' but don't throw
|
||||
// using 'lstat'
|
||||
try {
|
||||
const result = await lstat(normalize(current));
|
||||
const result = await fs.promises.lstat(normalize(current));
|
||||
if (result.isFile() || result.isSymbolicLink()) {
|
||||
return {
|
||||
label,
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { readFile, exists } from 'vs/base/node/pfs';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { exists } from 'vs/base/node/pfs';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { getCaseInsensitive } from 'vs/base/common/objects';
|
||||
|
||||
@@ -26,7 +27,7 @@ export async function getMainProcessParentEnv(baseEnvironment: IProcessEnvironme
|
||||
let name: string = codeProcessName;
|
||||
do {
|
||||
pid = ppid;
|
||||
const status = await readFile(`/proc/${pid}/status`, 'utf8');
|
||||
const status = await fs.promises.readFile(`/proc/${pid}/status`, 'utf8');
|
||||
const splitByLine = status.split('\n');
|
||||
splitByLine.forEach(line => {
|
||||
if (line.indexOf('Name:') === 0) {
|
||||
@@ -37,7 +38,7 @@ export async function getMainProcessParentEnv(baseEnvironment: IProcessEnvironme
|
||||
}
|
||||
});
|
||||
} while (name === codeProcessName);
|
||||
const rawEnv = await readFile(`/proc/${pid}/environ`, 'utf8');
|
||||
const rawEnv = await fs.promises.readFile(`/proc/${pid}/environ`, 'utf8');
|
||||
const env: IProcessEnvironment = {};
|
||||
rawEnv.split('\0').forEach(e => {
|
||||
const i = e.indexOf('=');
|
||||
|
||||
@@ -14,7 +14,6 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, FlowControlConstants } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { exec } from 'child_process';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { stat } from 'vs/base/node/pfs';
|
||||
import { findExecutable } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -122,7 +121,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
|
||||
private async _validateCwd(): Promise<undefined | ITerminalLaunchError> {
|
||||
try {
|
||||
const result = await stat(this._initialCwd);
|
||||
const result = await fs.promises.stat(this._initialCwd);
|
||||
if (!result.isDirectory()) {
|
||||
return { message: localize('launchFail.cwdNotDirectory', "Starting directory (cwd) \"{0}\" is not a directory", this._initialCwd.toString()) };
|
||||
}
|
||||
@@ -140,7 +139,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
throw new Error('IShellLaunchConfig.executable not set');
|
||||
}
|
||||
try {
|
||||
const result = await stat(slc.executable);
|
||||
const result = await fs.promises.stat(slc.executable);
|
||||
if (!result.isFile() && !result.isSymbolicLink()) {
|
||||
return { message: localize('launchFail.executableIsNotFileOrSymlink', "Path to shell executable \"{0}\" is not a file of a symlink", slc.executable) };
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import * as assert from 'assert';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { createHash } from 'crypto';
|
||||
import { tmpdir } from 'os';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { promises, existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { dirname, join } from 'vs/base/common/path';
|
||||
import { mkdirp, readdirSync, rimraf, writeFile } from 'vs/base/node/pfs';
|
||||
import { readdirSync, rimraf, writeFile } from 'vs/base/node/pfs';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { BackupFilesModel } from 'vs/workbench/services/backup/common/backupFileService';
|
||||
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
|
||||
@@ -140,7 +140,7 @@ suite('BackupFileService', () => {
|
||||
|
||||
service = new NodeTestBackupFileService(testDir, workspaceBackupPath);
|
||||
|
||||
await mkdirp(backupHome);
|
||||
await promises.mkdir(backupHome, { recursive: true });
|
||||
|
||||
return writeFile(workspacesJsonPath, '');
|
||||
});
|
||||
@@ -639,7 +639,7 @@ suite('BackupFileService', () => {
|
||||
});
|
||||
|
||||
test('resolve', async () => {
|
||||
await mkdirp(dirname(fooBackupPath));
|
||||
await promises.mkdir(dirname(fooBackupPath), { recursive: true });
|
||||
writeFileSync(fooBackupPath, 'foo');
|
||||
const model = new BackupFilesModel(service.fileService);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
@@ -150,7 +151,7 @@ export class CachedExtensionScanner {
|
||||
const cacheFile = path.join(cacheFolder, cacheKey);
|
||||
|
||||
try {
|
||||
const cacheRawContents = await pfs.readFile(cacheFile, 'utf8');
|
||||
const cacheRawContents = await fs.promises.readFile(cacheFile, 'utf8');
|
||||
return JSON.parse(cacheRawContents);
|
||||
} catch (err) {
|
||||
// That's ok...
|
||||
@@ -164,7 +165,7 @@ export class CachedExtensionScanner {
|
||||
const cacheFile = path.join(cacheFolder, cacheKey);
|
||||
|
||||
try {
|
||||
await pfs.mkdirp(cacheFolder);
|
||||
await fs.promises.mkdir(cacheFolder, { recursive: true });
|
||||
} catch (err) {
|
||||
// That's ok...
|
||||
}
|
||||
@@ -183,7 +184,7 @@ export class CachedExtensionScanner {
|
||||
}
|
||||
|
||||
try {
|
||||
const folderStat = await pfs.stat(input.absoluteFolderPath);
|
||||
const folderStat = await fs.promises.stat(input.absoluteFolderPath);
|
||||
input.mtime = folderStat.mtime.getTime();
|
||||
} catch (err) {
|
||||
// That's ok...
|
||||
@@ -223,7 +224,7 @@ export class CachedExtensionScanner {
|
||||
private static async _readTranslationConfig(): Promise<Translations> {
|
||||
if (platform.translationsConfigFile) {
|
||||
try {
|
||||
const content = await pfs.readFile(platform.translationsConfigFile, 'utf8');
|
||||
const content = await fs.promises.readFile(platform.translationsConfigFile, 'utf8');
|
||||
return JSON.parse(content) as Translations;
|
||||
} catch (err) {
|
||||
// no problemo
|
||||
@@ -262,7 +263,7 @@ export class CachedExtensionScanner {
|
||||
const builtInExtensions = Promise.resolve<IBuiltInExtension[]>(productService.builtInExtensions || []);
|
||||
|
||||
const controlFilePath = joinPath(environmentService.userHome, '.vscode-oss-dev', 'extensions', 'control.json').fsPath;
|
||||
const controlFile = pfs.readFile(controlFilePath, 'utf8')
|
||||
const controlFile = fs.promises.readFile(controlFilePath, 'utf8')
|
||||
.then<IBuiltInExtensionControl>(raw => JSON.parse(raw), () => ({} as any));
|
||||
|
||||
const input = new ExtensionScannerInput(version, commit, locale, devMode, getExtraDevSystemExtensionsRoot(), true, false, translations);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as semver from 'vs/base/common/semver/semver';
|
||||
@@ -57,7 +58,7 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
|
||||
}
|
||||
|
||||
public parse(): Promise<IExtensionDescription> {
|
||||
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
|
||||
return fs.promises.readFile(this._absoluteManifestPath).then((manifestContents) => {
|
||||
const errors: json.ParseError[] = [];
|
||||
const manifest = ExtensionManifestParser._fastParseJSON(manifestContents.toString(), errors);
|
||||
if (json.getNodeType(manifest) !== 'object') {
|
||||
@@ -127,7 +128,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
let translationPath = this._nlsConfig.translations[translationId];
|
||||
let localizedMessages: Promise<LocalizedMessages | undefined>;
|
||||
if (translationPath) {
|
||||
localizedMessages = pfs.readFile(translationPath, 'utf8').then<LocalizedMessages, LocalizedMessages>((content) => {
|
||||
localizedMessages = fs.promises.readFile(translationPath, 'utf8').then<LocalizedMessages, LocalizedMessages>((content) => {
|
||||
let errors: json.ParseError[] = [];
|
||||
let translationBundle: TranslationBundle = json.parse(content, errors);
|
||||
if (errors.length > 0) {
|
||||
@@ -144,7 +145,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
return { values: undefined, default: `${basename}.nls.json` };
|
||||
});
|
||||
} else {
|
||||
localizedMessages = pfs.fileExists(basename + '.nls' + extension).then<LocalizedMessages | undefined, LocalizedMessages | undefined>(exists => {
|
||||
localizedMessages = pfs.SymlinkSupport.existsFile(basename + '.nls' + extension).then<LocalizedMessages | undefined, LocalizedMessages | undefined>(exists => {
|
||||
if (!exists) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -152,7 +153,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
if (!messageBundle.localized) {
|
||||
return { values: undefined, default: messageBundle.original };
|
||||
}
|
||||
return pfs.readFile(messageBundle.localized, 'utf8').then(messageBundleContent => {
|
||||
return fs.promises.readFile(messageBundle.localized, 'utf8').then(messageBundleContent => {
|
||||
let errors: json.ParseError[] = [];
|
||||
let messages: MessageBag = json.parse(messageBundleContent, errors);
|
||||
if (errors.length > 0) {
|
||||
@@ -201,7 +202,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
private static resolveOriginalMessageBundle(originalMessageBundle: string | null, errors: json.ParseError[]) {
|
||||
return new Promise<{ [key: string]: string; } | null>((c, e) => {
|
||||
if (originalMessageBundle) {
|
||||
pfs.readFile(originalMessageBundle).then(originalBundleContent => {
|
||||
fs.promises.readFile(originalMessageBundle).then(originalBundleContent => {
|
||||
c(json.parse(originalBundleContent.toString(), errors));
|
||||
}, (err) => {
|
||||
c(null);
|
||||
@@ -220,7 +221,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
|
||||
return new Promise<{ localized: string; original: string | null; }>((c, e) => {
|
||||
function loop(basename: string, locale: string): void {
|
||||
let toCheck = `${basename}.nls.${locale}.json`;
|
||||
pfs.fileExists(toCheck).then(exists => {
|
||||
pfs.SymlinkSupport.existsFile(toCheck).then(exists => {
|
||||
if (exists) {
|
||||
c({ localized: toCheck, original: `${basename}.nls.json` });
|
||||
}
|
||||
@@ -548,7 +549,7 @@ export class ExtensionScanner {
|
||||
let obsolete: { [folderName: string]: boolean; } = {};
|
||||
if (!isBuiltin) {
|
||||
try {
|
||||
const obsoleteFileContents = await pfs.readFile(path.join(absoluteFolderPath, '.obsolete'), 'utf8');
|
||||
const obsoleteFileContents = await fs.promises.readFile(path.join(absoluteFolderPath, '.obsolete'), 'utf8');
|
||||
obsolete = JSON.parse(obsoleteFileContents);
|
||||
} catch (err) {
|
||||
// Don't care
|
||||
@@ -597,7 +598,7 @@ export class ExtensionScanner {
|
||||
const isBuiltin = input.isBuiltin;
|
||||
const isUnderDevelopment = input.isUnderDevelopment;
|
||||
|
||||
return pfs.fileExists(path.join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => {
|
||||
return pfs.SymlinkSupport.existsFile(path.join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => {
|
||||
if (exists) {
|
||||
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
|
||||
return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig).then((extensionDescription) => {
|
||||
|
||||
@@ -18,7 +18,6 @@ import { MainThreadTelemetryShape, IInitData } from 'vs/workbench/api/common/ext
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { promisify } from 'util';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
@@ -540,7 +539,7 @@ const linuxCaCertificatePaths = [
|
||||
async function readLinuxCaCertificates() {
|
||||
for (const certPath of linuxCaCertificatePaths) {
|
||||
try {
|
||||
const content = await promisify(fs.readFile)(certPath, { encoding: 'utf8' });
|
||||
const content = await fs.promises.readFile(certPath, { encoding: 'utf8' });
|
||||
const certs = new Set(content.split(/(?=-----BEGIN CERTIFICATE-----)/g)
|
||||
.filter(pem => !!pem.length));
|
||||
return {
|
||||
|
||||
+4
-3
@@ -5,10 +5,11 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { promises } from 'fs';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { ScanCodeBinding } from 'vs/base/common/scanCode';
|
||||
import { readFile, writeFile } from 'vs/base/node/pfs';
|
||||
import { writeFile } from 'vs/base/node/pfs';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IKeyboardMapper } from 'vs/platform/keyboardLayout/common/keyboardMapper';
|
||||
|
||||
@@ -52,7 +53,7 @@ export function assertResolveUserBinding(mapper: IKeyboardMapper, parts: (Simple
|
||||
}
|
||||
|
||||
export function readRawMapping<T>(file: string): Promise<T> {
|
||||
return readFile(getPathFromAmdModule(require, `vs/workbench/services/keybinding/test/electron-browser/${file}.js`)).then((buff) => {
|
||||
return promises.readFile(getPathFromAmdModule(require, `vs/workbench/services/keybinding/test/electron-browser/${file}.js`)).then((buff) => {
|
||||
let contents = buff.toString();
|
||||
let func = new Function('define', contents);
|
||||
let rawMappings: T | null = null;
|
||||
@@ -66,7 +67,7 @@ export function readRawMapping<T>(file: string): Promise<T> {
|
||||
export function assertMapping(writeFileIfDifferent: boolean, mapper: IKeyboardMapper, file: string): Promise<void> {
|
||||
const filePath = path.normalize(getPathFromAmdModule(require, `vs/workbench/services/keybinding/test/electron-browser/${file}`));
|
||||
|
||||
return readFile(filePath).then((buff) => {
|
||||
return promises.readFile(filePath).then((buff) => {
|
||||
const expected = buff.toString().replace(/\r\n/g, '\n');
|
||||
const actual = mapper.dumpDebugInfo().replace(/\r\n/g, '\n');
|
||||
if (actual !== expected && writeFileIfDifferent) {
|
||||
|
||||
+11
-10
@@ -2,14 +2,15 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { release, tmpdir } from 'os';
|
||||
import { resolveWorkbenchCommonProperties } from 'vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties';
|
||||
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
import { IStorageService, StorageScope, InMemoryStorageService, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { mkdirp, rimraf } from 'vs/base/node/pfs';
|
||||
import { rimraf } from 'vs/base/node/pfs';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
@@ -18,8 +19,8 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
|
||||
suite('Telemetry - common properties', function () {
|
||||
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'telemetryservice');
|
||||
const installSource = path.join(parentDir, 'installSource');
|
||||
const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'telemetryservice');
|
||||
const installSource = join(parentDir, 'installSource');
|
||||
|
||||
const commit: string = (undefined)!;
|
||||
const version: string = (undefined)!;
|
||||
@@ -43,9 +44,9 @@ suite('Telemetry - common properties', function () {
|
||||
});
|
||||
|
||||
test('default', async function () {
|
||||
await mkdirp(parentDir);
|
||||
await fs.promises.mkdir(parentDir, { recursive: true });
|
||||
fs.writeFileSync(installSource, 'my.install.source');
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, os.release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
assert.ok('commitHash' in props);
|
||||
assert.ok('sessionID' in props);
|
||||
assert.ok('timestamp' in props);
|
||||
@@ -66,7 +67,7 @@ suite('Telemetry - common properties', function () {
|
||||
assert.ok('common.instanceId' in props, 'instanceId');
|
||||
assert.ok('common.machineId' in props, 'machineId');
|
||||
fs.unlinkSync(installSource);
|
||||
const props_1 = await resolveWorkbenchCommonProperties(testStorageService, testFileService, os.release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
const props_1 = await resolveWorkbenchCommonProperties(testStorageService, testFileService, release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
assert.ok(!('common.source' in props_1));
|
||||
});
|
||||
|
||||
@@ -74,14 +75,14 @@ suite('Telemetry - common properties', function () {
|
||||
|
||||
testStorageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, os.release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
assert.ok('common.lastSessionDate' in props); // conditional, see below
|
||||
assert.ok('common.isNewSession' in props);
|
||||
assert.equal(props['common.isNewSession'], 0);
|
||||
});
|
||||
|
||||
test('values chance on ask', async function () {
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, os.release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, release(), commit, version, 'someMachineId', undefined, installSource);
|
||||
let value1 = props['common.sequence'];
|
||||
let value2 = props['common.sequence'];
|
||||
assert.ok(value1 !== value2, 'seq');
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises } from 'fs';
|
||||
import { localize } from 'vs/nls';
|
||||
import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService';
|
||||
import { ITextFileService, ITextFileStreamContent, ITextFileContent, IReadTextFileOptions, IWriteTextFileOptions } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
@@ -10,7 +11,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileStatWithMetadata, FileOperationError, FileOperationResult, IFileService, ByteSize } from 'vs/platform/files/common/files';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { stat, chmod, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs';
|
||||
import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { UTF8, UTF8_with_bom } from 'vs/workbench/services/textfile/common/encoding';
|
||||
@@ -108,11 +109,11 @@ export class NativeTextFileService extends AbstractTextFileService {
|
||||
|
||||
// check for overwriteReadonly property (only supported for local file://)
|
||||
try {
|
||||
if (options?.overwriteReadonly && resource.scheme === Schemas.file && await this.fileService.exists(resource)) {
|
||||
const fileStat = await stat(resource.fsPath);
|
||||
if (options?.overwriteReadonly && resource.scheme === Schemas.file) {
|
||||
const fileStat = await promises.stat(resource.fsPath);
|
||||
|
||||
// try to change mode to writeable
|
||||
await chmod(resource.fsPath, fileStat.mode | 0o200 /* File mode indicating writable by owner (fs.constants.S_IWUSR) */);
|
||||
await promises.chmod(resource.fsPath, fileStat.mode | 0o200 /* File mode indicating writable by owner (fs.constants.S_IWUSR) */);
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore and simply retry the operation
|
||||
@@ -131,7 +132,7 @@ export class NativeTextFileService extends AbstractTextFileService {
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_PERMISSION_DENIED) {
|
||||
let isReadonly = false;
|
||||
try {
|
||||
const fileStat = await stat(resource.fsPath);
|
||||
const fileStat = await promises.stat(resource.fsPath);
|
||||
if (!(fileStat.mode & 0o200 /* File mode indicating writable by owner (fs.constants.S_IWUSR) */)) {
|
||||
isReadonly = true;
|
||||
}
|
||||
|
||||
+11
-4
@@ -3,17 +3,18 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { tmpdir } from 'os';
|
||||
import { promises } from 'fs';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { rimraf, copy, readFile, exists, stat } from 'vs/base/node/pfs';
|
||||
import { rimraf, copy, exists } from 'vs/base/node/pfs';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||
import { tmpdir } from 'os';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { detectEncodingByBOM } from 'vs/workbench/services/textfile/test/node/encoding/encoding.test';
|
||||
@@ -29,6 +30,12 @@ flakySuite('Files - NativeTextFileService i/o', function () {
|
||||
let service: ITextFileService;
|
||||
let testDir: string;
|
||||
|
||||
function readFile(path: string): Promise<Buffer>;
|
||||
function readFile(path: string, encoding: string): Promise<string>;
|
||||
function readFile(path: string, encoding?: string): Promise<Buffer | string> {
|
||||
return promises.readFile(path, encoding);
|
||||
}
|
||||
|
||||
createSuite({
|
||||
setup: async () => {
|
||||
const instantiationService = workbenchInstantiationService();
|
||||
@@ -50,7 +57,7 @@ flakySuite('Files - NativeTextFileService i/o', function () {
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'textfileservice');
|
||||
const sourceDir = getPathFromAmdModule(require, './fixtures');
|
||||
|
||||
await copy(sourceDir, testDir);
|
||||
await copy(sourceDir, testDir, { preserveSymlinks: false });
|
||||
|
||||
return { service, testDir };
|
||||
},
|
||||
@@ -64,7 +71,7 @@ flakySuite('Files - NativeTextFileService i/o', function () {
|
||||
},
|
||||
|
||||
exists,
|
||||
stat,
|
||||
stat: promises.stat,
|
||||
readFile,
|
||||
detectEncodingByBOM
|
||||
});
|
||||
|
||||
@@ -879,7 +879,7 @@ suite('ExtHostSearch', () => {
|
||||
});
|
||||
|
||||
test('basic sibling clause', async () => {
|
||||
mockPFS.readdir = (_path: string) => {
|
||||
mockPFS.readdir = (_path: string): any => {
|
||||
if (_path === rootFolderA.fsPath) {
|
||||
return Promise.resolve([
|
||||
'file1.js',
|
||||
@@ -922,7 +922,7 @@ suite('ExtHostSearch', () => {
|
||||
});
|
||||
|
||||
test('multiroot sibling clause', async () => {
|
||||
mockPFS.readdir = (_path: string) => {
|
||||
mockPFS.readdir = (_path: string): any => {
|
||||
if (_path === joinPath(rootFolderA, 'folder').fsPath) {
|
||||
return Promise.resolve([
|
||||
'fileA.scss',
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IColorRegistry, Extensions, ColorContribution } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
import { asText } from 'vs/platform/request/common/request';
|
||||
import * as fs from 'fs';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as assert from 'assert';
|
||||
@@ -17,7 +17,6 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
|
||||
import 'vs/workbench/workbench.desktop.main';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
|
||||
interface ColorInfo {
|
||||
description: string;
|
||||
offset: number;
|
||||
@@ -106,7 +105,7 @@ async function getColorsFromExtension(): Promise<{ [id: string]: string }> {
|
||||
let result: { [id: string]: string } = Object.create(null);
|
||||
for (let folder of extFolders) {
|
||||
try {
|
||||
let packageJSON = JSON.parse((await pfs.readFile(path.join(extPath, folder, 'package.json'))).toString());
|
||||
let packageJSON = JSON.parse((await fs.promises.readFile(path.join(extPath, folder, 'package.json'))).toString());
|
||||
let contributes = packageJSON['contributes'];
|
||||
if (contributes) {
|
||||
let colors = contributes['colors'];
|
||||
|
||||
Reference in New Issue
Block a user