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

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

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