diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index b8b6707cda7..6cffb28f020 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -8,6 +8,8 @@ import stream = require('vs/base/node/stream'); import iconv = require('iconv-lite'); import { TPromise } from 'vs/base/common/winjs.base'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { exec } from 'child_process'; export const UTF8 = 'utf8'; export const UTF8_with_bom = 'utf8bom'; @@ -165,3 +167,46 @@ export function toCanonicalName(enc: string): string { return enc; } } + +export function resolveTerminalEncoding(verbose?: boolean): TPromise { + let rawEncodingPromise: TPromise; + + // Support a global environment variable to win over other mechanics + const cliEncodingEnv = process.env['VSCODE_CLI_ENCODING']; + if (cliEncodingEnv) { + rawEncodingPromise = TPromise.as(cliEncodingEnv); + } + + // Linux/Mac: use "locale charmap" command + else if (isLinux || isMacintosh) { + rawEncodingPromise = new TPromise(c => { + if (verbose) { + console.log('Running "locale charmap" to detect terminal encoding...'); + } + + exec('locale charmap', (err, stdout, stderr) => c(stdout)); + }); + } + + // Windows: educated guess + else { + rawEncodingPromise = TPromise.as('cp850'); + } + + return rawEncodingPromise.then(rawEncoding => { + if (verbose) { + console.log(`Detected raw terminal encoding: ${rawEncoding}`); + } + + if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) { + return UTF8; + } + + const iconvEncoding = toIconvLiteEncoding(rawEncoding); + if (iconv.encodingExists(iconvEncoding)) { + return iconvEncoding; + } + + return UTF8; + }); +} diff --git a/src/vs/base/test/node/encoding/encoding.test.ts b/src/vs/base/test/node/encoding/encoding.test.ts index ce229da6f90..7c5c56f7a9f 100644 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ b/src/vs/base/test/node/encoding/encoding.test.ts @@ -8,6 +8,7 @@ import assert = require('assert'); import encoding = require('vs/base/node/encoding'); +import { encodingExists } from 'vs/base/node/encoding'; suite('Encoding', () => { test('detectBOM UTF-8', (done: (err?: any) => void) => { @@ -54,4 +55,21 @@ suite('Encoding', () => { done(); }, done); }); + + test('resolve terminal encoding (detect)', function (done: (err?: any) => void) { + encoding.resolveTerminalEncoding().then(encoding => { + assert.ok(encodingExists(encoding)); + done(); + }, done); + }); + + test('resolve terminal encoding (environment)', function (done: (err?: any) => void) { + process.env['VSCODE_CLI_ENCODING'] = 'utf16le'; + + encoding.resolveTerminalEncoding().then(encoding => { + assert.ok(encodingExists(encoding)); + assert.equal(encoding, 'utf16le'); + done(); + }, done); + }); }); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index c1d3e5c7537..4d34b406b07 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -15,6 +15,8 @@ import * as os from 'os'; import * as fs from 'fs'; import { whenDeleted } from 'vs/base/node/pfs'; import { findFreePort } from 'vs/base/node/ports'; +import { resolveTerminalEncoding } from 'vs/base/node/encoding'; +import * as iconv from 'iconv-lite'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return !!argv['install-source'] @@ -93,10 +95,13 @@ export async function main(argv: string[]): TPromise { let stdinFileError: Error; stdinFilePath = paths.join(os.tmpdir(), `stdin-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 6)}.txt`); try { + const stdinFileStream = fs.createWriteStream(stdinFilePath); + resolveTerminalEncoding(verbose).done(encoding => { - // Pipe into tmp file - process.stdin.setEncoding('utf8'); - process.stdin.pipe(fs.createWriteStream(stdinFilePath)); + // Pipe into tmp file using terminals encoding + const converterStream = iconv.decodeStream(encoding); + process.stdin.pipe(converterStream).pipe(stdinFileStream); + }); // Make sure to open tmp file argv.push(stdinFilePath);