diff --git a/lua/custom/none-ls.lua b/lua/custom/none-ls.lua index 460136e..55e71db 100644 --- a/lua/custom/none-ls.lua +++ b/lua/custom/none-ls.lua @@ -1,5 +1,20 @@ local null_ls = require("null-ls") +-- phpstan: use the project's own phpstan.neon when there is one, otherwise fall +-- back to the generic config in dotfiles. +local phpstan_global_config = vim.fn.expand("~/repos/dotfiles/php/phpstan-global.neon") + +-- returns the config path to pass via -c, or nil when phpstan can discover a +-- local config itself (that also picks up its `includes:`, e.g. a baseline) +local function phpstan_config_arg(root) + for _, name in ipairs({ "phpstan.neon", "phpstan.neon.dist" }) do + if vim.loop.fs_stat(root .. "/" .. name) then + return nil + end + end + return phpstan_global_config +end + null_ls.setup({ sources = { null_ls.builtins.formatting.phpcbf.with({ @@ -7,16 +22,32 @@ null_ls.setup({ extra_args = { "--standard=PSR12", "--standard=phpcs.xml" }, }), null_ls.builtins.diagnostics.phpstan.with({ - command = "phpstan", - args = { - "analyse", - "--error-format", "raw", - "--no-progress", - "$FILENAME", -- or your project's entrypoint folder - }, - method = null_ls.methods.DIAGNOSTICS_ON_SAVE, -- or ON_OPEN or BOTH + -- prefer the project's own phpstan (extensions and baselines are + -- version bound), fall back to the mason binary + dynamic_command = function(params, done) + local local_bin = params.root .. "/vendor/bin/phpstan" + if vim.fn.executable(local_bin) == 1 then + return done(local_bin) + end + return done("phpstan") + end, + args = function(params) + local args = { "analyse", "--error-format", "json", "--no-progress" } + local config = phpstan_config_arg(params.root) + if config then + vim.list_extend(args, { "-c", config }) + end + table.insert(args, "$FILENAME") + return args + end, + -- analyse the file on disk, not a copy in /tmp: phpstan needs the + -- project's autoloader and its configured paths/scanDirectories + method = null_ls.methods.DIAGNOSTICS_ON_SAVE, + to_temp_file = false, + timeout = 60000, -- a cold run easily exceeds null-ls' 5s default condition = function(utils) return utils.root_has_file({ "phpstan.neon", "phpstan.neon.dist" }) + or vim.loop.fs_stat(phpstan_global_config) ~= nil end, }), null_ls.builtins.diagnostics.phpcs.with({ @@ -38,5 +69,4 @@ null_ls.setup({ -- no extra_args needed if config file is correctly named and in root }), }, - on_attach = on_attach, })