基本は utf-8 だけど shiftjis のファイルを開くこともよくある、という状況にあります。
vscodeで "files.autoGuessEncoding": true の設定にしていても、shiftjis なファイルを開くとちょくちょく誤判定するので困っていました。
機能拡張のAPIでファイルが表示される前の処理に介入するのも無理そうなので(※1)、ソースコードに手を入れることにしました、という話です。
※1: 調べたのは ver.1.19 くらいの頃。それ以降APIも変わっているかもしれません。
アップデートのたびにビルドし直すのも手間なので、極力少ない変更で済むようにしました。
言い換えれば、日本語文字コード判定の改善のみを目的とした大雑把な変更となっていますが、
同じような悩みを持つ方の参考になる部分もあるかと思い掲載します。
vscode のバージョンは 1.23 です。
Windows10 と Ubuntu 16.04 の 64bit版でビルドして使用しています。
node とか typescript とか普段あまり馴染みがないので、
間違いやお気づきの点がありましたら、ご指摘いただけると大変有難いです。
vscode の GitHub のリポジトリ にある、ソースからのビルドの手順 (How to build and run from source) に従ってソースコードを持ってきてから yarn を実行して node モジュールも揃えます。以下はリリースのブランチを取得しています。
git clone -b release/1.23 https://github.com/Microsoft/vscode.git
cd vscode
yarn
node モジュールの一つ、jschardet を変更します。以下では SBCSGroupProber, Latin1Prober をコメントアウトしています。
(vscode/node_modules/jschardet/src/universaldetector.js)
@@ -132,13 +132,13 @@ jschardet.UniversalDetector = function() {
this.done = true;
}
} else if( this._mInputState == _state.highbyte ) {
if( this._mCharsetProbers.length == 0 ) {
this._mCharsetProbers = [
- new jschardet.MBCSGroupProber(),
- new jschardet.SBCSGroupProber(),
- new jschardet.Latin1Prober()
+ new jschardet.MBCSGroupProber() //,
+ //new jschardet.SBCSGroupProber(),
+ //new jschardet.Latin1Prober()
];
}
for( var i = 0, prober; prober = this._mCharsetProbers[i]; i++ ) {
if( prober.feed(aBuf) == jschardet.Constants.foundIt ) {
this.result = {
この変更の副作用として、SBCSGroupProber に含まれるエンコーディング(※2) と Latin1Prober に含まれるエンコーディング(windows-1252) は自動判定されなくなります。これらのエンコーディングの自動判定が必要、という方は、この方法は使えないことになります。(「エンコード付きで再度開く」で指定することはできます。)
※2: windows-1251, KOI8-R, ISO-8859-5, MacCyrillic, IBM866, IBM855, ISO-8859-7, windows-1253, ISO-8859-5, ISO-8859-2, windows-1250, TIS-620
jschardet が使う定数、MINIMUM_THRESHOLD を 0.2 から 0.0 にします。
(vscode/src/vs/base/node/encoding.ts)
@@ -185,7 +185,7 @@ export function detectEncodingByBOM(file: string): TPromise<string> {
return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead));
}
-const MINIMUM_THRESHOLD = 0.2;
+const MINIMUM_THRESHOLD = 0.0;
const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32'];
/**
この値の意味は、GitHub の jschardet リポジトリ に以下の記載があります。 これの通りで、数字ばかりで後ろの方に少し日本語があるようなデータの場合、0.2 のままだと判定に失敗します。
// Default minimum accepted confidence level is 0.20 but sometimes this is not
// enough, specially when dealing with files mostly with numbers.
// To change this to 0 to always get something or any other value that can
// work for you.
変更箇所は以上です。あとは公式の手順に従ってビルド(Packaging)します。以下はコマンドの例です。
# windows
SET PYTHON=C:\Python27\python.exe
yarn run gulp vscode-win32-x64
# linux
yarn run gulp vscode-linux-x64
今回の変更をするしないにかかわらず、できあがった "Code - OSS" の設定には、サイドバーから機能拡張を取得するための Marketplace の場所が設定されていません。必要に応じて設定に追加する必要があります。
私は、正式版の product.json (C:\Program Files\Microsoft VS Code\resources\app\product.json) から、"extensionsGallery" の部分をコピーして、"Code - OSS" の方の同じファイルの適当な場所に挿入して保存しています。
参考:How do I enable extensions in the extensions dir in OSS Dev mode #23831
複数ファイルの一括検索は既定のエンコーディングで行っていると思われ、違うエンコーディングのファイルの中身の日本語は検索できません。(ファイルを開いていると検索できる模様。)この挙動は今回の変更を行っても変わりません。
必要に応じて workspace ごとの設定で "files.encoding" を shiftjis などに設定すればよいのですが、そのうち忘れて何かやらかしそうな不安。
このへんのエンコーディングも無効にすれば解決すると思われますが、変更箇所が多くなるので保留。
というわけで、誤判定については vscode というより jschardet の問題 (いや問題というか、私は文字コードの判定処理には全く疎いのですが、恐らく jschardet の判定処理自体は多くの文字コードに対応するための最適解で、これを改善するのはなかなか難しいのではないかという気が何となくします。) なので、上の変更のようなその場しのぎでなく、もっとまともな改善策があったとして github の Issues などで提案するにしても、大変まどろっこしい話になると思われるため、こういう形で対応していくしかないかなあと思っています。
以上です。
ソースコードを一部引用しているため、元コードのライセンスを付記します。
jschardet
Ported from python to JavaScript by António Afonso (https://github.com/aadsm/jschardet)
license : LGPL-2.1+
vscode
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. (https://github.com/Microsoft/vscode)
自ビルドまでしなくとも同じような変更は行えました