Skip to content

Commit

Permalink
fix: diagnostics in lines with multi-byte chars
Browse files Browse the repository at this point in the history
There's a conflict between the way Lua interprets strings with
multi-byte characters and the way we pass the `col` field through the
patterns.

For example, the length for the string: `* example typox` in every other
language would be `15`, but Lua counts the bytes in the string, not the
number of printable characters. This means that for the same string, lua
returns `16` as the length of the string.

The report coming from CSpell also counts only printable characters, so
for a file like this:

`test.md`
```markdown
* example typox
· example typox
```

The report will be:

`npx cspell -c cspell.json lint --language-id markdown test.md`
```
1/1 ./test.md 163.45ms X
./test.md:1:11 - Unknown word (typox)
./test.md:2:11 - Unknown word (typox)
```

Both lines have the same column as the start of the unknown word,
because CSpell doesn't count bytes when reporting the position of the
error.

So when we read the column from the report we just forward whatever we
got from the CSpell report.

The `end_col` ends up with the correct position because we calculate it
with the custom `from_quote` adapter, which finds the end column
programmatically.

To counter that discrepancy, I'm using the column reported by CSpell
only as an index to start looking for the word reported as an error in
the `end_col` function, and mutating the entries table to define the
`col` property in the same function.

I have a proof of concept that seems to work as expected, I'll test a
few scenarios before I push anything.

IMO, that feels a bit too hacky to keep as a long-term solution, we
should look into validating the `col` property in none-ls.
  • Loading branch information
davidmh committed Nov 21, 2023
1 parent 4a9843f commit d21276d
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 8 deletions.
29 changes: 25 additions & 4 deletions lua/cspell/diagnostics/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,36 @@ local custom_user_data = {
end,
}

--- CSpell doesn't care about multi-byte characters when calculating the
--- column number for the start of the error. Forwarding the column number
--- as reported by CSpell, would cause the error to be diagnostic to highlight
--- the wrong range.
--- So we delegate that value as a helper property that will help us find the
--- start and end of the word.
local custom_from_quote = {
end_col = function(entries, line)
local quote = entries["_quote"]
--- We use the column reported by CSpell as the start index to find the
--- current word in the line, in case the word shows up multiple times
--- in the same line.
local col, end_col = line:find(quote, entries["_col"], true)
--- HACK: Since the column reported by CSpell may not match the column
--- as counted by lua, we're mutating the entries table to define the
--- column property here, so we can account for special characters.
entries["col"] = col
return end_col + 1
end,
}

-- Finds the messages including a suggestions array, which comes from passing
-- the --show-suggestions flag to cspell.
-- That flag is only available when the user has registered the code action.
local matcher_with_suggestions = {
pattern = ".*:(%d+):(%d+)%s*-%s*(.*%((.*)%))%s*Suggestions:%s*%[(.*)%]",
groups = { "row", "col", "message", "_quote", "_suggestions" },
groups = { "row", "_col", "message", "_quote", "_suggestions" },
overrides = {
adapters = {
h.diagnostics.adapters.end_col.from_quote,
custom_from_quote,
custom_user_data,
},
},
Expand All @@ -38,10 +59,10 @@ local matcher_with_suggestions = {
-- used by the code actions.
local matcher_without_suggestions = {
pattern = [[.*:(%d+):(%d+)%s*-%s*(.*%((.*)%))]],
groups = { "row", "col", "message", "_quote" },
groups = { "row", "_col", "message", "_quote" },
overrides = {
adapters = {
h.diagnostics.adapters.end_col.from_quote,
custom_from_quote,
},
},
}
Expand Down
1 change: 1 addition & 0 deletions stylua.toml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
indent_type = "Spaces"
indent_width = 4
10 changes: 6 additions & 4 deletions tests/spec/diagnostics_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ describe("diagnostics", function()

it("should create a diagnostic", function()
local output = "/some/path/file.lua:1:18 - Unknown word (variabl)"
local diagnostic = parser(output, { content = content })
local diagnostic = parser(output, { content = { content } })

assert.same({
col = "18",
col = 18,
end_col = 25,
message = "Unknown word (variabl)",
row = "1",
}, diagnostic)
Expand All @@ -31,10 +32,11 @@ describe("diagnostics", function()
it("includes suggestions", function()
local output =
"/some/path/file.lua:1:18 - Unknown word (variabl) Suggestions: [variable, variably, variables, variant, variate]"
local diagnostic = parser(output, { content = content })
local diagnostic = parser(output, { content = { content } })

assert.same({
col = "18",
col = 18,
end_col = 25,
message = "Unknown word (variabl)",
row = "1",
user_data = {
Expand Down

0 comments on commit d21276d

Please sign in to comment.