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

Commit

Permalink
Merge branch 'main' into more-python-tools
Browse files Browse the repository at this point in the history
  • Loading branch information
qbedard committed Oct 31, 2022
2 parents 0fa5b85 + f1add23 commit 2d42aac
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 3 deletions.
36 changes: 36 additions & 0 deletions doc/BUILTINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,42 @@ local sources = { null_ls.builtins.diagnostics.phpstan }
- Requires a valid `phpstan.neon` at root.
- If in place validation is required set `method` to `diagnostics_on_save` and `to_temp_file` to `false`

### [pmd](https://pmd.github.io)

An extensible cross-language static code analyzer.

#### Usage

```lua
local sources = {
null_ls.builtins.diagnostics.pmd.with({
extra_args = {
"--rulesets",
"category/java/bestpractices.xml,category/jsp/bestpractices.xml" -- or path to self-written ruleset
},
}),
}
```

#### Defaults

- Filetypes: `{ "java", "jsp" }`
- Method: `diagnostics_on_save`
- Command: `pmd`
- Args: `{ "--format", "json", "--dir", "$ROOT" }`

#### Notes

- PMD only offers parameterized wrapper scripts as download. It is recommended to put an executable wrapper
script in your path.
Example wrapper script:
```bash
#!/usr/bin/env bash
path/to/pmd/bin/run.sh pmd "$@"
```
- PMD needs a mandatory `--rulesets`/`-rulesets`/`-R` argument. Use `extra_args` to add yours. `extra_args`
can also be a function to build more sophisticated logic.

### [proselint](https://github.com/amperser/proselint)

An English prose linter.
Expand Down
3 changes: 3 additions & 0 deletions lua/null-ls/builtins/_meta/diagnostics.lua
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ return {
phpstan = {
filetypes = { "php" }
},
pmd = {
filetypes = { "java", "jsp" }
},
proselint = {
filetypes = { "markdown", "tex" }
},
Expand Down
5 changes: 4 additions & 1 deletion lua/null-ls/builtins/_meta/filetype_map.lua
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ return {
formatting = { "djhtml", "djlint" }
},
java = {
diagnostics = { "checkstyle", "semgrep" },
diagnostics = { "checkstyle", "pmd", "semgrep" },
formatting = { "astyle", "clang_format", "google_java_format", "npm_groovy_lint", "uncrustify" }
},
javascript = {
Expand All @@ -196,6 +196,9 @@ return {
jsonc = {
formatting = { "deno_fmt", "prettier", "prettier_d_slim", "prettierd" }
},
jsp = {
diagnostics = { "pmd" }
},
just = {
formatting = { "just" }
},
Expand Down
6 changes: 4 additions & 2 deletions lua/null-ls/builtins/diagnostics/checkstyle.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ local function handle_checkstyle_output(params)

for _, result in ipairs(results) do
for _, location in ipairs(result.locations) do
local col = location.physicalLocation.region.startColumn

table.insert(output, {
row = location.physicalLocation.region.startLine,
col = location.physicalLocation.region.startColumn,
end_col = location.physicalLocation.region.startColumn + 1,
col = col,
end_col = col and col + 1,
code = result.ruleId,
message = result.message.text,
severity = h.diagnostics.severities[result.level],
Expand Down
95 changes: 95 additions & 0 deletions lua/null-ls/builtins/diagnostics/pmd.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
local h = require("null-ls.helpers")

local function parse_pmd_errors(params, output)
if params.err:match("The following option is required: %-%-rulesets") then
table.insert(output, {
message = "You need to specify a ruleset for PMD."
.. " See https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md#pmd",
severity = vim.diagnostic.severity.ERROR,
bufnr = params.bufnr,
})
return
end

if params.err:match("encourageToUseIncrementalAnalysis") then
table.insert(output, {
code = "encourageToUseIncrementalAnalysis",
message = vim.trim(vim.split(params.err, "\n")[2]),
severity = vim.diagnostic.severity.WARN,
bufnr = params.bufnr,
})
return
end

table.insert(output, {
message = vim.trim(params.err),
severity = vim.diagnostic.severity.ERROR,
bufnr = params.bufnr,
})
end

local function handle_pmd_output(params)
local output = {}

local files = params.output and params.output.files or {}

if params.err then
parse_pmd_errors(params, output)
end

for _, file in ipairs(files) do
for _, violation in ipairs(file.violations) do
table.insert(output, {
row = violation.beginline,
col = violation.begincolumn,
end_row = violation.endline,
end_col = violation.endcolumn and violation.endcolumn + 1,
code = violation.ruleset .. "/" .. violation.rule,
message = violation.description,
severity = violation.priority == 1 and violation.priority or violation.priority - 1,
filename = file.filename,
})
end
end

return output
end

return h.make_builtin({
name = "pmd",
meta = {
url = "https://pmd.github.io",
description = "An extensible cross-language static code analyzer.",
usage = [[local sources = {
null_ls.builtins.diagnostics.pmd.with({
extra_args = {
"--rulesets",
"category/java/bestpractices.xml,category/jsp/bestpractices.xml" -- or path to self-written ruleset
},
}),
}]],
notes = {
[[PMD only offers parameterized wrapper scripts as download. It is recommended to put an executable wrapper
script in your path.
Example wrapper script:
```bash
#!/usr/bin/env bash
path/to/pmd/bin/run.sh pmd "$@"
```]],
[[PMD needs a mandatory `--rulesets`/`-rulesets`/`-R` argument. Use `extra_args` to add yours. `extra_args`
can also be a function to build more sophisticated logic.]],
},
},
method = require("null-ls.methods").internal.DIAGNOSTICS_ON_SAVE,
filetypes = { "java", "jsp" },
generator_opts = {
args = { "--format", "json", "--dir", "$ROOT" },
check_exit_code = { 0, 4 },
command = "pmd",
format = "json_raw",
multiple_files = true,
on_output = handle_pmd_output,
to_stdin = false,
},
factory = h.generator_factory,
})
161 changes: 161 additions & 0 deletions test/spec/builtins/diagnostics_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1526,4 +1526,165 @@ describe("diagnostics", function()
}, parsed)
end)
end)

describe("pmd", function()
local linter = diagnostics.pmd
local parser = linter._opts.on_output

it("should parse the usual output", function()
local output = vim.json.decode([[
{
"formatVersion": 0,
"pmdVersion": "6.50.0",
"timestamp": "2022-10-21T20:44:51.872+02:00",
"files": [
{
"filename": "/home/someuser/Code/someproject/FooController.java",
"violations": [
{
"beginline": 1,
"begincolumn": 8,
"endline": 2,
"endcolumn": 1,
"description": "Class comments are required",
"rule": "CommentRequired",
"ruleset": "Documentation",
"priority": 3,
"externalInfoUrl": "https://pmd.github.io/pmd-6.50.0/pmd_rules_java_documentation.html#commentrequired"
}
]
},
{
"filename": "/home/someuser/Code/someproject/ReportController.java",
"violations": [
{
"beginline": 76,
"begincolumn": 8,
"endline": 712,
"endcolumn": 1,
"description": "Class comments are required 2",
"rule": "CommentRequired2",
"ruleset": "Documentation",
"priority": 3,
"externalInfoUrl": "https://pmd.github.io/pmd-6.50.0/pmd_rules_java_documentation.html#commentrequired"
},
{
"beginline": 77,
"begincolumn": 23,
"endline": 77,
"endcolumn": 57,
"description": "Field comments are required 3",
"rule": "CommentRequired3",
"ruleset": "Documentation",
"priority": 3,
"externalInfoUrl": "https://pmd.github.io/pmd-6.50.0/pmd_rules_java_documentation.html#commentrequired"
}
]
}
],
"suppressedViolations": [],
"processingErrors": [],
"configurationErrors": []
}
]])
local parsed = parser({ output = output })
assert.same({
{
row = 1,
col = 8,
end_row = 2,
end_col = 2,
code = "Documentation/CommentRequired",
message = "Class comments are required",
severity = vim.diagnostic.severity.WARN,
filename = "/home/someuser/Code/someproject/FooController.java",
},
{
row = 76,
col = 8,
end_row = 712,
end_col = 2,
code = "Documentation/CommentRequired2",
message = "Class comments are required 2",
severity = vim.diagnostic.severity.WARN,
filename = "/home/someuser/Code/someproject/ReportController.java",
},
{
row = 77,
col = 23,
end_row = 77,
end_col = 58,
code = "Documentation/CommentRequired3",
message = "Field comments are required 3",
severity = vim.diagnostic.severity.WARN,
filename = "/home/someuser/Code/someproject/ReportController.java",
},
}, parsed)
end)

it("should show the caching encouragement as warning", function()
local output = vim.json.decode([[
{
"formatVersion": 0,
"pmdVersion": "6.50.0",
"timestamp": "2022-10-21T20:44:51.872+02:00",
"files": [],
"suppressedViolations": [],
"processingErrors": [],
"configurationErrors": []
}
]])
local err = [[Oct 21, 2022 10:57:30 PM net.sourceforge.pmd.PMD encourageToUseIncrementalAnalysis
WARNING: This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.50.0/pmd_userdocs_incremental_analysis.html]]
local parsed = parser({ bufnr = 42, err = err, output = output })
assert.same({
{
code = "encourageToUseIncrementalAnalysis",
message = "WARNING: This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.50.0/pmd_userdocs_incremental_analysis.html",
severity = vim.diagnostic.severity.WARN,
bufnr = 42,
},
}, parsed)
end)

it("should rephrase the missing ruleset message", function()
local parsed = parser({
bufnr = 42,
output = nil,
err = [[The following option is required: --rulesets, -rulesets, -R
Run with --help for command line help.]],
})
assert.same({
{
message = "You need to specify a ruleset for PMD. See"
.. " https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md#pmd",
severity = vim.diagnostic.severity.ERROR,
bufnr = 42,
},
}, parsed)
end)

it("should show other error messages", function()
local output = vim.json.decode([[
{
"formatVersion": 0,
"pmdVersion": "6.50.0",
"timestamp": "2022-10-21T20:44:51.872+02:00",
"files": [],
"suppressedViolations": [],
"processingErrors": [],
"configurationErrors": []
}
]])
local err = [[Some other message.]]
local parsed = parser({ bufnr = 42, err = err, output = output })
assert.same({
{
message = err,
severity = vim.diagnostic.severity.ERROR,
bufnr = 42,
},
}, parsed)
end)
end)
end)

0 comments on commit 2d42aac

Please sign in to comment.