Add hdevtools linter for haskell
This adds support for the hdevtools haskell linter https://github.com/hdevtools/hdevtools The output for hdevtools is near identical to the ghc output so this also extracts the ghc handler into the handle file and adds tests
This commit is contained in:
		
							parent
							
								
									c460602cbb
								
							
						
					
					
						commit
						c4afd72792
					
				@ -5,63 +5,12 @@ if exists('g:loaded_ale_linters_haskell_ghc')
 | 
				
			|||||||
    finish
 | 
					    finish
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let g:loaded_ale_linters_haskell_ghc = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function! ale_linters#haskell#ghc#Handle(buffer, lines) abort
 | 
					 | 
				
			||||||
    " Look for lines like the following.
 | 
					 | 
				
			||||||
    "
 | 
					 | 
				
			||||||
    " /dev/stdin:28:26: Not in scope: `>>>>>'
 | 
					 | 
				
			||||||
    let l:pattern = '^[^:]\+:\(\d\+\):\(\d\+\): \(.\+\)$'
 | 
					 | 
				
			||||||
    let l:output = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    " For some reason the output coming out of the GHC through the wrapper
 | 
					 | 
				
			||||||
    " script breaks the lines up in strange ways. So we have to join some
 | 
					 | 
				
			||||||
    " lines back together again.
 | 
					 | 
				
			||||||
    let l:corrected_lines = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for l:line in a:lines
 | 
					 | 
				
			||||||
        if len(matchlist(l:line, l:pattern)) > 0
 | 
					 | 
				
			||||||
            call add(l:corrected_lines, l:line)
 | 
					 | 
				
			||||||
            if l:line !~# ': error:$'
 | 
					 | 
				
			||||||
                call add(l:corrected_lines, '')
 | 
					 | 
				
			||||||
            endif
 | 
					 | 
				
			||||||
        elseif l:line ==# ''
 | 
					 | 
				
			||||||
            call add(l:corrected_lines, l:line)
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            if len(l:corrected_lines) > 0
 | 
					 | 
				
			||||||
                let l:line = substitute(l:line, '\v\s+', ' ', '')
 | 
					 | 
				
			||||||
                let l:corrected_lines[-1] .= l:line
 | 
					 | 
				
			||||||
            endif
 | 
					 | 
				
			||||||
        endif
 | 
					 | 
				
			||||||
    endfor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for l:line in l:corrected_lines
 | 
					 | 
				
			||||||
        let l:match = matchlist(l:line, l:pattern)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if len(l:match) == 0
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        call add(l:output, {
 | 
					 | 
				
			||||||
        \   'bufnr': a:buffer,
 | 
					 | 
				
			||||||
        \   'lnum': l:match[1] + 0,
 | 
					 | 
				
			||||||
        \   'vcol': 0,
 | 
					 | 
				
			||||||
        \   'col': l:match[2] + 0,
 | 
					 | 
				
			||||||
        \   'text': l:match[3],
 | 
					 | 
				
			||||||
        \   'type': 'E',
 | 
					 | 
				
			||||||
        \   'nr': -1,
 | 
					 | 
				
			||||||
        \})
 | 
					 | 
				
			||||||
    endfor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return l:output
 | 
					 | 
				
			||||||
endfunction
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
call ale#linter#Define('haskell', {
 | 
					call ale#linter#Define('haskell', {
 | 
				
			||||||
\   'name': 'ghc',
 | 
					\   'name': 'ghc',
 | 
				
			||||||
\   'output_stream': 'stderr',
 | 
					\   'output_stream': 'stderr',
 | 
				
			||||||
\   'executable': 'ghc',
 | 
					\   'executable': 'ghc',
 | 
				
			||||||
\   'command': 'ghc -fno-code -v0 %t',
 | 
					\   'command': 'ghc -fno-code -v0 %t',
 | 
				
			||||||
\   'callback': 'ale_linters#haskell#ghc#Handle',
 | 
					\   'callback': 'ale#handlers#HandleGhcFormat',
 | 
				
			||||||
\})
 | 
					\})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
call ale#linter#Define('haskell', {
 | 
					call ale#linter#Define('haskell', {
 | 
				
			||||||
@ -69,5 +18,5 @@ call ale#linter#Define('haskell', {
 | 
				
			|||||||
\   'output_stream': 'stderr',
 | 
					\   'output_stream': 'stderr',
 | 
				
			||||||
\   'executable': 'stack',
 | 
					\   'executable': 'stack',
 | 
				
			||||||
\   'command': 'stack ghc -- -fno-code -v0 %t',
 | 
					\   'command': 'stack ghc -- -fno-code -v0 %t',
 | 
				
			||||||
\   'callback': 'ale_linters#haskell#ghc#Handle',
 | 
					\   'callback': 'ale#handlers#HandleGhcFormat',
 | 
				
			||||||
\})
 | 
					\})
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								ale_linters/haskell/hdevtools.vim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ale_linters/haskell/hdevtools.vim
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					" Author: w0rp <devw0rp@gmail.com>
 | 
				
			||||||
 | 
					" Description: hdevtools for Haskell files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					call ale#linter#Define('haskell', {
 | 
				
			||||||
 | 
					\   'name': 'hdevtools',
 | 
				
			||||||
 | 
					\   'executable': 'hdevtools',
 | 
				
			||||||
 | 
					\   'command': 'hdevtools check -g -Wall -p %s %t',
 | 
				
			||||||
 | 
					\   'callback': 'ale#handlers#HandleGhcFormat',
 | 
				
			||||||
 | 
					\})
 | 
				
			||||||
@ -220,3 +220,49 @@ function! ale#handlers#HandleStyleLintFormat(buffer, lines) abort
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return l:output
 | 
					    return l:output
 | 
				
			||||||
endfunction
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function! ale#handlers#HandleGhcFormat(buffer, lines) abort
 | 
				
			||||||
 | 
					    " Look for lines like the following.
 | 
				
			||||||
 | 
					    "
 | 
				
			||||||
 | 
					    " /dev/stdin:28:26: Not in scope: `>>>>>'
 | 
				
			||||||
 | 
					     "Appoint/Lib.hs:8:1: warning:
 | 
				
			||||||
 | 
					    let l:pattern = '^[^:]\+:\(\d\+\):\(\d\+\):\s\{-}\(warning\|error\)\(.\+\)$'
 | 
				
			||||||
 | 
					    let l:output = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let l:corrected_lines = []
 | 
				
			||||||
 | 
					    for l:line in a:lines
 | 
				
			||||||
 | 
					        if len(matchlist(l:line, l:pattern)) > 0
 | 
				
			||||||
 | 
					            call add(l:corrected_lines, l:line)
 | 
				
			||||||
 | 
					            if l:line !~# '\(: error\|warning\):$'
 | 
				
			||||||
 | 
					                call add(l:corrected_lines, '')
 | 
				
			||||||
 | 
					            endif
 | 
				
			||||||
 | 
					        elseif l:line ==# ''
 | 
				
			||||||
 | 
					            call add(l:corrected_lines, l:line)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            if len(l:corrected_lines) > 0
 | 
				
			||||||
 | 
					                let l:line = substitute(l:line, '\v^\s+', ' ', '')
 | 
				
			||||||
 | 
					                let l:corrected_lines[-1] .= l:line
 | 
				
			||||||
 | 
					            endif
 | 
				
			||||||
 | 
					        endif
 | 
				
			||||||
 | 
					    endfor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for l:line in l:corrected_lines
 | 
				
			||||||
 | 
					        let l:match = matchlist(l:line, l:pattern)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if len(l:match) == 0
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        call add(l:output, {
 | 
				
			||||||
 | 
					        \   'bufnr': a:buffer,
 | 
				
			||||||
 | 
					        \   'lnum': l:match[1] + 0,
 | 
				
			||||||
 | 
					        \   'vcol': 0,
 | 
				
			||||||
 | 
					        \   'col': l:match[2] + 0,
 | 
				
			||||||
 | 
					        \   'text': l:match[4],
 | 
				
			||||||
 | 
					        \   'type': toupper(l:match[3][0]),
 | 
				
			||||||
 | 
					        \   'nr': -1,
 | 
				
			||||||
 | 
					        \})
 | 
				
			||||||
 | 
					    endfor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return l:output
 | 
				
			||||||
 | 
					endfunction
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										30
									
								
								test/test_ghc_handler.vader
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								test/test_ghc_handler.vader
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					Execute(The ghc handler should handle hdevtools output):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AssertEqual
 | 
				
			||||||
 | 
					  \ [
 | 
				
			||||||
 | 
					  \   {'lnum': 147, 'bufnr': 12, 'vcol': 0, 'nr': -1, 'type': 'W', 'col': 62, 'text': ':• Couldnt match type ‘a -> T.Text’ with ‘T.Text’ Expected type: [T.Text]'},
 | 
				
			||||||
 | 
					  \ ],
 | 
				
			||||||
 | 
					  \ ale#handlers#HandleGhcFormat(12, [
 | 
				
			||||||
 | 
					  \ '/path/to/foo.hs:147:62: warning:',
 | 
				
			||||||
 | 
					  \ '• Couldnt match type ‘a -> T.Text’ with ‘T.Text’',
 | 
				
			||||||
 | 
					  \ '  Expected type: [T.Text]',
 | 
				
			||||||
 | 
					  \ ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Execute(The ghc handler should handle ghc output):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AssertEqual
 | 
				
			||||||
 | 
					  \ [
 | 
				
			||||||
 | 
					  \   {'lnum': 6, 'bufnr': 47, 'vcol': 0, 'nr': -1, 'type': 'E', 'col': 1, 'text': ': Failed to load interface for ‘GitHub.Data’ Use -v to see a list of the files searched for.'},
 | 
				
			||||||
 | 
					  \   {'lnum': 7, 'bufnr': 47, 'vcol': 0, 'nr': -1, 'type': 'E', 'col': 1, 'text': ': Failed to load interface for ‘GitHub.Endpoints.PullRequests’ Use -v to see a list of the files searched for.'},
 | 
				
			||||||
 | 
					  \ ],
 | 
				
			||||||
 | 
					  \ ale#handlers#HandleGhcFormat(47, [
 | 
				
			||||||
 | 
					  \ '',
 | 
				
			||||||
 | 
					  \ 'src/Appoint/Lib.hs:6:1: error:',
 | 
				
			||||||
 | 
					  \ '    Failed to load interface for ‘GitHub.Data’',
 | 
				
			||||||
 | 
					  \ '    Use -v to see a list of the files searched for.',
 | 
				
			||||||
 | 
					  \ '',
 | 
				
			||||||
 | 
					  \ 'src/Appoint/Lib.hs:7:1: error:',
 | 
				
			||||||
 | 
					  \ '    Failed to load interface for ‘GitHub.Endpoints.PullRequests’',
 | 
				
			||||||
 | 
					  \ '    Use -v to see a list of the files searched for.',
 | 
				
			||||||
 | 
					  \ ])
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user