1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
" Author: Zach Perrault -- @zperrault
" Author: Florian Beeres <yuuki@protonmail.com>
" Description: FlowType checking for JavaScript files
call ale#Set('javascript_flow_executable', 'flow')
call ale#Set('javascript_flow_use_home_config', 0)
call ale#Set('javascript_flow_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('javascript_flow_use_respect_pragma', 1)
function! ale_linters#javascript#flow#GetExecutable(buffer) abort
let l:flow_config = ale#path#FindNearestFile(a:buffer, '.flowconfig')
if empty(l:flow_config)
" Don't run Flow if we can't find a .flowconfig file.
return ''
endif
" Don't run Flow with a configuration file from the home directory by
" default, which can eat all of your RAM.
if fnamemodify(l:flow_config, ':h') is? $HOME
\&& !ale#Var(a:buffer, 'javascript_flow_use_home_config')
return ''
endif
return ale#path#FindExecutable(a:buffer, 'javascript_flow', [
\ 'node_modules/.bin/flow',
\])
endfunction
function! ale_linters#javascript#flow#GetCommand(buffer, version) abort
" If we can parse the version number, then only use --respect-pragma
" if the version is >= 0.36.0, which added the argument.
let l:use_respect_pragma = ale#Var(a:buffer, 'javascript_flow_use_respect_pragma')
\ && (empty(a:version) || ale#semver#GTE(a:version, [0, 36]))
return '%e check-contents'
\ . (l:use_respect_pragma ? ' --respect-pragma': '')
\ . ' --json --from ale %s < %t'
\ . (!has('win32') ? '; echo' : '')
endfunction
" Filter lines of flow output until we find the first line where the JSON
" output starts.
function! s:GetJSONLines(lines) abort
let l:start_index = 0
for l:line in a:lines
if l:line[:0] is# '{'
break
endif
let l:start_index += 1
endfor
return a:lines[l:start_index :]
endfunction
function! s:ExtraErrorMsg(current, new) abort
let l:newMsg = ''
if a:current is# ''
" extra messages appear to already have a :
let l:newMsg = a:new
else
let l:newMsg = a:current . ' ' . a:new
endif
return l:newMsg
endfunction
function! s:GetDetails(error) abort
let l:detail = ''
for l:extra_error in a:error.extra
if has_key(l:extra_error, 'message')
for l:extra_message in l:extra_error.message
let l:detail = s:ExtraErrorMsg(l:detail, l:extra_message.descr)
endfor
endif
if has_key(l:extra_error, 'children')
for l:child in l:extra_error.children
for l:child_message in l:child.message
let l:detail = l:detail . ' ' . l:child_message.descr
endfor
endfor
endif
endfor
return l:detail
endfunction
function! ale_linters#javascript#flow#Handle(buffer, lines) abort
let l:str = join(s:GetJSONLines(a:lines), '')
if empty(l:str)
return []
endif
let l:flow_output = json_decode(l:str)
let l:output = []
for l:error in get(l:flow_output, 'errors', [])
" Each error is broken up into parts
let l:text = ''
let l:line = 0
let l:col = 0
for l:message in l:error.message
" Comments have no line of column information, so we skip them.
" In certain cases, `l:message.loc.source` points to a different path
" than the buffer one, thus we skip this loc information too.
if has_key(l:message, 'loc')
\&& l:line is# 0
\&& ale#path#IsBufferPath(a:buffer, l:message.loc.source)
let l:line = l:message.loc.start.line + 0
let l:col = l:message.loc.start.column + 0
endif
if l:text is# ''
let l:text = l:message.descr . ':'
else
let l:text = l:text . ' ' . l:message.descr
endif
endfor
if has_key(l:error, 'operation')
let l:text = l:text . ' See also: ' . l:error.operation.descr
endif
let l:errorToAdd = {
\ 'lnum': l:line,
\ 'col': l:col,
\ 'text': l:text,
\ 'type': has_key(l:error, 'level') && l:error.level is# 'error' ? 'E' : 'W',
\}
if has_key(l:error, 'extra')
let l:errorToAdd.detail = l:errorToAdd.text
\ . "\n" . s:GetDetails(l:error)
endif
call add(l:output, l:errorToAdd)
endfor
return l:output
endfunction
call ale#linter#Define('javascript', {
\ 'name': 'flow',
\ 'executable': function('ale_linters#javascript#flow#GetExecutable'),
\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
\ buffer,
\ ale_linters#javascript#flow#GetExecutable(buffer),
\ '%e --version',
\ function('ale_linters#javascript#flow#GetCommand'),
\ )},
\ 'callback': 'ale_linters#javascript#flow#Handle',
\ 'read_buffer': 0,
\})
|