dense-analysis / ale

Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
BSD 2-Clause "Simplified" License
13.57k stars 1.44k forks source link

Add support for codenarc #4672

Open rmartine-ias opened 11 months ago

rmartine-ias commented 11 months ago

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', \}) ```