I am trying to write an ale linter for the norminette program. How can I make it work ?
It's a program that checks if C source code files follow the coding norm of the 42 school network and it would save me time to be able to have it integrate tis he ale plugin.
I am not well versed in VimL and this is what I was able to come up with, putting this in the ~/.vim/plugged/ale/ale_linters/c
directory :
call ale#Set('c_norminette_executable', 'norminette')
call ale#Set('c_norminette_options', '')
function! ale_linters#c#norminette#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'c_norminette_executable')
endfunction
function! ale_linters#c#norminette#GetCommand(buffer) abort
return ale#Escape(ale_linters#c#norminette#GetExecutable(a:buffer))
\ . ale#Var(a:buffer, 'c_norminette_options')
\ . ' %t'
endfunction
function! ale_linters#c#norminette#Opscript(buffer, lines) abort
" Look for lines like the following.
"
"ft_lstsize.c: Error!
"Error: SPACE_REPLACE_TAB (line: 17, col: 11): Found space when expecting tab
"ft_calloc.c: OK!
"ft_memcpy.c: Error!
"Error: SPACE_AFTER_KW (line: 22, col: 19): Missing space after keyword
"test.c: Error!
"Error: SPACE_BEFORE_FUNC (line: 6, col: 4): space before function name
"Error: WRONG_SCOPE_COMMENT (line: 12, col: 9): Comment is invalid in this scope
"ft_isalnum.c: OK!
let l:pattern = '\(^\(\h\+\.[ch]\): \(\w\+\)!$\|^Error: \h\+\s\+(line:\s\+\(\d\+\),\s\+col:\s\+\(\d\+\)):\s\+\(.*\)\)'
let l:output = []
let l:curr_file = ''
for l:match in ale#util#GetMatches(a:lines, l:pattern)
if l:match[2] == 'OK'
continue
elseif l:match[2] == "Error"
let l:curr_file = l:match[1]
" if ale#path#IsBufferPath(a:buffer, l:curr_file) && l:match[1] == "Error"
else
call add(l:output, {
\ 'lnum': str2nr(l:match[1]),
\ 'col': str2nr(l:match[2]),
\ 'type': 'E',
\ 'text': l:match[3],
\})
endif
endfor
return l:output
endfunction
call ale#linter#Define('c', {
\ 'name': 'norminette',
\ 'output_stream': 'both',
\ 'executable': function('ale_linters#c#norminette#GetExecutable'),
\ 'command': function('ale_linters#c#norminette#GetCommand'),
\ 'callback': 'ale_linters#c#norminette#Opscript',
\})
There's an comment in the function that shows what the norminette
ouputs to the terminal.
I know that my pattern works because I tested it on the output thanks to the set incsearch
parameter.
" Given a single line, or a List of lines, and a single pattern, or a List
" of patterns, return all of the matches for the lines(s) from the given
" patterns, using matchlist().
"
" Only the first pattern which matches a line will be returned.
function! ale#util#GetMatches(lines, patterns) abort
let l:matches = []
let l:lines = type(a:lines) is v:t_list ? a:lines : [a:lines]
let l:patterns = type(a:patterns) is v:t_list ? a:patterns : [a:patterns]
for l:line in l:lines
for l:pattern in l:patterns
let l:match = matchlist(l:line, l:pattern)
if !empty(l:match)
call add(l:matches, l:match)
break
endif
endfor
endfor
return l:matches
endfunction
If you want to see it for yourself and install it, it as easy as running:
python3 -m pip install --upgrade pip setuptools
python3 -m pip install norminette
Here is the ale_linters#c#norminette#GetExecutable function :
" Given a single line, or a List of lines, and a single pattern, or a List
" of patterns, return all of the matches for the lines(s) from the given
" patterns, using matchlist().
"
" Only the first pattern which matches a line will be returned.
function! ale#util#GetMatches(lines, patterns) abort
let l:matches = []
let l:lines = type(a:lines) is v:t_list ? a:lines : [a:lines]
let l:patterns = type(a:patterns) is v:t_list ? a:patterns : [a:patterns]
for l:line in l:lines
for l:pattern in l:patterns
let l:match = matchlist(l:line, l:pattern)
if !empty(l:match)
call add(l:matches, l:match)
break
endif
endfor
endfor
return l:matches
endfunction
I am sorry to ask such a specific question. I've given it my best and I've tried other channels for help but I have not found any. This is my last attempt before I actually buy a book to learn how to program in Vimscript :) (I am getting quite good at vimregex though)
filename: Error
and then looking at the lines after that for the messages and such (nested loops, two patterns instead of one).