Skip to content

Instantly share code, notes, and snippets.

@SuperSpyTX
Last active February 20, 2022 12:10
Show Gist options
  • Save SuperSpyTX/887922786834aa8e1914cfb0ee0d4177 to your computer and use it in GitHub Desktop.
Save SuperSpyTX/887922786834aa8e1914cfb0ee0d4177 to your computer and use it in GitHub Desktop.
42 Norminette linter for ALE.

Norminette Linter for ALE (Asynchronous Linting Engine)

https://github.com/w0rp/ale

Installation

Install this in your ale plugin directory (For Vundle + NeoVim that may be ~/.config/nvim/bundle/ale/ale_linters/c/norminette.vim)

You will of course, need the norminette ruby gem in your PATH. gem install --user --pre norminette

Configuration

This is how I configure it:

let g:ale_linters = {
\	'c': ['clang', 'norminette'],
\   'cpp': ['clang', 'norminette'],
\   'h': ['clang', 'norminette'],
\   'hpp': ['clang', 'norminette']
\}

But the default behavior of ALE is to use every possible linter.

Norme errors will now show up as errors for C files.

I have been unable to get it working for header files.

" Author: Joe <[email protected]>
" Description: norminette linter for C files.
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.
"
" Error (line 27): multiple empty lines
let l:pattern = '\v^(Norme|Error|Warning)( \(line (\d+)(, col (\d+))?\))?\:(.+)$'
let l:output = []
let l:curr_file = ''
let l:lel = ale#util#GetMatches(a:lines, l:pattern)
for l:match in ale#util#GetMatches(a:lines, l:pattern)
if l:match[1] == "Norme"
let l:curr_file = l:match[6]
endif
" if ale#path#IsBufferPath(a:buffer, l:curr_file) && l:match[1] == "Error"
if l:match[1] == "Error" || l:match[1] == "Warning"
call add(l:output, {
\ 'lnum': str2nr(l:match[3]),
\ 'col': l:match[5] is# '' ? 0 : str2nr(l:match[5]),
\ 'type': l:match[1] is# 'Error' ? 'E' : 'W',
\ 'text': l:match[0],
\})
endif
endfor
return l:output
endfunction
call ale#linter#Define('c', {
\ 'name': 'norminette',
\ 'output_stream': 'both',
\ 'executable_callback': 'ale_linters#c#norminette#GetExecutable',
\ 'command_callback': 'ale_linters#c#norminette#GetCommand',
\ 'callback': 'ale_linters#c#norminette#Opscript',
\})
@fratajczak
Copy link

Fix for ALE 3.0:

--- norminette.vim.old  2020-09-17 02:37:18.453351330 +0200
+++ /home/fratajcz/.config/nvim/plugged/ale/ale_linters/c/norminette.vim        2020-09-17 02:37:21.633317337 +0200
@@ -45,8 +45,8 @@
 call ale#linter#Define('c', {
 \   'name': 'norminette',
 \   'output_stream': 'both',
-\   'executable_callback': 'ale_linters#c#norminette#GetExecutable',
-\   'command_callback': 'ale_linters#c#norminette#GetCommand',
+\   'executable': function('ale_linters#c#norminette#GetExecutable'),
+\   'command': function('ale_linters#c#norminette#GetCommand'),
 \   'callback': 'ale_linters#c#norminette#Opscript',
 \})

executable_callback and command_callback were deprecated, see https://github.com/dense-analysis/ale/releases/tag/v3.0.0

@cassepipe
Copy link

cassepipe commented Aug 31, 2021

Sadly it does not work well with the new norminette enforcing norm version 3. ALE only shows one error at a time and is not able to move
the >> to the line with the norm error. I guess the fix would be to modify the pattern but I am not yet versed enough in vimscript.

@cassepipe
Copy link

cassepipe commented Sep 15, 2021

So this is the new pattern in good old vimregexp :
^Error: \h\+\s\+(line:\s\+\(\d\+\),\s\+col:\s\+\(\d\+\)):\s\+\(.*\)

It tries to match the line, column and error text at the end (look out for the \( \) if your eyes are not bleeding yet)

A good tip to test your vimregexp is to set incsearch in vim so you can test in real time what you are matching !

@SuperSpyTX
Copy link
Author

So this is the new pattern in good old vimregexp :
^Error: \h\+\s\+(line:\s\+\(\d\+\),\s\+col:\s\+\(\d\+\)):\s\+\(.*\)
With line col Error msg
fot matches (look out for the \( \) if your eyes are not bleeding yet)

I still don't know much more vimscript but Imma give it a try to make it work

A good tip to test your vimregexp is to set incsearch in vim so you can test in real time what you are matching !

Can you post an example of a norm error with the new version?

@cassepipe
Copy link

Error: SPACE_REPLACE_TAB    (line:  17, col:  11):	Found space when expecting tab
ft_memchr.c: Error!
Error: SPACE_AFTER_KW       (line:  22, col:  22):	Missing space after keyword
Error: TERNARY_FBIDDEN      (line:  26, col:   1):	Ternaries are forbidden
ft_strlen.c: Error!
Error: SPACE_REPLACE_TAB    (line:  17, col:  11):	Found space when expecting tab

@cassepipe
Copy link

cassepipe commented Sep 17, 2021

You can easily install the new norminette version 3 with

python3 -m pip install --upgrade pip setuptools
python3 -m pip install norminette

Repo at : https://github.com/42School/norminette

@cassepipe
Copy link

cassepipe commented Sep 19, 2021

" Author: cassepipe <[email protected]>
" Heavily based on Joe's <[email protected]> work. May he be here thanked
" Description: norminette linter for C files.
"
" Get the norminette with :
"
" python3 -m pip install --upgrade pip setuptools
" python3 -m pip install norminette
"
" or at : https://github.com/42School/norminette

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.
	" :set incsearch to test your patterns in real-time !
    "
	"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 = ''
	
	"A good tip to check what is at each index of l:match is to run inside Vim :
	":let pattern='\(^\(\h\+\.[ch]\): \(\w\+\)!$\|^Error: \h\+\s\+(line:\s\+\(\d\+\),\s\+col:\s\+\(\d\+\)):\s\+\(.*\)\)'
	":echo ale#util#GetMatches(['ft_lstsize.c: Error!'], pattern)
	"				^^^^^^^^^^^^^^^^^^^^^^
	"				Replace with each line you want to  match
	"				

    for l:match in ale#util#GetMatches(a:lines, l:pattern)
		if l:match[3] == 'OK'
			continue
		elseif l:match[3] == "Error"
			let l:curr_file = l:match[2]
		else
			call add(l:output, {
            \   'filename': l:curr_file,
            \   'lnum': str2nr(l:match[4]),
            \  'col': str2nr(l:match[5]),
            \   'type': 'W',
            \   'text': "Norminette : " . l:match[6],
            \})
        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',
\})

@cassepipe
Copy link

This above should be functional with the new norminette.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment