-- or { , , , , filetype= } -- filetype defaults to . If different, specify it. -- any positional argument can be skipped with nil local languages = { -- Docs / Config 'vim', 'vimdoc', 'markdown', 'markdown_inline', 'yaml', 'toml', -- Data 'gitcommit', 'gitignore', 'dockerfile', 'diff', { 'json', 'jsonls', nil, 'prettier' }, { 'jsonc', 'jsonls', nil, 'prettier' }, -- Shell / Scripting { 'bash', 'bashls', 'shellcheck', 'shfmt' }, { 'lua', 'luals', 'luacheck', 'stylua' }, { 'sql', 'sqlls', nil, 'sqlfluff' }, -- Programming { 'c', 'clangd', nil, 'clang-format' }, { 'cpp', 'clangd', nil, 'clang-format' }, { 'go', 'gopls', 'golangci-lint', 'gofmt' }, { 'rust', 'rust_analyzer', 'clippy', 'rustfmt' }, { 'python', 'pyright', 'ruff', 'black' }, -- Web stack { 'html', 'html', nil, 'prettier' }, { 'css', 'cssls', 'stylelint', 'prettier' }, { 'javascript', 'tsls', 'eslint', 'prettier' }, { 'typescript', 'tsls', 'eslint', 'prettier' }, { nil, 'tsls', 'eslint', 'prettier', filetype = 'javascriptreact' }, { 'tsx', 'tsls', 'eslint', 'prettier', filetype = 'typescriptreact' }, } local function normalize(lang) if type(lang) == 'string' then lang = { lang } end local t = { treesitter = lang[1], lsp = lang[2], linter = lang[3], formatter = lang[4], filetype = lang.filetype or lang[1], } return t end local normalized = vim.tbl_map(normalize, languages) local general = { ts_parsers = {}, lsps = {}, linters_by_ft = {}, formatters_by_ft = {}, } for _, opts in ipairs(normalized) do if opts.treesitter then table.insert(general.ts_parsers, opts.treesitter) end if opts.lsp then table.insert(general.lsps, opts.lsp) end if opts.linter then general.linters_by_ft[opts.filetype] = { opts.linter } end if opts.formatter then general.formatters_by_ft[opts.filetype] = { opts.formatter } end end return { { 'windwp/nvim-ts-autotag', config = true }, { 'windwp/nvim-autopairs', event = 'InsertEnter', config = true }, { 'mason-org/mason.nvim', config = true }, -- { 'saghen/blink.cmp', version = '1.*' }, { 'nvim-treesitter/nvim-treesitter', build = ':TSUpdate', main = 'nvim-treesitter.configs', opts = { highlight = { enable = true }, incremental_selection = { enable = true }, textobjects = { enable = true }, fold = { enable = true }, ensure_installed = general.ts_parsers, }, }, { 'mfussenegger/nvim-lint', event = { 'BufReadPre', 'BufNewFile' }, opts = { linters_by_ft = general.linters_by_ft }, config = function(_, opts) local lint = require('lint') lint.linters_by_ft = opts.linters_by_ft vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { group = vim.api.nvim_create_augroup('lint_autocmd', { clear = true }), callback = function() lint.try_lint() end, }) end, }, { 'stevearc/conform.nvim', event = { 'BufWritePre' }, opts = { -- Automatically format on save format_on_save = { timeout_ms = 500, lsp_format = 'fallback', -- Use LSP when no formatter is configured }, -- Formatters per filetype default_format_opts = { stop_after_first = true }, formatters_by_ft = general.formatters_by_ft, }, config = function(_, opts) require('conform').setup(opts) -- Create a command to format manually vim.api.nvim_create_user_command('Format', function() require('conform').format({ async = true, lsp_format = 'fallback', }) end, { desc = 'Format current buffer' }) end, }, }