diff options
author | w0rp <devw0rp@gmail.com> | 2016-09-09 00:23:26 +0100 |
---|---|---|
committer | w0rp <devw0rp@gmail.com> | 2016-09-09 00:23:26 +0100 |
commit | 11c11e578f22cda52048fdec1354f9675b413495 (patch) | |
tree | 6609b83f02e56f3d151deb0599ca349dbbce6e3a /plugin | |
download | ale-11c11e578f22cda52048fdec1354f9675b413495.zip |
Add linting with eslint in NeoVim, with a few bugs.
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/ale/aaflags.vim | 42 | ||||
-rw-r--r-- | plugin/ale/cursor.vim | 72 | ||||
-rw-r--r-- | plugin/ale/sign.vim | 42 | ||||
-rw-r--r-- | plugin/ale/zmain.vim | 182 |
4 files changed, 338 insertions, 0 deletions
diff --git a/plugin/ale/aaflags.vim b/plugin/ale/aaflags.vim new file mode 100644 index 00000000..70021a6e --- /dev/null +++ b/plugin/ale/aaflags.vim @@ -0,0 +1,42 @@ +" This file sets up configuration settings for the ALE plugin. +" Flags can be set in vimrc files and so on to disable particular features, +" etc. + +if exists('g:loaded_ale_flags') + finish +endif + +let g:loaded_ale_flags = 1 + +" This flag can be set to 0 to disable linting when text is changed. +if !exists('g:ale_lint_on_text_changed') + let g:ale_lint_on_text_changed = 1 +endif + +" This flag can be set with a number of milliseconds for delaying the +" execution of a linter when text is changed. The timeout will be set and +" cleared each time text is changed, so repeated edits won't trigger the +" jobs for linting until enough time has passed after editing is done. +if !exists('g:ale_lint_delay') + let g:ale_lint_delay = 100 +endif + +" This flag can be set to 0 to disable linting when the buffer is entered. +if !exists('g:ale_lint_on_enter') + let g:ale_lint_on_enter = 1 +endif + +" This flag can be set to 0 to disable setting the loclist. +if !exists('g:ale_set_loclist') + let g:ale_set_loclist = 1 +endif + +" This flag can be set to 0 to disable setting signs. +if !exists('g:ale_set_signs') + let g:ale_set_signs = 1 +endif + +" This flag can be set to 0 to disable echoing when the cursor moves. +if !exists('g:ale_echo_cursor') + let g:ale_echo_cursor = 1 +endif diff --git a/plugin/ale/cursor.vim b/plugin/ale/cursor.vim new file mode 100644 index 00000000..0159302a --- /dev/null +++ b/plugin/ale/cursor.vim @@ -0,0 +1,72 @@ +if exists('g:loaded_ale_cursor') + finish +endif + +let g:loaded_ale_cursor = 1 + +" This function will perform a binary search to find a message from the +" loclist to echo when the cursor moves. +function! s:BinarySearch(loclist, line, column) + let min = 0 + let max = len(a:loclist) - 1 + let last_column_match = -1 + + while 1 + if max < min + return last_column_match + endif + + let mid = (min + max) / 2 + let obj = a:loclist[mid] + + " Binary search to get on the same line + if a:loclist[mid]['lnum'] < a:line + let min = mid + 1 + elseif a:loclist[mid]['lnum'] > a:line + let max = mid - 1 + else + let last_column_match = mid + + " Binary search to get the same column, or near it + if a:loclist[mid]['col'] < a:column + let min = mid + 1 + elseif a:loclist[mid]['col'] > a:column + let max = mid - 1 + else + return mid + endif + endif + endwhile +endfunction + +function! ale#cursor#TruncatedEcho(message) + let message = a:message + " Change tabs to spaces. + let message = substitute(message, "\t", ' ', 'g') + " Remove any newlines in the message. + let message = substitute(message, "\n", '', 'g') + + let truncated_message = join(split(message, '\zs')[:&columns - 2], '') + + " Echo the message truncated to fit without creating a prompt. + echo truncated_message +endfunction + +function! ale#cursor#EchoCursorWarning() + let pos = getcurpos() + + let index = s:BinarySearch(b:ale_loclist, pos[1], pos[2]) + + if index >= 0 + call ale#cursor#TruncatedEcho(b:ale_loclist[index]['text']) + else + echo + endif +endfunction + +if g:ale_echo_cursor + augroup ALECursorGroup + autocmd! + autocmd CursorMoved * call ale#cursor#EchoCursorWarning() + augroup END +endif diff --git a/plugin/ale/sign.vim b/plugin/ale/sign.vim new file mode 100644 index 00000000..29753011 --- /dev/null +++ b/plugin/ale/sign.vim @@ -0,0 +1,42 @@ +if exists('g:loaded_ale_sign') + finish +endif + +let g:loaded_ale_sign = 1 + +if !hlexists('ALEErrorSign') + highlight link ALErrorSign error +endif + +if !hlexists('ALEWarningSign') + highlight link ALEWarningSign todo +endif + +if !hlexists('ALEError') + highlight link ALEError SpellBad +endif + +if !hlexists('ALEWarning') + highlight link ALEWarning SpellCap +endif + +" Signs show up on the left for error markers. +sign define ALEErrorSign text=>> texthl=ALEErrorSign +sign define ALEWarningSign text=-- texthl=ALEWarningSign + +" This function will set the signs which show up on the left. +function! ale#sign#SetSigns(loclist) + sign unplace * + + for i in range(0, len(a:loclist) - 1) + let obj = a:loclist[i] + let name = obj['type'] ==# 'W' ? 'ALEWarningSign' : 'ALEErrorSign' + + let sign_line = 'sign place ' . (i + 1) + \. ' line=' . obj['lnum'] + \. ' name=' . name + \. ' buffer=' . obj['bufnr'] + + exec sign_line + endfor +endfunction diff --git a/plugin/ale/zmain.vim b/plugin/ale/zmain.vim new file mode 100644 index 00000000..2db0d028 --- /dev/null +++ b/plugin/ale/zmain.vim @@ -0,0 +1,182 @@ +" Always set buffer variables for each buffer +let b:ale_should_reset_loclist = 0 +let b:ale_loclist = [] + +if exists('g:loaded_ale_zmain') + finish +endif + +let g:loaded_ale_zmain = 1 + +let s:lint_timer = -1 +let s:linters = {} +let s:job_linter_map = {} +let s:job_output_map = {} + +function! s:ClearJob(job) + if a:job != -1 + let linter = s:job_linter_map[a:job] + + call jobstop(a:job) + call remove(s:job_output_map, a:job) + call remove(s:job_linter_map, a:job) + + let linter.job = -1 + endif +endfunction + +function! s:GatherOutput(job, data, event) + if !has_key(s:job_output_map, a:job) + return + endif + + call extend(s:job_output_map[a:job], a:data) +endfunction + +function! s:LocItemCompare(left, right) + if a:left['lnum'] < a:right['lnum'] + return -1 + endif + + if a:left['lnum'] > a:right['lnum'] + return 1 + endif + + if a:left['col'] < a:right['col'] + return -1 + endif + + if a:left['col'] > a:right['col'] + return 1 + endif + + return 0 +endfunction + +function! s:HandleExit(job, data, event) + if !has_key(s:job_linter_map, a:job) + return + endif + + let linter = s:job_linter_map[a:job] + let output = s:job_output_map[a:job] + + call s:ClearJob(a:job) + + let linter_loclist = function(linter.callback)(output) + + if b:ale_should_reset_loclist + let b:ale_should_reset_loclist = 0 + let b:ale_loclist = [] + endif + + " Add the loclist items from the linter. + call extend(b:ale_loclist, linter_loclist) + + " Sort the loclist again. + " We need a sorted list so we can run a binary search against it + " for efficient lookup of the messages in the cursor handler. + call sort(b:ale_loclist, 's:LocItemCompare') + + if g:ale_set_loclist + call setloclist(0, b:ale_loclist) + endif + + if g:ale_set_signs + call ale#sign#SetSigns(b:ale_loclist) + endif + + " Mark line 200, column 17 with a squiggly line or something + " matchadd('ALEError', '\%200l\%17v') +endfunction + +function! s:ApplyLinter(linter) + " Stop previous jobs for the same linter. + call s:ClearJob(a:linter.job) + + let a:linter.job = jobstart(a:linter.command, { + \ 'on_stdout': 's:GatherOutput', + \ 'on_exit': 's:HandleExit', + \}) + + let s:job_linter_map[a:linter.job] = a:linter + let s:job_output_map[a:linter.job] = [] + + call jobsend(a:linter.job, join(getline(1, '$'), "\n") . "\n") + call jobclose(a:linter.job, 'stdin') +endfunction + +function! s:TimerHandler() + let filetype = &filetype + let linters = ALEGetLinters(filetype) + + " Set a variable telling us to clear the loclist later. + let b:ale_should_reset_loclist = 1 + + for linter in linters + call s:ApplyLinter(linter) + endfor +endfunction + +function! ALEAddLinter(filetype, linter) + " Check if the linter program is executable before adding it. + if !executable(a:linter.executable) + return + endif + + if !has_key(s:linters, a:filetype) + let s:linters[a:filetype] = [] + endif + + call add(s:linters[a:filetype], { + \ 'job': -1, + \ 'command': a:linter.command, + \ 'callback': a:linter.callback, + \}) +endfunction + +function! ALEGetLinters(filetype) + if !has_key(s:linters, a:filetype) + return [] + endif + + return s:linters[a:filetype] +endfunction + +function! ALELint(delay) + let filetype = &filetype + let linters = ALEGetLinters(filetype) + + if s:lint_timer != -1 + call timer_stop(s:lint_timer) + let s:lint_timer = -1 + endif + + if len(linters) == 0 + " There are no linters to lint with, so stop here. + return + endif + + if a:delay > 0 + let s:lint_timer = timer_start(a:delay, 's:TimerHandler') + else + call s:TimerHandler() + endif +endfunction + +" Load all of the linters for each filetype. +runtime ale_linters/*/*.vim + +if g:ale_lint_on_text_changed + augroup ALERunOnTextChangedGroup + autocmd! + autocmd TextChanged,TextChangedI * call ALELint(g:ale_lint_delay) + augroup END +endif + +if g:ale_lint_on_enter + augroup ALERunOnEnterGroup + autocmd! + autocmd BufEnter * call ALELint(0) + augroup END +endif |