refactor how we represent and store checkers using python as a demo
Add 2 classes: SyntasticChecker and SyntasticRegistry. SyntasticChecker represents a checker. It holds funcrefs to the checker func, the highlight regex func and a new `isAvailable()` func (that essentially just checks if the checker exe is installed) SyntasticRegistry is responsible for: * loading checkers * storing checkers * fetching the checkers to use according to availability and the users settings Motivation/benefits: * in the current system only one checker can be loaded per filetype * syntax checkers cant be "chained" together * the system is hard to add features to since fundamental concepts like syntax checkers and location lists arent represented explicitly Things left to do: * add a call to g:SyntasticRegistry.CreateAndRegisterChecker() to all checkers * add an `isAvailable` function to all checkers * move all checkers into `syntax_checkers/filetype/checkername.vim` - g:SyntasticRegistry assumes this layout, and its a good idea anyway for consistency and it makes it easier for users to add their own checkers Things to do after all of the above: * add a LocationList class and move all the filtering functions onto it * possibly add an Error class that wraps up each item in a loc list Random notes: * with the new system you can select the checkers to use with e.g. `let g:syntastic_python_checkers=['flake8', 'pylint']` This will try flake8 first, and if no errors are detected it will move onto pylint.
This commit is contained in:
parent
8095909dcc
commit
58ba8d3161
@ -17,6 +17,8 @@ if exists("g:loaded_syntastic_plugin")
|
|||||||
endif
|
endif
|
||||||
let g:loaded_syntastic_plugin = 1
|
let g:loaded_syntastic_plugin = 1
|
||||||
|
|
||||||
|
runtime plugin/syntastic/*.vim
|
||||||
|
|
||||||
let s:running_windows = has("win16") || has("win32")
|
let s:running_windows = has("win16") || has("win32")
|
||||||
|
|
||||||
if !exists("g:syntastic_enable_signs")
|
if !exists("g:syntastic_enable_signs")
|
||||||
@ -103,6 +105,8 @@ if !exists("g:syntastic_loc_list_height")
|
|||||||
let g:syntastic_loc_list_height = 10
|
let g:syntastic_loc_list_height = 10
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let s:registry = g:SyntasticRegistry.Instance()
|
||||||
|
|
||||||
command! SyntasticToggleMode call s:ToggleMode()
|
command! SyntasticToggleMode call s:ToggleMode()
|
||||||
command! SyntasticCheck call s:UpdateErrors(0) <bar> call s:Redraw()
|
command! SyntasticCheck call s:UpdateErrors(0) <bar> call s:Redraw()
|
||||||
command! Errors call s:ShowLocList()
|
command! Errors call s:ShowLocList()
|
||||||
@ -199,15 +203,22 @@ function! s:CacheErrors()
|
|||||||
"functions legally for filetypes like "gentoo-metadata"
|
"functions legally for filetypes like "gentoo-metadata"
|
||||||
let fts = substitute(&ft, '-', '_', 'g')
|
let fts = substitute(&ft, '-', '_', 'g')
|
||||||
for ft in split(fts, '\.')
|
for ft in split(fts, '\.')
|
||||||
if SyntasticCheckable(ft)
|
let checkers = s:registry.getActiveCheckers(ft)
|
||||||
let errors = SyntaxCheckers_{ft}_GetLocList()
|
for checker in checkers
|
||||||
|
let errors = checker.getLocList()
|
||||||
|
|
||||||
|
if !empty(errors)
|
||||||
"keep only lines that effectively match an error/warning
|
"keep only lines that effectively match an error/warning
|
||||||
let errors = s:FilterLocList({'valid': 1}, errors)
|
let errors = s:FilterLocList({'valid': 1}, errors)
|
||||||
"make errors have type "E" by default
|
"make errors have type "E" by default
|
||||||
call SyntasticAddToErrors(errors, {'type': 'E'})
|
call SyntasticAddToErrors(errors, {'type': 'E'})
|
||||||
call extend(s:LocList(), errors)
|
call extend(s:LocList(), errors)
|
||||||
|
|
||||||
|
"only get errors from one checker at a time
|
||||||
|
break
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
|
endfor
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@ -531,18 +542,7 @@ endfunction
|
|||||||
"check if a syntax checker exists for the given filetype - and attempt to
|
"check if a syntax checker exists for the given filetype - and attempt to
|
||||||
"load one
|
"load one
|
||||||
function! SyntasticCheckable(ft)
|
function! SyntasticCheckable(ft)
|
||||||
"users can just define a syntax checking function and it will override the
|
return s:registry.checkable(a:ft)
|
||||||
"syntastic default
|
|
||||||
if exists("*SyntaxCheckers_". a:ft ."_GetLocList")
|
|
||||||
return 1
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists("g:loaded_" . a:ft . "_syntax_checker")
|
|
||||||
exec "runtime syntax_checkers/" . a:ft . ".vim"
|
|
||||||
let {"g:loaded_" . a:ft . "_syntax_checker"} = 1
|
|
||||||
endif
|
|
||||||
|
|
||||||
return exists("*SyntaxCheckers_". a:ft ."_GetLocList")
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
"the args must be arrays of the form [major, minor, macro]
|
"the args must be arrays of the form [major, minor, macro]
|
||||||
|
47
plugin/syntastic/checker.vim
Normal file
47
plugin/syntastic/checker.vim
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
if exists("g:loaded_syntastic_checker")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let g:loaded_syntastic_checker=1
|
||||||
|
|
||||||
|
let g:SyntasticChecker = {}
|
||||||
|
|
||||||
|
" Public methods {{{1
|
||||||
|
|
||||||
|
function! g:SyntasticChecker.New(args)
|
||||||
|
let newObj = copy(self)
|
||||||
|
|
||||||
|
let newObj._locListFunc = a:args['loclistFunc']
|
||||||
|
let newObj._isAvailableFunc = a:args['isAvailableFunc']
|
||||||
|
let newObj._filetype = a:args['filetype']
|
||||||
|
let newObj._name = a:args['name']
|
||||||
|
|
||||||
|
let newObj._highlightRegexFunc = get(a:args, 'highlightRegexFunc', '')
|
||||||
|
|
||||||
|
return newObj
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticChecker.filetype()
|
||||||
|
return self._filetype
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticChecker.name()
|
||||||
|
return self._name
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticChecker.getLocList()
|
||||||
|
return self._locListFunc()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticChecker.getHighlightRegexFor(error)
|
||||||
|
if empty(self._highlightRegexFunc)
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
|
||||||
|
return self._highlightRegexFunc(error)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticChecker.isAvailable()
|
||||||
|
return self._isAvailableFunc()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" vim: set sw=4 sts=4 et fdm=marker:
|
80
plugin/syntastic/registry.vim
Normal file
80
plugin/syntastic/registry.vim
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
if exists("g:loaded_syntastic_registry")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let g:loaded_syntastic_registry=1
|
||||||
|
|
||||||
|
let g:SyntasticRegistry = {}
|
||||||
|
|
||||||
|
" Public methods {{{1
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry.Instance()
|
||||||
|
if !exists('s:SyntasticRegistryInstance')
|
||||||
|
let s:SyntasticRegistryInstance = copy(self)
|
||||||
|
let s:SyntasticRegistryInstance._checkerMap = {}
|
||||||
|
endif
|
||||||
|
|
||||||
|
return s:SyntasticRegistryInstance
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry.CreateAndRegisterChecker(args)
|
||||||
|
let checker = g:SyntasticChecker.New(a:args)
|
||||||
|
let registry = g:SyntasticRegistry.Instance()
|
||||||
|
call registry.registerChecker(checker)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry.registerChecker(checker)
|
||||||
|
let ft = a:checker.filetype()
|
||||||
|
|
||||||
|
if !has_key(self._checkerMap, ft)
|
||||||
|
let self._checkerMap[ft] = []
|
||||||
|
endif
|
||||||
|
|
||||||
|
call add(self._checkerMap[ft], a:checker)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry.checkable(filetype)
|
||||||
|
return !empty(self.getActiveCheckers(a:filetype))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry.getActiveCheckers(filetype)
|
||||||
|
let allCheckers = copy(self._checkersFor(a:filetype))
|
||||||
|
|
||||||
|
"only use checkers the user has specified
|
||||||
|
if exists("g:syntastic_" . a:filetype . "_checkers")
|
||||||
|
let whitelist = g:syntastic_{a:filetype}_checkers
|
||||||
|
call filter(allCheckers, "index(whitelist, v:val.name()) != -1")
|
||||||
|
endif
|
||||||
|
|
||||||
|
"only use available checkers
|
||||||
|
return filter(allCheckers, "v:val.isAvailable()")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
" Private methods {{{1
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry._checkersFor(filetype)
|
||||||
|
call self._loadCheckers(a:filetype)
|
||||||
|
if empty(self._checkerMap[a:filetype])
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
|
||||||
|
return self._checkerMap[a:filetype]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry._loadCheckers(filetype)
|
||||||
|
if self._haveLoadedCheckers(a:filetype)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
exec "runtime! syntax_checkers/" . a:filetype . "/*.vim"
|
||||||
|
|
||||||
|
if !has_key(self._checkerMap, a:filetype)
|
||||||
|
let self._checkerMap[a:filetype] = []
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! g:SyntasticRegistry._haveLoadedCheckers(filetype)
|
||||||
|
return has_key(self._checkerMap, a:filetype)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" vim: set sw=4 sts=4 et fdm=marker:
|
@ -5,7 +5,11 @@
|
|||||||
" kstep <me@kstep.me>
|
" kstep <me@kstep.me>
|
||||||
"
|
"
|
||||||
"============================================================================
|
"============================================================================
|
||||||
function! SyntaxCheckers_python_GetHighlightRegex(i)
|
function! SyntaxCheckers_python_flake8_IsAvailable()
|
||||||
|
return executable('flake8')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! SyntaxCheckers_python_flake8_GetHighlightRegex(i)
|
||||||
if match(a:i['text'], 'is assigned to but never used') > -1
|
if match(a:i['text'], 'is assigned to but never used') > -1
|
||||||
\ || match(a:i['text'], 'imported but unused') > -1
|
\ || match(a:i['text'], 'imported but unused') > -1
|
||||||
\ || match(a:i['text'], 'undefined name') > -1
|
\ || match(a:i['text'], 'undefined name') > -1
|
||||||
@ -21,11 +25,17 @@ function! SyntaxCheckers_python_GetHighlightRegex(i)
|
|||||||
return ''
|
return ''
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! SyntaxCheckers_python_GetLocList()
|
function! SyntaxCheckers_python_flake8_GetLocList()
|
||||||
let makeprg = syntastic#makeprg#build({
|
let makeprg = syntastic#makeprg#build({
|
||||||
\ 'exe': 'flake8',
|
\ 'exe': 'flake8',
|
||||||
\ 'args': g:syntastic_python_checker_args,
|
|
||||||
\ 'subchecker': 'flake8' })
|
\ 'subchecker': 'flake8' })
|
||||||
let errorformat = '%E%f:%l: could not compile,%-Z%p^,%E%f:%l:%c: %m,%W%f:%l: %m,%-G%.%#'
|
let errorformat = '%E%f:%l: could not compile,%-Z%p^,%E%f:%l:%c: %m,%W%f:%l: %m,%-G%.%#'
|
||||||
return SyntasticMake({ 'makeprg': makeprg, 'errorformat': errorformat })
|
return SyntasticMake({ 'makeprg': makeprg, 'errorformat': errorformat })
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
call g:SyntasticRegistry.CreateAndRegisterChecker({
|
||||||
|
\ 'loclistFunc': function("SyntaxCheckers_python_flake8_GetLocList"),
|
||||||
|
\ 'highlightRegexFunc': function("SyntaxCheckers_python_flake8_GetHighlightRegex"),
|
||||||
|
\ 'filetype': 'python',
|
||||||
|
\ 'name': 'flake8',
|
||||||
|
\ 'isAvailableFunc': function("SyntaxCheckers_python_flake8_IsAvailable")} )
|
||||||
|
@ -6,7 +6,11 @@
|
|||||||
" Parantapa Bhattacharya <parantapa@gmail.com>
|
" Parantapa Bhattacharya <parantapa@gmail.com>
|
||||||
"
|
"
|
||||||
"============================================================================
|
"============================================================================
|
||||||
function! SyntaxCheckers_python_GetHighlightRegex(i)
|
function! SyntaxCheckers_python_pyflakes_IsAvailable()
|
||||||
|
return executable('pyflakes')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! SyntaxCheckers_python_pyflakes_GetHighlightRegex(i)
|
||||||
if match(a:i['text'], 'is assigned to but never used') > -1
|
if match(a:i['text'], 'is assigned to but never used') > -1
|
||||||
\ || match(a:i['text'], 'imported but unused') > -1
|
\ || match(a:i['text'], 'imported but unused') > -1
|
||||||
\ || match(a:i['text'], 'undefined name') > -1
|
\ || match(a:i['text'], 'undefined name') > -1
|
||||||
@ -22,16 +26,21 @@ function! SyntaxCheckers_python_GetHighlightRegex(i)
|
|||||||
return ''
|
return ''
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! SyntaxCheckers_python_GetLocList()
|
function! SyntaxCheckers_python_pyflakes_GetLocList()
|
||||||
let makeprg = syntastic#makeprg#build({
|
let makeprg = syntastic#makeprg#build({
|
||||||
\ 'exe': 'pyflakes',
|
\ 'exe': 'pyflakes',
|
||||||
\ 'args': g:syntastic_python_checker_args,
|
\ 'args': g:syntastic_python_checker_args,
|
||||||
\ 'subchecker': 'pyflakes' })
|
\ 'subchecker': 'pyflakes' })
|
||||||
let errorformat = '%E%f:%l: could not compile,%-Z%p^,%E%f:%l:%c: %m,%E%f:%l: %m,%-G%.%#'
|
let errorformat = '%E%f:%l: could not compile,%-Z%p^,%E%f:%l:%c: %m,%E%f:%l: %m,%-G%.%#'
|
||||||
|
|
||||||
let errors = SyntasticMake({ 'makeprg': makeprg,
|
return SyntasticMake({ 'makeprg': makeprg,
|
||||||
\ 'errorformat': errorformat,
|
\ 'errorformat': errorformat,
|
||||||
\ 'defaults': {'text': "Syntax error"} })
|
\ 'defaults': {'text': "Syntax error"} })
|
||||||
|
|
||||||
return errors
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
call g:SyntasticRegistry.CreateAndRegisterChecker({
|
||||||
|
\ 'loclistFunc': function('SyntaxCheckers_python_pyflakes_GetLocList'),
|
||||||
|
\ 'highlightRegexFunc': function('SyntaxCheckers_python_pyflakes_GetHighlightRegex'),
|
||||||
|
\ 'filetype': 'python',
|
||||||
|
\ 'name': 'pyflakes',
|
||||||
|
\ 'isAvailableFunc': function('SyntaxCheckers_python_pyflakes_IsAvailable')} )
|
||||||
|
@ -4,7 +4,11 @@
|
|||||||
"Author: Parantapa Bhattacharya <parantapa at gmail dot com>
|
"Author: Parantapa Bhattacharya <parantapa at gmail dot com>
|
||||||
"
|
"
|
||||||
"============================================================================
|
"============================================================================
|
||||||
function! SyntaxCheckers_python_GetLocList()
|
function! SyntaxCheckers_python_pylint_IsAvailable()
|
||||||
|
return executable('pylint')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! SyntaxCheckers_python_pylint_GetLocList()
|
||||||
let makeprg = syntastic#makeprg#build({
|
let makeprg = syntastic#makeprg#build({
|
||||||
\ 'exe': 'pylint',
|
\ 'exe': 'pylint',
|
||||||
\ 'args': g:syntastic_python_checker_args. ' -f parseable -r n -i y',
|
\ 'args': g:syntastic_python_checker_args. ' -f parseable -r n -i y',
|
||||||
@ -18,5 +22,10 @@ endfunction
|
|||||||
function! s:MakeprgTail()
|
function! s:MakeprgTail()
|
||||||
return ' 2>&1 \| sed ''s_: \[\([RCW]\)_: \[W] \[\1_''' .
|
return ' 2>&1 \| sed ''s_: \[\([RCW]\)_: \[W] \[\1_''' .
|
||||||
\ ' \| sed ''s_: \[\([FE]\)_:\ \[E] \[\1_'''
|
\ ' \| sed ''s_: \[\([FE]\)_:\ \[E] \[\1_'''
|
||||||
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
call g:SyntasticRegistry.CreateAndRegisterChecker({
|
||||||
|
\ 'loclistFunc': function('SyntaxCheckers_python_pylint_GetLocList'),
|
||||||
|
\ 'filetype': 'python',
|
||||||
|
\ 'name': 'pylint',
|
||||||
|
\ 'isAvailableFunc': function('SyntaxCheckers_python_pylint_IsAvailable')} )
|
||||||
|
@ -7,8 +7,11 @@
|
|||||||
" http://www.vim.org/scripts/download_script.php?src_id=1392
|
" http://www.vim.org/scripts/download_script.php?src_id=1392
|
||||||
"
|
"
|
||||||
"============================================================================
|
"============================================================================
|
||||||
|
function! SyntaxCheckers_python_pyflakes_IsAvailable()
|
||||||
|
return executable('python')
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! SyntaxCheckers_python_GetLocList()
|
function! SyntaxCheckers_python_pyflakes_GetLocList()
|
||||||
let l:path = shellescape(expand('%'))
|
let l:path = shellescape(expand('%'))
|
||||||
let l:cmd = "compile(open(" . l:path . ").read(), " . l:path . ", 'exec')"
|
let l:cmd = "compile(open(" . l:path . ").read(), " . l:path . ", 'exec')"
|
||||||
let l:makeprg = 'python -c "' . l:cmd . '"'
|
let l:makeprg = 'python -c "' . l:cmd . '"'
|
||||||
@ -25,3 +28,9 @@ function! SyntaxCheckers_python_GetLocList()
|
|||||||
|
|
||||||
return SyntasticMake({ 'makeprg': l:makeprg, 'errorformat': l:errorformat })
|
return SyntasticMake({ 'makeprg': l:makeprg, 'errorformat': l:errorformat })
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
call g:SyntasticRegistry.CreateAndRegisterChecker({
|
||||||
|
\ 'loclistFunc': function('SyntaxCheckers_python_pyflakes_GetLocList'),
|
||||||
|
\ 'filetype': 'python',
|
||||||
|
\ 'name': 'python',
|
||||||
|
\ 'isAvailableFunc': function('SyntaxCheckers_python_pyflakes_IsAvailable')} )
|
||||||
|
Loading…
Reference in New Issue
Block a user