├── .github ├── dependabot.yml └── workflows │ ├── lint.yml │ └── problem_matchers │ └── selene.json ├── LICENSE ├── README.md ├── lua ├── diagnosticls.lua └── diagnosticls │ ├── filetypes.lua │ ├── formatters.lua │ └── linters.lua ├── neovim.toml ├── selene.toml └── stylua.toml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | lint: 13 | runs-on: [ubuntu-latest] 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Add $HOME/.local/bin to $PATH 18 | run: echo "$HOME/.local/bin" >> $GITHUB_PATH 19 | 20 | - name: Setup selene 21 | run: | 22 | wget --no-verbose "https://github.com/Kampfkarren/selene/releases/download/$VERSION/selene-$VERSION-linux.zip" 23 | echo "$SHA256_CHECKSUM selene-$VERSION-linux.zip" > "selene-$VERSION-linux.zip.checksum" 24 | sha256sum --check "selene-$VERSION-linux.zip.checksum" 25 | unzip "selene-$VERSION-linux.zip" 26 | install -Dp selene "$HOME/.local/bin/selene" 27 | 28 | echo "::add-matcher::.github/workflows/problem_matchers/selene.json" 29 | env: 30 | VERSION: "0.14.0" 31 | SHA256_CHECKSUM: "6a6a975fa6d503fbf1d46bfb570cc75b2844f6901ad98901961a5d355d472385" 32 | 33 | - name: Install stylua 34 | run: | 35 | wget --no-verbose "https://github.com/JohnnyMorganz/StyLua/releases/download/v$VERSION/stylua-$VERSION-linux.zip" 36 | echo "$SHA256_CHECKSUM stylua-$VERSION-linux.zip" > "stylua-$VERSION-linux.zip.checksum" 37 | sha256sum --check "stylua-$VERSION-linux.zip.checksum" 38 | unzip "stylua-$VERSION-linux.zip" 39 | install -Dp stylua "$HOME/.local/bin/stylua" 40 | env: 41 | VERSION: "0.10.1" 42 | SHA256_CHECKSUM: "3a2b44de3c0d36b4ef4b617364eced7b560a6b19284f61550e8b679804efd2a3" 43 | 44 | - name: Run selene 45 | run: selene . 46 | 47 | - name: Run stylua 48 | run: stylua --check . 49 | -------------------------------------------------------------------------------- /.github/workflows/problem_matchers/selene.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "selene-error", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^([^:]+):(\\d+):(\\d+):\\serror(.*)$", 9 | "file": 1, 10 | "line": 2, 11 | "column": 3, 12 | "message": 4 13 | } 14 | ] 15 | }, 16 | { 17 | "owner": "selene-warning", 18 | "severity": "warning", 19 | "pattern": [ 20 | { 21 | "regexp": "^([^:]+):(\\d+):(\\d+):\\swarning(.*)$", 22 | "file": 1, 23 | "line": 2, 24 | "column": 3, 25 | "message": 4 26 | } 27 | ] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Peter Lithammer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [`diagnostic-languageserver`](https://github.com/iamcco/diagnostic-languageserver) 2 | configuration for Neovim's language server client. 3 | 4 | ```lua 5 | local lspconfig = require("lspconfig") 6 | local diagnosticls = require("diagnosticls") 7 | 8 | lspconfig.diagnosticls.setup({ 9 | filetypes = { 10 | "haskell", 11 | unpack(diagnosticls.filetypes), 12 | }, 13 | init_options = { 14 | linters = vim.tbl_deep_extend("force", diagnosticls.linters, { 15 | hlint = { 16 | command = "hlint", 17 | -- ... 18 | }, 19 | }), 20 | formatters = diagnosticls.formatters, 21 | filetypes = { 22 | haskell = "hlint", 23 | lua = { "luacheck", "selene" }, 24 | markdown = { "markdownlint" }, 25 | python = { "flake8", "mypy" }, 26 | scss = "stylelint", 27 | sh = "shellcheck", 28 | vim = "vint", 29 | yaml = "yamllint", 30 | }, 31 | formatFiletypes = { 32 | fish = "fish_indent", 33 | javascript = "prettier", 34 | javascriptreact = "prettier", 35 | json = "prettier", 36 | lua = { "lua-format", "stylua" }, 37 | python = { "isort", "black", "autoflake" }, 38 | sh = "shfmt", 39 | sql = "pg_format", 40 | typescript = "prettier", 41 | typescriptreact = "prettier", 42 | }, 43 | }, 44 | }) 45 | ``` 46 | -------------------------------------------------------------------------------- /lua/diagnosticls.lua: -------------------------------------------------------------------------------- 1 | local M = { 2 | formatters = require("diagnosticls/formatters"), 3 | filetypes = require("diagnosticls/filetypes"), 4 | linters = require("diagnosticls/linters"), 5 | } 6 | 7 | return M 8 | -------------------------------------------------------------------------------- /lua/diagnosticls/filetypes.lua: -------------------------------------------------------------------------------- 1 | local filetypes = { 2 | "css", 3 | "dockerfile", 4 | "fish", 5 | "go", 6 | "javascript", 7 | "javascriptreact", 8 | "json", 9 | "lua", 10 | "markdown", 11 | "python", 12 | "scss", 13 | "sh", 14 | "sql", 15 | "typescript", 16 | "typescriptreact", 17 | "vim", 18 | "yaml", 19 | "yaml.ansible", 20 | } 21 | 22 | return filetypes 23 | -------------------------------------------------------------------------------- /lua/diagnosticls/formatters.lua: -------------------------------------------------------------------------------- 1 | local formatters = { 2 | autoflake = { 3 | command = "autoflake", 4 | args = { "--quiet", "-" }, 5 | rootPatterns = { "pyproject.toml" }, 6 | }, 7 | 8 | black = { 9 | command = "black", 10 | args = { "--quiet", "-" }, 11 | rootPatterns = { "pyproject.toml" }, 12 | }, 13 | 14 | fish_indent = { 15 | command = "fish_indent", 16 | }, 17 | 18 | isort = { 19 | command = "isort", 20 | args = { "--quiet", "-" }, 21 | rootPatterns = { "pyproject.toml", ".isort.cfg" }, 22 | }, 23 | 24 | ["lua-format"] = { 25 | command = "lua-format", 26 | args = { "-i" }, 27 | requiredFiles = { ".lua-format" }, 28 | rootPatterns = { ".lua-format" }, 29 | }, 30 | 31 | pg_format = { 32 | command = "pg_format", 33 | -- https://github.com/darold/pgFormatter/issues/250 34 | args = { "--config", ".pg_format" }, 35 | -- args = { "-B", "-L" }, 36 | rootPatterns = { ".pg_format" }, 37 | requiredFiles = { ".pg_format" }, 38 | }, 39 | 40 | prettier = { 41 | command = "./node_modules/.bin/prettier", 42 | args = { "--stdin", "--stdin-filepath", "%filepath" }, 43 | rootPatterns = { 44 | ".prettierrc", 45 | ".prettierrc.json", 46 | ".prettierrc.toml", 47 | ".prettierrc.json", 48 | ".prettierrc.yml", 49 | ".prettierrc.yaml", 50 | ".prettierrc.json5", 51 | ".prettierrc.js", 52 | ".prettierrc.cjs", 53 | "prettier.config.js", 54 | "prettier.config.cjs", 55 | }, 56 | }, 57 | 58 | shfmt = { 59 | command = "shfmt", 60 | args = { "-filename", "%filepath" }, 61 | rootPatterns = { ".editorconfig" }, 62 | }, 63 | 64 | stylua = { 65 | command = "stylua", 66 | args = { "-" }, 67 | rootPatterns = { "stylua.toml" }, 68 | requiredFiles = { "stylua.toml" }, 69 | }, 70 | } 71 | 72 | return formatters 73 | -------------------------------------------------------------------------------- /lua/diagnosticls/linters.lua: -------------------------------------------------------------------------------- 1 | local linters = { 2 | ["ansible-lint"] = { 3 | command = "ansible-lint", 4 | args = { "--parseable-severity", "--nocolor", "-" }, 5 | sourceName = "ansible-lint", 6 | rootPatterns = { ".ansible-lint" }, 7 | formatPattern = { 8 | "^[^:]+:(\\d+):\\s\\[\\w+\\]\\s\\[(\\w+)\\]\\s(.*)$", 9 | { 10 | line = 1, 11 | security = 2, 12 | message = 3, 13 | }, 14 | }, 15 | securities = { 16 | VERY_LOW = "hint", 17 | LOW = "info", 18 | HIGH = "warning", 19 | VERY_HIGH = "error", 20 | }, 21 | }, 22 | 23 | cpplint = { 24 | command = "cpplint", 25 | args = { "%file" }, 26 | debounce = 100, 27 | isStderr = true, 28 | isStdout = false, 29 | sourceName = "cpplint", 30 | offsetLine = 0, 31 | offsetColumn = 0, 32 | formatPattern = { 33 | "^[^:]+:(\\d+):(\\d+)?\\s+([^:]+?)\\s\\[(\\d)\\]$", 34 | { 35 | line = 1, 36 | column = 2, 37 | message = 3, 38 | security = 4, 39 | }, 40 | }, 41 | securities = { 42 | ["1"] = "info", 43 | ["2"] = "warning", 44 | ["3"] = "warning", 45 | ["4"] = "error", 46 | ["5"] = "error", 47 | }, 48 | }, 49 | 50 | dmypy = { 51 | sourceName = "dmypy", 52 | command = "dmypy", 53 | args = { 54 | "run", 55 | "--timeout", 56 | "300", 57 | "--", 58 | "--hide-error-codes", 59 | "--hide-error-context", 60 | "--no-color-output", 61 | "--no-error-summary", 62 | "--no-pretty", 63 | "--show-column-numbers", 64 | "%file", 65 | }, 66 | rootPatterns = { "mypy.ini", ".mypy.ini", "pyproject.toml", "setup.cfg" }, 67 | requiredFiles = { "mypy.ini", ".mypy.ini", "pyproject.toml", "setup.cfg" }, 68 | formatPattern = { 69 | "^.*:(\\d+?):(\\d+?): ([a-z]+?): (.*)$", 70 | { 71 | line = 1, 72 | column = 2, 73 | security = 3, 74 | message = 4, 75 | }, 76 | }, 77 | securities = { 78 | error = "error", 79 | }, 80 | }, 81 | 82 | eslint = { 83 | command = "./node_modules/.bin/eslint", 84 | args = { "--stdin", "--stdin-filename", "%filepath", "--format", "json" }, 85 | rootPatterns = { 86 | ".eslintrc.js", 87 | ".eslintrc.cjs", 88 | ".eslintrc.yaml", 89 | ".eslintrc.yml", 90 | ".eslintrc.json", 91 | "package.json", 92 | }, 93 | debounce = 100, 94 | sourceName = "eslint", 95 | parseJson = { 96 | errorsRoot = "[0].messages", 97 | line = "line", 98 | column = "column", 99 | endLine = "endLine", 100 | endColumn = "endColumn", 101 | message = "${message} [${ruleId}]", 102 | security = "severity", 103 | }, 104 | securities = { 105 | ["2"] = "error", 106 | ["1"] = "warning", 107 | }, 108 | }, 109 | 110 | fish = { 111 | command = "fish", 112 | args = { "-n", "%file" }, 113 | isStdout = false, 114 | isStderr = true, 115 | sourceName = "fish", 116 | formatLines = 1, 117 | formatPattern = { "^.*\\(line (\\d+)\\): (.*)$", { 118 | line = 1, 119 | message = 2, 120 | } }, 121 | }, 122 | 123 | flake8 = { 124 | command = "flake8", 125 | args = { "--format=%(row)d,%(col)d,%(code).1s,%(code)s: %(text)s", "-" }, 126 | debounce = 100, 127 | rootPatterns = { ".flake8", "setup.cfg", "tox.ini" }, 128 | offsetLine = 0, 129 | offsetColumn = 0, 130 | sourceName = "flake8", 131 | formatLines = 1, 132 | formatPattern = { 133 | "(\\d+),(\\d+),([A-Z]),(.*)(\\r|\\n)*$", 134 | { 135 | line = 1, 136 | column = 2, 137 | security = 3, 138 | message = 4, 139 | }, 140 | }, 141 | securities = { 142 | W = "warning", 143 | E = "error", 144 | F = "error", 145 | C = "error", 146 | N = "error", 147 | }, 148 | }, 149 | 150 | ["golangci-lint"] = { 151 | command = "golangci-lint", 152 | rootPatterns = { "go.mod", ".git" }, 153 | debounce = 100, 154 | args = { "run", "--out-format", "json" }, 155 | sourceName = "golangci-lint", 156 | parseJson = { 157 | sourceName = "Pos.Filename", 158 | sourceNameFilter = true, 159 | errorsRoot = "Issues", 160 | line = "Pos.Line", 161 | column = "Pos.Column", 162 | message = "${Text} [${FromLinter}]", 163 | }, 164 | }, 165 | 166 | hadolint = { 167 | command = "hadolint", 168 | sourceName = "hadolint", 169 | args = { "-f", "json", "-" }, 170 | parseJson = { 171 | line = "line", 172 | column = "column", 173 | security = "level", 174 | message = "${message} [${code}]", 175 | }, 176 | securities = { 177 | error = "error", 178 | warning = "warning", 179 | info = "info", 180 | style = "hint", 181 | }, 182 | }, 183 | 184 | luacheck = { 185 | command = "luacheck", 186 | args = { 187 | "--formatter", 188 | "plain", 189 | "--codes", 190 | "--ranges", 191 | "--filename", 192 | "%filepath", 193 | "-", 194 | }, 195 | sourceName = "luacheck", 196 | formatPattern = { 197 | "^[^:]+:(\\d+):(\\d+)-(\\d+):\\s+\\((\\w)\\d+\\)\\s+(.*)$", 198 | { 199 | line = 1, 200 | column = 2, 201 | endLine = 1, 202 | endColumn = 3, 203 | security = 4, 204 | message = 5, 205 | }, 206 | }, 207 | rootPatterns = { ".luacheckrc" }, 208 | requiredFiles = { ".luacheckrc" }, 209 | debounce = 100, 210 | securities = { 211 | W = "warning", 212 | E = "error", 213 | }, 214 | }, 215 | 216 | markdownlint = { 217 | command = "markdownlint", 218 | args = { "--stdin" }, 219 | isStderr = true, 220 | debounce = 100, 221 | offsetLine = 0, 222 | offsetColumn = 0, 223 | sourceName = "markdownlint", 224 | formatLines = 1, 225 | formatPattern = { 226 | "^.*?:\\s?(\\d+)(:(\\d+)?)?\\s(MD\\d{3}\\/[A-Za-z0-9-/]+)\\s(.*)$", 227 | { 228 | line = 1, 229 | column = 3, 230 | message = { 4 }, 231 | }, 232 | }, 233 | rootPatterns = { ".markdownlint.json" }, 234 | }, 235 | 236 | mypy = { 237 | sourceName = "mypy", 238 | command = "mypy", 239 | args = { 240 | "--follow-imports=silent", 241 | "--hide-error-codes", 242 | "--hide-error-context", 243 | "--no-color-output", 244 | "--no-error-summary", 245 | "--no-pretty", 246 | "--show-column-numbers", 247 | "%file", 248 | }, 249 | rootPatterns = { "mypy.ini", ".mypy.ini", "pyproject.toml", "setup.cfg" }, 250 | formatPattern = { 251 | "^.*:(\\d+?):(\\d+?): ([a-z]+?): (.*)$", 252 | { 253 | line = 1, 254 | column = 2, 255 | security = 3, 256 | message = 4, 257 | }, 258 | }, 259 | securities = { 260 | error = "error", 261 | }, 262 | }, 263 | 264 | pylint = { 265 | sourceName = "pylint", 266 | command = "pylint", 267 | debounce = 500, 268 | args = { 269 | "--output-format", 270 | "text", 271 | "--score", 272 | "no", 273 | "--msg-template", 274 | "'{line}:{column}:{category}:{msg} ({msg_id}:{symbol})'", 275 | "%file", 276 | }, 277 | formatPattern = { 278 | "^(\\d+?):(\\d+?):([a-z]+?):(.*)$", 279 | { 280 | line = 1, 281 | column = 2, 282 | security = 3, 283 | message = 4, 284 | }, 285 | }, 286 | rootPatterns = { "pyproject.toml", "setup.py", ".git" }, 287 | securities = { 288 | informational = "hint", 289 | refactor = "info", 290 | convention = "info", 291 | warning = "warning", 292 | error = "error", 293 | fatal = "error", 294 | }, 295 | offsetColumn = 1, 296 | formatLines = 1, 297 | }, 298 | 299 | revive = { 300 | command = "revive", 301 | rootPatterns = { "go.mod", ".git" }, 302 | debounce = 100, 303 | args = { "%file" }, 304 | sourceName = "revive", 305 | formatPattern = { 306 | "^[^:]+:(\\d+):(\\d+):\\s+(.*)$", 307 | { 308 | line = 1, 309 | column = 2, 310 | message = { 3 }, 311 | }, 312 | }, 313 | }, 314 | 315 | selene = { 316 | command = "selene", 317 | args = { 318 | "--display-style", 319 | "quiet", 320 | "-", 321 | }, 322 | sourceName = "selene", 323 | formatPattern = { 324 | "^[^:]+:(\\d+):(\\d+):\\s(\\w+)\\[\\w+\\]:\\s(.*)$", 325 | { 326 | line = 1, 327 | column = 2, 328 | endLine = 1, 329 | endColumn = 2, 330 | security = 3, 331 | message = 4, 332 | }, 333 | }, 334 | rootPatterns = { "selene.toml" }, 335 | requiredFiles = { "selene.toml" }, 336 | debounce = 100, 337 | securities = { 338 | error = "error", 339 | warning = "warning", 340 | }, 341 | }, 342 | 343 | shellcheck = { 344 | command = "shellcheck", 345 | debounce = 100, 346 | args = { "--format", "json", "--external-sources", "-" }, 347 | sourceName = "shellcheck", 348 | parseJson = { 349 | line = "line", 350 | column = "column", 351 | endLine = "endLine", 352 | endColumn = "endColumn", 353 | message = "${message} [${code}]", 354 | security = "level", 355 | }, 356 | securities = { 357 | error = "error", 358 | warning = "warning", 359 | info = "info", 360 | style = "hint", 361 | }, 362 | }, 363 | 364 | stylelint = { 365 | command = "./node_modules/.bin/stylelint", 366 | args = { "--formatter", "json", "--stdin-filename", "%filepath" }, 367 | rootPatterns = { 368 | ".stylelintrc", 369 | ".stylelintrc.js", 370 | ".stylelintrc.json", 371 | ".stylelintrc.yaml", 372 | ".stylelintrc.yml", 373 | "stylelint.config.js", 374 | "stylelint.config.cjs", 375 | "package.json", 376 | }, 377 | requiredFiles = { 378 | ".stylelintrc", 379 | ".stylelintrc.js", 380 | ".stylelintrc.json", 381 | ".stylelintrc.yaml", 382 | ".stylelintrc.yml", 383 | "stylelint.config.js", 384 | "stylelint.config.cjs", 385 | "package.json", 386 | }, 387 | debounce = 100, 388 | sourceName = "stylelint", 389 | parseJson = { 390 | errorsRoot = "[0].warnings", 391 | line = "line", 392 | column = "column", 393 | message = "${text}", 394 | security = "severity", 395 | }, 396 | securities = { 397 | error = "error", 398 | warning = "warning", 399 | }, 400 | }, 401 | 402 | vint = { 403 | command = "vint", 404 | debounce = 100, 405 | args = { "--enable-neovim", "--stdin-display-name", "%filepath", "-" }, 406 | offsetLine = 0, 407 | offsetColumn = 0, 408 | sourceName = "vint", 409 | formatLines = 1, 410 | formatPattern = { 411 | "[^:]+:(\\d+):(\\d+):\\s*(.*)(\\r|\\n)*$", 412 | { 413 | line = 1, 414 | column = 2, 415 | message = 3, 416 | }, 417 | }, 418 | }, 419 | 420 | yamllint = { 421 | args = { "-f", "parsable", "-" }, 422 | command = "yamllint", 423 | debounce = 100, 424 | formatLines = 1, 425 | formatPattern = { 426 | "^.*?:(\\d+):(\\d+): \\[(.*?)] (.*) \\((.*)\\)", 427 | { 428 | line = 1, 429 | endline = 1, 430 | column = 2, 431 | endColumn = 2, 432 | message = 4, 433 | code = 5, 434 | security = 3, 435 | }, 436 | }, 437 | securities = { 438 | error = "error", 439 | warning = "warning", 440 | }, 441 | sourceName = "yamllint", 442 | }, 443 | } 444 | 445 | return linters 446 | -------------------------------------------------------------------------------- /neovim.toml: -------------------------------------------------------------------------------- 1 | [selene] 2 | base = "lua51" 3 | name = "neovim" 4 | 5 | [[vim.tbl_deep_extend.args]] 6 | type = "string" 7 | [[vim.tbl_deep_extend.args]] 8 | type = "table" 9 | [[vim.tbl_deep_extend.args]] 10 | type = "table" 11 | -------------------------------------------------------------------------------- /selene.toml: -------------------------------------------------------------------------------- 1 | std = "neovim" 2 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | # column_width = 100 2 | indent_type = "Spaces" 3 | indent_width = 2 4 | --------------------------------------------------------------------------------