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