scriptencoding utf8 " Author: w0rp " Description: Draws error and warning signs into signcolumn let b:dummy_sign_set_map = {} if !hlexists('ALEErrorSign') highlight link ALEErrorSign error endif if !hlexists('ALEStyleErrorSign') highlight link ALEStyleErrorSign ALEErrorSign endif if !hlexists('ALEWarningSign') highlight link ALEWarningSign todo endif if !hlexists('ALEStyleWarningSign') highlight link ALEStyleWarningSign ALEWarningSign endif if !hlexists('ALEInfoSign') highlight link ALEInfoSign ALEWarningSign endif " Signs show up on the left for error markers. execute 'sign define ALEErrorSign text=' . g:ale_sign_error \ . ' texthl=ALEErrorSign linehl=ALEErrorLine' execute 'sign define ALEStyleErrorSign text=' . g:ale_sign_style_error \ . ' texthl=ALEStyleErrorSign linehl=ALEStyleErrorSign' execute 'sign define ALEWarningSign text=' . g:ale_sign_warning \ . ' texthl=ALEWarningSign linehl=ALEWarningLine' execute 'sign define ALEStyleWarningSign text=' . g:ale_sign_style_warning \ . ' texthl=ALEStyleWarningSign linehl=ALEStyleWarningSign' execute 'sign define ALEInfoSign text=' . g:ale_sign_info \ . ' texthl=ALEInfoSign linehl=ALEInfoLine' sign define ALEDummySign " Read sign data for a buffer to a list of lines. function! ale#sign#ReadSigns(buffer) abort redir => l:output silent exec 'sign place buffer=' . a:buffer redir end return split(l:output, "\n") endfunction " Given a list of lines for sign output, return a List of pairs [line, id] function! ale#sign#ParseSigns(line_list) abort " Matches output like : " line=4 id=1 name=ALEErrorSign " строка=1 id=1000001 имя=ALEErrorSign " 行=1 識別子=1000001 名前=ALEWarningSign " línea=12 id=1000001 nombre=ALEWarningSign " riga=1 id=1000001, nome=ALEWarningSign let l:pattern = '\v^.*\=(\d+).*\=(\d+).*\=(ALE[a-zA-Z]+Sign)' let l:result = [] for l:line in a:line_list let l:match = matchlist(l:line, l:pattern) if len(l:match) > 0 call add(l:result, [ \ str2nr(l:match[1]), \ str2nr(l:match[2]), \ l:match[3], \]) endif endfor return l:result endfunction function! ale#sign#FindCurrentSigns(buffer) abort let l:line_list = ale#sign#ReadSigns(a:buffer) return ale#sign#ParseSigns(l:line_list) endfunction " Given a loclist, group the List into with one List per line. function! s:GroupLoclistItems(loclist) abort let l:grouped_items = [] let l:last_lnum = -1 for l:obj in a:loclist " Create a new sub-List when we hit a new line. if l:obj.lnum != l:last_lnum call add(l:grouped_items, []) endif call add(l:grouped_items[-1], l:obj) let l:last_lnum = l:obj.lnum endfor return l:grouped_items endfunction function! s:IsDummySignSet(current_id_list) abort for [l:line, l:id, l:name] in a:current_id_list if l:id == g:ale_sign_offset return 1 endif if l:line > 1 return 0 endif endfor return 0 endfunction function! s:SetDummySignIfNeeded(buffer, current_sign_list, new_signs) abort let l:is_dummy_sign_set = s:IsDummySignSet(a:current_sign_list) " If we haven't already set a dummy sign, and we have some previous signs " or always want a dummy sign, then set one, to keep the sign column open. if !l:is_dummy_sign_set && (a:new_signs || g:ale_sign_column_always) execute 'sign place ' . g:ale_sign_offset \ . ' line=1 name=ALEDummySign buffer=' \ . a:buffer let l:is_dummy_sign_set = 1 endif return l:is_dummy_sign_set endfunction function! ale#sign#GetSignType(sublist) abort let l:highest_level = 100 for l:item in a:sublist let l:level = (l:item.type ==# 'I' ? 2 : l:item.type ==# 'W') if get(l:item, 'sub_type', '') ==# 'style' let l:level += 10 endif if l:level < l:highest_level let l:highest_level = l:level endif endfor if l:highest_level == 10 return 'ALEStyleErrorSign' elseif l:highest_level == 11 return 'ALEStyleWarningSign' elseif l:highest_level == 2 return 'ALEInfoSign' elseif l:highest_level == 1 return 'ALEWarningSign' endif return 'ALEErrorSign' endfunction function! s:PlaceNewSigns(buffer, grouped_items) abort " Add the new signs, for l:index in range(0, len(a:grouped_items) - 1) let l:sign_id = l:index + g:ale_sign_offset + 1 let l:sublist = a:grouped_items[l:index] let l:type = ale#sign#GetSignType(a:grouped_items[l:index]) " Save the sign IDs we are setting back on our loclist objects. " These IDs will be used to preserve items which are set many times. for l:obj in l:sublist let l:obj.sign_id = l:sign_id endfor execute 'sign place ' . l:sign_id \ . ' line=' . l:sublist[0].lnum \ . ' name=' . l:type \ . ' buffer=' . a:buffer endfor endfunction " Get items grouped by any current sign IDs they might have. function! s:GetItemsWithSignIDs(loclist) abort let l:items_by_sign_id = {} for l:item in a:loclist if has_key(l:item, 'sign_id') if !has_key(l:items_by_sign_id, l:item.sign_id) let l:items_by_sign_id[l:item.sign_id] = [] endif call add(l:items_by_sign_id[l:item.sign_id], l:item) endif endfor return l:items_by_sign_id endfunction " Given some current signs and a loclist, look for items with sign IDs, " and change the line numbers for loclist items to match the signs. function! s:UpdateLineNumbers(current_sign_list, loclist) abort let l:items_by_sign_id = s:GetItemsWithSignIDs(a:loclist) " Do nothing if there's nothing to work with. if empty(l:items_by_sign_id) return endif for [l:line, l:sign_id, l:name] in a:current_sign_list for l:obj in get(l:items_by_sign_id, l:sign_id, []) let l:obj.lnum = l:line endfor endfor " Sort items again. call sort(a:loclist, 'ale#util#LocItemCompare') endfunction " This function will set the signs which show up on the left. function! ale#sign#SetSigns(buffer, loclist) abort if !bufexists(str2nr(a:buffer)) " Stop immediately when attempting to set signs for a buffer which " does not exist. return endif " Find the current markers let l:current_sign_list = ale#sign#FindCurrentSigns(a:buffer) call s:UpdateLineNumbers(l:current_sign_list, a:loclist) let l:grouped_items = s:GroupLoclistItems(a:loclist) " Set the dummy sign if we need to. " This keeps the sign gutter open while we remove things, etc. let l:is_dummy_sign_set = s:SetDummySignIfNeeded( \ a:buffer, \ l:current_sign_list, \ !empty(l:grouped_items), \) " Now remove the previous signs. The dummy will hold the column open " while we add the new signs, if we had signs before. for [l:line, l:sign_id, l:name] in l:current_sign_list if l:sign_id != g:ale_sign_offset exec 'sign unplace ' . l:sign_id . ' buffer=' . a:buffer endif endfor call s:PlaceNewSigns(a:buffer, l:grouped_items) " Remove the dummy sign now we've updated the signs, unless we want " to keep it, which will keep the sign column open even when there are " no warnings or errors. if l:is_dummy_sign_set && !g:ale_sign_column_always execute 'sign unplace ' . g:ale_sign_offset . ' buffer=' . a:buffer endif endfunction