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

View File

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