From d16e82d468eb0d5bb1e662ac4812c0ca6fc0fc64 Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Tue, 25 Feb 2020 14:47:03 +0300 Subject: reorganized repo to be easier to use with GNU stow; added script to stow --- dotfiles/.vim/autoload/neomake/list.vim | 1020 +++++++++++++++++++++++++++++++ 1 file changed, 1020 insertions(+) create mode 100644 dotfiles/.vim/autoload/neomake/list.vim (limited to 'dotfiles/.vim/autoload/neomake/list.vim') diff --git a/dotfiles/.vim/autoload/neomake/list.vim b/dotfiles/.vim/autoload/neomake/list.vim new file mode 100644 index 0000000..120b665 --- /dev/null +++ b/dotfiles/.vim/autoload/neomake/list.vim @@ -0,0 +1,1020 @@ +scriptencoding utf-8 +" Create a List object from a quickfix/location list. +" TODO: (optionally?) add entries sorted? (errors first, grouped by makers (?) etc) + +let s:can_set_qf_title = has('patch-7.4.2200') +let s:can_set_qf_context = has('patch-8.0.0590') +let s:can_set_qf_items = has('patch-8.0.0657') +let s:has_support_for_qfid = has('patch-8.0.1023') +let s:use_efm_parsing = has('patch-8.0.1040') " 'efm' in setqflist/getqflist + +" Do we need to replace (instead of append) the location/quickfix list, for +" :lwindow to not open it with only invalid entries?! +" Without patch-7.4.379 this does not work though, and a new list needs to +" be created (which is not done). +" @vimlint(EVL108, 1) +let s:needs_to_replace_qf_for_lwindow = has('patch-7.4.379') + \ && (!has('patch-7.4.1752') || (has('nvim') && !has('nvim-0.2.0'))) +" @vimlint(EVL108, 0) +let s:needs_to_init_qf_for_lwindow = !has('patch-8.1.0622') + +function! neomake#list#ListForMake(make_info) abort + let type = a:make_info.options.file_mode ? 'loclist' : 'quickfix' + let list = neomake#list#List(type) + let list.make_info = a:make_info + if type ==# 'loclist' + let info = get(w:, '_neomake_info', {}) + let info['loclist'] = list + let w:_neomake_info = info + else + let info = get(g:, '_neomake_info', {}) + let info['qflist'] = list + let g:_neomake_info = info + endif + return list +endfunction + +" a:type: "loclist" or "quickfix" +function! neomake#list#List(type) abort + let list = deepcopy(s:base_list) + let list.type = a:type + " Display debug messages about changed entries. + let list.debug = get(g:, 'neomake_debug_list', + \ exists('g:neomake_test_messages') + \ || !empty(get(g:, 'neomake_logfile')) + \ || neomake#utils#get_verbosity() >= 3) + return list +endfunction + +" Internal base list implementation. +let s:base_list = { + \ 'need_init': 1, + \ 'entries': [], + \ } +" Info about contained jobs. +let s:base_list.job_entries = {} +let s:base_list.maker_info_by_jobid = {} + +function! s:base_list.sort_by_location() dict abort + let entries = get(self, '_sorted_entries_by_location', copy(self.entries)) + let self._sorted_entries_by_location = sort(entries, 's:cmp_listitem_loc') + return self._sorted_entries_by_location +endfunction + +" a:1: optional jobinfo +function! s:base_list.add_entries(entries, ...) dict abort + let idx = len(self.entries) + if a:0 && !has_key(self.job_entries, a:1.id) + let self.job_entries[a:1.id] = [] + let self.maker_info_by_jobid[a:1.id] = a:1.maker + endif + for entry in a:entries + let idx += 1 + let e = extend(copy(entry), {'nmqfidx': idx}) + if a:0 + call add(self.job_entries[a:1.id], e) + let e.job_id = a:1.id + endif + call add(self.entries, e) + endfor + if self.debug + let indexes = map(copy(self.entries), 'v:val.nmqfidx') + if len(neomake#compat#uniq(sort(copy(indexes)))) != len(indexes) + call neomake#log#error(printf('Duplicate qf indexes in list entries: %s.', + \ string(indexes))) + endif + endif + " Sort if it was sorted before. + if has_key(self, '_sorted_entries_by_location') + call extend(self._sorted_entries_by_location, a:entries) + call self.sort_by_location() + endif +endfunction + +" Add entries for a job (non-efm method). +function! s:base_list.add_entries_for_job(entries, jobinfo) dict abort + let tempfiles = get(self.make_info, 'tempfiles', []) + if !empty(tempfiles) + let mapped = 0 + for e in a:entries + if has_key(e, 'filename') && get(e, 'bufnr', 0) == 0 + if index(tempfiles, e.filename) != -1 + unlet e.filename + let e.bufnr = a:jobinfo.bufnr + let mapped += 1 + endif + endif + endfor + if mapped + call neomake#log#debug(printf('Mapped %d bufnrs from temporary files.', mapped), a:jobinfo) + endif + endif + return self._appendlist(a:entries, a:jobinfo) +endfunction + +function! neomake#list#get_title(prefix, bufnr, maker_info) abort + let prefix = 'Neomake' + if !empty(a:prefix) + let prefix .= '['.a:prefix.']' + endif + if a:bufnr + let bufname = bufname(a:bufnr) + if empty(bufname) + let bufname = 'buf:'.a:bufnr + else + let bufname = pathshorten(bufname) + endif + let maker_info = bufname + if empty(a:maker_info) + let maker_info = bufname + else + let maker_info = bufname.' ('.a:maker_info.')' + endif + else + let maker_info = a:maker_info + endif + let title = prefix + if !empty(maker_info) + let title = prefix.': '.maker_info + endif + return title +endfunction + +function! s:base_list._get_title() abort + let maker_info = [] + for job in self.make_info.finished_jobs + let info = job.maker.name + let ok = 1 + if get(job, 'aborted', 0) + let info .= '!' + let ok = 0 + endif + if has_key(self.job_entries, job.id) + let c = len(self.job_entries[job.id]) + let info .= '('.c.')' + let ok = 0 + endif + if ok + let info .= '✓' + endif + call add(maker_info, info) + endfor + for job in self.make_info.active_jobs + let info = job.maker.name + let info .= '...' + if has_key(self.job_entries, job.id) + let c = len(self.job_entries[job.id]) + let info .= '('.c.')' + endif + call add(maker_info, info) + endfor + for job in self.make_info.jobs_queue + let info = job.maker.name + let info .= '?' + call add(maker_info, info) + endfor + for job in get(self.make_info, 'aborted_jobs', []) + let info = job.maker.name + let info .= '-' + call add(maker_info, info) + endfor + let maker_info_str = join(maker_info, ', ') + if self.type ==# 'loclist' + let bufnr = self.make_info.options.bufnr + else + let bufnr = 0 + endif + if get(self.make_info.options, 'automake') + let prefix = 'auto' + elseif self.make_info.options.file_mode + let prefix = 'file' + else + let prefix = 'project' + endif + return neomake#list#get_title(prefix, bufnr, maker_info_str) +endfunction + +function! s:base_list.finish_for_make() abort + if self.need_init + if self.type ==# 'loclist' + call neomake#log#debug('Cleaning location list.', self.make_info) + else + call neomake#log#debug('Cleaning quickfix list.', self.make_info) + endif + call self._call_qf_fn('set', [], ' ') + else + " Set title, but only if list window is still valid. + if s:has_support_for_qfid + let valid = self._has_valid_qf() + elseif self.type ==# 'loclist' + let valid = self._get_loclist_win(1) != -1 + else + let valid = 1 + endif + if !valid + call neomake#log#debug('list: finish: list is not valid anymore.', self.make_info) + return + endif + call self.set_title() + endif +endfunction + +function! s:base_list._call_qf_fn(action, ...) abort + let fns_args = call(self._get_fn_args, [a:action] + a:000, self) + + if a:action ==# 'get' + let [fn, args] = fns_args[0] + if s:has_support_for_qfid + let args[-1].items = 1 + if self.debug + call neomake#log#debug(printf('list: call: "get", returning items: %s.', string(fns_args))) + endif + return call(fn, args).items + endif + if self.debug + call neomake#log#debug(printf('list: call: "get": %s.', string(fns_args))) + endif + return call(fn, args) + endif + + for fns in fns_args + let [fn, args] = fns + + if self.debug + if a:action ==# 'set' + let log_args = deepcopy(args) + " Only display 5 items. + if self.type ==# 'loclist' + let log_args[1] = neomake#utils#shorten_list_for_log(log_args[1], 5) + else + let log_args[0] = neomake#utils#shorten_list_for_log(log_args[0], 5) + endif + " Massage options dict. + if type(log_args[-1]) == type({}) + let neomake_context = get(get(log_args[-1], 'context', {}), 'neomake', {}) + if !empty(neomake_context) + for [k, v] in items(neomake_context) + if k ==# 'make_info' + " Fixes self-ref, and makes it much shorter. + let neomake_context[k] = 'make_id='.v.make_id + endif + endfor + endif + " Only display 5 items. + if has_key(log_args[-1], 'items') + let log_args[-1].items = neomake#utils#shorten_list_for_log(log_args[-1].items, 5) + endif + endif + call neomake#log#debug(printf('list: call: set: %s.', string(log_args))) + else + call neomake#log#debug(printf('list: call: "%s": %s.', a:action, string(args))) + endif + endif + + call call(fn, args, self) + endfor + + " Get qfid. + if self.need_init + if a:action ==# 'set' && s:has_support_for_qfid + if self.type ==# 'loclist' + let loclist_win = self._get_loclist_win() + let self.qfid = getloclist(loclist_win, {'id': 0}).id + else + let self.qfid = getqflist({'id': 0}).id + endif + if self.debug + call neomake#log#debug(printf('list: got qfid (action=%s): %s.', a:action, self.qfid)) + endif + endif + let self.need_init = 0 + endif +endfunction + +function! s:base_list.set_title() abort + if s:can_set_qf_title + call self._call_qf_fn('title', self._get_title()) + endif +endfunction + +" Check if quickfix list is still valid, which might not be the case anymore +" if more than 10 new lists have been opened etc. +" Returns -1 without suffort for quickfix ids. +function! s:base_list._has_valid_qf() abort + if !s:has_support_for_qfid + return -1 + endif + + if self.type ==# 'loclist' + let loclist_win = self._get_loclist_win(1) + if loclist_win is -1 + return 0 + endif + if !get(getloclist(loclist_win, {'id': self.qfid}), 'id') + return 0 + endif + else + if !get(getqflist({'id': self.qfid}), 'id') + return 0 + endif + endif + return 1 +endfunction + +" Get winnr/winid to be used with loclist functions. +" a:1: return -1 instead of throwing when no window could be found? +function! s:base_list._get_loclist_win(...) abort + if !has_key(self, 'make_info') + throw 'cannot handle type=loclist without make_info' + endif + let loclist_win = 0 + let make_id = self.make_info.make_id + " NOTE: prefers using 0 for when winid is not supported with + " setloclist() yet (vim74-xenial). + if index(get(w:, 'neomake_make_ids', []), make_id) == -1 + if has_key(self.make_info.options, 'winid') + let loclist_win = self.make_info.options.winid + else + let [t, w] = neomake#core#get_tabwin_for_makeid(make_id) + if [t, w] == [-1, -1] + if a:0 && a:1 + return -1 + endif + throw printf('Neomake: could not find location list for make_id %d.', make_id) + endif + if t != tabpagenr() + if a:0 && a:1 + return -1 + endif + throw printf('Neomake: trying to use location list from another tab (current=%d != target=%d).', tabpagenr(), t) + endif + let loclist_win = w + endif + endif + return loclist_win +endfunction + +" Return a list of commands to be called. +" action: "get", "set", "init", "title" +" a:000: optional args (for set/init/title) +function! s:base_list._get_fn_args(action, ...) abort + if self.type ==# 'loclist' + if a:action ==# 'get' + let fn = 'getloclist' + else + let fn = 'setloclist' + endif + else + if a:action ==# 'get' + let fn = 'getqflist' + else + let fn = 'setqflist' + endif + endif + + if self.type ==# 'loclist' + let args = [self._get_loclist_win()] + else + let args = [] + endif + + let options = {} + if !self.need_init + let valid = self._has_valid_qf() + if valid == 1 + let options.id = self.qfid + elseif valid == 0 + if self.type ==# 'loclist' + let loclist_win = args[0] + throw printf('Neomake: qfid %d for location list (%d) has become invalid.', self.qfid, loclist_win) + else + throw printf('Neomake: qfid %d for quickfix list has become invalid.', self.qfid) + endif + endif + endif + + if a:action ==# 'title' + call extend(args, [[], 'a']) + if exists('*vader#assert#true') + call vader#assert#true(s:can_set_qf_title) + endif + let options.title = a:1 + else + call extend(args, a:000) + if a:action ==# 'set' + if exists('*vader#assert#equal') + call vader#assert#equal(len(a:000), 2) + endif + if s:can_set_qf_items + let options.items = a:1 + let args[-2] = [] + endif + endif + endif + if !empty(options) + call add(args, options) + endif + + let r = [] + if a:action ==# 'set' + if self.need_init && get(self, 'reset_existing_qflist') + if self.type ==# 'loclist' + let args[2] = 'r' " action + else + let args[1] = 'r' " action + endif + if self.type ==# 'loclist' + let msg = 'Reusing location list for entries.' + else + let msg = 'Reusing quickfix list for entries.' + endif + call neomake#log#debug(msg, self.make_info) + endif + + " Experimental: set make_info into context. + " This is used to access make info from the qf window itself. + if self.need_init && s:can_set_qf_context + let options.context = {'neomake': {'make_info': self.make_info}} + endif + + " Handle setting title, which gets done initially and when maker + " names are updated. This has to be done in a separate call + " without patch-8.0.0657. + if s:can_set_qf_title + let title = self._get_title() + if s:can_set_qf_items + if type(args[-1]) != type({}) + call add(args, {'title': title, 'items': args[1]}) + else + let args[-1].title = title + endif + else + " Update title after actual call. + call add(r, [fn, args]) + + if self.type ==# 'loclist' + let args = [args[0], [], 'a', {'title': title}] + else + let args = [[], 'a', {'title': title}] + endif + endif + endif + endif + call add(r, [fn, args]) + return r +endfunction + +function! s:mark_entry_with_nmcfg(entry, maker_info) abort + let maker_name = a:maker_info.name + let config = { + \ 'name': maker_name, + \ 'short': get(a:maker_info, 'short_name', maker_name[:3]), + \ } + let marker_entry = copy(a:entry) + let marker_entry.text .= printf(' nmcfg:%s', string(config)) + return marker_entry +endfunction + +function! s:base_list._replace_qflist_entries(entries) abort + let set_entries = a:entries + + " Handle nmcfg markers when setting all entries without jobinfo. + if neomake#quickfix#is_enabled() + let set_entries = copy(set_entries) + let prev_job_id = 0 + + " Handle re-setting all entries. This is meant to be used later + " for replacing the whole list. + let i = 0 + for e in set_entries + if e.job_id != prev_job_id + let maker_info = self.maker_info_by_jobid[e.job_id] + let set_entries[i] = s:mark_entry_with_nmcfg(e, maker_info) + let prev_job_id = e.job_id + endif + let i += 1 + endfor + endif + + call self._set_qflist_entries(set_entries, 'r') +endfunction + +function! s:base_list._set_qflist_entries(entries, action) abort + let action = a:action + if self.need_init && !get(self, 'reset_existing_qflist') + if self.type ==# 'loclist' + let msg = 'Creating location list for entries.' + else + let msg = 'Creating quickfix list for entries.' + endif + call neomake#log#debug(msg, self.make_info) + + if s:needs_to_init_qf_for_lwindow + " Clean list without autocommands (customqf etc) to avoid + " flicker. This is only to work around a Vim bug anyway. + noautocmd call self._call_qf_fn('set', [], ' ') + else + let action = ' ' + endif + endif + call self._call_qf_fn('set', a:entries, action) +endfunction + +function! s:base_list._get_qflist_entries() abort + return self._call_qf_fn('get') +endfunction + +" Append entries to location/quickfix list. +function! s:base_list._appendlist(entries, jobinfo) abort + call neomake#log#debug(printf('Adding %d list entries.', len(a:entries)), self.make_info) + + let set_entries = a:entries + let action = 'a' + if !self.need_init + let action = 'a' + if s:needs_to_replace_qf_for_lwindow || neomake#quickfix#is_enabled() + " Need to replace whole list with customqf to trigger FileType + " autocmd (which is not done for action='a'). + " This should be enhanced to only format new entries instead + " later, but needs support for changing non-current buffer lines. + let action = 'r' + if self.type ==# 'loclist' + let set_entries = self._get_qflist_entries() + set_entries + else + let set_entries = getqflist() + set_entries + endif + endif + endif + + " Add marker for custom quickfix to the first (new) entry. + let needs_custom_qf_marker = neomake#quickfix#is_enabled() + if needs_custom_qf_marker + if action ==# 'a' + let prev_idx = 0 + else + let prev_idx = len(self.entries) + endif + let set_entries = copy(set_entries) + let set_entries[prev_idx] = s:mark_entry_with_nmcfg(set_entries[prev_idx], a:jobinfo.maker) + endif + + " NOTE: need to fetch (or pre-parse with new patch) to get updated bufnr etc. + call self._set_qflist_entries(set_entries, action) + let added = self._get_qflist_entries()[len(self.entries) :] + + if needs_custom_qf_marker + " Remove marker that should only be in the quickfix list. + let added[0].text = substitute(added[0].text, ' nmcfg:{.\{-}}$', '', '') + endif + + if self.debug && added != a:entries + let diff = neomake#list#_diff_new_entries(a:entries, added) + if !empty(diff) + for [k, v] in items(diff) + " TODO: handle valid=1 being added? + call neomake#log#debug(printf( + \ 'Entry %d differs after adding: %s.', + \ k+1, + \ string(v)), + \ a:jobinfo) + endfor + endif + endif + + let parsed_entries = copy(a:entries) + let idx = 0 + for e in added + if parsed_entries[idx].bufnr != e.bufnr + call neomake#log#debug(printf( + \ 'Updating entry bufnr: %s => %s.', + \ a:entries[idx].bufnr, e.bufnr)) + let parsed_entries[idx].bufnr = e.bufnr + endif + let idx += 1 + endfor + + call self.add_entries(parsed_entries, a:jobinfo) + return parsed_entries +endfunction + +function! neomake#list#_diff_new_entries(orig, new) abort + if a:orig == a:new + return {} + endif + let i = 0 + let r = {} + for new in a:new + let orig = copy(get(a:orig, i, {})) + for [k, v] in items({'pattern': '', 'module': '', 'valid': 1}) + if has_key(new, k) + let orig[k] = v + endif + endfor + if new != orig + " 'removed': {'length': 4, 'filename': 'from.rs', + " 'maker_name': 'cargo'}} + let new = copy(new) + for k in ['length', 'maker_name'] + if has_key(orig, k) + let new[k] = orig[k] + endif + endfor + let diff = neomake#utils#diff_dict(orig, new) + if !empty(diff) + let r[i] = diff + endif + endif + let i += 1 + endfor + return r +endfunction + +" Add raw lines using errorformat. +" This either pre-parses them with newer versions, or uses +" :laddexpr/:caddexpr. +function! s:base_list.add_lines_with_efm(lines, jobinfo) dict abort + let maker = a:jobinfo.maker + let file_mode = self.type ==# 'loclist' + + if s:use_efm_parsing + let efm = a:jobinfo.maker.errorformat + let parsed_entries = get(getqflist({'lines': a:lines, 'efm': efm}), 'items', -1) + if parsed_entries is -1 + call neomake#log#error(printf('Failed to get items via efm-parsing. Invalid errorformat? (%s)', efm), a:jobinfo) + let parsed_entries = getqflist({'lines': a:lines, 'efm': &errorformat}).items + endif + if empty(parsed_entries) + return [] + endif + else + if self.need_init + if self.type ==# 'loclist' + let msg = 'Creating location list.' + else + let msg = 'Creating quickfix list.' + endif + call neomake#log#debug(msg, self.make_info) + call self._call_qf_fn('set', [], ' ') + endif + let olderrformat = &errorformat + try + let &errorformat = maker.errorformat + catch + call neomake#log#error(printf('Failed to set errorformat (%s): %s.', string(maker.errorformat), v:exception), a:jobinfo) + endtry + try + if file_mode + let cmd = 'laddexpr' + else + let cmd = 'caddexpr' + endif + let a:jobinfo._delayed_qf_autocmd = 'QuickfixCmdPost '.cmd + let cmd = 'noautocmd '.cmd.' a:lines' + if self.debug + call neomake#log#debug(printf('list: exe: %s (with %d lines).', cmd, len(a:lines)), a:jobinfo) + endif + exe cmd + finally + let &errorformat = olderrformat + call a:jobinfo.cd_back() + endtry + + let new_list = self._get_qflist_entries() + let parsed_entries = new_list[len(self.entries) :] + if empty(parsed_entries) + return [] + endif + endif + + let l:Postprocess = neomake#utils#GetSetting('postprocess', maker, [], a:jobinfo.ft, a:jobinfo.bufnr) + if type(Postprocess) != type([]) + let postprocessors = [Postprocess] + else + let postprocessors = Postprocess + endif + + let default_type = 'unset' + + let entries = [] + let changed_entries = {} + let removed_entries = [] + let different_bufnrs = {} + let bufnr_from_temp = {} + let bufnr_from_stdin = {} + let tempfile_bufnrs = has_key(self.make_info, 'tempfiles') ? map(copy(self.make_info.tempfiles), 'bufnr(v:val)') : [] + let uses_stdin = get(a:jobinfo, 'uses_stdin', 0) + + let entry_idx = -1 + for entry in parsed_entries + let entry_idx += 1 + let before = copy(entry) + " Handle unlisted buffers via tempfiles and uses_stdin. + if entry.bufnr && entry.bufnr != a:jobinfo.bufnr + \ && (!empty(tempfile_bufnrs) || uses_stdin) + let map_bufnr = index(tempfile_bufnrs, entry.bufnr) + if map_bufnr != -1 + let entry.bufnr = a:jobinfo.bufnr + let map_bufnr = tempfile_bufnrs[map_bufnr] + if !has_key(bufnr_from_temp, map_bufnr) + let bufnr_from_temp[map_bufnr] = [] + endif + let bufnr_from_temp[map_bufnr] += [entry_idx+1] + elseif uses_stdin + if !buflisted(entry.bufnr) && bufexists(entry.bufnr) + if !has_key(bufnr_from_stdin, entry.bufnr) + let bufnr_from_stdin[entry.bufnr] = [] + endif + let bufnr_from_stdin[entry.bufnr] += [entry_idx+1] + let entry.bufnr = a:jobinfo.bufnr + endif + endif + endif + if self.debug && entry.bufnr && entry.bufnr != a:jobinfo.bufnr + if !has_key(different_bufnrs, entry.bufnr) + let different_bufnrs[entry.bufnr] = 1 + else + let different_bufnrs[entry.bufnr] += 1 + endif + endif + if !empty(postprocessors) + let g:neomake_postprocess_context = {'jobinfo': a:jobinfo} + try + for l:F in postprocessors + if type(F) == type({}) + call call(F.fn, [entry], F) + else + call call(F, [entry], maker) + endif + unlet! F " vim73 + endfor + finally + unlet! g:neomake_postprocess_context " Might be unset already with sleep in postprocess. + endtry + endif + if entry != before + let changed_entries[entry_idx] = entry + if self.debug + " Ignore bufnr changes for tempfiles/stdin (logged together + " later). + let diff = neomake#utils#diff_dict(before, entry) + let changed_bufnr = get(get(diff, 'changed', {}), 'bufnr', []) + if !empty(changed_bufnr) && ( + \ has_key(bufnr_from_temp, changed_bufnr[0]) + \ || has_key(bufnr_from_stdin, changed_bufnr[0])) + unlet diff.changed.bufnr + if empty(diff.changed) + unlet diff.changed + endif + endif + if !empty(diff) + call neomake#log#debug(printf( + \ 'Modified list entry %d (postprocess): %s.', + \ entry_idx + 1, + \ substitute(string(diff), '\n', '\\n', 'g')), + \ a:jobinfo) + endif + endif + endif + + if entry.valid <= 0 + if entry.valid < 0 || maker.remove_invalid_entries + call insert(removed_entries, entry_idx) + let entry_copy = copy(entry) + call neomake#log#debug(printf( + \ 'Removing invalid entry: %s (%s).', + \ remove(entry_copy, 'text'), + \ string(entry_copy)), a:jobinfo) + continue + endif + endif + + if empty(entry.type) && entry.valid + if default_type ==# 'unset' + let default_type = neomake#utils#GetSetting('default_entry_type', maker, 'W', a:jobinfo.ft, a:jobinfo.bufnr) + endif + if !empty(default_type) + let entry.type = default_type + let changed_entries[entry_idx] = entry + endif + endif + call add(entries, entry) + endfor + + if !s:use_efm_parsing + let new_index = len(self.entries) + " Add marker for custom quickfix to the first (new) entry. + if neomake#quickfix#is_enabled() + let changed_entries[0] = s:mark_entry_with_nmcfg(entries[0], maker) + endif + + if !empty(changed_entries) || !empty(removed_entries) + " Need to update/replace current list. + let list = self._get_qflist_entries() + if !empty(changed_entries) + for k in keys(changed_entries) + let list[new_index + k] = changed_entries[k] + endfor + endif + if !empty(removed_entries) + for k in removed_entries + call remove(list, new_index + k) + endfor + endif + call self._set_qflist_entries(list, 'r') + endif + endif + + if !empty(bufnr_from_temp) || !empty(bufnr_from_stdin) + if !has_key(self.make_info, '_wipe_unlisted_buffers') + let self.make_info._wipe_unlisted_buffers = [] + endif + let self.make_info._wipe_unlisted_buffers += keys(bufnr_from_stdin) + keys(bufnr_from_stdin) + if !empty(bufnr_from_temp) + for [tempbuf, entries_idx] in items(bufnr_from_temp) + let log_entries_idx = join(neomake#utils#shorten_list_for_log(entries_idx, 50), ', ') + call neomake#log#debug(printf( + \ 'Used bufnr from temporary buffer %d (%s) for %d entries: %s.', + \ tempbuf, + \ bufname(+tempbuf), + \ len(entries_idx), + \ log_entries_idx), a:jobinfo) + endfor + endif + if !empty(bufnr_from_stdin) + for [tempbuf, entries_idx] in items(bufnr_from_stdin) + let log_entries_idx = join(neomake#utils#shorten_list_for_log(entries_idx, 50), ', ') + call neomake#log#debug(printf( + \ 'Used bufnr from stdin buffer %d (%s) for %d entries: %s.', + \ tempbuf, + \ bufname(+tempbuf), + \ len(entries_idx), + \ log_entries_idx), a:jobinfo) + endfor + endif + endif + if !empty(different_bufnrs) + call neomake#log#debug(printf('WARN: seen entries with bufnr different from jobinfo.bufnr (%d): %s, current bufnr: %d.', a:jobinfo.bufnr, string(different_bufnrs), bufnr('%'))) + endif + + if empty(entries) + return [] + endif + + if s:use_efm_parsing + call self._appendlist(entries, a:jobinfo) + else + call self.add_entries(entries, a:jobinfo) + endif + return entries +endfunction + +" Get the current location or quickfix list. +function! neomake#list#get() abort + let winnr = winnr() + let win_info = neomake#compat#getwinvar(winnr, '_neomake_info', {}) + if has_key(win_info, 'loclist') + return win_info['loclist'] + endif + let info = get(g:, '_neomake_info', {}) + if has_key(info, 'qflist') + return info['qflist'] + endif + return {} +endfunction + +function! neomake#list#get_loclist(...) abort + let winnr = a:0 ? a:1 : winnr() + let info = neomake#compat#getwinvar(winnr, '_neomake_info', {}) + if !has_key(info, 'loclist') + " Create a new list, not bound to a job. + call neomake#log#debug('Creating new List object.') + let list = neomake#list#List('loclist') + call list.add_entries(getloclist(winnr)) + let info['loclist'] = list + call setwinvar(winnr, '_neomake_info', info) + endif + return info['loclist'] +endfunction + +" TODO: save project-maker quickfix list. +function! neomake#list#get_qflist() abort + let info = get(g:, '_neomake_info', {}) + if !has_key(info, 'qflist') + " Create a new list, not bound to a job. + call neomake#log#debug('Creating new List object.') + let list = neomake#list#List('quickfix') + call list.add_entries(getqflist()) + let info['qflist'] = list + let g:_neomake_info = info + endif + return info['qflist'] +endfunction + +function! s:get_list(file_mode) abort + if a:file_mode + let list = neomake#list#get_loclist() + let g:unimpaired_prevnext = ['NeomakePrevLoclist', 'NeomakeNextLoclist'] + else + let list = neomake#list#get_qflist() + let g:unimpaired_prevnext = ['NeomakePrevQuickfix', 'NeomakeNextQuickfix'] + endif + return list +endfunction + +function! neomake#list#next(c, ...) abort + let file_mode = a:0 ? a:1 : 1 + let list = s:get_list(file_mode) + call s:goto_nearest(list, a:c == 0 ? 1 : a:c) +endfunction + +function! neomake#list#prev(c, ...) abort + let file_mode = a:0 ? a:1 : 1 + let list = s:get_list(file_mode) + call s:goto_nearest(list, a:c == 0 ? -1 : -a:c) +endfunction + +" TODO: global / already used somewhere else? / config +let g:neomake#list#type_prio = { + \ 'E': 0, + \ 'W': 1, + \ 'I': 2, + \ } + +" TODO: allow to customize via own callback(s)? +function! s:cmp_listitem_loc(a, b) abort + let buf_diff = a:a.bufnr - a:b.bufnr + if buf_diff + return buf_diff + endif + + if exists('*vader#assert#not_equal') + call vader#assert#not_equal(a:a.bufnr, -1) + call vader#assert#not_equal(a:b.bufnr, -1) + endif + + let lnum_diff = a:a.lnum - a:b.lnum + if lnum_diff + return lnum_diff + endif + + let col_diff = a:a.col - a:b.col + if col_diff + return col_diff + endif + + let prio = g:neomake#list#type_prio + return get(prio, a:a.type, 99) - get(prio, a:b.type, 99) +endfunction + +function! s:goto_nearest(list, offset) abort + let [lnum, col] = getpos('.')[1:2] + if a:offset == 0 + throw 'a:offset must not be 0' + endif + + if !has_key(a:list, '_sorted_entries_by_location') + call a:list.sort_by_location() + endif + let entries = a:list._sorted_entries_by_location + if a:offset < 0 + call reverse(entries) + endif + + let c = a:offset + let step = a:offset > 0 ? 1 : -1 + let found = 0 + for item in entries + if (a:offset > 0 && (item.lnum > lnum || (item.lnum == lnum && item.col > col))) + \ || (a:offset < 0 && (item.lnum < lnum || (item.lnum == lnum && item.col < col))) + let c -= step + let found = item.nmqfidx + if c == 0 + break + endif + endif + endfor + + if found + if a:list.type ==# 'loclist' + if exists('*vader#assert#equal') + " @vimlint(EVL102, 1, l:ll_item) + let ll_item = getloclist(0)[found-1] + call vader#assert#equal([ll_item.bufnr, ll_item.lnum], [item.bufnr, item.lnum]) + endif + execute 'll '.found + else + if exists('*vader#assert#equal') + " @vimlint(EVL102, 1, l:cc_item) + let cc_item = getqflist()[found-1] + call vader#assert#equal([cc_item.bufnr, cc_item.lnum], [item.bufnr, item.lnum]) + endif + execute 'cc '.found + endif + elseif c > 0 + call neomake#log#error('No more next items.') + elseif c < 0 + call neomake#log#error('No more previous items.') + endif +endfunction + +" vim: ts=4 sw=4 et -- cgit v1.2.3