【VSCode】タスクの問題マッチャ―(problemMatcher)を理解してみる
更新日:2023/02/06
VSCodeのタスクを定義する task.json には、"problemMatcher"というプロパティを登録できます。
少し時間がかかりましたが英語のドキュメントと格闘して使い方を理解しました。
あまり使う機会がないという印象です。
しかし、せっかく調べたので忘れないうちに内容をまとめておきます。
問題マッチャ―の役割
タスクには、ソースファイルを読み込んで処理するプログラム、例えばコンパイラやESLintなどからの出力を解析して、ソースファイルの問題点を問題パネルに表示する機能があります。
次の図は、タスクで実行したプログラムからの出力を解析して、問題パネルに表示したものです。
[Ln 2、Col 5]は、2行目の5桁目という意味です。
ソースコードの該当する位置に、エラーを意味する記号(赤い波線)が表示されていて、カーソルを重ねるとエラー内容が表示されました。
タスクの結果と連動しているのがわかりますね。
問題マッチャ―は、この機能を定義します。
定義済み問題マッチャ―
VSCodeにはあらかじめいくつかの問題マッチャ―が定義されています。
例
次のタスク定義で、$tscが定義済問題マッチャ―です。
{
"label": "test",
"type": "shell",
"command": "tsc ${file}",
"problemMatcher": ["$tsc"]
}
配列([ ])内に文字列(""で囲む)として定義している点に注意ですね。
一覧
次の問題マッチャ―が定義されています。
問題マッチャ―名 | 内容 |
---|---|
$tsc | TypeScriptからの出力 |
$tsc-watch | TypeScriptの監視モードからの出力 |
$jshint | JSHintからの出力 |
$jshint-stylish | JSHintのスタイリッシュ形式からの出力 |
$eslint-compact | ESLintからの出力 |
$eslint-stylish | ESLintのスタイリッシュ形式からの出力 |
$go | Goコンパイラからの出力 |
$mscompile | CSharp と VB コンパイラからの出力 |
$lessc | Lessc コンパイラからの出力 |
$node-sass | Node Sass コンパイラからの出力 |
表にある機能をタスクから実行するときは、定義済問題マッチャ―を使用しましょう。
カスタマイズ
定義済問題マッチャ―を、独自にカスタマイズすることもできます。
"problemMatcher"の"base"に定義済問題マッチャ―を記述します。
{
"label": "test",
"type": "shell",
"command": "tsc ${file}",
"problemMatcher": {
"base": "$tsc",
他のプロパティを定義
}
}
まず、定義済問題マッチャ―が元となるマッチャ―として読み込まれます。
その後、"problemMatcher"の他のプロパティで上書きされます。
出力形式が似ているけれど少し異なる場合などは、この機能が役立ちますね。
1行問題マッチャ―
定義済みマッチャ―にないプログラムの処理結果は、独自の問題マッチャーを定義して解析します。
まずは、一番簡単な1行問題マッチャ―を Node.js で再現してみます。
今回作成したのは、メッセージを出力するだけの非常に単純なコードです。
test.js
console.log( "error:test2.js:行2:桁5:〇〇エラーです:エラーコードxx-error" );
内容は、「test2.js の 2行目の5桁目でエラーが発生した」というものです。
test2.js は、実際にエラーがあるかどうかは関係ないのでソースを掲載しません。
次に、test.jsを実行して結果を問題マッチャ―で解析する tasks.json を作成します。
tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "test",
"type": "shell",
"command": "node test.js",
"problemMatcher": {
"owner": "javatestscript",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": {
"kind": "location",
"regexp": "^(.*):(.*):行(\\d+):桁(\\d+):(.+):エラーコード(.+)$",
"file": 2,
"line" : 3,
"column": 4,
"severity": 1,
"message": 5,
"code": 6
}
}
}
]
}
タスクを実行すると、問題パネルに次のような問題が表示されます。
問題マッチャーは、"problemMatcher"で定義します。
"problemMatcher"の各プロパティは、次のような意味があります。
"owner"プロパティ
問題の所有者。たぶん言語のID。
規定値は "external"
"fileLocation"プロパティ
ファイル名として取り込んだパスの扱い方を指定します。
値 | 意味 |
---|---|
"absolute" | 絶対パスとして扱う |
"relative"(規定値) | 相対パスとして扱う |
["relative", パス ] | パスに対する相対パスとして扱う |
"autodetect" | ワークスペースディレクトリに対する相対パスとして扱う。 ただし、ファイルが存在しない場合は絶対パスとして扱う。 |
["autodetect", パス ] | パスに対する相対パスとして扱う。 ただし、ファイルが存在しない場合は絶対パスとして扱う。 |
"pattern"プロパティ
プログラムから出力された文字列を正規表現で解析するために、必要な情報を定義します。
ここでは、次のようなプロパティを指定しています。
プロパティ | 意味 | 値 |
---|---|---|
"kind" | 問題の範囲。 規定値"location" | "file":ファイル全体の問題 "location":内容の問題 |
"regexp" | 行に対する正規表現 | 正規表現パターン |
"file" | ファイル名の位置 | 正規表現の キャプチャグループ番号 |
"line" | 問題の行。 "kind"が"file"の時無効 | |
"column" | 問題の桁。 "kind"が"file"の時無効 | |
"severity" | 問題の重要度。 "error"、"warning"、"info"のどれか | |
"message" | 問題の内容 | |
"code" | エラーコード |
この他に、いくつかのプロパティを指定できます。
プロパティ | 意味 | 値 |
---|---|---|
"endLine" | 問題が発生した終わり行。 このプロパティがあるときは"line"が開始行 | 正規表現の キャプチャグループ番号 |
"endColumn" | 問題が発生した終わり桁。 このプロパティがあるときは"column"が開始桁 | |
"location" | カンマ区切りでの位置指定 取得した値をプロパティに展開する 数値 → "line" |
"location"は、例えばプログラムから次のような文字列を取得したとき、
xxx.js(5,2):error
プロパティが次のようになっていると、
"regexp": "(.+)\\((.+)\\):(.+)"
"location": 2
5,2 を自動で展開して、"line"に 5、"column"に 2 がセットされます。
(結果を見るとセットされているように感じるだけで、実際にはセットされていないかもしれません)
"source"プロパティ
上記のコード内にありませんが、エディタのインテリセンス機能で候補として表示されるので紹介します。
インテリセンスでは次のような説明が表示されます。
「'typescript' または 'super lint' のような、この診断のソースを記述する解読可能な文字列」
問題を報告したツールの名前を指定するようですね。
"applyTo"プロパティ
"applyTo"はVSCodeのドキュメントで少しだけ触れていますが、詳細不明なプロパティです。
インテリセンスの説明は、次のようになっています。
「テキスト ドキュメントで報告された問題が、開いているドキュメントのみ、閉じられたドキュメントのみ、すべてのドキュメントのいずれに適用されるかを制御します。」
そして3つの値が候補として表示されます。
値 | 意味 |
---|---|
"allDocuments" | 全てのドキュメントに適用 |
"openDocuments" | 未保存(編集中)ドキュメントに適用 |
"closedDocuments" | 保存済ドキュメントに適用 |
「閉じている/開いている」はエディターで表示しているかどうかのような気がしますが、実際に動作させてみると「保存済み/編集中」という意味のようです。
規定値は"allDocuments"です。
複数行問題マッチャ―
問題が複数行にわたる場合につい解説します。
全ての行が同じフォーマット
実際の問題通知は、次のように複数の問題が通知されるケースが多いです。
test.js
console.log( "error:test2.js:行2:桁5:〇〇エラーその1です:エラーコードxx-error" );
console.log( "error:test2.js:行1:桁2:〇〇エラーその2です:エラーコードxx-error" );
console.log( "warning:test2.js:行3:桁1:警告です:エラーコードxx-warning" );
console.log( "info:test2.js:行1:桁1:情報です:エラーコードxx-info" );
このようにプログラムから出力される問題が全て同じフォーマットの場合、"pattern" に 値が true の"loop"プロパティを追加します。
"loop"プロパティが falseなら1回のみ、trueなら繰り返しマッチング処理をおこなってくれます。
tasks.json に"loop"プロパティを追加してみます。
tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "test",
"type": "shell",
"command": "node test.js",
"problemMatcher": {
"owner": "javatestscript",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": {
"kind": "location",
"regexp": "^(.*):(.*):行(\\d+):桁(\\d+):(.+):エラーコード(.+)$",
"file": 2,
"line" : 3,
"column": 4,
"severity": 1,
"message": 5,
"code": 6,
"loop" : true
}
}
}
]
}
タスクを実行すると、次のように複数の問題が取り込まれて表示されました。
行のフォーマットが異なる場合
出力された行のフォーマットが異なる場合は、少し面倒です。
例えば、次のようにファイル名の行と、その他のエラー内容の行が出力されるとします。
test.js
console.log( "test2.js" );
console.log( "error:行2:桁5:〇〇エラーその1です:エラーコードxx-error" );
console.log( "error:行1:桁2:〇〇エラーその2です:エラーコードxx-error" );
console.log( "warning:行3:桁1:警告です:エラーコードxx-warning" );
console.log( "info:行1:桁1:情報です:エラーコードxx-info" );
このケースでは、"pattern"の内容を配列化して、1行目を処理するパターンと、その他を処理するパターンを作成します。
1行目のパターンは "regexp"と"file"プロパティを指定します。
2行目のパターンは "regexp"と"loop"、および "file"以外の位置指定プロパティを指定します。
tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "test",
"type": "shell",
"command": "node test.js",
"problemMatcher": {
"owner": "javatestscript",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": [
{
"regexp": "^(.*)$",
"file": 1,
},
{
"regexp": "^(.+):行(\\d+):桁(\\d+):(.+):エラーコード(.+)$",
"line" : 2,
"column": 3,
"severity": 1,
"message": 4,
"code": 5,
"loop" : true
}
]
}
}
]
}
"loop"は最後のパターンのみに使用できます。
複数回使用したり、途中のパターンで使用したりするとエラーになります。
タスクを実行すると、次のような結果になります。
全ての行が同じフォーマットのときと、同じですね。
上記のタスクは、次のように同じ形式の繰り返しにも対応してくれます。
test.js
console.log( "test2.js" );
console.log( "error:行2:桁5:〇〇エラーその1です:エラーコードxx-error" );
console.log( "error:行1:桁2:〇〇エラーその2です:エラーコードxx-error" );
console.log( "test3.js" );
console.log( "warning:行3:桁1:警告です:エラーコードxx-warning" );
console.log( "test2.js" );
console.log( "info:行1:桁1:情報です:エラーコードxx-info" );
タスクを実行すると、次のように全て取り込んでくれます。
バックグランドタスクの問題マッチャ―
ツールにはファイルの変更などを監視して、ファイルの問題を報告するものがあります。
このようなツールはバックグランドで動作します。
タスク機能で起動させたら、バックグランドタスクですね。
バックグランドタスクから問題を取り込む方法をお伝えします。
isBackgroundプロパティを使用
通常のタスクは、タスク終了時に問題が処理されます。
しかしバックグランドタスクは問題が報告される都度、処理する必要があります。
そこで、バックグラウンドで動作することを示すプロパティ"isBackground"を使用します。
"isBackground"の規定値は false です。
バックグラウンドタスクには、trueを指定しましょう。
"isBackground"は、"label"や"type"などと同じ階層で使用します。
tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "test",
"type": "shell",
"command": "node test.js",
"isBackground": true,
"problemMatcher": {
"owner": "javatestscript",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": [
{
"regexp": "^(.*)$",
"file": 1,
},
{
"regexp": "^(.+):行(\\d+):桁(\\d+):(.+):エラーコード(.+)$",
"line" : 2,
"column": 3,
"severity": 1,
"message": 4,
"code": 5,
"loop" : true
}
]
}
}
]
}
問題を出力する test.js は、タイマーで10秒ごとにメッセージ出力するようにコードを作成しました。
test.js
const intervalFunc = ()=>{
const col = Math.floor(Math.random() * 10);
console.log( "test2.js" );
console.log(`error:行2:桁${col}:〇〇エラーです:エラーコードxx-error`);
};
intervalFunc();
setInterval(intervalFunc, 10000);
最初は"isBackground": を false にしてタスクを実行してみてください。
ターミナルにログ出力されますが問題として処理されないはずです。
そしてタスクを終了すると、処理されます。
次は"isBackground": を true にしてタスクを実行してみてください。
ターミナルにログ出力されると、連動して問題パネルに表示されるはずです。
問題が通知されるたびに、問題パネルの行が増えていきますね。
backgroundプロパティ
ファイルの更新時に内容をチェックして問題を通知する場合などは、前回の問題を消去するのが望ましいです。
このようなケースでは、backgroundプロパティで問題通知の開始と終了を定義します。
今回は 前項(isBackgroundプロパティを使用)の状況に、"report start:" で通知開始、"report end:" で通知が終了するという条件を加えてみます。
tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "test",
"type": "shell",
"command": "node test.js",
"isBackground": true,
"problemMatcher": {
"owner": "javatestscript",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": [
{
"regexp": "^(.*)$",
"file": 1,
},
{
"regexp": "^(.+):行(\\d+):桁(\\d+):(.+):エラーコード(.+)$",
"line" : 2,
"column": 3,
"severity": 1,
"message": 4,
"code": 5,
"loop" : true
}
],
"background": {
"activeOnStart": false,
"beginsPattern": "^report start:$",
"endsPattern": "^report end:$"
}
},
}
]
}
"background"のプロパティは3つです。
次のようになっています。
プロパティ名 | 内容 |
---|---|
"activeOnStart" | タスク起動時にウォッチャーをアクティブにするかどうか。beginPattern に一致する行を発行することと同じ。 |
"beginsPattern" | 通知開始の正規表現パターン |
"endsPattern" | 通知終了の正規表現パターン |
"activeOnStart"は、trueまたはfalseを指定します。
これについては、よくわかりません。
今回のケースでは、trueでもfalseでも動作は同じです。
上手くいかないときに、変更してみてください。
test.js のコードも掲載しておきます。
開始と終了のテキスト出力を追加しただけです。
test.js
const intervalFunc = ()=>{
console.log( "report start:" );
const col = Math.floor(Math.random() * 10);
console.log( "test2.js" );
console.log(`error:行2:桁${col}:〇〇エラーです:エラーコードxx-error`);
console.log( "report end:");
};
intervalFunc();
setInterval(intervalFunc, 10000);
このコードでタスクを実行すると、"report start:"で前回の問題がクリアされて、新しい問題が表示されます。
更新日:2023/02/06
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。