120 lines
3.9 KiB
Lua
120 lines
3.9 KiB
Lua
-- none-ls.nvim: Formatting only (linting moved to nvim-lint)
|
|
-- Philosophy: Formatters are authoritative. Project-local executables preferred.
|
|
-- Note: none-ls removed most linters from builtins, so we use nvim-lint for diagnostics
|
|
|
|
return {
|
|
"nvimtools/none-ls.nvim",
|
|
dependencies = { "nvim-lua/plenary.nvim" },
|
|
event = { "BufReadPre", "BufNewFile" },
|
|
config = function()
|
|
local null_ls = require("null-ls")
|
|
local augroup = vim.api.nvim_create_augroup("LspFormatting", {})
|
|
|
|
-- Helper: Find project-local executable, fallback to global
|
|
-- Searches node_modules/.bin/, vendor/bin/, and Mason bin first
|
|
local function find_executable(names)
|
|
local cwd = vim.fn.getcwd()
|
|
local mason_bin = vim.fn.stdpath("data") .. "/mason/bin/"
|
|
|
|
-- Try project-local paths first, then Mason, then global
|
|
local search_paths = {
|
|
cwd .. "/node_modules/.bin/",
|
|
cwd .. "/vendor/bin/",
|
|
mason_bin,
|
|
}
|
|
|
|
for _, name in ipairs(names) do
|
|
for _, path in ipairs(search_paths) do
|
|
local full_path = path .. name
|
|
if vim.fn.executable(full_path) == 1 then
|
|
return full_path
|
|
end
|
|
end
|
|
|
|
-- Fallback to system PATH
|
|
if vim.fn.executable(name) == 1 then
|
|
return name
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
-- Formatters
|
|
local formatting = null_ls.builtins.formatting
|
|
|
|
-- Note: Diagnostics (linters) moved to nvim-lint plugin
|
|
-- Note: Python formatting handled by ruff LSP
|
|
|
|
null_ls.setup({
|
|
sources = {
|
|
-- Prettier (JS, TS, CSS, SCSS, JSON, Markdown, HTML)
|
|
formatting.prettier.with({
|
|
command = find_executable({ "prettier" }),
|
|
prefer_local = "node_modules/.bin",
|
|
}),
|
|
|
|
-- PHP CodeSniffer (phpcbf) - WordPress standards
|
|
formatting.phpcbf.with({
|
|
command = find_executable({ "phpcbf" }),
|
|
prefer_local = "vendor/bin",
|
|
-- Respects phpcs.xml in project root or uses WordPress standard
|
|
extra_args = function()
|
|
local phpcs_xml = vim.fn.findfile("phpcs.xml", ".;")
|
|
if phpcs_xml == "" then
|
|
phpcs_xml = vim.fn.findfile("phpcs.xml.dist", ".;")
|
|
end
|
|
if phpcs_xml == "" then
|
|
return { "--standard=WordPress" }
|
|
end
|
|
return {}
|
|
end,
|
|
}),
|
|
|
|
-- stylua (Lua)
|
|
formatting.stylua.with({
|
|
command = find_executable({ "stylua" }),
|
|
}),
|
|
},
|
|
|
|
-- Format on save
|
|
on_attach = function(client, bufnr)
|
|
if client.supports_method("textDocument/formatting") then
|
|
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
|
|
vim.api.nvim_create_autocmd("BufWritePre", {
|
|
group = augroup,
|
|
buffer = bufnr,
|
|
callback = function()
|
|
-- Only format if format-on-save is enabled (global flag)
|
|
if vim.g.format_on_save ~= false then
|
|
vim.lsp.buf.format({ bufnr = bufnr })
|
|
end
|
|
end,
|
|
})
|
|
end
|
|
end,
|
|
})
|
|
|
|
-- Format-on-save is enabled by default
|
|
vim.g.format_on_save = true
|
|
|
|
-- Keymaps
|
|
-- Toggle format-on-save
|
|
vim.keymap.set("n", "<leader>lt", function()
|
|
vim.g.format_on_save = not vim.g.format_on_save
|
|
local status = vim.g.format_on_save and "enabled" or "disabled"
|
|
vim.notify("Format on save " .. status, vim.log.levels.INFO)
|
|
end, { desc = "Formatting: Toggle format on save", silent = true, noremap = true })
|
|
|
|
-- Manual format (buffer)
|
|
vim.keymap.set("n", "<leader>lf", function()
|
|
vim.lsp.buf.format({ async = false })
|
|
end, { desc = "Formatting: Format buffer", silent = true, noremap = true })
|
|
|
|
-- Manual format (visual range)
|
|
vim.keymap.set("v", "<leader>lf", function()
|
|
vim.lsp.buf.format({ async = false })
|
|
end, { desc = "Formatting: Format selection", silent = true, noremap = true })
|
|
end,
|
|
}
|