dense-analysis / dense-analysis/ale

Add support for codenarc

Open
#4,672 0 comments 0 reactions 0 assignees View on GitHub
new tool
Dominant language
Vim Script
Stars
14k
Forks
1.5k
Avg merge
17h 49m
Merged PRs (30d)
1

Description

**Name:** Codenarc
**URL:** https://codenarc.org/

> CodeNarc analyzes Groovy code for defects, bad practices, inconsistencies, style issues and more. A flexible framework for rules, rulesets and custom rules means it’s easy to configure CodeNarc to fit into your project. Build tool, framework support, and report generation are all enterprise ready.

We use this at my work; I have a hacked together patch to ALE which I have not had the time to clean up. It is docker-only. I'm adding it below in case it's a useful starting point.

ale_linters/groovy/codenarc.vim

```vim
call ale#Set('groovy_codenarc_use_docker', 'always')
" TODO test with official image
" TODO PR new image
call ale#Set('groovy_codenarc_docker_image', 'codenarc/codenarc')
call ale#Set('groovy_codenarc_options', '')
" call ale#Set('groovy_codenarc_classpath', '')
" Look at kotlinc.vim for inspiration
" Here's a partial
" #!/usr/bin/env bash

" export CODENARC_JAR="$HOME/bin/CodeNarc-3.1.0.jar"
" export SLF4J_JAR="/Users/rmartine/.m2/repository/org/slf4j/slf4j-api/1.7.35/slf4j-api-1.7.35.jar"
" export SLF4J_SIMPLE="/Users/rmartine/.m2/repository/org/slf4j/slf4j-simple/1.7.35/slf4j-simple-1.7.35.jar"

" export GROOVY_HOME="/Users/rmartine/.sdkman/candidates/groovy/current/"
" export GROOVY_JAR="$GROOVY_HOME/lib/*:$GROOVY_HOME/lib/extras-jaxb/*"

" java \
" -classpath "$GROOVY_JAR:$CODENARC_JAR:$SLF4J_JAR:$SLF4J_SIMPLE" \
" org.codenarc.CodeNarc

function! ale_linters#groovy#codenarc#GetExecutable(buffer) abort
let l:use_docker = ale#Var(a:buffer, 'groovy_codenarc_use_docker')

" check for mandatory directives
if l:use_docker is# 'never'
"TODO Implement running local like
return 'UNIMPLEMENTED'
elseif l:use_docker is# 'always'
return 'docker'
endif

" if we reach here, we want to use 'hadolint' if present...
" if executable('hadolint')
" return 'hadolint'
" endif

"... and 'docker' as a fallback.
return 'docker'
endfunction

function! ale_linters#groovy#codenarc#GetCommand(buffer) abort
let l:command = ale_linters#groovy#codenarc#GetExecutable(a:buffer)

if l:command is# 'docker'
let l:path = fnamemodify(bufname(a:buffer), ':p')
let l:file = fnamemodify(l:path, ':t')
let l:tmpdir = fnamemodify(tempname(), ':h:h')

return 'docker run --rm'
\ . ' -v %t:h:"/ws/":ro'
\ . ale#Pad(ale#Var(a:buffer, 'groovy_codenarc_docker_image'))
\ . ale#Pad(ale#Var(a:buffer, 'groovy_codenarc_options'))
\ . ' -includes=./%t:t'
\ . ' -rulesetfiles="rulesets/groovyism.xml,rulesets/basic.xml,rulesets/braces.xml,rulesets/imports.xml"'
\ . ' -report=json:stdout'
endif

return 'UNIMPLEMENTED'
endfunction

function! ale_linters#groovy#codenarc#Handle(buffer, lines) abort
let l:output = []
let l:json = {}

for l:line in a:lines
if l:line[0:10] isnot# '{"codeNarc"'
continue
endif

let l:json = json_decode(l:line)
break
endfor

if empty(l:json)
echoerr join(a:lines, '\n')
return
endif

if !has_key(l:json, 'summary')
\|| l:json['summary']['filesWithViolations'] == 0
return []
endif

let l:file = l:json['packages'][0]['files'][0]

" TODO error handling
" TODO standardize on [''] or . syntax
if l:file.name isnot# fnamemodify(bufname(a:buffer), ':p:t')
return []
endif

for l:violation in l:file['violations']
let l:text = l:violation.ruleName
if has_key(l:violation, 'message')
let l:text = l:violation.message
endif
let l:lint = {
\ 'lnum': l:violation.lineNumber,
\ 'code': l:violation.ruleName,
\ 'text': l:text,
\ 'type': l:violation.priority == 1 ? 'E': 'W',
\}

let l:rules = filter(l:json["rules"], 'v:val.name is# l:violation.ruleName')

if len(l:rules) == 1
let l:lint.detail = l:rules[0].name . ":\n" . l:rules[0].description
let l:lint.text = l:lint.text . ': ' . l:rules[0].description
elseif len(l:rules) > 1
echoerr 'Multiple rules with name ' . l:violation.ruleName
echoerr join(l:json["rules"], ";")
endif
" Sometimes you get zero, like for GstringExpressionWithinString

call add(l:output, l:lint)
endfor

return l:output
endfunction

call ale#linter#Define('groovy', {
\ 'name': 'CodeNarc',
\ 'executable': function('ale_linters#groovy#codenarc#GetExecutable'),
\ 'command': function('ale_linters#groovy#codenarc#GetCommand'),
\ 'callback': 'ale_linters#groovy#codenarc#Handle',
\ 'output_stream': 'both',
\})
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.