diff --git a/flake.lock b/flake.lock index 34a2243..c07fadc 100644 --- a/flake.lock +++ b/flake.lock @@ -412,6 +412,22 @@ "type": "github" } }, + "plugin-conform-nvim": { + "flake": false, + "locked": { + "lastModified": 1726168403, + "narHash": "sha256-AWgG+16Bh/xu50pU78mKIcQy9MKzWF1YKdbEt5jX0WQ=", + "owner": "stevearc", + "repo": "conform.nvim", + "rev": "1a99fdc1d3aa9ccdf3021e67982a679a8c5c740c", + "type": "github" + }, + "original": { + "owner": "stevearc", + "repo": "conform.nvim", + "type": "github" + } + }, "plugin-copilot-cmp": { "flake": false, "locked": { @@ -1229,6 +1245,22 @@ "type": "github" } }, + "plugin-nvim-lint": { + "flake": false, + "locked": { + "lastModified": 1727002360, + "narHash": "sha256-mg3IqNfIeWimasYEHilqv4Yx67hTHBubq19nLTNrjLk=", + "owner": "mfussenegger", + "repo": "nvim-lint", + "rev": "968a35d54b3a4c1ce66609cf80b14d4ae44fe77f", + "type": "github" + }, + "original": { + "owner": "mfussenegger", + "repo": "nvim-lint", + "type": "github" + } + }, "plugin-nvim-lspconfig": { "flake": false, "locked": { @@ -1929,6 +1961,7 @@ "plugin-cmp-treesitter": "plugin-cmp-treesitter", "plugin-codewindow-nvim": "plugin-codewindow-nvim", "plugin-comment-nvim": "plugin-comment-nvim", + "plugin-conform-nvim": "plugin-conform-nvim", "plugin-copilot-cmp": "plugin-copilot-cmp", "plugin-copilot-lua": "plugin-copilot-lua", "plugin-crates-nvim": "plugin-crates-nvim", @@ -1980,6 +2013,7 @@ "plugin-nvim-dap-ui": "plugin-nvim-dap-ui", "plugin-nvim-docs-view": "plugin-nvim-docs-view", "plugin-nvim-lightbulb": "plugin-nvim-lightbulb", + "plugin-nvim-lint": "plugin-nvim-lint", "plugin-nvim-lspconfig": "plugin-nvim-lspconfig", "plugin-nvim-metals": "plugin-nvim-metals", "plugin-nvim-navbuddy": "plugin-nvim-navbuddy", diff --git a/flake.nix b/flake.nix index d12bdc5..172ea94 100644 --- a/flake.nix +++ b/flake.nix @@ -211,6 +211,18 @@ flake = false; }; + # Formatters + plugin-conform-nvim = { + url = "github:stevearc/conform.nvim"; + flake = false; + }; + + # Diagnostics + plugin-nvim-lint = { + url = "github:mfussenegger/nvim-lint"; + flake = false; + }; + # Copying/Registers plugin-registers = { url = "github:tversteeg/registers.nvim"; diff --git a/lib/languages.nix b/lib/languages.nix index 52f1b5b..bf9bb88 100644 --- a/lib/languages.nix +++ b/lib/languages.nix @@ -5,6 +5,15 @@ inherit (lib.types) bool; inherit (lib.nvim.attrsets) mapListToAttrs; in { + # A wrapper around `mkOption` to create a boolean option that is + # used for Language Server modules. + mkEnable = desc: + mkOption { + description = "Turn on ${desc} for enabled languages by default"; + type = bool; + default = false; + }; + # Converts a boolean to a yes/no string. This is used in lots of # configuration formats. diagnosticsToLua = { @@ -27,11 +36,4 @@ in { value = diagnosticsProviders.${type}.nullConfig package; }) config; - - mkEnable = desc: - mkOption { - description = "Turn on ${desc} for enabled languages by default"; - type = bool; - default = false; - }; } diff --git a/modules/modules.nix b/modules/modules.nix index 1204e43..e736aa6 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -24,6 +24,7 @@ "dashboard" "debugger" "filetree" + "formatter" "git" "languages" "lsp" diff --git a/modules/plugins/diagnostics/default.nix b/modules/plugins/diagnostics/default.nix new file mode 100644 index 0000000..3789640 --- /dev/null +++ b/modules/plugins/diagnostics/default.nix @@ -0,0 +1,3 @@ +{ + imports = [./nvim-lint]; +} diff --git a/modules/plugins/diagnostics/nvim-lint/config.nix b/modules/plugins/diagnostics/nvim-lint/config.nix new file mode 100644 index 0000000..dac2c2f --- /dev/null +++ b/modules/plugins/diagnostics/nvim-lint/config.nix @@ -0,0 +1,20 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf; + inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.lua) toLuaObject; + + cfg = config.vim.diagnostics.nvim-lint; +in { + config = mkIf cfg.enable { + vim = { + startPlugins = ["nvim-lint"]; + pluginRC.nvim-lint = entryAnywhere '' + require("lint").setup(${toLuaObject cfg.setupOpts}) + ''; + }; + }; +} diff --git a/modules/plugins/diagnostics/nvim-lint/default.nix b/modules/plugins/diagnostics/nvim-lint/default.nix new file mode 100644 index 0000000..00c526f --- /dev/null +++ b/modules/plugins/diagnostics/nvim-lint/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./nvim-lint.nix + ./config.nix + ]; +} diff --git a/modules/plugins/diagnostics/nvim-lint/nvim-lint.nix b/modules/plugins/diagnostics/nvim-lint/nvim-lint.nix new file mode 100644 index 0000000..45674a5 --- /dev/null +++ b/modules/plugins/diagnostics/nvim-lint/nvim-lint.nix @@ -0,0 +1,36 @@ +{ + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption mkEnableOption literalExpression; + inherit (lib.types) attrs attrsOf listOf str; + inherit (lib.nvim.types) mkPluginSetupOption; +in { + options.vim.diagnostics.nvim-lint = { + enable = mkEnableOption "asynchronous linter plugin for Neovim [nvim-lint]"; + setupOpts = mkPluginSetupOption "conform.nvim" { + linters_by_ft = mkOption { + type = attrsOf (listOf str); + default = {}; + example = { + text = ["vale"]; + markdown = ["vale"]; + }; + description = '' + Map of filetype to formatters. This option takes a set of + `key = value` format where the `value will` be converted + to its Lua equivalent. You are responsible for passing the + correct Nix data types to generate a correct Lua value that + conform is able to accept. + ''; + }; + + default_format_opts = mkOption { + type = attrs; + default = {lsp_format = "fallback";}; + description = "Default values when calling `conform.format()`"; + }; + }; + }; +} diff --git a/modules/plugins/formatter/conform-nvim/config.nix b/modules/plugins/formatter/conform-nvim/config.nix new file mode 100644 index 0000000..0b83d81 --- /dev/null +++ b/modules/plugins/formatter/conform-nvim/config.nix @@ -0,0 +1,20 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf; + inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.lua) toLuaObject; + + cfg = config.vim.formatter.conform-nvim; +in { + config = mkIf cfg.enable { + vim = { + startPlugins = ["conform-nvim"]; + pluginRC.conform-nvim = entryAnywhere '' + require("conform").setup(${toLuaObject cfg.setupOpts}) + ''; + }; + }; +} diff --git a/modules/plugins/formatter/conform-nvim/conform-nvim.nix b/modules/plugins/formatter/conform-nvim/conform-nvim.nix new file mode 100644 index 0000000..be977e6 --- /dev/null +++ b/modules/plugins/formatter/conform-nvim/conform-nvim.nix @@ -0,0 +1,61 @@ +{ + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption mkEnableOption literalExpression; + inherit (lib.types) attrs enum; + inherit (lib.nvim.types) mkPluginSetupOption; +in { + options.vim.formatter.conform-nvim = { + enable = mkEnableOption "lightweight yet powerful formatter plugin for Neovim [conform-nvim]"; + setupOpts = mkPluginSetupOption "conform.nvim" { + formatters_by_ft = mkOption { + type = attrs; + default = {}; + example = literalExpression "lua = [\"${pkgs.stylua}/bin/stylua\"]"; + description = '' + Map of filetype to formatters. This option takes a set of + `key = value` format where the `value will` be converted + to its Lua equivalent. You are responsible for passing the + correct Nix data types to generate a correct Lua value that + conform is able to accept. + ''; + }; + + default_format_opts = mkOption { + type = attrs; + default = {lsp_format = "fallback";}; + description = "Default values when calling `conform.format()`"; + }; + + format_on_save = mkOption { + type = attrs; + default = { + lsp_format = "fallback"; + timeout_ms = 500; + }; + description = '' + Table that will be passed to `conform.format()`. If this + is set, Conform will run the formatter on save. + ''; + }; + + format_after_save = mkOption { + type = attrs; + default = {lsp_format = "fallback";}; + description = '' + Table that will be passed to `conform.format()`. If this + is set, Conform will run the formatter asynchronously after + save. + ''; + }; + + log_level = mkOption { + type = enum ["vim.log.levels.ERROR" "vim.log.levels.WARN" "vim.log.levels.INFO" "vim.log.levels.DEBUG"]; + default = "vim.log.levels.ERROR"; # TODO: make this luaInline + description = "Logging level for conform-nvim"; + }; + }; + }; +} diff --git a/modules/plugins/formatter/conform-nvim/default.nix b/modules/plugins/formatter/conform-nvim/default.nix new file mode 100644 index 0000000..56c90f3 --- /dev/null +++ b/modules/plugins/formatter/conform-nvim/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./conform-nvim.nix + ./config.nix + ]; +} diff --git a/modules/plugins/formatter/default.nix b/modules/plugins/formatter/default.nix new file mode 100644 index 0000000..f57ad66 --- /dev/null +++ b/modules/plugins/formatter/default.nix @@ -0,0 +1,3 @@ +{ + imports = [./conform-nvim]; +} diff --git a/modules/plugins/languages/html.nix b/modules/plugins/languages/html.nix index 0bef276..749347c 100644 --- a/modules/plugins/languages/html.nix +++ b/modules/plugins/languages/html.nix @@ -4,17 +4,54 @@ lib, ... }: let - inherit (lib.options) mkEnableOption mkOption; + inherit (builtins) attrNames; + inherit (lib.options) mkOption mkEnableOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) bool; - inherit (lib.lists) optional; + inherit (lib.lists) optional isList; + inherit (lib.types) enum either package listOf str bool; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.lua) expToLua; inherit (lib.nvim.dag) entryAnywhere; cfg = config.vim.languages.html; + + defaultServer = "html"; + servers = { + html = { + package = pkgs.vscode-langservers-extracted; + lspConfig = '' + lspconfig.html.setup{ + capabilities = capabilities; + on_attach = default_on_attach; + cmd = ${ + if isList cfg.lsp.package + then expToLua cfg.lsp.package + else ''{"${cfg.lsp.package}/bin/vscode-html-language-server", "--stdio"}'' + }; + } + ''; + }; + }; in { options.vim.languages.html = { enable = mkEnableOption "HTML language support"; + + lsp = { + enable = mkEnableOption "Enable HTML LSP support" // {default = config.vim.languages.enableLSP;}; + + server = mkOption { + type = enum (attrNames servers); + default = defaultServer; + description = "HTML LSP server to use"; + }; + + package = mkOption { + type = either package (listOf str); + default = pkgs.vscode-langservers-extracted; + description = "html-language-server package, or the command to run as a list of strings"; + }; + }; + treesitter = { enable = mkEnableOption "HTML treesitter support" // {default = config.vim.languages.enableTreesitter;}; package = mkGrammarOption pkgs "html"; @@ -27,6 +64,11 @@ in { }; config = mkIf cfg.enable (mkMerge [ + (mkIf cfg.lsp.enable { + vim.lsp.lspconfig.enable = true; + vim.lsp.lspconfig.sources.html-lsp = servers.${cfg.lsp.server}.lspConfig; + }) + (mkIf cfg.treesitter.enable { vim = { startPlugins = optional cfg.treesitter.autotagHtml "nvim-ts-autotag";