mfussenegger / nvim-lint

An asynchronous linter plugin for Neovim complementary to the built-in Language Server Protocol support.
GNU General Public License v3.0
2.02k stars 209 forks source link

Linting of injected langauges #439

Open magnuslarsen opened 1 year ago

magnuslarsen commented 1 year ago

I've been thinking for a while on how to implement "linting of injected languages" using the existing code-base as-is, and (in theory anyway) it's not too difficult, I would just like to bring it up for a discussing before actually trying to implement it

conform.nvim has the same feature, but for formatting, and we can just re-use the core logic here (getting the injected languages via treesitter). I see it sort of like this:

  1. Get the injected languages, and for each:
  2. Create a new hidden-scratch buffer, set ft to injected.language
  3. Set said scratch-buffer's content to injected.content (without additional new lines, see below)
  4. Call nvim_buf_call(scatch_bufnr, M.lint()) on the scratch-buffer
  5. Get all the diagnostics for scratch_bufnr (if LSP's are setup for the target language, these will also be picked up - feature?)
  6. Update the diagnostic' line_num to include the offset from the main buffer for each diagnostic (see below)
  7. vim.diagnostics.set() the diagnostics in the main buffer
  8. Sync/delete hidden buffers - I need to test what is fastest here

I know of otter.nvim which does something similar, but for a limited set of languages, and only includes LSP features (which I'm not interested in). Otter.nvim creates a hidden buffer, but using newlines as offset, which I personally do not like, because some linters requires the first line to be a shebang (see shellcheck as an example), which is impossible to satisfy using the newline-approach

Instead, I'd suggest making a new buffer without newlines, but instead bump the lnum of the diagnostic itself afterwards, so it matches in the main buffer (i.e. if the injected block starts a line 5 in the main buffer, bump the lnum of the diagnostic by 4 (offset - 1). This makes the shellcheck example possible to "fix"


Since nvim-lint's cmd-field can only return a path to an external command (or a function that returns said command), above requires a new field, which can take a pure-lua function\ conform.nvim has command-field which is equivalent to nvim-lint's cmd field, but they also have a format-field, which accepts said pure-lua function (they also have a condition-field, which I think could also be useful here)

(having a linter field (pure-lua) would also allow new linter tools, for-example these two from null-ls, and this one I wrote, just before switching to nvim-lint/conform.nvim)

This bit is what is holding me back from writing the injected-linter; I'm not too comfortable adding logic to the core-logic :-)

mfussenegger commented 1 year ago

Relates a bit to https://github.com/mfussenegger/nvim-lint/issues/235

I'd first like to see if there's more interest in this, before considering it.

JeanMertz commented 10 months ago

It does appear there's a desire for this feature to be implemented. It currently has the highest number of votes.

pete3n commented 9 months ago

This would be awesome for languages like Nix where I often end up injecting Lua or shell scripts with unchecked '' ''; blocks.

Doekeb commented 3 weeks ago

Throwing my hat in the ring here as well. Injected SQL is my use-case. I have almost a perfect setup with treesitter queries and conform. Linting is the only missing piece.

catgoose commented 3 weeks ago

Throwing my hat in the ring here as well. Injected SQL is my use-case. I have almost a perfect setup with treesitter queries and conform. Linting is the only missing piece.

Do you mind sharing?

Doekeb commented 3 weeks ago

Do you mind sharing?

Sure!

I'm set up for SQL inside of Python, but you should be able to replicate with any language inside any other.