Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 58 additions & 19 deletions ale_linters/elixir/mix.vim
Original file line number Diff line number Diff line change
@@ -1,45 +1,84 @@
" Author: evnu - https://github.com/evnu
" Author: colbydehart - https://github.com/colbydehart
" Author: toupeira - https://github.com/toupeira
" Description: Mix compile checking for Elixir files

function! ale_linters#elixir#mix#Handle(buffer, lines) abort
" Matches patterns like the following:
" Example output from Elixir 1.20.0:
"
" Error format
" ** (CompileError) apps/sim/lib/sim/server.ex:87: undefined function update_in/4
" warning: variable "a" is unused (if the variable is not meant to be used, prefix it with an underscore)
" │
" 16 │ a = missing_var
" │ ~
" │
" └─ lib/foo.ex:16:5: Foo.hello/0
"
" TODO: Warning format
" warning: variable "foobar" does not exist and is being expanded to "foobar()", please use parentheses to remove the ambiguity or change the variable name
let l:pattern = '\v\(([^\)]+Error)\) ([^:]+):([^:]+): (.+)$'
" warning: variable "b" is unused (if the variable is not meant to be used, prefix it with an underscore)
" │
" 17 │ b = another_missing_var
" │ ~
" │
" └─ lib/foo.ex:17:5: Foo.hello/0
"
" error: undefined variable "another_missing_var"
" │
" 17 │ b = another_missing_var
" │ ^^^^^^^^^^^^^^^^^^^
" │
" └─ lib/foo.ex:17:9: Foo.hello/0
"
" error: undefined variable "missing_var"
" │
" 16 │ a = missing_var
" │ ^^^^^^^^^^^
" │
" └─ lib/foo.ex:16:9: Foo.hello/0
"
"
" == Compilation error in file lib/foo.ex ==
" ** (CompileError) lib/foo.ex: cannot compile module Foo (errors have been logged)
"
" NOTE: The line with the error class is not captured because it always
" seems to be either a `CompileError` or a `SyntaxError`, with a generic
" message.
"
let l:pattern_text = '\v^ (warning|error): (.+)$'
let l:pattern_file = '\v^ └─ ([^ :]+):([0-9]+):([0-9]+)?'

let l:match_texts = ale#util#GetMatches(a:lines, l:pattern_text)
let l:match_files = ale#util#GetMatches(a:lines, l:pattern_file)

let l:output = []
let l:index = 0

" Always add the full output as detail
let l:detail = join(a:lines, "\n")

for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:type = 'E'
let l:text = l:match[4]
for l:match_text in l:match_texts
let l:type = toupper(l:match_text[1][0])
let l:match_file = get(l:match_files, l:index, [])

call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[3] + 0,
\ 'col': 0,
\ 'type': l:type,
\ 'text': l:text,
\ 'detail': l:detail,
\ 'text': get(l:match_text, 2, v:null),
\ 'lnum': get(l:match_file, 2, v:null),
\ 'col': get(l:match_file, 3, v:null),
\})

let l:index += 1
endfor

return l:output
endfunction

function! ale_linters#elixir#mix#GetCommand(buffer) abort
let l:temp_dir = ale#command#CreateDirectory(a:buffer)

return ale#Env('MIX_BUILD_PATH', l:temp_dir) . 'mix compile %s'
endfunction

call ale#linter#Define('elixir', {
\ 'name': 'mix',
\ 'executable': 'mix',
\ 'cwd': function('ale#handlers#elixir#FindMixProjectRoot'),
\ 'command': function('ale_linters#elixir#mix#GetCommand'),
\ 'command': '%e compile',
\ 'output_stream': 'stderr',
\ 'callback': 'ale_linters#elixir#mix#Handle',
\ 'lint_file': 1,
\})
7 changes: 2 additions & 5 deletions test/linter/test_elixir_mix.vader
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
Before:
call ale#assert#SetUpLinterTest('elixir', 'mix')
call ale#test#SetFilename('../test-files/elixir/mix_project/lib/app.ex')
let g:env_prefix = ale#Env('MIX_BUILD_PATH', 'TEMP_DIR')

After:
unlet! g:env_prefix

call ale#assert#TearDownLinterTest()

Execute(The default mix command should be correct):
AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/elixir/mix_project')
AssertLinter 'mix', g:env_prefix . 'mix compile %s'
AssertLinter 'mix', 'mix compile'

Execute(Build mix commands with an umbrella root):
call ale#test#SetFilename('../test-files/elixir/umbrella_project/apps/mix_project/lib/app.ex')

AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/elixir/umbrella_project')
AssertLinter 'mix', g:env_prefix . 'mix compile %s'
AssertLinter 'mix', 'mix compile'