add a class to encapsulate loclists

Add SyntasticLoclist class to wrap up loclists, and move loclist query
methods from syntatic.vim to the new class.

Make SyntasticChecker#getLocList() return a SyntasticLoclist.
This commit is contained in:
Martin Grenfell 2013-02-01 14:16:04 +00:00
parent f67d4881ca
commit 8f4695c6de
3 changed files with 130 additions and 80 deletions

View File

@ -137,7 +137,8 @@ function! s:UpdateErrors(auto_invoked)
call s:CacheErrors()
end
call setloclist(0, s:LocList())
let loclist = s:LocList()
call setloclist(0, loclist.toRaw())
if g:syntastic_enable_balloons
call s:RefreshBalloons()
@ -151,7 +152,7 @@ function! s:UpdateErrors(auto_invoked)
call s:HighlightErrors()
endif
if g:syntastic_auto_jump && s:BufHasErrorsOrWarningsToDisplay()
if g:syntastic_auto_jump && loclist.hasErrorsOrWarningsToDisplay()
silent! ll
endif
@ -161,7 +162,8 @@ endfunction
"automatically open/close the location list window depending on the users
"config and buffer error state
function! s:AutoToggleLocList()
if s:BufHasErrorsOrWarningsToDisplay()
let loclist = s:LocList()
if loclist.hasErrorsOrWarningsToDisplay()
if g:syntastic_auto_loc_list == 1
call s:ShowLocList()
endif
@ -178,16 +180,14 @@ endfunction
"lazy init the loc list for the current buffer
function! s:LocList()
if !exists("b:syntastic_loclist")
let b:syntastic_loclist = []
let b:syntastic_loclist = g:SyntasticLoclist.New([])
endif
return b:syntastic_loclist
endfunction
"clear the loc list for the buffer
function! s:ClearCache()
let b:syntastic_loclist = []
unlet! b:syntastic_errors
unlet! b:syntastic_warnings
unlet! b:syntastic_loclist
endfunction
"detect and cache all syntax errors in this buffer
@ -196,6 +196,7 @@ endfunction
"elsewhere
function! s:CacheErrors()
call s:ClearCache()
let newLoclist = g:SyntasticLoclist.New([])
if filereadable(expand("%"))
@ -205,14 +206,10 @@ function! s:CacheErrors()
for ft in split(fts, '\.')
let checkers = s:registry.getActiveCheckers(ft)
for checker in checkers
let errors = checker.getLocList()
let loclist = checker.getLocList()
if !empty(errors)
"keep only lines that effectively match an error/warning
let errors = s:FilterLocList({'valid': 1}, errors)
"make errors have type "E" by default
call SyntasticAddToErrors(errors, {'type': 'E'})
call extend(s:LocList(), errors)
if !loclist.isEmpty()
let newLoclist = newLoclist.extend(loclist)
"only get errors from one checker at a time
break
@ -220,6 +217,8 @@ function! s:CacheErrors()
endfor
endfor
endif
let b:syntastic_loclist = newLoclist
endfunction
"toggle the g:syntastic_mode_map['mode']
@ -252,56 +251,6 @@ function! s:ModeMapAllowsAutoChecking()
endif
endfunction
function! s:BufHasErrorsOrWarningsToDisplay()
if empty(s:LocList())
return 0
endif
return len(s:Errors()) || !g:syntastic_quiet_warnings
endfunction
function! s:Errors()
if !exists("b:syntastic_errors")
let b:syntastic_errors = s:FilterLocList({'type': "E"})
endif
return b:syntastic_errors
endfunction
function! s:Warnings()
if !exists("b:syntastic_warnings")
let b:syntastic_warnings = s:FilterLocList({'type': "W"})
endif
return b:syntastic_warnings
endfunction
"Filter a loc list (defaults to s:LocList()) by a:filters
"e.g.
" s:FilterLocList({'bufnr': 10, 'type': 'e'})
"
"would return all errors in s:LocList() for buffer 10.
"
"Note that all comparisons are done with ==?
function! s:FilterLocList(filters, ...)
let llist = a:0 ? a:1 : s:LocList()
let rv = []
for error in llist
let passes_filters = 1
for key in keys(a:filters)
if error[key] !=? a:filters[key]
let passes_filters = 0
break
endif
endfor
if passes_filters
call add(rv, error)
endif
endfor
return rv
endfunction
if g:syntastic_enable_signs
"define the signs used to display syntax and style errors/warns
exe 'sign define SyntasticError text='.g:syntastic_error_symbol.' texthl=error'
@ -318,9 +267,10 @@ let s:next_sign_id = s:first_sign_id
"place signs by all syntax errs in the buffer
function! s:SignErrors()
if s:BufHasErrorsOrWarningsToDisplay()
let loclist = s:LocList()
if loclist.hasErrorsOrWarningsToDisplay()
let errors = s:FilterLocList({'bufnr': bufnr('')})
let errors = loclist.filter({'bufnr': bufnr('')})
for i in errors
let sign_severity = 'Error'
let sign_subtype = ''
@ -348,7 +298,8 @@ function! s:WarningMasksError(error, llist)
return 0
endif
return len(s:FilterLocList({ 'type': "E", 'lnum': a:error['lnum'] }, a:llist)) > 0
let loclist = g:SyntasticLoclist.New(a:llist)
return len(loclist.filter({ 'type': "E", 'lnum': a:error['lnum'] })) > 0
endfunction
"remove the signs with the given ids from this buffer
@ -377,8 +328,9 @@ endfunction
"display the cached errors for this buf in the location list
function! s:ShowLocList()
if !empty(s:LocList())
call setloclist(0, s:LocList())
let loclist = s:LocList()
if !loclist.isEmpty()
call setloclist(0, loclist.toRaw())
let num = winnr()
exec "lopen " . g:syntastic_loc_list_height
if num != winnr()
@ -398,11 +350,12 @@ endfunction
"the callback even if it can be highlighted automatically.
function! s:HighlightErrors()
call s:ClearErrorHighlights()
let loclist = s:LocList()
let fts = substitute(&ft, '-', '_', 'g')
for ft in split(fts, '\.')
for item in s:LocList()
for item in loclist.toRaw()
let force_callback = has_key(item, 'force_highlight_callback') && item['force_highlight_callback']
@ -436,8 +389,9 @@ endfunction
"set up error ballons for the current set of errors
function! s:RefreshBalloons()
let b:syntastic_balloons = {}
if s:BufHasErrorsOrWarningsToDisplay()
for i in s:LocList()
let loclist = s:LocList()
if loclist.hasErrorsOrWarningsToDisplay()
for i in loclist.toRaw()
let b:syntastic_balloons[i['lnum']] = i['text']
endfor
set beval bexpr=SyntasticErrorBalloonExpr()
@ -471,9 +425,10 @@ endfunction
"echo out the first error we find for the current line in the cmd window
function! s:EchoCurrentError()
let loclist = s:LocList()
"If we have an error or warning at the current line, show it
let errors = s:FilterLocList({'lnum': line("."), "type": 'e'})
let warnings = s:FilterLocList({'lnum': line("."), "type": 'w'})
let errors = loclist.filter({'lnum': line("."), "type": 'e'})
let warnings = loclist.filter({'lnum': line("."), "type": 'w'})
let b:syntastic_echoing_error = len(errors) || len(warnings)
if len(errors)
@ -540,9 +495,10 @@ endfunction
"
"return '' if no errors are cached for the buffer
function! SyntasticStatuslineFlag()
if s:BufHasErrorsOrWarningsToDisplay()
let errors = s:Errors()
let warnings = s:Warnings()
let loclist = s:LocList()
if loclist.hasErrorsOrWarningsToDisplay()
let errors = loclist.errors()
let warnings = loclist.warnings()
let num_errors = len(errors)
let num_warnings = len(warnings)
@ -558,13 +514,14 @@ function! SyntasticStatuslineFlag()
"hide stuff wrapped in %B(...) unless there are both errors and warnings
let output = substitute(output, '\C%B{\([^}]*\)}', (num_warnings && num_errors) ? '\1' : '' , 'g')
"sub in the total errors/warnings/both
let output = substitute(output, '\C%w', num_warnings, 'g')
let output = substitute(output, '\C%e', num_errors, 'g')
let output = substitute(output, '\C%t', len(s:LocList()), 'g')
let output = substitute(output, '\C%t', loclist.length(), 'g')
"first error/warning line num
let output = substitute(output, '\C%F', s:LocList()[0]['lnum'], 'g')
let output = substitute(output, '\C%F', loclist.toRaw()[0]['lnum'], 'g')
"first error line num
let output = substitute(output, '\C%fe', num_errors ? errors[0]['lnum'] : '', 'g')

View File

@ -36,7 +36,8 @@ function! g:SyntasticChecker.name()
endfunction
function! g:SyntasticChecker.getLocList()
return self._locListFunc()
let list = self._locListFunc()
return g:SyntasticLoclist.New(list)
endfunction
function! g:SyntasticChecker.getHighlightRegexFor(error)

View File

@ -0,0 +1,92 @@
if exists("g:loaded_syntastic_loclist")
finish
endif
let g:loaded_syntastic_list=1
let g:SyntasticLoclist = {}
" Public methods {{{1
function! g:SyntasticLoclist.New(rawLoclist)
let newObj = copy(self)
let newObj._quietWarnings = g:syntastic_quiet_warnings
let llist = copy(a:rawLoclist)
let llist = filter(llist, 'v:val["valid"] == 1')
for e in llist
if empty(e['type'])
let e['type'] = 'E'
endif
endfor
let newObj._rawLoclist = llist
return newObj
endfunction
function! g:SyntasticLoclist.extend(other)
let list = self.toRaw()
call extend(list, a:other.toRaw())
return g:SyntasticLoclist.New(list)
endfunction
function! g:SyntasticLoclist.toRaw()
return copy(self._rawLoclist)
endfunction
function! g:SyntasticLoclist.isEmpty()
return empty(self._rawLoclist)
endfunction
function! g:SyntasticLoclist.length()
return len(self._rawLoclist)
endfunction
function! g:SyntasticLoclist.hasErrorsOrWarningsToDisplay()
if empty(self._rawLoclist)
return 0
endif
return len(self.errors()) || !self._quietWarnings
endfunction
function! g:SyntasticLoclist.errors()
if !exists("self._cachedErrors")
let self._cachedErrors = self.filter({'type': "E"})
endif
return self._cachedErrors
endfunction
function! SyntasticLoclist.warnings()
if !exists("self._cachedWarnings")
let self._cachedWarnings = self.filter({'type': "W"})
endif
return self._cachedWarnings
endfunction
"Filter the list and return new native loclist
"e.g.
" .filter({'bufnr': 10, 'type': 'e'})
"
"would return all errors for buffer 10.
"
"Note that all comparisons are done with ==?
function! g:SyntasticLoclist.filter(filters)
let rv = []
for error in self._rawLoclist
let passes_filters = 1
for key in keys(a:filters)
if error[key] !=? a:filters[key]
let passes_filters = 0
break
endif
endfor
if passes_filters
call add(rv, error)
endif
endfor
return rv
endfunction