conform.nvim
conform.nvim copied to clipboard
bug: Help with eslint_d timeout
Neovim version (nvim -v)
NVIM v0.10.0-dev-2258+g1405e5c8c
Operating system/version
MacOS 14.4.1
Add the debug logs
- [X] I have set
log_level = vim.log.levels.DEBUGand pasted the log contents below.
Log file
10:31:21[DEBUG] Running formatters on /Users/jrreed/dev/networking-solutions/src/main.tsx: { "prettier", "eslint_d" }
10:31:21[INFO] Run prettier on /Users/jrreed/dev/networking-solutions/src/main.tsx
10:31:21[DEBUG] Run command: { "/Users/jrreed/dev/networking-solutions/node_modules/.bin/prettier", "--stdin-filepath", "/Users/jrreed/dev/networking-solutions/src/main.tsx" }
10:31:21[DEBUG] Run CWD: /Users/jrreed/dev/networking-solutions
10:31:22[DEBUG] prettier exited with code 0
10:31:22[INFO] Run eslint_d on /Users/jrreed/dev/networking-solutions/src/main.tsx
10:31:22[DEBUG] Run command: { "eslint_d", "--fix-to-stdout", "--stdin", "--stdin-filename", "/Users/jrreed/dev/networking-solutions/src/main.tsx" }
10:31:22[DEBUG] Run CWD: /Users/jrreed/dev/networking-solutions
10:31:22[DEBUG] eslint_d exited with code 0
10:31:22[DEBUG] Running formatters on /Users/jrreed/dev/networking-solutions/src/main.tsx: { "prettier", "eslint_d" }
10:31:22[INFO] Run prettier on /Users/jrreed/dev/networking-solutions/src/main.tsx
10:31:22[DEBUG] Run command: { "/Users/jrreed/dev/networking-solutions/node_modules/.bin/prettier", "--stdin-filepath", "/Users/jrreed/dev/networking-solutions/src/main.tsx" }
10:31:22[DEBUG] Run CWD: /Users/jrreed/dev/networking-solutions
10:31:23[DEBUG] prettier exited with code 0
10:31:23[INFO] Run eslint_d on /Users/jrreed/dev/networking-solutions/src/main.tsx
10:31:23[DEBUG] Run command: { "eslint_d", "--fix-to-stdout", "--stdin", "--stdin-filename", "/Users/jrreed/dev/networking-solutions/src/main.tsx" }
10:31:23[DEBUG] Run CWD: /Users/jrreed/dev/networking-solutions
10:31:23[WARN] Formatter 'eslint_d' timeout
10:31:23[INFO] eslint_d exited with code 143
10:31:23[DEBUG] eslint_d stdout: { "" }
10:31:23[DEBUG] eslint_d stderr: { "" }
Describe the bug
I have a typescriptreact file, and for my formatters i have :
typescriptreact = { "prettier", "eslint_d" },
I also use https://github.com/pmizio/typescript-tools.nvim
My format_on_save function:
format_on_save = function()
if vim.g.disable_autoformat then
return
end
return {
lsp_fallback = true,
timeout_ms = 500,
}
end,
but I also have an autocmd setup because of tstools:
vim.api.nvim_create_autocmd("BufWritePre", {
group = vim.api.nvim_create_augroup("ts_fix_imports", { clear = true }),
desc = "Add missing imports and remove unused imports for TS",
pattern = { "*.ts", "*.tsx", "*.js", "*.jsx" },
callback = function(args)
vim.cmd("TSToolsAddMissingImports sync")
vim.cmd("TSToolsRemoveUnusedImports sync")
if package.loaded["conform"] then
require("conform").format({ bufnr = args.buf })
end
end,
})
this autocmd is setup because on save I want TSTools to add missing imports, remove unused imports, then call conforms format
The funny thing is, the eslint_d seems to format on save, but then says it times out after
What is the severity of this bug?
breaking (some functionality is broken)
Steps To Reproduce
- Open any typescript react file and save
- eslint_d will format but will then notify via timeout error
Expected Behavior
I expect eslint_d to not timeout
Minimal example file
Any typescript react file seems to do this
Minimal init.lua
-- DO NOT change the paths and don't remove the colorscheme
local root = vim.fn.fnamemodify("./.repro", ":p")
-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"--single-branch",
"https://github.com/folke/lazy.nvim.git",
lazypath,
})
end
vim.opt.runtimepath:prepend(lazypath)
-- install plugins
local plugins = {
{
"stevearc/conform.nvim",
cond = not vim.g.vscode,
lazy = true,
event = { "BufReadPre", "BufNewFile" }, -- to disable, comment this out
config = function()
local conform = require("conform")
vim.api.nvim_create_user_command("FormatToggle", function(args)
if args.bang then
-- FormatToggle! will disable formatting just for this buffer
if vim.b.disable_autoformat then
vim.b.disable_autoformat = false
require("notify")("Formatting enabled for buffer")
else
vim.b.disable_autoformat = true
require("notify")("Formatting disabled for buffer")
end
else
if vim.g.disable_autoformat then
vim.g.disable_autoformat = false
vim.b.disable_autoformat = false
require("notify")("Formatting enabled")
else
vim.g.disable_autoformat = true
vim.b.disable_autoformat = true
require("notify")("Formatting disabled")
end
end
end, {
desc = "Toggle autoformat",
bang = true,
})
conform.setup({
quiet = true,
formatters_by_ft = {
javascript = { "prettier", "eslint_d" },
typescript = { "prettier", "eslint_d" },
javascriptreact = { "prettier", "eslint_d" },
typescriptreact = { "prettier", "eslint_d" },
svelte = { "prettier" },
css = { "prettier" },
html = { "prettier" },
json = { "prettier" },
yaml = { "prettier" },
markdown = { "prettier" },
graphql = { "prettier" },
lua = { "stylua" },
python = { "isort", "black" },
rust = { "rustfmt" },
},
format_on_save = function()
if vim.g.disable_autoformat then
return
end
return {
lsp_fallback = true,
timeout_ms = 500,
}
end,
log_level = vim.log.levels.DEBUG,
})
vim.keymap.set({ "n", "v" }, "<leader>fv", function()
conform.format({
lsp_fallback = true,
async = false,
timeout_ms = 1000,
})
end, { desc = "Format file or range (in visual mode)" })
vim.keymap.set({ "n" }, "<leader>cft", "<cmd>FormatToggle<cr>", { desc = "Toggle autoformat" })
vim.keymap.set({ "n" }, "<leader>cfT", "<cmd>FormatToggle!<cr>", { desc = "Toggle autoformat for buffer" })
end,
},
{
"mfussenegger/nvim-lint",
cond = not vim.g.vscode,
lazy = true,
event = { "BufReadPre", "BufNewFile" },
config = function()
local lint = require("lint")
lint.linters_by_ft = {
javascript = { "eslint_d" },
typescript = { "eslint_d" },
javascriptreact = { "eslint_d" },
typescriptreact = { "eslint_d" },
svelte = { "eslint_d" },
python = { "pylint" },
}
local lint_augroup = vim.api.nvim_create_augroup("lint", { clear = true })
vim.api.nvim_create_autocmd({ "BufEnter", "BufWritePost", "InsertLeave" }, {
group = lint_augroup,
callback = function()
lint.try_lint(nil, { ignore_errors = true })
end,
})
vim.keymap.set("n", "<leader>cl", function()
lint.try_lint()
end, { desc = "Trigger linting for current file" })
end,
},
{
"pmizio/typescript-tools.nvim",
cond = not vim.g.vscode,
dependencies = { "nvim-lua/plenary.nvim", "neovim/nvim-lspconfig" },
opts = {
settings = {
expose_as_code_action = { "fix_all", "add_missing_imports", "remove_unused", "remove_unused_imports" },
},
},
},
{
"williamboman/mason.nvim",
cond = not vim.g.vscode,
opts = {},
dependencies = {
"williamboman/mason-lspconfig.nvim",
"WhoIsSethDaniel/mason-tool-installer.nvim",
},
config = function()
local mason = require("mason")
local mason_lspconfig = require("mason-lspconfig")
local mason_tool_installer = require("mason-tool-installer")
mason.setup({
ui = {
icons = {
package_installed = "",
package_uninstalled = "",
package_pending = "",
},
},
})
mason_lspconfig.setup({
ensure_installed = {
"tsserver",
"html",
"cssls",
"lua_ls",
"emmet_ls",
"pyright",
"jsonls",
"gopls",
},
})
mason_tool_installer.setup({
ensure_installed = {
"prettier", -- prettier formatter
"stylua", -- lua formatter
"eslint_d", -- js linter
"jsonlint", -- json formatter
},
})
end,
}
-- add any other plugins here
}
require("lazy").setup(plugins, {
root = root .. "/plugins",
})
-- add anything else here
vim.api.nvim_create_autocmd("BufWritePre", {
group = vim.api.nvim_create_augroup("ts_fix_imports", { clear = true }),
desc = "Add missing imports and remove unused imports for TS",
pattern = { "*.ts", "*.tsx", "*.js", "*.jsx" },
callback = function(args)
vim.cmd("TSToolsAddMissingImports sync")
vim.cmd("TSToolsRemoveUnusedImports sync")
if package.loaded["conform"] then
require("conform").format({ bufnr = args.buf })
end
end,
})
Additional context
No response
I had the same issue, adding async=true fixed it, conform.format({lsp_fallback=true, async=true})
I had the same issue, adding
async=truefixed it,conform.format({lsp_fallback=true, async=true})
Thanks I'll give that a try
I had the same issue, adding
async=truefixed it,conform.format({lsp_fallback=true, async=true})
I think this fixed it, haven't seen it again since
I'm facing the same issue, and I think the solution is not yet found. The problem is that eslint_d takes too much time to format when using conform.nvim. Although, we have workarounds like waiting for 10 seconds each save or using unstable async formatting.
Same issue still, async workaround is buggy at best
In what way is async formatting buggy?
In what way is async formatting buggy?
random timeouts mostly
Timeouts? Async formatting doesn't have timeouts. Unless you're talking about eslint_d itself timing out?
Timeouts? Async formatting doesn't have timeouts. Unless you're talking about eslint_d itself timing out?
ya sorry, like the above comment from @Lippiece eslint_d timeouts, so is this whole issue eslint_d related and not related to conform?
That's what it sounds like. If running the command directly on the command line doesn't time out, then maybe there's something we can do.
Ok ill try to do some more isolated testing this weekend when i get time
did you found solution? @jacobrreed
Not sure if I am adding much to the conversation but async is more of a band-aid workaround than a solution
- ~Async does not work for format_on_save,
format_after_saveis not always an option if you have quick-fingers that type:wqinstead of:wand then:q~ (EDIT: I know I've seen this problem before but I could not reproduce it now) - I could be mistaken but I recall async did not work for formatexpr to acutate on
gqeither.
I personally find it sorrowingly hilarious that the solution meant to speed up eslint has chronic timeout issues and that we are considering an async solution.
The way I solved this is I got rid of "eslint_d" entirely, I have only "eslint-lsp" installed and I've created autocmd which on PreWrite runs "EslintFixAll".
It works like a charm for me - performance of save increased by magnitudes. While moving files in oil.nvim I no longer toss a double sided coin where on each side there is "it's gonna break". Generally it made my DX a LOT better.
That's how my current conform.lua looks like.
return {
"stevearc/conform.nvim",
lazy = false,
config = function()
require("conform").setup({
format_on_save = {
timeout_ms = 500,
lsp_format = "fallback",
},
notify_no_formatters = true,
notify_on_error = true,
formatters_by_ft = {
lua = { "stylua" },
markdown = { "markdownlint" },
terraform = { "tflint" },
json = { "jsonlint", "prettierd" },
dockerfile = { "hadolint" },
javascript = { "prettierd" },
typescript = { "prettierd" },
svelte = { "prettierd" },
go = { "golines", "goimports", "golangci", "golangci_lint_ls", "golangci-lint" },
html = { "htmlbeautifier", "htmlhint" },
tmpl = { "htmlbeautifier", "htmlhint" },
},
})
vim.api.nvim_create_autocmd("BufWritePre", {
group = vim.api.nvim_create_augroup("EslintFixAll", { clear = true }),
pattern = { "*.tsx", "*.ts", "*.jsx", "*.js" },
command = "silent! EslintFixAll",
})
end,
}
@dabrowskif If using the LSP for this, I recommend checking out @stylistic/eslint-plugin. But also be warned that :EslintFixAll might do things such as deleting unreachable depending on configuration, which usually shouldn't be done in a formatting process, and can be very frustrating. I do EslintFixAll on a mapping, and use eslint_d with conform.
On a side note, I wish this was supported:
local options = {
formatters_by_ft = {
vue = { "eslint_d", "prettierd", "prettier", stop_after_first = true },
},
format_on_save = {
timeout_ms = 500,
lsp_fallback = true,
},
formatters = {
eslint_d = {
format_on_save = { -- Can I change the config based on the formatter used?
timeout_ms = 1000,
lsp_fallback = true,
},
},
},
}