提交 d6a36ba2 编写于 作者: B Benjamin Pasero

another fix for #42542 (fix it also when encoding is used)

上级 c362830e
......@@ -8,7 +8,7 @@
declare module 'iconv-lite' {
export function decode(buffer: NodeBuffer, encoding: string): string;
export function encode(content: string, encoding: string, options?: { addBOM?: boolean }): NodeBuffer;
export function encode(content: string | NodeBuffer, encoding: string, options?: { addBOM?: boolean }): NodeBuffer;
export function encodingExists(encoding: string): boolean;
......
......@@ -32,7 +32,7 @@ export function decode(buffer: NodeBuffer, encoding: string): string {
return iconv.decode(buffer, toNodeEncoding(encoding));
}
export function encode(content: string, encoding: string, options?: { addBOM?: boolean }): NodeBuffer {
export function encode(content: string | NodeBuffer, encoding: string, options?: { addBOM?: boolean }): NodeBuffer {
return iconv.encode(content, toNodeEncoding(encoding), options);
}
......
......@@ -9,11 +9,11 @@ import * as uuid from 'vs/base/common/uuid';
import * as strings from 'vs/base/common/strings';
import * as platform from 'vs/base/common/platform';
import * as flow from 'vs/base/node/flow';
import * as fs from 'fs';
import * as paths from 'path';
import { TPromise } from 'vs/base/common/winjs.base';
import { nfcall } from 'vs/base/common/async';
import { encode, encodeStream } from 'vs/base/node/encoding';
const loop = flow.loop;
......@@ -319,8 +319,17 @@ export function mv(source: string, target: string, callback: (error: Error) => v
});
}
export interface IWriteFileOptions {
mode?: number;
flag?: string;
encoding?: {
charset: string;
addBOM: boolean;
};
}
let canFlush = true;
export function writeFileAndFlush(path: string, data: string | NodeBuffer | NodeJS.ReadableStream, options: { mode?: number; flag?: string; }, callback: (error?: Error) => void): void {
export function writeFileAndFlush(path: string, data: string | NodeBuffer | NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void {
options = ensureOptions(options);
if (typeof data === 'string' || Buffer.isBuffer(data)) {
......@@ -330,7 +339,7 @@ export function writeFileAndFlush(path: string, data: string | NodeBuffer | Node
}
}
function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: { mode?: number; flag?: string; }, callback: (error?: Error) => void): void {
function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void {
// finish only once
let finished = false;
......@@ -369,6 +378,12 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream,
fd = descriptor;
isOpen = true;
// if an encoding is provided, we need to pipe the stream through
// an encoder stream and forward the encoding related options
if (options.encoding) {
reader = reader.pipe(encodeStream(options.encoding.charset, { addBOM: options.encoding.addBOM }));
}
// start data piping only when we got a successful open. this ensures that we do
// not consume the stream when an error happens and helps to fix this issue:
// https://github.com/Microsoft/vscode/issues/42542
......@@ -415,9 +430,13 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream,
// not in some cache.
//
// See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194
function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: { mode?: number; flag?: string; }, callback: (error?: Error) => void): void {
function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: IWriteFileOptions, callback: (error?: Error) => void): void {
if (options.encoding) {
data = encode(data, options.encoding.charset, { addBOM: options.encoding.addBOM });
}
if (!canFlush) {
return fs.writeFile(path, data, options, callback);
return fs.writeFile(path, data, { mode: options.mode, flag: options.flag }, callback);
}
// Open the file with same flags and mode as fs.writeFile()
......@@ -448,11 +467,15 @@ function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: {
});
}
export function writeFileAndFlushSync(path: string, data: string | NodeBuffer, options?: { mode?: number; flag?: string; }): void {
export function writeFileAndFlushSync(path: string, data: string | NodeBuffer, options?: IWriteFileOptions): void {
options = ensureOptions(options);
if (options.encoding) {
data = encode(data, options.encoding.charset, { addBOM: options.encoding.addBOM });
}
if (!canFlush) {
return fs.writeFileSync(path, data, options);
return fs.writeFileSync(path, data, { mode: options.mode, flag: options.flag });
}
// Open the file with same flags and mode as fs.writeFile()
......@@ -475,12 +498,12 @@ export function writeFileAndFlushSync(path: string, data: string | NodeBuffer, o
}
}
function ensureOptions(options?: { mode?: number; flag?: string; }): { mode: number, flag: string } {
function ensureOptions(options?: IWriteFileOptions): IWriteFileOptions {
if (!options) {
return { mode: 0o666, flag: 'w' };
}
const ensuredOptions = { mode: options.mode, flag: options.flag };
const ensuredOptions: IWriteFileOptions = { mode: options.mode, flag: options.flag, encoding: options.encoding };
if (typeof ensuredOptions.mode !== 'number') {
ensuredOptions.mode = 0o666;
......
......@@ -99,11 +99,11 @@ export function readFile(path: string, encoding?: string): TPromise<Buffer | str
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
const writeFilePathQueue: { [path: string]: Queue<void> } = Object.create(null);
export function writeFile(path: string, data: string, options?: { mode?: number; flag?: string; }): TPromise<void>;
export function writeFile(path: string, data: NodeBuffer, options?: { mode?: number; flag?: string; }): TPromise<void>;
export function writeFile(path: string, data: NodeJS.ReadableStream, options?: { mode?: number; flag?: string; }): TPromise<void>;
export function writeFile(path: string, data: any, options?: { mode?: number; flag?: string; }): TPromise<void> {
let queueKey = toQueueKey(path);
export function writeFile(path: string, data: string, options?: extfs.IWriteFileOptions): TPromise<void>;
export function writeFile(path: string, data: NodeBuffer, options?: extfs.IWriteFileOptions): TPromise<void>;
export function writeFile(path: string, data: NodeJS.ReadableStream, options?: extfs.IWriteFileOptions): TPromise<void>;
export function writeFile(path: string, data: any, options?: extfs.IWriteFileOptions): TPromise<void> {
const queueKey = toQueueKey(path);
return ensureWriteFileQueue(queueKey).queue(() => nfcall(extfs.writeFileAndFlush, path, data, options));
}
......
......@@ -344,11 +344,15 @@ suite('Extfs', () => {
fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory!
extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => {
const readable = toReadable('Hello World');
extfs.writeFileAndFlush(testFile, readable, null, error => {
if (!error || (<any>error).code !== 'EISDIR') {
return onError(new Error('Expected EISDIR error for writing to folder but got: ' + (error ? (<any>error).code : 'no error')), done);
}
// verify that the stream is still consumable (for https://github.com/Microsoft/vscode/issues/42542)
assert.equal(readable.read(), 'Hello World');
extfs.del(parentDir, os.tmpdir(), done, ignore);
});
});
......
......@@ -41,6 +41,7 @@ import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/
import { getBaseLabel } from 'vs/base/common/labels';
import { assign } from 'vs/base/common/objects';
import { Readable } from 'stream';
import { IWriteFileOptions } from 'vs/base/node/extfs';
export interface IEncodingOverride {
resource: uri;
......@@ -582,22 +583,19 @@ export class FileService implements IFileService {
private doSetContentsAndResolve(resource: uri, absolutePath: string, value: string | ITextSnapshot, addBOM: boolean, encodingToWrite: string, options?: { mode?: number; flag?: string; }): TPromise<IFileStat> {
let writeFilePromise: TPromise<void>;
// Write fast if we do UTF 8 without BOM
if (!addBOM && encodingToWrite === encoding.UTF8) {
if (typeof value === 'string') {
writeFilePromise = pfs.writeFile(absolutePath, value, options);
} else {
writeFilePromise = pfs.writeFile(absolutePath, this.snapshotToReadableStream(value), options);
}
// Configure encoding related options as needed
const writeFileOptions: IWriteFileOptions = options ? options : Object.create(null);
if (addBOM || encodingToWrite !== encoding.UTF8) {
writeFileOptions.encoding = {
charset: encodingToWrite,
addBOM
};
}
// Otherwise use encoding lib
else {
if (typeof value === 'string') {
writeFilePromise = pfs.writeFile(absolutePath, encoding.encode(value, encodingToWrite, { addBOM }), options);
} else {
writeFilePromise = pfs.writeFile(absolutePath, this.snapshotToReadableStream(value).pipe(encoding.encodeStream(encodingToWrite, { addBOM })), options);
}
if (typeof value === 'string') {
writeFilePromise = pfs.writeFile(absolutePath, value, writeFileOptions);
} else {
writeFilePromise = pfs.writeFile(absolutePath, this.snapshotToReadableStream(value), writeFileOptions);
}
// set contents
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册