coc.nvimはVimのLSPクライアント・プラグインの一つです。
cocは自分自身がVimプラグインでありながら、拡張機能(エクステンション)の機構を持っており、LSPサーバをエクステンションをとしてインストールすることで機能を追加することができます。
PHPのLSPサーバのcocエクステンションもいくつかあります。例えばPHPのLSPサーバであるIntelephenseのcocエクステンションとしてcoc-phplsがあります。
coc-phplsをインストールするとcoc経由でPHPの自動補完・定義ジャンプ・ホバー(ヒント)表示・バグ検出などの機能を使えるようになります。
coc-phplsだけでもある程度のバグ検知は可能ですが、Intelephenseが提供するバグ検出は現時点ではPHPStanに比べると弱く、またPHP_CodeSnifferのようにコーディング規約違反を検出するものではありません。
こういうときに使えるのがdiagnostic-languageserverのcocエクステンションであるcoc-diagnosticです。
diagnostic-languageserverは各種linterと統合することで診断機能を提供する汎用言語サーバです。そのcocエクステンションであるcoc-diagnosticを使えば、PHPStanやPHP_CodeSnifferをcoc経由で使えるようになります。
インストール方法
-
coc-diagnosticをインストールする。
:CocInstall coc-diagnostic
-
PHPStan、PHP_CodeSnifferをインストールする。
composer require --dev phpstan/phpstan
composer require squizlabs/php_codesniffer
-
coc-settings.json
に下記を設定する。
{
"diagnostic-languageserver.filetypes": {
"php": ["phpcs", "phpstan"]
}
}
coc-diagnosticはデフォルトの設定で./vendor/bin/phpstan
や./vendor/bin/phpcs
を実行するようになっているので、Composerでローカルインストールしたのであればこれだけで使えるはずです。
こんな感じでcoc経由でPHPStanやPHP_CodeSnifferの出力が見れると思います。
linterの設定をカスタマイズする
coc-diagnosticはlinterの設定をカスタマイズすることもできます。下記に例を載せます。
{
"diagnostic-languageserver.mergeConfig": true,
"diagnostic-languageserver.linters": {
"phpcs": {
"command": "./tools/phpcs",
"args": ["--report=emacs", "-s", "-"],
},
"phpstan": {
"command": "./tools/phpstan"
}
}
}
diagnostic-languageserver.mergeConfig
をtrue
に設定すると、追加した設定をデフォルトの設定とマージしてくれるので、差分だけを記載することができます。
上記の例ではcommand
で実行ファイルのパスをComposerではなくPHIVEでローカルインストールした場合のパス(./tools/phpstan
や./tools/phpcs
)に変更しています。また、PHP_CodeSnifferのデフォルトのargs
から"--standard=PSR2"
を削除することで、プロジェクトディレクトリ配下にあるphpcs.xml
を使うようにしています。
Dockerコンテナ上のPHPを使う
私はDocker(Laradock)を使って開発しており、ローカルにはPHPをインストールしていません。なので当然ですが、ローカルのPHPStanやPHP_CodeSnifferを実行できないため、上記の設定では動きません。こんな場合でも設定をひと工夫すれば、少し無理やりですがDockerコンテナ上のPHPを使って動かすことができます。
{
"diagnostic-languageserver.mergeConfig": true,
"diagnostic-languageserver.linters": {
"ngmy.laradock.phive.phpcs": {
"command": "docker",
"debounce": 100,
"rootPatterns": ["composer.json", "composer.lock", "vendor", ".git"],
"args": ["exec", "-u", "laradock", "laradock_workspace_1", "php", "tools/phpcs", "--report=emacs"],
"offsetLine": 0,
"offsetColumn": 0,
"sourceName": "phpcs",
"formatLines": 1,
"formatPattern": [
"(^.*):(\\d+):(\\d+):\\s+(.*)\\s+-\\s+(.*)(\\r|\\n)*$",
{
"sourceName": 1,
"sourceNameFilter": true,
"line": 2,
"column": 3,
"message": 5,
"security": 4
}
],
"securities": {
"error": "error",
"warning": "warning"
}
},
"ngmy.laradock.phive.phpstan": {
"command": "docker",
"debounce": 100,
"rootPatterns": ["composer.json", "composer.lock", "vendor", ".git"],
"args": ["exec", "-u", "laradock", "laradock_workspace_1", "php", "tools/phpstan", "analyse", "--error-format", "raw", "--no-progress"],
"offsetLine": 0,
"offsetColumn": 0,
"sourceName": "phpstan",
"formatLines": 1,
"formatPattern": [
"^/var/www/([^:]+):(\\d+):(.*)(\\r|\\n)*$",
{
"sourceName": 1,
"sourceNameFilter": true,
"line": 2,
"message": 3
}
]
}
},
"diagnostic-languageserver.filetypes": {
"php": ["ngmy.laradock.phive.phpcs", "ngmy.laradock.phive.phpstan"]
}
}
PHPStanとPHP_CodeSnifferの新しいlinter設定を追加しています。設定内容はデフォルトの設定をベースにしています。デフォルトの設定を上書きしていない理由は、Docker越しに使うという用途が特殊なので、デフォルトの設定を残しておきたいと思ったからです。設定名はなんでも良いのですが、デフォルトの設定と被らないようにしています。
command
とargs
を使ってdocker exec
を実行することで、Dockerコンテナ(私の場合はLaradockのworkspaceコンテナ)でPHPStanやPHP_CodeSnifferを実行するようにしています。
PHPStanのデフォルトのargs
の最後にある"%file"
を削除しています。%file
はdiagnostic-languageserverが用意しているargs
で使える変数で、Vimで開いているファイルのフルパスに展開されます。他にもいくつかの変数が用意されています。削除する理由は、フルパスに展開されるとローカルのパスとDockerコンテナ上のパスが食い違ってしまい解析できないためです。削除すると、プロジェクトディレクトリ配下のphpstan.neon
を使うようになるので、そこに記載されている全対象ファイルに対してPHPStanが実行されるようになります。
全対象ファイルに対して実行されたPHPStanの出力を今Vimで開いているファイルに対してだけ表示するために、formatPattern
の正規表現を変更して相対パスを抽出してSourceName
として使用しています。さらに複数ファイルを開いたときに出力が混ざらないように、sourceNameFilter
をtrue
にしています。
もし相対パスに展開してくれる変数があればそれで済む話ですが、今のところないようなので、こんな設定をしています。
同じ理由で、PHP_CodeSnifferもargs
から"-s", "-"
を削除して、formatPattern
を変更しています。
diagnostic-languageserver.mergeConfig
をtrue
にしているのはコンテナ名を上書きできるようにするためです(後述)。
全対象ファイルに対して実行しているせいか少し処理が遅い気がしますが、数十ファイル程度の小さなプロジェクトであれば許容範囲内に感じています。
任意のDockerコンテナ上のPHPを使う
プロジェクトごとにLaradockをインストールしている場合、プロジェクトごとにコンテナ名が異なると思います。そういうときはプロジェクトディレクトリの配下に.vim/coc-settings.json
を作ります。
cocは~/.vim/coc-settings.json
とプロジェクトディレクトリ配下の.vim/coc-settings.json
をマージしてくれます。この辺りの仕様についてはここに記載があります。
{
"diagnostic-languageserver.linters": {
"ngmy.laradock.phive.phpcs": {
"args": ["exec", "-u", "laradock", "laradock-some-project_workspace_1", "php", "tools/phpcs", "--report=emacs"]
},
"ngmy.laradock.phive.phpstan": {
"args": ["exec", "-u", "laradock", "laradock-some-project_workspace_1", "php", "tools/phpstan", "analyse", "--error-format", "raw", "--no-progress"],
}
}
}
args
でコンテナ名を変更しています。
こんな感じの設定を、cocを使いたい各プロジェクトに書いています。
以上です。
2021年5月7日追記
diagnostic-languageserverに相対パスに展開してくれる変数%relativepath
が追加されたので、Dockerコンテナ上のPHPを使う場合、下記の設定で単一のファイルに対してのみlinterを実行できるようになりました。
{
"diagnostic-languageserver.mergeConfig": true,
"diagnostic-languageserver.linters": {
"ngmy.laradock.phive.phpcs": {
"command": "docker",
"debounce": 100,
"rootPatterns": ["composer.json", "composer.lock", "vendor", ".git"],
"args": ["exec", "-u", "laradock", "laradock-workspace_1", "php", "tools/phpcs", "--report=emacs", "%relativepath"],
"offsetLine": 0,
"offsetColumn": 0,
"sourceName": "phpcs",
"formatLines": 1,
"formatPattern": [
"^.*:(\\d+):(\\d+):\\s+(.*)\\s+-\\s+(.*)(\\r|\\n)*$",
{
"line": 1,
"column": 2,
"message": 4,
"security": 3
}
],
"securities": {
"error": "error",
"warning": "warning"
}
},
"ngmy.laradock.phive.phpstan": {
"command": "docker",
"debounce": 100,
"rootPatterns": ["composer.json", "composer.lock", "vendor", ".git"],
"args": ["exec", "-u", "laradock", "laradock-workspace_1", "php", "tools/phpstan", "analyse", "--error-format", "raw", "--no-progress", "%relativepath"],
"offsetLine": 0,
"offsetColumn": 0,
"sourceName": "phpstan",
"formatLines": 1,
"formatPattern": [
"^[^:]+:(\\d+):(.*)(\\r|\\n)*$",
{
"line": 1,
"message": 2
}
]
}
},
"diagnostic-languageserver.filetypes": {
"php": ["ngmy.laradock.phive.phpcs", "ngmy.laradock.phive.phpstan"]
}
}
{
"diagnostic-languageserver.linters": {
"ngmy.laradock.phive.phpcs": {
"args": ["exec", "-u", "laradock", "laradock-some-project_workspace_1", "php", "tools/phpcs", "--report=emacs", "%relativepath"]
},
"ngmy.laradock.phive.phpstan": {
"args": ["exec", "-u", "laradock", "laradock-some-project_workspace_1", "php", "tools/phpstan", "analyse", "--error-format", "raw", "--no-progress", "%relativepath"],
}
}
}