diff options
author | Bjorn Neergaard <bjorn@neersighted.com> | 2016-10-10 13:51:29 -0500 |
---|---|---|
committer | w0rp <w0rp@users.noreply.github.com> | 2016-10-10 19:51:29 +0100 |
commit | 7f0ce89d2b574fd5bdd0c050eaad92deeb63086d (patch) | |
tree | c508480ff3ccbf36ef7283610fdcaefe476ae6e1 /autoload | |
parent | 0680f875fe9ef07daceb3e9a90224bee613730df (diff) | |
download | ale-7f0ce89d2b574fd5bdd0c050eaad92deeb63086d.zip |
First pass at optimizing ale to autoload (#80)
* First pass at optimizing ale to autoload
First off, the structure/function names should be revised a bit,
but I will wait for @w0rp's input before unifying the naming style.
Second off, the docs probably need some more work, I just did some
simple find-and-replace work.
With that said, this pull brings major performance gains for ale. On my
slowest system, fully loading ale and all its code takes around 150ms.
I have moved all of ale's autoload-able code to autoload/, and in
addition, implemented lazy-loading of linters. This brings load time on
that same system down to 5ms.
The only downside of lazy loading is that `g:ale_linters` cannot be
changed at runtime; however, it also speeds up performance at runtime by
simplfying the logic greatly.
Please let me know what you think!
Closes #59
* Address Travis/Vint errors
For some reason, ale isn't running vint for me...
* Incorporate feedback, make fixes
Lazy-loading logic is much improved.
* Add header comments; remove incorrect workaround
* Remove unneeded plugin guards
* Fix lazy-loading linter logic
Set the wrong variable....
* Fix capitialization
Diffstat (limited to 'autoload')
-rw-r--r-- | autoload/ale.vim | 48 | ||||
-rw-r--r-- | autoload/ale/cleanup.vim | 16 | ||||
-rw-r--r-- | autoload/ale/cursor.vim | 119 | ||||
-rw-r--r-- | autoload/ale/engine.vim | 236 | ||||
-rw-r--r-- | autoload/ale/handlers.vim | 76 | ||||
-rw-r--r-- | autoload/ale/linter.vim | 66 | ||||
-rw-r--r-- | autoload/ale/sign.vim | 138 | ||||
-rw-r--r-- | autoload/ale/statusline.vim | 39 | ||||
-rw-r--r-- | autoload/ale/util.vim | 65 |
9 files changed, 803 insertions, 0 deletions
diff --git a/autoload/ale.vim b/autoload/ale.vim new file mode 100644 index 00000000..b25bcc99 --- /dev/null +++ b/autoload/ale.vim @@ -0,0 +1,48 @@ +" Author: w0rp <devw0rp@gmail.com> +" Description: Primary code path for the plugin +" Manages execution of linters when requested by autocommands + +let s:lint_timer = -1 + +function! ale#Queue(delay) abort + if s:lint_timer != -1 + call timer_stop(s:lint_timer) + let s:lint_timer = -1 + endif + + let linters = ale#linter#Get(&filetype) + 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, function('ale#Lint')) + else + call ale#Lint() + endif +endfunction + +function! ale#Lint(...) abort + let buffer = bufnr('%') + let linters = ale#linter#Get(&filetype) + + " Set a variable telling us to clear the loclist later. + let g:ale_buffer_should_reset_map[buffer] = 1 + + for linter in linters + " Check if a given linter has a program which can be executed. + if has_key(linter, 'executable_callback') + let l:executable = ale#util#GetFunction(linter.executable_callback)(buffer) + else + let l:executable = linter.executable + endif + + if !executable(l:executable) + " The linter's program cannot be executed, so skip it. + continue + endif + + call ale#engine#Invoke(buffer, linter) + endfor +endfunction diff --git a/autoload/ale/cleanup.vim b/autoload/ale/cleanup.vim new file mode 100644 index 00000000..2f636eee --- /dev/null +++ b/autoload/ale/cleanup.vim @@ -0,0 +1,16 @@ +" Author: w0rp <devw0rp@gmail.com> +" Description: Utility functions related to cleaning state. + +function! ale#cleanup#Buffer(buffer) abort + if has_key(g:ale_buffer_should_reset_map, a:buffer) + call remove(g:ale_buffer_should_reset_map, a:buffer) + endif + + if has_key(g:ale_buffer_loclist_map, a:buffer) + call remove(g:ale_buffer_loclist_map, a:buffer) + endif + + if has_key(g:ale_buffer_sign_dummy_map, a:buffer) + call remove(g:ale_buffer_sign_dummy_map, a:buffer) + endif +endfunction diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim new file mode 100644 index 00000000..eaa466b2 --- /dev/null +++ b/autoload/ale/cursor.vim @@ -0,0 +1,119 @@ +" Author: w0rp <devw0rp@gmail.com> +" Description: Echoes lint message for the current line, if any + +" Return a formatted message according to g:ale_echo_msg_format variable +function! s:GetMessage(linter, type, text) abort + let msg = g:ale_echo_msg_format + let type = a:type ==# 'E' + \ ? g:ale_echo_msg_error_str + \ : g:ale_echo_msg_warning_str + " Capitalize the 1st character + let text = toupper(a:text[0]) . a:text[1:-1] + + " Replace handlers if they exist + for [k, v] in items({'linter': a:linter, 'severity': type}) + let msg = substitute(msg, '\V%' . k . '%', v, '') + endfor + + return printf(msg, text) +endfunction + +" 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) abort + 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) abort + 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') + + " We need to turn T for truncated messages on for shortmess, + " and then then we need to reset the option back to what it was. + let shortmess_options = getbufvar('%', '&shortmess') + + try + " Echo the message truncated to fit without creating a prompt. + setlocal shortmess+=T + exec "norm :echomsg message\n" + finally + call setbufvar('%', '&shortmess', shortmess_options) + endtry +endfunction + +function! ale#cursor#EchoCursorWarning(...) abort + " Only echo the warnings in normal mode, otherwise we will get problems. + if mode() !=# 'n' + return + endif + + let buffer = bufnr('%') + + if !has_key(g:ale_buffer_loclist_map, buffer) + return + endif + + let loclist = g:ale_buffer_loclist_map[buffer] + + let pos = getcurpos() + + let index = s:BinarySearch(loclist, pos[1], pos[2]) + + if index >= 0 + let l = loclist[index] + let msg = s:GetMessage(l.linter_name, l.type, l.text) + call ale#cursor#TruncatedEcho(msg) + else + echo + endif +endfunction + +let s:cursor_timer = -1 + +function! ale#cursor#EchoCursorWarningWithDelay() abort + if s:cursor_timer != -1 + call timer_stop(s:cursor_timer) + let s:cursor_timer = -1 + endif + + let s:cursor_timer = timer_start(10, function('ale#cursor#EchoCursorWarning')) +endfunction + +if g:ale_has_required_features && g:ale_echo_cursor + augroup ALECursorGroup + autocmd! + autocmd CursorMoved,CursorHold * call ale#cursor#EchoCursorWarningWithDelay() + augroup END +endif diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim new file mode 100644 index 00000000..3d7374c6 --- /dev/null +++ b/autoload/ale/engine.vim @@ -0,0 +1,236 @@ +" Author: w0rp <devw0rp@gmail.com> +" Description: Backend execution and job management +" Executes linters in the background, using NeoVim or Vim 8 jobs + +" Stores information for each job including: +" +" linter: The linter dictionary for the job. +" buffer: The buffer number for the job. +" output: The array of lines for the output of the job. +let s:job_info_map = {} + +function! s:GetJobID(job) abort + if has('nvim') + "In NeoVim, job values are just IDs. + return a:job + endif + + " In Vim 8, the job is a special variable, and we open a channel for each + " job. We'll use the ID of the channel instead as the job ID. + return ch_info(job_getchannel(a:job)).id +endfunction + +function! s:ClearJob(job) abort + let job_id = s:GetJobID(a:job) + let linter = s:job_info_map[job_id].linter + + if has('nvim') + call jobstop(a:job) + else + " We must close the channel for reading the buffer if it is open + " when stopping a job. Otherwise, we will get errors in the status line. + if ch_status(job_getchannel(a:job)) ==# 'open' + call ch_close_in(job_getchannel(a:job)) + endif + + call job_stop(a:job) + endif + + call remove(s:job_info_map, job_id) + call remove(linter, 'job') +endfunction + +function! s:GatherOutput(job, data) abort + let job_id = s:GetJobID(a:job) + + if !has_key(s:job_info_map, job_id) + return + endif + + call extend(s:job_info_map[job_id].output, a:data) +endfunction + +function! s:GatherOutputVim(channel, data) abort + call s:GatherOutput(ch_getjob(a:channel), [a:data]) +endfunction + +function! s:GatherOutputNeoVim(job, data, event) abort + call s:GatherOutput(a:job, a:data) +endfunction + +function! s:HandleExit(job) abort + if a:job ==# 'no process' + " Stop right away when the job is not valid in Vim 8. + return + endif + + let job_id = s:GetJobID(a:job) + + if !has_key(s:job_info_map, job_id) + return + endif + + let job_info = s:job_info_map[job_id] + + call s:ClearJob(a:job) + + let linter = job_info.linter + let output = job_info.output + let buffer = job_info.buffer + + let linter_loclist = ale#util#GetFunction(linter.callback)(buffer, output) + + " Make some adjustments to the loclists to fix common problems. + call s:FixLocList(buffer, linter_loclist) + + if g:ale_buffer_should_reset_map[buffer] + let g:ale_buffer_should_reset_map[buffer] = 0 + let g:ale_buffer_loclist_map[buffer] = [] + endif + + " Add the loclist items from the linter. + call extend(g:ale_buffer_loclist_map[buffer], 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(g:ale_buffer_loclist_map[buffer], 'ale#util#LocItemCompare') + + if g:ale_set_loclist + call setloclist(0, g:ale_buffer_loclist_map[buffer]) + endif + + if g:ale_set_signs + call ale#sign#SetSigns(buffer, g:ale_buffer_loclist_map[buffer]) + endif + + " Mark line 200, column 17 with a squiggly line or something + " matchadd('ALEError', '\%200l\%17v') +endfunction + +function! s:HandleExitNeoVim(job, data, event) abort + call s:HandleExit(a:job) +endfunction + +function! s:HandleExitVim(channel) abort + call s:HandleExit(ch_getjob(a:channel)) +endfunction + +function! s:FixLocList(buffer, loclist) abort + " Some errors have line numbers beyond the end of the file, + " so we need to adjust them so they set the error at the last line + " of the file instead. + let last_line_number = ale#util#GetLineCount(a:buffer) + + for item in a:loclist + if item.lnum == 0 + " When errors appear at line 0, put them at line 1 instead. + let item.lnum = 1 + elseif item.lnum > last_line_number + let item.lnum = last_line_number + endif + endfor +endfunction + +function! ale#engine#Invoke(buffer, linter) abort + if has_key(a:linter, 'job') + " Stop previous jobs for the same linter. + call s:ClearJob(a:linter.job) + endif + + if has_key(a:linter, 'command_callback') + " If there is a callback for generating a command, call that instead. + let command = ale#util#GetFunction(a:linter.command_callback)(a:buffer) + else + let command = a:linter.command + endif + + if command =~# '%s' + " If there is a '%s' in the command string, replace it with the name + " of the file. + let command = printf(command, shellescape(fnamemodify(bufname(a:buffer), ':p'))) + endif + + if has('nvim') + if a:linter.output_stream ==# 'stderr' + " Read from stderr instead of stdout. + let job = jobstart(command, { + \ 'on_stderr': 's:GatherOutputNeoVim', + \ 'on_exit': 's:HandleExitNeoVim', + \}) + elseif a:linter.output_stream ==# 'both' + let job = jobstart(command, { + \ 'on_stdout': 's:GatherOutputNeoVim', + \ 'on_stderr': 's:GatherOutputNeoVim', + \ 'on_exit': 's:HandleExitNeoVim', + \}) + else + let job = jobstart(command, { + \ 'on_stdout': 's:GatherOutputNeoVim', + \ 'on_exit': 's:HandleExitNeoVim', + \}) + endif + else + let job_options = { + \ 'in_mode': 'nl', + \ 'out_mode': 'nl', + \ 'err_mode': 'nl', + \ 'close_cb': function('s:HandleExitVim'), + \} + + if a:linter.output_stream ==# 'stderr' + " Read from stderr instead of stdout. + let job_options.err_cb = function('s:GatherOutputVim') + elseif a:linter.output_stream ==# 'both' + " Read from both streams. + let job_options.out_cb = function('s:GatherOutputVim') + let job_options.err_cb = function('s:GatherOutputVim') + else + let job_options.out_cb = function('s:GatherOutputVim') + endif + + if has('win32') + " job_start commands on Windows have to be run with cmd /c, + " othwerwise %PATHTEXT% will not be used to programs ending int + " .cmd, .bat, .exe, etc. + let l:command = 'cmd /c ' . l:command + else + " On Unix machines, we can send the Vim buffer directly. + " This is faster than reading the lines ourselves. + let job_options.in_io = 'buffer' + let job_options.in_buf = a:buffer + endif + + " Vim 8 will read the stdin from the file's buffer. + let job = job_start(l:command, l:job_options) + endif + + " Only proceed if the job is being run. + if has('nvim') || (job !=# 'no process' && job_status(job) ==# 'run') + let a:linter.job = job + + " Store the ID for the job in the map to read back again. + let s:job_info_map[s:GetJobID(job)] = { + \ 'linter': a:linter, + \ 'buffer': a:buffer, + \ 'output': [], + \} + + if has('nvim') + " In NeoVim, we have to send the buffer lines ourselves. + let input = join(getbufline(a:buffer, 1, '$'), "\n") . "\n" + + call jobsend(job, input) + call jobclose(job, 'stdin') + elseif has('win32') + " On some Vim versions, we have to send the buffer data ourselves. + let input = join(getbufline(a:buffer, 1, '$'), "\n") . "\n" + let channel = job_getchannel(job) + + if ch_status(channel) ==# 'open' + call ch_sendraw(channel, input) + call ch_close_in(channel) + endif + endif + endif +endfunction diff --git a/autoload/ale/handlers.vim b/autoload/ale/handlers.vim new file mode 100644 index 00000000..6a8bd246 --- /dev/null +++ b/autoload/ale/handlers.vim @@ -0,0 +1,76 @@ +scriptencoding utf-8 +" Author: w0rp <devw0rp@gmail.com> +" Description: This file defines some standard error format handlers. Any +" linter which outputs warnings and errors in a format accepted by one of +" these functions can simply use one of these pre-defined error handlers. + +function! ale#handlers#HandleGCCFormat(buffer, lines) abort + " Look for lines like the following. + " + " <stdin>:8:5: warning: conversion lacks type at end of format [-Wformat=] + " <stdin>:10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) + " -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] + let pattern = '^.\+:\(\d\+\):\(\d\+\): \([^:]\+\): \(.\+\)$' + let output = [] + + for line in a:lines + let l:match = matchlist(line, pattern) + + if len(l:match) == 0 + continue + endif + + call add(output, { + \ 'bufnr': a:buffer, + \ 'lnum': l:match[1] + 0, + \ 'vcol': 0, + \ 'col': l:match[2] + 0, + \ 'text': l:match[4], + \ 'type': l:match[3] ==# 'error' ? 'E' : 'W', + \ 'nr': -1, + \}) + endfor + + return output +endfunction + +function! ale#handlers#HandleCSSLintFormat(buffer, lines) abort + " Matches patterns line the following: + " + " something.css: line 2, col 1, Error - Expected RBRACE at line 2, col 1. (errors) + " something.css: line 2, col 5, Warning - Expected (inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex) but found 'wat'. (known-properties) + " + " These errors can be very massive, so the type will be moved to the front + " so you can actually read the error type. + let pattern = '^.*: line \(\d\+\), col \(\d\+\), \(Error\|Warning\) - \(.\+\) (\([^)]\+\))$' + let output = [] + + for line in a:lines + let l:match = matchlist(line, pattern) + + if len(l:match) == 0 + continue + endif + + let text = l:match[4] + let type = l:match[3] + let errorGroup = l:match[5] + + " Put the error group at the front, so we can see what kind of error + " it is on small echo lines. + let text = '(' . errorGroup . ') ' . text + + " vcol is Needed to indicate that the column is a character. + call add(output, { + \ 'bufnr': a:buffer, + \ 'lnum': l:match[1] + 0, + \ 'vcol': 0, + \ 'col': l:match[2] + 0, + \ 'text': text, + \ 'type': type ==# 'Warning' ? 'W' : 'E', + \ 'nr': -1, + \}) + endfor + + return output +endfunction diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim new file mode 100644 index 00000000..c87fa8df --- /dev/null +++ b/autoload/ale/linter.vim @@ -0,0 +1,66 @@ +" Author: w0rp <devw0rp@gmail.com> +" Description: Linter registration and lazy-loading +" Retrieves linters as requested by the engine, loading them if needed. + +let s:linters = {} + +function! ale#linter#Define(filetype, linter) abort + if !has_key(s:linters, a:filetype) + let s:linters[a:filetype] = [] + endif + + let new_linter = { + \ 'name': a:linter.name, + \ 'callback': a:linter.callback, + \} + + if has_key(a:linter, 'executable_callback') + let new_linter.executable_callback = a:linter.executable_callback + else + let new_linter.executable = a:linter.executable + endif + + if has_key(a:linter, 'command_callback') + let new_linter.command_callback = a:linter.command_callback + else + let new_linter.command = a:linter.command + endif + + if has_key(a:linter, 'output_stream') + let new_linter.output_stream = a:linter.output_stream + else + let new_linter.output_stream = 'stdout' + endif + + " TODO: Assert the value of the output_stream to be something sensible. + + call add(s:linters[a:filetype], new_linter) +endfunction + +function! ale#linter#Get(filetype) abort + if a:filetype ==# '' + " Empty filetype? Nothing to be done about that. + return [] + endif + + if has_key(s:linters, a:filetype) + " We already loaded a linter, great! + return s:linters[a:filetype] + endif + + if has_key(g:ale_linters, a:filetype) + " Filter loaded linters according to list of linters specified in option. + for linter in g:ale_linters[a:filetype] + execute 'runtime! ale_linters/' . a:filetype . '/' . linter . '.vim' + endfor + else + execute 'runtime! ale_linters/' . a:filetype . '/*.vim' + endif + + if !has_key(s:linters, a:filetype) + " If we couldn't load any linters, let everyone know. + let s:linters[a:filetype] = [] + endif + + return s:linters[a:filetype] +endfunction diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim new file mode 100644 index 00000000..5cdd97c1 --- /dev/null +++ b/autoload/ale/sign.vim @@ -0,0 +1,138 @@ +scriptencoding utf8 +" Author: w0rp <devw0rp@gmail.com> +" Description: Draws error and warning signs into signcolumn + +let b:dummy_sign_set_map = {} + +if !hlexists('ALEErrorSign') + highlight link ALEErrorSign 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 + +" Global variables for signs +let g:ale_sign_error = get(g:, 'ale_sign_error', '>>') +let g:ale_sign_warning = get(g:, 'ale_sign_warning', '--') +" An offset which can be set for sign IDs. +" This ID can be changed depending on what IDs are set for other plugins. +" The dummy sign will use the ID exactly equal to the offset. +let g:ale_sign_offset = get(g:, 'ale_sign_offset', 1000000) + +" Signs show up on the left for error markers. +execute 'sign define ALEErrorSign text=' . g:ale_sign_error +\ . ' texthl=ALEErrorSign' +execute 'sign define ALEWarningSign text=' . g:ale_sign_warning +\ . ' texthl=ALEWarningSign' +sign define ALEDummySign + +function! ale#sign#FindCurrentSigns(buffer) abort + " Matches output like : + " line=4 id=1 name=ALEErrorSign + " строка=1 id=1000001 имя=ALEErrorSign + let pattern = 'id=\(\d\+\).*=ALE\(Warning\|Error\)Sign' + + redir => output + silent exec 'sign place buffer=' . a:buffer + redir END + + let id_list = [] + + for line in split(output, "\n") + let match = matchlist(line, pattern) + + if len(match) > 0 + call add(id_list, match[1] + 0) + endif + endfor + + return id_list +endfunction + +" Given a loclist, combine the loclist into a list of signs such that only +" one sign appears per line. Error lines will take precedence. +" The loclist will have been previously sorted. +function! ale#sign#CombineSigns(loclist) abort + let signlist = [] + + for obj in a:loclist + let should_append = 1 + + if obj.lnum < 1 + " Skip warnings and errors at line 0, etc. + continue + endif + + if len(signlist) > 0 && signlist[-1].lnum == obj.lnum + " We can't add the same line twice, because signs must be + " unique per line. + let should_append = 0 + + if signlist[-1].type ==# 'W' && obj.type ==# 'E' + " If we had a warning previously, but now have an error, + " we replace the object to set an error instead. + let signlist[-1] = obj + endif + endif + + if should_append + call add(signlist, obj) + endif + endfor + + return signlist +endfunction + +" This function will set the signs which show up on the left. +function! ale#sign#SetSigns(buffer, loclist) abort + let signlist = ale#sign#CombineSigns(a:loclist) + + if len(signlist) > 0 || g:ale_sign_column_always + if !get(g:ale_buffer_sign_dummy_map, a:buffer, 0) + " Insert a dummy sign if one is missing. + execute 'sign place ' . g:ale_sign_offset + \ . ' line=1 name=ALEDummySign buffer=' + \ . a:buffer + + let g:ale_buffer_sign_dummy_map[a:buffer] = 1 + endif + endif + + " Find the current signs with the markers we use. + let current_id_list = ale#sign#FindCurrentSigns(a:buffer) + + " Remove those markers. + for current_id in current_id_list + exec 'sign unplace ' . current_id . ' buffer=' . a:buffer + endfor + + " Now set all of the signs. + for i in range(0, len(signlist) - 1) + let obj = signlist[i] + let name = obj['type'] ==# 'W' ? 'ALEWarningSign' : 'ALEErrorSign' + + let sign_line = 'sign place ' . (i + g:ale_sign_offset + 1) + \. ' line=' . obj['lnum'] + \. ' name=' . name + \. ' buffer=' . a:buffer + + exec sign_line + endfor + + if !g:ale_sign_column_always && len(signlist) > 0 + if get(g:ale_buffer_sign_dummy_map, a:buffer, 0) + execute 'sign unplace ' . g:ale_sign_offset . ' buffer=' . a:buffer + + let g:ale_buffer_sign_dummy_map[a:buffer] = 0 + endif + endif +endfunction diff --git a/autoload/ale/statusline.vim b/autoload/ale/statusline.vim new file mode 100644 index 00000000..ded5283b --- /dev/null +++ b/autoload/ale/statusline.vim @@ -0,0 +1,39 @@ +" Author: KabbAmine <amine.kabb@gmail.com> +" Description: Statusline related function(s) + +function! ale#statusline#Status() abort + " Returns a formatted string that can be integrated in the + " statusline + + let buf = bufnr('%') + let bufLoclist = g:ale_buffer_loclist_map + + if !has_key(bufLoclist, buf) + return '' + endif + + let errors = 0 + let warnings = 0 + for e in bufLoclist[buf] + if e.type ==# 'E' + let errors += 1 + else + let warnings += 1 + endif + endfor + + let errors = errors ? printf(g:ale_statusline_format[0], errors) : '' + let warnings = warnings ? printf(g:ale_statusline_format[1], warnings) : '' + let no_errors = g:ale_statusline_format[2] + + " Different formats if no errors or no warnings + if empty(errors) && empty(warnings) + let res = no_errors + elseif !empty(errors) && !empty(warnings) + let res = printf('%s %s', errors, warnings) + else + let res = empty(errors) ? warnings : errors + endif + + return res +endfunction diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim new file mode 100644 index 00000000..e292c990 --- /dev/null +++ b/autoload/ale/util.vim @@ -0,0 +1,65 @@ +" Author: w0rp <devw0rp@gmail.com> +" Description: Contains miscellaneous functions + +function! s:FindWrapperScript() abort + for parent in split(&runtimepath, ',') + " Expand the path to deal with ~ issues. + let path = expand(parent . '/' . 'stdin-wrapper') + + if filereadable(path) + if has('win32') + return path . '.exe' + endif + + return path + endif + endfor +endfunction + +let g:ale#util#stdin_wrapper = s:FindWrapperScript() + +" A null file for sending output to nothing. +let g:ale#util#nul_file = '/dev/null' + +if has('win32') + let g:ale#util#nul_file = 'nul' +endif + +" Return the number of lines for a given buffer. +function! ale#util#GetLineCount(buffer) abort + return len(getbufline(a:buffer, 1, '$')) +endfunction + +" Given a buffer and a filename, find the nearest file by searching upwards +" through the paths relative to the given buffer. +function! ale#util#FindNearestFile(buffer, filename) abort + return findfile(a:filename, fnamemodify(bufname(a:buffer), ':p') . ';') +endfunction + +function! ale#util#GetFunction(string_or_ref) abort + if type(a:string_or_ref) == type('') + return function(a:string_or_ref) + endif + + return a:string_or_ref +endfunction + +function! ale#util#LocItemCompare(left, right) abort + 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 |