Skip to content
This repository has been archived by the owner on Aug 12, 2023. It is now read-only.

Add code action and diagnostic builtins for typos #995

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

folliehiyuki
Copy link

typos is a code spell checker for common misspelled words written in Rust.

typos has a brief format for output which is easier to parse for diagnostic source but I decided to go for json format instead (it outputs 1 json object per typo). The code action is entirely yanked from #915.

A screenshot for diagnostic:

2022-08-06-235436_grim

And here is the output formats:

$ typos diagnostics_spec.lua
error: `langauge` should be `language`
  --> diagnostics_spec.lua:757:30
    |
757 |                 message = [["langauge" is a misspelling of "language"]],
    |                              ^^^^^^^^
    |
error: `Ba` should be `By`, `Be`
  --> diagnostics_spec.lua:1154:33
     |
1154 |                     message = "`Ba` should be `By`, `Be`",
     |                                 ^^
     |

$ typos --format brief diagnostics_spec.lua
diagnostics_spec.lua:757:29: `langauge` -> `language`
diagnostics_spec.lua:1154:32: `Ba` -> `By`, `Be`

$ typos --format json diagnostics_spec.lua
{"type":"typo","path":"diagnostics_spec.lua","line_num":757,"byte_offset":29,"typo":"langauge","corrections":["language"]}
{"type":"typo","path":"diagnostics_spec.lua","line_num":1146,"byte_offset":115,"typo":"Ba","corrections":["By","Be"]}

@jose-elias-alvarez
Copy link
Owner

At a glance this looks good - I'll have time for hands-on testing later this week and will give more detailed feedback then. In the meantime, I'll ask a couple of the same questions that came up in #915

  1. How long does it take to generate suggestions on demand (that is, in the code action generator itself)?
  2. How much (if any) overhead does it add to generate corrections in the diagnostics source?

As I mentioned in the other PR, I generally prefer for sources to remain independent unless there's a clear advantage to using them together. In this case, it looks like corrections are automatically generated, so I don't think there's really any overhead, but I think it still adds some complexity to make one source dependent on the other.

@folliehiyuki
Copy link
Author

I don't think a user would use the code action without the diagnostic so I group them together in this PR (this way the user of both doesn't run the same typos command twice each time).

I can separate them if you prefer, using --format brief for diagnostic and --format json for code action.

Unlike cspell the linting message of typos always contains suggestions so yes, there shouldn't be any significant overhead.

@jose-elias-alvarez
Copy link
Owner

I don't think a user would use the code action without the diagnostic so I group them together in this PR (this way the user of both doesn't run the same typos command twice each time).

I can separate them if you prefer, using --format brief for diagnostic and --format json for code action.

Unlike cspell the linting message of typos always contains suggestions so yes, there shouldn't be any significant overhead.

Thanks for checking!

I installed the typos CLI and saw that running typos --format json $FILENAME is very quick (20-30ms on my end, even with 100+ misspelled words). The only reason for the unusual situation in #915 is that cspell takes about a second to run when requesting code actions, which is a pretty noticeable delay for the user. If startup time is not an issue, I'd lean towards keeping the two sources separate, but it's up to you, especially if you think users will always want to run both sources (and if we add a mention of the code action source's dependency in the documentation).

@folliehiyuki
Copy link
Author

I'm more convinced of separating the code action and diagnostic source now (I don't mind running the command twice as it is pretty fast anyways).

I'll change the PR in the weekend.

@folliehiyuki
Copy link
Author

folliehiyuki commented Aug 13, 2022

I changed the diagnostic source. For the code action, here is my attempt so far. It doesn't seem to work so I'd love to have some help.

local h = require("null-ls.helpers")
local methods = require("null-ls.methods")

local CODE_ACTION = methods.internal.CODE_ACTION

return h.make_builtin({
    name = "typos",
    meta = {
        url = "https://github.com/crate-ci/typos",
        description = "Source code spell checker written in Rust.",
    },
    method = CODE_ACTION,
    filetypes = {},
    generator_opts = {
        command = "typos",
        args = {
            "--format",
            "json",
            "$FILENAME",
        },
        to_stdin = true,
        format = "line",
        check_exit_code = function(code)
            return code == 2
        end,
        on_output = function(line, params)
            local actions = {}
            local ok, decoded = pcall(vim.json.decode, line)
            if not ok then
                return nil
            end

            local typo = decoded.typo
            local col = decoded.byte_offset + 1
            local end_col = col + typo:len()
            local lnum = decoded.line_num

            if params.col >= col and params.col < end_col and params.row == lnum then
                for _, correction in ipairs(decoded.corrections) do
                    table.insert(actions, {
                        title = string.format("Use `%s`", correction),
                        action = function()
                            vim.api.nvim_buf_set_text(
                                params.bufnr,
                                lnum - 1,
                                col,
                                lnum,
                                end_col,
                                { correction }
                            )
                        end,
                    })
                end
                return actions
            end

            return nil
        end,
    },
    factory = h.generator_factory,
})

Here is the error I get when hovering the code action range:

Error executing vim.schedule lua callback: ...pack/packer/opt/plenary.nvim/lua/plenary/async/async.lua:14: The coroutine failed with this message: .../site/pack/packer/opt/null-ls.nvim/lua/null-ls/state.lua:28:
 table index is nil
stack traceback:
        [C]: in function 'error'
        ...pack/packer/opt/plenary.nvim/lua/plenary/async/async.lua:14: in function 'callback_or_next'
        ...pack/packer/opt/plenary.nvim/lua/plenary/async/async.lua:40: in function <...pack/packer/opt/plenary.nvim/lua/plenary/async/async.lua:39>

@jose-elias-alvarez
Copy link
Owner

Hmm, not 100% sure what that error is about, but I fixed a couple of issues in the code:

local h = require("null-ls.helpers")
local methods = require("null-ls.methods")

local CODE_ACTION = methods.internal.CODE_ACTION

return h.make_builtin({
    name = "typos",
    meta = {
        url = "https://github.com/crate-ci/typos",
        description = "Source code spell checker written in Rust.",
    },
    method = CODE_ACTION,
    filetypes = {},
    generator_opts = {
        command = "typos",
        args = {
            "--format",
            "json",
            "$FILENAME",
        },
        to_stdin = true,
        check_exit_code = function(code)
            return code == 2
        end,
        on_output = function(params, done)
            local output = {}
            -- manually decode and handle each line here to make sure code actions are simultaneously sent to the handler
            for _, line in ipairs(vim.split(params.output, "\n")) do
                local ok, decoded = pcall(vim.json.decode, line)
                if ok then
                    table.insert(output, decoded)
                end
            end

            local actions = {}
            for _, decoded in ipairs(output) do
                local typo = decoded.typo
                local col = decoded.byte_offset + 1
                local end_col = col + typo:len()
                local lnum = decoded.line_num

                -- simply match against the current line, since params.end_col is undefined
                if params.row == lnum then
                    for _, correction in ipairs(decoded.corrections) do
                        table.insert(actions, {
                            title = string.format("Use `%s`", correction),
                            action = function()
                                -- this logic is not currently working on my end
                                vim.api.nvim_buf_set_text(params.bufnr, lnum - 1, col, lnum, end_col, { correction })
                            end,
                        })
                    end
                end
            end

            return done(actions)
        end,
    },
    factory = h.generator_factory,
})

This correctly generates code actions, but the logic to replace the misspelled word is not currently working on my end (it either replaces nothing or replaces a mismatching area of the buffer). Hopefully with that we can get this over the finish line!

@folliehiyuki
Copy link
Author

Thanks for the code snippet. I'll look into it.

@RayJameson
Copy link

RayJameson commented Jan 22, 2023

@folliehiyuki Hello! Can you tell please what plugin/config are you using to show diagnostics under line?
image

@folliehiyuki
Copy link
Author

@RayJameson it's lsp_lines.nvim.

@esn89
Copy link

esn89 commented Feb 3, 2023

@folliehiyuki

Hi, I was wondering how you got the red "L" line for the diagnostics message as shown here:
https://user-images.githubusercontent.com/67634026/183258932-a3d2c209-f983-4b9f-8670-cd5390c4058b.png

@RayJameson
Copy link

RayJameson commented Feb 3, 2023

@folliehiyuki

Hi, I was wondering how you got the red "L" line for the diagnostics message as shown here:
https://user-images.githubusercontent.com/67634026/183258932-a3d2c209-f983-4b9f-8670-cd5390c4058b.png

Check my comment above, I asked the same question. And he already answered it...

@guruor
Copy link
Contributor

guruor commented Jun 8, 2023

@folliehiyuki Hey, I just wrote the diagnostic source for the typos and then I came across this PR. Looks like you are almost done with the code_actions as well.
Any progress on the above issue ? If it is taking time to fix the action issue, can we at least get the diagnosis source merged than we can work on action source separately.

@folliehiyuki
Copy link
Author

I currently don't have much free time to fix this PR. If you can open another one to implement typos as a diagnostic source (and maybe also the formatting source) then feel free to do so. I'll close this PR in favor of yours.

@guruor
Copy link
Contributor

guruor commented Jun 10, 2023

@folliehiyuki I have separated the diagnostic source from this PR and raising a separator PR. Maybe by tomorrow I'll add the formatting source as well.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants