551 changed files with 66981 additions and 0 deletions
@ -0,0 +1 @@ |
|||
/pack/minpac/* |
|||
@ -0,0 +1,5 @@ |
|||
setlocal expandtab |
|||
setlocal shiftwidth=4 |
|||
setlocal softtabstop=4 |
|||
setlocal autoindent |
|||
let g:rustfmt_autosave = 1 |
|||
@ -0,0 +1,18 @@ |
|||
language: viml |
|||
sudo: required |
|||
dist: trusty |
|||
|
|||
install: |
|||
- export VIM_VERSION=master |
|||
- bash test/install-vim.sh |
|||
- export PATH=$HOME/vim/bin:$PATH |
|||
|
|||
before_script: |
|||
- git clone --depth 1 --branch v1.5.4 --single-branch https://github.com/thinca/vim-themis /tmp/vim-themis |
|||
|
|||
script: |
|||
- pip install --user --upgrade vim-vint pathlib enum34 typing |
|||
- python --version |
|||
- vim --version |
|||
- vint autoload |
|||
- /tmp/vim-themis/bin/themis --reporter dot |
|||
@ -0,0 +1,21 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2016 Prabir Shrestha |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
@ -0,0 +1,86 @@ |
|||
# async.vim |
|||
normalize async job control api for vim and neovim |
|||
|
|||
## sample usage |
|||
|
|||
```vim |
|||
function! s:handler(job_id, data, event_type) |
|||
echo a:job_id . ' ' . a:event_type |
|||
echo join(a:data, "\n") |
|||
endfunction |
|||
|
|||
if has('win32') || has('win64') |
|||
let argv = ['cmd', '/c', 'dir c:\ /b'] |
|||
else |
|||
let argv = ['bash', '-c', 'ls'] |
|||
endif |
|||
|
|||
let jobid = async#job#start(argv, { |
|||
\ 'on_stdout': function('s:handler'), |
|||
\ 'on_stderr': function('s:handler'), |
|||
\ 'on_exit': function('s:handler'), |
|||
\ }) |
|||
|
|||
if jobid > 0 |
|||
echom 'job started' |
|||
else |
|||
echom 'job failed to start' |
|||
endif |
|||
|
|||
" If you want to get the process id of the job |
|||
let pid = async#job#pid(jobid) |
|||
|
|||
" If you want to wait the job: |
|||
call async#job#wait([jobid], 5000) " timeout: 5 sec |
|||
|
|||
" If you want to stop the job: |
|||
call async#job#stop(jobid) |
|||
``` |
|||
|
|||
## APIs |
|||
|
|||
APIs are based on neovim's job control APIs. |
|||
|
|||
* [job-control](https://neovim.io/doc/user/job_control.html#job-control) |
|||
* [jobsend()](https://neovim.io/doc/user/eval.html#jobsend%28%29) |
|||
* [jobstart()](https://neovim.io/doc/user/eval.html#jobstart%28%29) |
|||
* [jobstop()](https://neovim.io/doc/user/eval.html#jobstop%28%29) |
|||
* [jobwait()](https://neovim.io/doc/user/eval.html#jobwait%28%29) |
|||
* [jobpid()](https://neovim.io/doc/user/eval.html#jobpid%28%29) |
|||
|
|||
## Embedding |
|||
|
|||
Async.vim can be either embedded with other plugins or be used as an external plugin. |
|||
If you want to embed all you need is to change these 5 function names async#job# to what ever you want. E.g.: |
|||
|
|||
```vim |
|||
" public apis {{{ |
|||
function! yourplugin#job#start(cmd, opts) abort |
|||
return s:job_start(a:cmd, a:opts) |
|||
endfunction |
|||
|
|||
function! yourplugin#job#stop(jobid) abort |
|||
call s:job_stop(a:jobid) |
|||
endfunction |
|||
|
|||
function! yourplugin#job#send(jobid, data) abort |
|||
call s:job_send(a:jobid, a:data) |
|||
endfunction |
|||
|
|||
function! yourplugin#job#wait(jobids, ...) abort |
|||
let l:timeout = get(a:000, 0, -1) |
|||
return s:job_wait(a:jobids, l:timeout) |
|||
endfunction |
|||
|
|||
function! yourplugin#job#pid(jobid) abort |
|||
return s:job_pid(a:jobid) |
|||
endfunction |
|||
" }}} |
|||
``` |
|||
|
|||
## Todos |
|||
* Fallback to sync `system()` calls in vim that doesn't support `job` |
|||
* `job_stop` and `job_send` is treated as noop when using `system()` |
|||
* `on_stderr` doesn't work when using `system()` |
|||
* Fallback to python/ruby threads and vimproc instead of using `system()` for better compatibility (PRs welcome!!!) |
|||
|
|||
@ -0,0 +1,302 @@ |
|||
" Author: Prabir Shrestha <mail at prabir dot me> |
|||
" Website: https://github.com/prabirshrestha/async.vim |
|||
" License: The MIT License {{{ |
|||
" The MIT License (MIT) |
|||
" |
|||
" Copyright (c) 2016 Prabir Shrestha |
|||
" |
|||
" Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
" of this software and associated documentation files (the "Software"), to deal |
|||
" in the Software without restriction, including without limitation the rights |
|||
" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
" copies of the Software, and to permit persons to whom the Software is |
|||
" furnished to do so, subject to the following conditions: |
|||
" |
|||
" The above copyright notice and this permission notice shall be included in all |
|||
" copies or substantial portions of the Software. |
|||
" |
|||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
" SOFTWARE. |
|||
" }}} |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
let s:jobidseq = 0 |
|||
let s:jobs = {} " { job, opts, type: 'vimjob|nvimjob'} |
|||
let s:job_type_nvimjob = 'nvimjob' |
|||
let s:job_type_vimjob = 'vimjob' |
|||
let s:job_error_unsupported_job_type = -2 " unsupported job type |
|||
|
|||
function! s:job_supported_types() abort |
|||
let l:supported_types = [] |
|||
if has('nvim') |
|||
let l:supported_types += [s:job_type_nvimjob] |
|||
endif |
|||
if !has('nvim') && has('job') && has('channel') && has('lambda') |
|||
let l:supported_types += [s:job_type_vimjob] |
|||
endif |
|||
return l:supported_types |
|||
endfunction |
|||
|
|||
function! s:job_supports_type(type) abort |
|||
return index(s:job_supported_types(), a:type) >= 0 |
|||
endfunction |
|||
|
|||
function! s:out_cb(jobid, opts, job, data) abort |
|||
if has_key(a:opts, 'on_stdout') |
|||
call a:opts.on_stdout(a:jobid, split(a:data, "\n", 1), 'stdout') |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:err_cb(jobid, opts, job, data) abort |
|||
if has_key(a:opts, 'on_stderr') |
|||
call a:opts.on_stderr(a:jobid, split(a:data, "\n", 1), 'stderr') |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:exit_cb(jobid, opts, job, status) abort |
|||
if has_key(a:opts, 'on_exit') |
|||
call a:opts.on_exit(a:jobid, a:status, 'exit') |
|||
endif |
|||
if has_key(s:jobs, a:jobid) |
|||
call remove(s:jobs, a:jobid) |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:on_stdout(jobid, data, event) abort |
|||
if has_key(s:jobs, a:jobid) |
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
if has_key(l:jobinfo.opts, 'on_stdout') |
|||
call l:jobinfo.opts.on_stdout(a:jobid, a:data, a:event) |
|||
endif |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:on_stderr(jobid, data, event) abort |
|||
if has_key(s:jobs, a:jobid) |
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
if has_key(l:jobinfo.opts, 'on_stderr') |
|||
call l:jobinfo.opts.on_stderr(a:jobid, a:data, a:event) |
|||
endif |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:on_exit(jobid, status, event) abort |
|||
if has_key(s:jobs, a:jobid) |
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
if has_key(l:jobinfo.opts, 'on_exit') |
|||
call l:jobinfo.opts.on_exit(a:jobid, a:status, a:event) |
|||
endif |
|||
if has_key(s:jobs, a:jobid) |
|||
call remove(s:jobs, a:jobid) |
|||
endif |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:job_start(cmd, opts) abort |
|||
let l:jobtypes = s:job_supported_types() |
|||
let l:jobtype = '' |
|||
|
|||
if has_key(a:opts, 'type') |
|||
if type(a:opts.type) == type('') |
|||
if !s:job_supports_type(a:opts.type) |
|||
return s:job_error_unsupported_job_type |
|||
endif |
|||
let l:jobtype = a:opts.type |
|||
else |
|||
let l:jobtypes = a:opts.type |
|||
endif |
|||
endif |
|||
|
|||
if empty(l:jobtype) |
|||
" find the best jobtype |
|||
for l:jobtype2 in l:jobtypes |
|||
if s:job_supports_type(l:jobtype2) |
|||
let l:jobtype = l:jobtype2 |
|||
endif |
|||
endfor |
|||
endif |
|||
|
|||
if l:jobtype ==? '' |
|||
return s:job_error_unsupported_job_type |
|||
endif |
|||
|
|||
if l:jobtype == s:job_type_nvimjob |
|||
let l:job = jobstart(a:cmd, { |
|||
\ 'on_stdout': function('s:on_stdout'), |
|||
\ 'on_stderr': function('s:on_stderr'), |
|||
\ 'on_exit': function('s:on_exit'), |
|||
\}) |
|||
if l:job <= 0 |
|||
return l:job |
|||
endif |
|||
let l:jobid = l:job " nvimjobid and internal jobid is same |
|||
let s:jobs[l:jobid] = { |
|||
\ 'type': s:job_type_nvimjob, |
|||
\ 'opts': a:opts, |
|||
\ } |
|||
let s:jobs[l:jobid].job = l:job |
|||
elseif l:jobtype == s:job_type_vimjob |
|||
let s:jobidseq = s:jobidseq + 1 |
|||
let l:jobid = s:jobidseq |
|||
let l:jobopt = { |
|||
\ 'out_cb': function('s:out_cb', [l:jobid, a:opts]), |
|||
\ 'err_cb': function('s:err_cb', [l:jobid, a:opts]), |
|||
\ 'exit_cb': function('s:exit_cb', [l:jobid, a:opts]), |
|||
\ 'mode': 'raw', |
|||
\ } |
|||
if has('patch-8.1.889') |
|||
let l:jobopt['noblock'] = 1 |
|||
endif |
|||
let l:job = job_start(a:cmd, l:jobopt) |
|||
if job_status(l:job) !=? 'run' |
|||
return -1 |
|||
endif |
|||
let s:jobs[l:jobid] = { |
|||
\ 'type': s:job_type_vimjob, |
|||
\ 'opts': a:opts, |
|||
\ 'job': l:job, |
|||
\ 'channel': job_getchannel(l:job), |
|||
\ 'buffer': '' |
|||
\ } |
|||
else |
|||
return s:job_error_unsupported_job_type |
|||
endif |
|||
|
|||
return l:jobid |
|||
endfunction |
|||
|
|||
function! s:job_stop(jobid) abort |
|||
if has_key(s:jobs, a:jobid) |
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
if l:jobinfo.type == s:job_type_nvimjob |
|||
" See: vital-Whisky/System.Job |
|||
try |
|||
call jobstop(a:jobid) |
|||
catch /^Vim\%((\a\+)\)\=:E900/ |
|||
" NOTE: |
|||
" Vim does not raise exception even the job has already closed so fail |
|||
" silently for 'E900: Invalid job id' exception |
|||
endtry |
|||
elseif l:jobinfo.type == s:job_type_vimjob |
|||
call job_stop(s:jobs[a:jobid].job) |
|||
endif |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:job_send(jobid, data) abort |
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
if l:jobinfo.type == s:job_type_nvimjob |
|||
call jobsend(a:jobid, a:data) |
|||
elseif l:jobinfo.type == s:job_type_vimjob |
|||
if has('patch-8.1.0818') |
|||
call ch_sendraw(l:jobinfo.channel, a:data) |
|||
else |
|||
let l:jobinfo.buffer .= a:data |
|||
call s:flush_vim_sendraw(a:jobid, v:null) |
|||
endif |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:flush_vim_sendraw(jobid, timer) abort |
|||
" https://github.com/vim/vim/issues/2548 |
|||
" https://github.com/natebosch/vim-lsc/issues/67#issuecomment-357469091 |
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
sleep 1m |
|||
if len(l:jobinfo.buffer) <= 4096 |
|||
call ch_sendraw(l:jobinfo.channel, l:jobinfo.buffer) |
|||
let l:jobinfo.buffer = '' |
|||
else |
|||
let l:to_send = l:jobinfo.buffer[:4095] |
|||
let l:jobinfo.buffer = l:jobinfo.buffer[4096:] |
|||
call ch_sendraw(l:jobinfo.channel, l:to_send) |
|||
call timer_start(1, function('s:flush_vim_sendraw', [a:jobid])) |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:job_wait_single(jobid, timeout, start) abort |
|||
if !has_key(s:jobs, a:jobid) |
|||
return -3 |
|||
endif |
|||
|
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
if l:jobinfo.type == s:job_type_nvimjob |
|||
let l:timeout = a:timeout - reltimefloat(reltime(a:start)) * 1000 |
|||
return jobwait([a:jobid], float2nr(l:timeout))[0] |
|||
elseif l:jobinfo.type == s:job_type_vimjob |
|||
let l:timeout = a:timeout / 1000.0 |
|||
try |
|||
while l:timeout < 0 || reltimefloat(reltime(a:start)) < l:timeout |
|||
let l:info = job_info(l:jobinfo.job) |
|||
if l:info.status ==# 'dead' |
|||
return l:info.exitval |
|||
elseif l:info.status ==# 'fail' |
|||
return -3 |
|||
endif |
|||
sleep 1m |
|||
endwhile |
|||
catch /^Vim:Interrupt$/ |
|||
return -2 |
|||
endtry |
|||
endif |
|||
return -1 |
|||
endfunction |
|||
|
|||
function! s:job_wait(jobids, timeout) abort |
|||
let l:start = reltime() |
|||
let l:exitcode = 0 |
|||
let l:ret = [] |
|||
for l:jobid in a:jobids |
|||
if l:exitcode != -2 " Not interrupted. |
|||
let l:exitcode = s:job_wait_single(l:jobid, a:timeout, l:start) |
|||
endif |
|||
let l:ret += [l:exitcode] |
|||
endfor |
|||
return l:ret |
|||
endfunction |
|||
|
|||
function! s:job_pid(jobid) abort |
|||
if !has_key(s:jobs, a:jobid) |
|||
return 0 |
|||
endif |
|||
|
|||
let l:jobinfo = s:jobs[a:jobid] |
|||
if l:jobinfo.type == s:job_type_nvimjob |
|||
return jobpid(a:jobid) |
|||
elseif l:jobinfo.type == s:job_type_vimjob |
|||
let l:vimjobinfo = job_info(a:jobid) |
|||
if type(l:vimjobinfo) == type({}) && has_key(l:vimjobinfo, 'process') |
|||
return l:vimjobinfo['process'] |
|||
endif |
|||
endif |
|||
return 0 |
|||
endfunction |
|||
|
|||
" public apis {{{ |
|||
function! async#job#start(cmd, opts) abort |
|||
return s:job_start(a:cmd, a:opts) |
|||
endfunction |
|||
|
|||
function! async#job#stop(jobid) abort |
|||
call s:job_stop(a:jobid) |
|||
endfunction |
|||
|
|||
function! async#job#send(jobid, data) abort |
|||
call s:job_send(a:jobid, a:data) |
|||
endfunction |
|||
|
|||
function! async#job#wait(jobids, ...) abort |
|||
let l:timeout = get(a:000, 0, -1) |
|||
return s:job_wait(a:jobids, l:timeout) |
|||
endfunction |
|||
|
|||
function! async#job#pid(jobid) abort |
|||
return s:job_pid(a:jobid) |
|||
endfunction |
|||
" }}} |
|||
@ -0,0 +1,2 @@ |
|||
call themis#option('recursive', 1) |
|||
call themis#helper('command').with(themis#helper('assert')) |
|||
@ -0,0 +1,76 @@ |
|||
function! s:on_stdout(ctx, id, data, event) abort |
|||
call add(a:ctx.output, join(a:data, '')) |
|||
call async#job#stop(a:id) |
|||
endfunction |
|||
|
|||
Describe async |
|||
Before |
|||
let scope = themis#helper('scope') |
|||
let vars = scope.vars('autoload/async/job.vim') |
|||
End |
|||
|
|||
Describe async#job#start |
|||
It can start cmmand and return numbered job-id |
|||
let job = async#job#start('vim --version', {}) |
|||
Assert Equals(type(job), v:t_number) |
|||
End |
|||
End |
|||
|
|||
Describe async#job#stop |
|||
It can stop the job specified |
|||
let job = async#job#start('bash -c "sleep 2 && touch i-love-vim"', {}) |
|||
sleep 3 |
|||
call async#job#stop(job) |
|||
Assert filereadable('i-love-vim') |
|||
call delete('i-love-vim') |
|||
|
|||
let job = async#job#start('bash -c "sleep 2 && touch i-love-vim"', {}) |
|||
sleep 1 |
|||
call async#job#stop(job) |
|||
Assert !filereadable('i-love-vim') |
|||
End |
|||
|
|||
It invokes 'on_exit' callback |
|||
let ns = { 'called': 0 } |
|||
let job = async#job#start('bash -c "sleep 2 && touch i-love-vim"', { |
|||
\ 'on_exit': { -> extend(ns, { 'called': 1 }) }, |
|||
\}) |
|||
call async#job#stop(job) |
|||
sleep 1m |
|||
Assert Equals(ns, {'called': 1}) |
|||
End |
|||
|
|||
It removes a corresponding job from an internal variable |
|||
let job1 = async#job#start('bash -c "sleep 2 && touch i-love-vim"', {}) |
|||
let job2 = async#job#start('bash -c "sleep 2 && touch i-love-vim"', {}) |
|||
Assert Equals(sort(keys(vars.jobs)), sort([string(job1), string(job2)])) |
|||
call async#job#stop(job1) |
|||
sleep 1m |
|||
Assert Equals(sort(keys(vars.jobs)), sort([string(job2)])) |
|||
sleep 3 |
|||
Assert Equals(sort(keys(vars.jobs)), []) |
|||
End |
|||
End |
|||
|
|||
Describe async#job#wait |
|||
It can wait the job specified |
|||
let job = async#job#start('bash -c "sleep 2"', {}) |
|||
let start = reltime() |
|||
call async#job#wait([job]) |
|||
let seconds = reltimefloat(reltime(start)) |
|||
call async#job#stop(job) |
|||
Assert seconds > 2 |
|||
End |
|||
End |
|||
|
|||
Describe async#job#send |
|||
It can send input text to the job |
|||
let ctx = { 'output': [] } |
|||
let job = async#job#start('cat', {'on_stdout': function('s:on_stdout', [ctx])}) |
|||
call async#job#send(job, "i-love-vim\n") |
|||
call async#job#wait([job]) |
|||
let out = join(ctx.output, '') |
|||
Assert Equals(out, 'i-love-vim') |
|||
End |
|||
End |
|||
End |
|||
@ -0,0 +1,27 @@ |
|||
#!/bin/bash |
|||
|
|||
set -ev |
|||
|
|||
case "${TRAVIS_OS_NAME}" in |
|||
linux) |
|||
if [[ "${VIM_VERSION}" == "" ]]; then |
|||
exit |
|||
fi |
|||
git clone --depth 1 --branch "${VIM_VERSION}" https://github.com/vim/vim /tmp/vim |
|||
cd /tmp/vim |
|||
./configure --prefix="${HOME}/vim" --with-features=huge --enable-pythoninterp \ |
|||
--enable-python3interp --enable-fail-if-missing |
|||
make -j2 |
|||
make install |
|||
;; |
|||
osx) |
|||
brew install macvim |
|||
# Instead of --with-override-system-vim, manually link the executable because |
|||
# it prevents MacVim installation with a bottle. |
|||
ln -fs "$(brew --prefix macvim)/bin/mvim" "/usr/local/bin/vim" |
|||
;; |
|||
*) |
|||
echo "Unknown value of \${TRAVIS_OS_NAME}: ${TRAVIS_OS_NAME}" |
|||
exit 65 |
|||
;; |
|||
esac |
|||
@ -0,0 +1 @@ |
|||
* text=auto |
|||
@ -0,0 +1 @@ |
|||
tags |
|||
@ -0,0 +1,7 @@ |
|||
LSP source for asyncomplete.vim vim-lsp |
|||
======================================= |
|||
|
|||
Provide [Language Server Protocol](https://github.com/Microsoft/language-server-protocol) autocompletion source for [asyncomplete.vim](https://github.com/prabirshrestha/asyncomplete.vim) and [vim-lsp](https://github.com/prabirshrestha/vim-lsp). |
|||
|
|||
## Installing |
|||
Refer to asyncomplete.vim docs on Language Server Protocol at https://github.com/prabirshrestha/asyncomplete.vim#language-server-protocol-lsp. |
|||
@ -0,0 +1,97 @@ |
|||
if exists('g:asyncomplete_lsp_loaded') |
|||
finish |
|||
endif |
|||
let g:asyncomplete_lsp_loaded = 1 |
|||
|
|||
let s:servers = {} " { server_name: 1 } |
|||
|
|||
au User lsp_server_init call s:server_initialized() |
|||
au User lsp_server_exit call s:server_exited() |
|||
|
|||
function! s:server_initialized() abort |
|||
let l:server_names = lsp#get_server_names() |
|||
for l:server_name in l:server_names |
|||
if !has_key(s:servers, l:server_name) |
|||
let l:init_capabilities = lsp#get_server_capabilities(l:server_name) |
|||
if has_key(l:init_capabilities, 'completionProvider') |
|||
let l:name = s:generate_asyncomplete_name(l:server_name) |
|||
let l:source_opt = { |
|||
\ 'name': l:name, |
|||
\ 'completor': function('s:completor', [l:server_name]), |
|||
\ } |
|||
if type(l:init_capabilities['completionProvider']) == type({}) && has_key(l:init_capabilities['completionProvider'], 'triggerCharacters') |
|||
let l:source_opt['triggers'] = { '*': l:init_capabilities['completionProvider']['triggerCharacters'] } |
|||
endif |
|||
let l:server = lsp#get_server_info(l:server_name) |
|||
if has_key(l:server, 'whitelist') |
|||
let l:source_opt['whitelist'] = l:server['whitelist'] |
|||
endif |
|||
if has_key(l:server, 'blacklist') |
|||
let l:source_opt['blacklist'] = l:server['blacklist'] |
|||
endif |
|||
if has_key(l:server, 'priority') |
|||
let l:source_opt['priority'] = l:server['priority'] |
|||
endif |
|||
call asyncomplete#register_source(l:source_opt) |
|||
let s:servers[l:server_name] = 1 |
|||
endif |
|||
endif |
|||
endfor |
|||
endfunction |
|||
|
|||
function! s:server_exited() abort |
|||
let l:server_names = lsp#get_server_names() |
|||
for l:server_name in l:server_names |
|||
if has_key(s:servers, l:server_name) |
|||
let l:name = s:generate_asyncomplete_name(l:server_name) |
|||
if s:servers[l:server_name] |
|||
call asyncomplete#unregister_source(l:name) |
|||
endif |
|||
unlet s:servers[l:server_name] |
|||
endif |
|||
endfor |
|||
endfunction |
|||
|
|||
function! s:generate_asyncomplete_name(server_name) abort |
|||
return 'asyncomplete_lsp_' . a:server_name |
|||
endfunction |
|||
|
|||
function! s:completor(server_name, opt, ctx) abort |
|||
call lsp#send_request(a:server_name, { |
|||
\ 'method': 'textDocument/completion', |
|||
\ 'params': { |
|||
\ 'textDocument': lsp#get_text_document_identifier(), |
|||
\ 'position': lsp#get_position(), |
|||
\ }, |
|||
\ 'on_notification': function('s:handle_completion', [a:server_name, a:opt, a:ctx]), |
|||
\ }) |
|||
endfunction |
|||
|
|||
function! s:handle_completion(server_name, opt, ctx, data) abort |
|||
if lsp#client#is_error(a:data) || !has_key(a:data, 'response') || !has_key(a:data['response'], 'result') |
|||
return |
|||
endif |
|||
|
|||
let l:result = a:data['response']['result'] |
|||
|
|||
if type(l:result) == type([]) |
|||
let l:items = l:result |
|||
let l:incomplete = 0 |
|||
elseif type(l:result) == type({}) |
|||
let l:items = l:result['items'] |
|||
let l:incomplete = l:result['isIncomplete'] |
|||
else |
|||
let l:items = [] |
|||
let l:incomplete = 0 |
|||
endif |
|||
|
|||
call map(l:items, 'lsp#omni#get_vim_completion_item(v:val, a:server_name)') |
|||
|
|||
let l:col = a:ctx['col'] |
|||
let l:typed = a:ctx['typed'] |
|||
let l:kw = matchstr(l:typed, '\k\+$') |
|||
let l:kwlen = len(l:kw) |
|||
let l:startcol = l:col - l:kwlen |
|||
|
|||
call asyncomplete#complete(a:opt['name'], a:ctx, l:startcol, l:items, l:incomplete) |
|||
endfunction |
|||
@ -0,0 +1 @@ |
|||
* text=auto |
|||
@ -0,0 +1,12 @@ |
|||
# These are supported funding model platforms |
|||
|
|||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] |
|||
patreon: # Replace with a single Patreon username |
|||
open_collective: asyncomplete |
|||
ko_fi: # Replace with a single Ko-fi username |
|||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel |
|||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry |
|||
liberapay: # Replace with a single Liberapay username |
|||
issuehunt: # Replace with a single IssueHunt username |
|||
otechie: # Replace with a single Otechie username |
|||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] |
|||
@ -0,0 +1 @@ |
|||
tags |
|||
@ -0,0 +1,21 @@ |
|||
MIT License |
|||
|
|||
Copyright (c) 2019 Prabir Shrestha |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
@ -0,0 +1,226 @@ |
|||
asyncomplete.vim |
|||
================ |
|||
|
|||
Async autocompletion for Vim 8 and Neovim with |timers|. |
|||
|
|||
This is inspired by [nvim-complete-manager](https://github.com/roxma/nvim-complete-manager) but written |
|||
in pure Vim Script. |
|||
|
|||
### Installing |
|||
|
|||
```viml |
|||
Plug 'prabirshrestha/asyncomplete.vim' |
|||
``` |
|||
|
|||
#### Tab completion |
|||
|
|||
```vim |
|||
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>" |
|||
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>" |
|||
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<cr>" |
|||
``` |
|||
|
|||
### Force refresh completion |
|||
|
|||
```vim |
|||
imap <c-space> <Plug>(asyncomplete_force_refresh) |
|||
``` |
|||
|
|||
### Auto popup |
|||
By default asyncomplete will automatically show the autocomplete popup menu as you start typing. |
|||
If you would like to disable the default behavior set `g:asyncomplete_auto_popup` to 0. |
|||
|
|||
```vim |
|||
let g:asyncomplete_auto_popup = 0 |
|||
``` |
|||
|
|||
You can use the above `<Plug>(asyncomplete_force_refresh)` to show the popup |
|||
or can you tab to show the autocomplete. |
|||
|
|||
```vim |
|||
let g:asyncomplete_auto_popup = 0 |
|||
|
|||
function! s:check_back_space() abort |
|||
let col = col('.') - 1 |
|||
return !col || getline('.')[col - 1] =~ '\s' |
|||
endfunction |
|||
|
|||
inoremap <silent><expr> <TAB> |
|||
\ pumvisible() ? "\<C-n>" : |
|||
\ <SID>check_back_space() ? "\<TAB>" : |
|||
\ asyncomplete#force_refresh() |
|||
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>" |
|||
``` |
|||
|
|||
#### Preview Window |
|||
|
|||
To disable preview window: |
|||
|
|||
```vim |
|||
set completeopt-=preview |
|||
``` |
|||
|
|||
To enable preview window: |
|||
|
|||
```vim |
|||
set completeopt+=preview |
|||
``` |
|||
|
|||
To auto close preview window when completion is done. |
|||
|
|||
```vim |
|||
autocmd! CompleteDone * if pumvisible() == 0 | pclose | endif |
|||
``` |
|||
|
|||
### Sources |
|||
|
|||
asyncomplete.vim deliberately does not contain any sources. Please use one of the following sources or create your own. |
|||
|
|||
#### Language Server Protocol (LSP) |
|||
[Language Server Protocol](https://github.com/Microsoft/language-server-protocol) via [vim-lsp](https://github.com/prabirshrestha/vim-lsp) and [asyncomplete-lsp.vim](https://github.com/prabirshrestha/asyncomplete-lsp.vim) |
|||
|
|||
**Please note** that vim-lsp setup for neovim requires neovim v0.2.0 or higher, since it uses lambda setup. |
|||
|
|||
```vim |
|||
Plug 'prabirshrestha/asyncomplete.vim' |
|||
Plug 'prabirshrestha/async.vim' |
|||
Plug 'prabirshrestha/vim-lsp' |
|||
Plug 'prabirshrestha/asyncomplete-lsp.vim' |
|||
|
|||
if executable('pyls') |
|||
" pip install python-language-server |
|||
au User lsp_setup call lsp#register_server({ |
|||
\ 'name': 'pyls', |
|||
\ 'cmd': {server_info->['pyls']}, |
|||
\ 'whitelist': ['python'], |
|||
\ }) |
|||
endif |
|||
``` |
|||
|
|||
**Refer to [vim-lsp wiki](https://github.com/prabirshrestha/vim-lsp/wiki/Servers) for configuring other language servers.** Besides auto-complete language server support other features such as go to definition, find references, renaming symbols, document symbols, find workspace symbols, formatting and so on. |
|||
|
|||
*in alphabetical order* |
|||
|
|||
| Languages/FileType/Source | Links | |
|||
|-------------------------------|----------------------------------------------------------------------------------------------------| |
|||
| Buffer | [asyncomplete-buffer.vim](https://github.com/prabirshrestha/asyncomplete-buffer.vim) | |
|||
| Emoji | [asyncomplete-emoji.vim](https://github.com/prabirshrestha/asyncomplete-emoji.vim) | |
|||
| Filenames / directories | [asyncomplete-file.vim](https://github.com/prabirshrestha/asyncomplete-file.vim) | |
|||
| [NeoInclude][neoinclude] | [asyncomplete-neoinclude.vim](https://github.com/kyouryuukunn/asyncomplete-neoinclude.vim) | |
|||
| Go | [asyncomplete-gocode.vim](https://github.com/prabirshrestha/asyncomplete-gocode.vim) | |
|||
| JavaScript (Flow) | [asyncomplete-flow.vim](https://github.com/prabirshrestha/asyncomplete-flow.vim) | |
|||
| [Neosnippet][neosnippet] | [asyncomplete-neosnippet.vim](https://github.com/prabirshrestha/asyncomplete-neosnippet.vim) | |
|||
| Omni | [asyncomplete-omni.vim](https://github.com/yami-beta/asyncomplete-omni.vim) | |
|||
| PivotalTracker stories | [asyncomplete-pivotaltracker.vim](https://github.com/hauleth/asyncomplete-pivotaltracker.vim) | |
|||
| Racer | [asyncomplete-racer.vim](https://github.com/keremc/asyncomplete-racer.vim) | |
|||
| [tmux complete][tmuxcomplete] | [tmux-complete.vim][tmuxcomplete] | |
|||
| Typescript | [asyncomplete-tscompletejob.vim](https://github.com/prabirshrestha/asyncomplete-tscompletejob.vim) | |
|||
| [UltiSnips][ultisnips] | [asyncomplete-ultisnips.vim](https://github.com/prabirshrestha/asyncomplete-ultisnips.vim) | |
|||
| Vim Syntax | [asyncomplete-necosyntax.vim](https://github.com/prabirshrestha/asyncomplete-necosyntax.vim) | |
|||
| Vim tags | [asyncomplete-tags.vim](https://github.com/prabirshrestha/asyncomplete-tags.vim) | |
|||
| Vim | [asyncomplete-necovim.vim](https://github.com/prabirshrestha/asyncomplete-necovim.vim) | |
|||
|
|||
[neosnippet]: https://github.com/Shougo/neosnippet.vim |
|||
[neoinclude]: https://github.com/Shougo/neoinclude.vim |
|||
[tmuxcomplete]: https://github.com/wellle/tmux-complete.vim |
|||
[ultisnips]: https://github.com/SirVer/ultisnips |
|||
|
|||
*can't find what you are looking for? write one instead an send a PR to be included here or search github topics tagged with asyncomplete at https://github.com/topics/asyncomplete.* |
|||
|
|||
#### Using existing vim plugin sources |
|||
|
|||
Rather than writing your own completion source from scratch you could also suggests other plugin authors to provide a async completion api that works for asyncomplete.vim or any other async autocomplete libraries without taking a dependency on asyncomplete.vim. The plugin can provide a function that takes a callback which returns the list of candidates and the startcol from where it must show the popup. Candidates can be list of words or vim's `complete-items`. |
|||
|
|||
```vim |
|||
function s:completor(opt, ctx) |
|||
call mylanguage#get_async_completions({candidates, startcol -> asyncomplete#complete(a:opt['name'], a:ctx, startcol, candidates) }) |
|||
endfunction |
|||
|
|||
au User asyncomplete_setup call asyncomplete#register_source({ |
|||
\ 'name': 'mylanguage', |
|||
\ 'whitelist': [*], |
|||
\ 'completor': function('s:completor'), |
|||
\ }) |
|||
``` |
|||
|
|||
### Example |
|||
|
|||
```vim |
|||
function! s:js_completor(opt, ctx) abort |
|||
let l:col = a:ctx['col'] |
|||
let l:typed = a:ctx['typed'] |
|||
|
|||
let l:kw = matchstr(l:typed, '\v\S+$') |
|||
let l:kwlen = len(l:kw) |
|||
|
|||
let l:startcol = l:col - l:kwlen |
|||
|
|||
let l:matches = [ |
|||
\ "do", "if", "in", "for", "let", "new", "try", "var", "case", "else", "enum", "eval", "null", "this", "true", |
|||
\ "void", "with", "await", "break", "catch", "class", "const", "false", "super", "throw", "while", "yield", |
|||
\ "delete", "export", "import", "public", "return", "static", "switch", "typeof", "default", "extends", |
|||
\ "finally", "package", "private", "continue", "debugger", "function", "arguments", "interface", "protected", |
|||
\ "implements", "instanceof" |
|||
\ ] |
|||
|
|||
call asyncomplete#complete(a:opt['name'], a:ctx, l:startcol, l:matches) |
|||
endfunction |
|||
|
|||
au User asyncomplete_setup call asyncomplete#register_source({ |
|||
\ 'name': 'javascript', |
|||
\ 'whitelist': ['javascript'], |
|||
\ 'completor': function('s:js_completor'), |
|||
\ }) |
|||
``` |
|||
|
|||
The above sample shows synchronous completion. If you would like to make it async just call `asyncomplete#complete` whenever you have the results ready. |
|||
|
|||
```vim |
|||
call timer_start(2000, {timer-> asyncomplete#complete(a:opt['name'], a:ctx, l:startcol, l:matches)}) |
|||
``` |
|||
|
|||
If you are returning incomplete results and would like to trigger completion on the next keypress pass `1` as the fifth parameter to `asyncomplete#complete` |
|||
which signifies the result is incomplete. |
|||
|
|||
```vim |
|||
call asyncomplete#complete(a:opt['name'], a:ctx, l:startcol, l:matches, 1) |
|||
``` |
|||
|
|||
As a source author you do not have to worry about synchronization issues in case the server returns the async completion after the user has typed more |
|||
characters. asyncomplete.vim uses partial caching as well as ignores if the context changes when calling `asyncomplete#complete`. |
|||
This is one of the core reason why the original context must be passed when calling `asyncomplete#complete`. |
|||
|
|||
### Credits |
|||
All the credit goes to the following projects |
|||
* [https://github.com/roxma/nvim-complete-manager](https://github.com/roxma/nvim-complete-manager) |
|||
* [https://github.com/maralla/completor.vim](https://github.com/maralla/completor.vim) |
|||
|
|||
## Contributors |
|||
|
|||
### Code Contributors |
|||
|
|||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. |
|||
<a href="https://github.com/prabirshrestha/asyncomplete.vim/graphs/contributors"><img src="https://opencollective.com/asyncomplete/contributors.svg?width=890&button=false" /></a> |
|||
|
|||
### Financial Contributors |
|||
|
|||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/asyncomplete/contribute)] |
|||
|
|||
#### Individuals |
|||
|
|||
<a href="https://opencollective.com/asyncomplete"><img src="https://opencollective.com/asyncomplete/individuals.svg?width=890"></a> |
|||
|
|||
#### Organizations |
|||
|
|||
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/asyncomplete/contribute)] |
|||
|
|||
<a href="https://opencollective.com/asyncomplete/organization/0/website"><img src="https://opencollective.com/asyncomplete/organization/0/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/1/website"><img src="https://opencollective.com/asyncomplete/organization/1/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/2/website"><img src="https://opencollective.com/asyncomplete/organization/2/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/3/website"><img src="https://opencollective.com/asyncomplete/organization/3/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/4/website"><img src="https://opencollective.com/asyncomplete/organization/4/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/5/website"><img src="https://opencollective.com/asyncomplete/organization/5/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/6/website"><img src="https://opencollective.com/asyncomplete/organization/6/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/7/website"><img src="https://opencollective.com/asyncomplete/organization/7/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/8/website"><img src="https://opencollective.com/asyncomplete/organization/8/avatar.svg"></a> |
|||
<a href="https://opencollective.com/asyncomplete/organization/9/website"><img src="https://opencollective.com/asyncomplete/organization/9/avatar.svg"></a> |
|||
@ -0,0 +1,500 @@ |
|||
function! asyncomplete#log(...) abort |
|||
if !empty(g:asyncomplete_log_file) |
|||
call writefile([json_encode(a:000)], g:asyncomplete_log_file, 'a') |
|||
endif |
|||
endfunction |
|||
|
|||
" do nothing, place it here only to avoid the message |
|||
augroup asyncomplete_silence_messages |
|||
au! |
|||
autocmd User asyncomplete_setup silent |
|||
augroup END |
|||
|
|||
if !has('timers') |
|||
echohl ErrorMsg |
|||
echomsg 'Vim/Neovim compiled with timers required for asyncomplete.vim.' |
|||
echohl NONE |
|||
if has('nvim') |
|||
call asyncomplete#log('neovim compiled with timers required.') |
|||
else |
|||
call asyncomplete#log('vim compiled with timers required.') |
|||
endif |
|||
finish |
|||
endif |
|||
|
|||
let s:already_setup = 0 |
|||
let s:sources = {} |
|||
let s:matches = {} " { server_name: { incomplete: 1, startcol: 0, items: [], refresh: 0, status: 'idle|pending|success|failure', ctx: ctx } } |
|||
let s:has_complete_info = exists('*complete_info') |
|||
|
|||
function! s:setup_if_required() abort |
|||
if !s:already_setup |
|||
" register asyncomplete change manager |
|||
for l:change_manager in g:asyncomplete_change_manager |
|||
call asyncomplete#log('core', 'initializing asyncomplete change manager', l:change_manager) |
|||
if type(l:change_manager) == type('') |
|||
execute 'let s:on_change_manager = function("'. l:change_manager .'")()' |
|||
else |
|||
let s:on_change_manager = l:change_manager() |
|||
endif |
|||
if has_key(s:on_change_manager, 'error') |
|||
call asyncomplete#log('core', 'initializing asyncomplete change manager failed', s:on_change_manager['name'], s:on_change_manager['error']) |
|||
else |
|||
call s:on_change_manager.register(function('s:on_change')) |
|||
call asyncomplete#log('core', 'initializing asyncomplete change manager complete', s:on_change_manager['name']) |
|||
break |
|||
endif |
|||
endfor |
|||
|
|||
augroup asyncomplete |
|||
autocmd! |
|||
autocmd InsertEnter * call s:on_insert_enter() |
|||
autocmd InsertLeave * call s:on_insert_leave() |
|||
augroup END |
|||
|
|||
doautocmd User asyncomplete_setup |
|||
let s:already_setup = 1 |
|||
endif |
|||
endfunction |
|||
|
|||
function! asyncomplete#enable_for_buffer() abort |
|||
call s:setup_if_required() |
|||
let b:asyncomplete_enable = 1 |
|||
endfunction |
|||
|
|||
function! asyncomplete#disable_for_buffer() abort |
|||
let b:asyncomplete_enable = 0 |
|||
endfunction |
|||
|
|||
function! asyncomplete#get_source_names() abort |
|||
return keys(s:sources) |
|||
endfunction |
|||
|
|||
function! asyncomplete#get_source_info(source_name) abort |
|||
return s:sources[a:source_name] |
|||
endfunction |
|||
|
|||
function! asyncomplete#register_source(info) abort |
|||
if has_key(s:sources, a:info['name']) |
|||
call asyncomplete#log('core', 'duplicate asyncomplete#register_source', a:info['name']) |
|||
return -1 |
|||
else |
|||
let s:sources[a:info['name']] = a:info |
|||
if has_key(a:info, 'events') && has_key(a:info, 'on_event') |
|||
execute 'augroup asyncomplete_source_event_' . a:info['name'] |
|||
for l:event in a:info['events'] |
|||
let l:exec = 'if get(b:,"asyncomplete_enable",0) | call s:notify_event_to_source("' . a:info['name'] . '", "'.l:event.'",asyncomplete#context()) | endif' |
|||
if type(l:event) == type('') |
|||
execute 'au ' . l:event . ' * ' . l:exec |
|||
elseif type(l:event) == type([]) |
|||
execute 'au ' . join(l:event,' ') .' ' . l:exec |
|||
endif |
|||
endfor |
|||
execute 'augroup end' |
|||
endif |
|||
|
|||
if exists('b:asyncomplete_active_sources') |
|||
unlet b:asyncomplete_active_sources |
|||
call s:get_active_sources_for_buffer() |
|||
endif |
|||
|
|||
if exists('b:asyncomplete_triggers') |
|||
unlet b:asyncomplete_triggers |
|||
call s:update_trigger_characters() |
|||
endif |
|||
|
|||
return 1 |
|||
endif |
|||
endfunction |
|||
|
|||
function! asyncomplete#unregister_source(info_or_server_name) abort |
|||
if type(a:info_or_server_name) == type({}) |
|||
let l:server_name = a:info_or_server_name['name'] |
|||
else |
|||
let l:server_name = a:info_or_server_name |
|||
endif |
|||
if has_key(s:sources, l:server_name) |
|||
let l:server = s:sources[l:server_name] |
|||
if has_key(l:server, 'unregister') |
|||
call l:server.unregister() |
|||
endif |
|||
unlet s:sources[l:server_name] |
|||
return 1 |
|||
else |
|||
return -1 |
|||
endif |
|||
endfunction |
|||
|
|||
function! asyncomplete#context() abort |
|||
let l:ret = {'bufnr':bufnr('%'), 'curpos':getcurpos(), 'changedtick':b:changedtick} |
|||
let l:ret['lnum'] = l:ret['curpos'][1] |
|||
let l:ret['col'] = l:ret['curpos'][2] |
|||
let l:ret['filetype'] = &filetype |
|||
let l:ret['filepath'] = expand('%:p') |
|||
let l:ret['typed'] = strpart(getline(l:ret['lnum']),0,l:ret['col']-1) |
|||
return l:ret |
|||
endfunction |
|||
|
|||
function! s:on_insert_enter() abort |
|||
call s:get_active_sources_for_buffer() " call to cache |
|||
call s:update_trigger_characters() |
|||
endfunction |
|||
|
|||
function! s:on_insert_leave() abort |
|||
call s:disable_popup_skip() |
|||
let s:matches = {} |
|||
if exists('s:update_pum_timer') |
|||
call timer_stop(s:update_pum_timer) |
|||
unlet s:update_pum_timer |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:get_active_sources_for_buffer() abort |
|||
if exists('b:asyncomplete_active_sources') |
|||
" active sources were cached for buffer |
|||
return b:asyncomplete_active_sources |
|||
endif |
|||
|
|||
call asyncomplete#log('core', 'computing active sources for buffer', bufnr('%')) |
|||
let b:asyncomplete_active_sources = [] |
|||
for [l:name, l:info] in items(s:sources) |
|||
let l:blacklisted = 0 |
|||
|
|||
if has_key(l:info, 'blacklist') |
|||
for l:filetype in l:info['blacklist'] |
|||
if l:filetype == &filetype || l:filetype is# '*' |
|||
let l:blacklisted = 1 |
|||
break |
|||
endif |
|||
endfor |
|||
endif |
|||
|
|||
if l:blacklisted |
|||
continue |
|||
endif |
|||
|
|||
if has_key(l:info, 'whitelist') |
|||
for l:filetype in l:info['whitelist'] |
|||
if l:filetype == &filetype || l:filetype is# '*' |
|||
let b:asyncomplete_active_sources += [l:name] |
|||
break |
|||
endif |
|||
endfor |
|||
endif |
|||
endfor |
|||
|
|||
call asyncomplete#log('core', 'active source for buffer', bufnr('%'), b:asyncomplete_active_sources) |
|||
|
|||
return b:asyncomplete_active_sources |
|||
endfunction |
|||
|
|||
function! s:update_trigger_characters() abort |
|||
if exists('b:asyncomplete_triggers') |
|||
" triggers were cached for buffer |
|||
return b:asyncomplete_triggers |
|||
endif |
|||
let b:asyncomplete_triggers = {} " { char: { 'sourcea': 1, 'sourceb': 2 } } |
|||
|
|||
for l:source_name in s:get_active_sources_for_buffer() |
|||
let l:source_info = s:sources[l:source_name] |
|||
if has_key(l:source_info, 'triggers') && has_key(l:source_info['triggers'], &filetype) |
|||
let l:triggers = l:source_info['triggers'][&filetype] |
|||
elseif has_key(l:source_info, 'triggers') && has_key(l:source_info['triggers'], '*') |
|||
let l:triggers = l:source_info['triggers']['*'] |
|||
elseif has_key(g:asyncomplete_triggers, &filetype) |
|||
let l:triggers = g:asyncomplete_triggers[&filetype] |
|||
elseif has_key(g:asyncomplete_triggers, '*') |
|||
let l:triggers = g:asyncomplete_triggers['*'] |
|||
else |
|||
let l:triggers = [] |
|||
endif |
|||
|
|||
for l:trigger in l:triggers |
|||
let l:last_char = l:trigger[len(l:trigger) -1] |
|||
if !has_key(b:asyncomplete_triggers, l:last_char) |
|||
let b:asyncomplete_triggers[l:last_char] = {} |
|||
endif |
|||
if !has_key(b:asyncomplete_triggers[l:last_char], l:source_name) |
|||
let b:asyncomplete_triggers[l:last_char][l:source_name] = [] |
|||
endif |
|||
call add(b:asyncomplete_triggers[l:last_char][l:source_name], l:trigger) |
|||
endfor |
|||
endfor |
|||
call asyncomplete#log('core', 'trigger characters for buffer', bufnr('%'), b:asyncomplete_triggers) |
|||
endfunction |
|||
|
|||
function! s:enable_popup_skip() abort |
|||
let s:skip_popup = 1 |
|||
endfunction |
|||
|
|||
function! s:disable_popup_skip() abort |
|||
let s:skip_popup = 0 |
|||
endfunction |
|||
|
|||
function! s:should_skip_popup() abort |
|||
if get(s:, 'skip_popup', 0) |
|||
return 1 |
|||
else |
|||
return 0 |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:should_skip() abort |
|||
if mode() isnot# 'i' || !get(b:, 'asyncomplete_enable', 0) |
|||
return 1 |
|||
else |
|||
return 0 |
|||
endif |
|||
endfunction |
|||
|
|||
function! asyncomplete#close_popup() abort |
|||
call s:enable_popup_skip() |
|||
return pumvisible() ? "\<C-y>" : '' |
|||
endfunction |
|||
|
|||
function! asyncomplete#cancel_popup() abort |
|||
call s:enable_popup_skip() |
|||
return pumvisible() ? "\<C-e>" : '' |
|||
endfunction |
|||
|
|||
function! s:on_change() abort |
|||
if s:should_skip() | return | endif |
|||
|
|||
if !g:asyncomplete_auto_popup |
|||
return |
|||
endif |
|||
|
|||
let l:ctx = asyncomplete#context() |
|||
let l:startcol = l:ctx['col'] |
|||
let l:last_char = l:ctx['typed'][l:startcol - 2] " col is 1-indexed, but str 0-indexed |
|||
|
|||
let l:sources_to_notify = {} |
|||
|
|||
" match sources based on the last character if it is a trigger character |
|||
if has_key(b:asyncomplete_triggers, l:last_char) |
|||
" TODO: also check for multiple chars instead of just last chars for |
|||
" languages such as cpp which uses -> and :: |
|||
for l:source_name in keys(b:asyncomplete_triggers[l:last_char]) |
|||
if !has_key(s:matches, l:source_name) || s:matches[l:source_name]['ctx']['lnum'] != l:ctx['lnum'] || s:matches[l:source_name]['startcol'] != l:startcol |
|||
let l:sources_to_notify[l:source_name] = 1 |
|||
let s:matches[l:source_name] = { 'startcol': l:startcol, 'status': 'idle', 'items': [], 'refresh': 0, 'ctx': l:ctx } |
|||
endif |
|||
endfor |
|||
endif |
|||
|
|||
" loop left and find the start of the word and set it as the startcol for the source instead of refresh_pattern |
|||
let l:refresh_pattern = '\(\k\+$\)' |
|||
let [l:_, l:startidx, l:endidx] = asyncomplete#utils#matchstrpos(l:ctx['typed'], l:refresh_pattern) |
|||
let l:startcol = l:startidx + 1 |
|||
|
|||
if l:startidx > -1 |
|||
if s:should_skip_popup() | return | endif |
|||
for l:source_name in b:asyncomplete_active_sources |
|||
if !has_key(l:sources_to_notify, l:source_name) |
|||
if has_key(s:matches, l:source_name) && s:matches[l:source_name]['ctx']['lnum'] ==# l:ctx['lnum'] && s:matches[l:source_name]['startcol'] ==# l:startcol |
|||
continue |
|||
endif |
|||
let l:sources_to_notify[l:source_name] = 1 |
|||
let s:matches[l:source_name] = { 'startcol': l:startcol, 'status': 'idle', 'items': [], 'refresh': 0, 'ctx': l:ctx } |
|||
endif |
|||
endfor |
|||
else |
|||
call s:disable_popup_skip() |
|||
endif |
|||
|
|||
call s:trigger(l:ctx) |
|||
call s:update_pum() |
|||
endfunction |
|||
|
|||
function! s:trigger(ctx) abort |
|||
" send cancellation request if supported |
|||
for [l:source_name, l:matches] in items(s:matches) |
|||
call asyncomplete#log('core', 's:trigger', l:matches) |
|||
if l:matches['refresh'] || l:matches['status'] == 'idle' || l:matches['status'] == 'failure' |
|||
let l:matches['status'] = 'pending' |
|||
try |
|||
" TODO: check for min chars |
|||
call asyncomplete#log('core', 's:trigger.completor()', l:source_name, s:matches[l:source_name], a:ctx) |
|||
call s:sources[l:source_name].completor(s:sources[l:source_name], a:ctx) |
|||
catch |
|||
let l:matches['status'] = 'failure' |
|||
call asyncomplete#log('core', 's:trigger', 'error', v:exception) |
|||
continue |
|||
endtry |
|||
endif |
|||
endfor |
|||
endfunction |
|||
|
|||
function! asyncomplete#complete(name, ctx, startcol, items, ...) abort |
|||
let l:refresh = a:0 > 0 ? a:1 : 0 |
|||
let l:ctx = asyncomplete#context() |
|||
if !has_key(s:matches, a:name) || l:ctx['lnum'] != a:ctx['lnum'] " TODO: handle more context changes |
|||
call asyncomplete#log('core', 'asyncomplete#log', 'ignoring due to context chnages', a:name, a:ctx, a:startcol, l:refresh, a:items) |
|||
call s:update_pum() |
|||
return |
|||
endif |
|||
|
|||
call asyncomplete#log('asyncomplete#complete', a:name, a:ctx, a:startcol, l:refresh, a:items) |
|||
|
|||
let l:matches = s:matches[a:name] |
|||
let l:matches['items'] = s:normalize_items(a:items) |
|||
let l:matches['refresh'] = l:refresh |
|||
let l:matches['startcol'] = a:startcol |
|||
let l:matches['status'] = 'success' |
|||
|
|||
call s:update_pum() |
|||
endfunction |
|||
|
|||
function! s:normalize_items(items) abort |
|||
if len(a:items) > 0 && type(a:items[0]) ==# type('') |
|||
let l:items = [] |
|||
for l:item in a:items |
|||
let l:items += [{'word': l:item }] |
|||
endfor |
|||
return l:items |
|||
else |
|||
return a:items |
|||
endif |
|||
endfunction |
|||
|
|||
function! asyncomplete#force_refresh() abort |
|||
return asyncomplete#menu_selected() ? "\<c-y>\<c-r>=asyncomplete#_force_refresh()\<CR>" : "\<c-r>=asyncomplete#_force_refresh()\<CR>" |
|||
endfunction |
|||
|
|||
function! asyncomplete#_force_refresh() abort |
|||
call s:disable_popup_skip() |
|||
if s:should_skip() | return | endif |
|||
|
|||
let l:ctx = asyncomplete#context() |
|||
let l:startcol = l:ctx['col'] |
|||
let l:last_char = l:ctx['typed'][l:startcol - 2] |
|||
|
|||
" loop left and find the start of the word or trigger chars and set it as the startcol for the source instead of refresh_pattern |
|||
let l:refresh_pattern = '\(\k\+$\)' |
|||
let [l:_, l:startidx, l:endidx] = asyncomplete#utils#matchstrpos(l:ctx['typed'], l:refresh_pattern) |
|||
" When no word here, startcol is current col |
|||
let l:startcol = l:startidx == -1 ? col('.') : l:startidx + 1 |
|||
|
|||
let s:matches = {} |
|||
|
|||
for l:source_name in b:asyncomplete_active_sources |
|||
let s:matches[l:source_name] = { 'startcol': l:startcol, 'status': 'idle', 'items': [], 'refresh': 0, 'ctx': l:ctx } |
|||
endfor |
|||
|
|||
call s:trigger(l:ctx) |
|||
call s:update_pum() |
|||
return '' |
|||
endfunction |
|||
|
|||
function! s:update_pum() abort |
|||
if exists('s:update_pum_timer') |
|||
call timer_stop(s:update_pum_timer) |
|||
unlet s:update_pum_timer |
|||
endif |
|||
call asyncomplete#log('core', 's:update_pum') |
|||
let s:update_pum_timer = timer_start(g:asyncomplete_popup_delay, function('s:recompute_pum')) |
|||
endfunction |
|||
|
|||
function! s:recompute_pum(...) abort |
|||
if s:should_skip() | return | endif |
|||
if s:should_skip_popup() | return | endif |
|||
|
|||
" TODO: add support for remote recomputation of complete items, |
|||
" Ex: heavy computation such as fuzzy search can happen in a python thread |
|||
|
|||
call asyncomplete#log('core', 's:recompute_pum') |
|||
|
|||
if asyncomplete#menu_selected() |
|||
call asyncomplete#log('core', 's:recomputed_pum', 'ignorning refresh pum due to menu selection') |
|||
return |
|||
endif |
|||
|
|||
let l:ctx = asyncomplete#context() |
|||
|
|||
let l:startcols = [] |
|||
let l:matches_to_filter = {} |
|||
|
|||
for [l:source_name, l:match] in items(s:matches) |
|||
let l:startcol = l:match['startcol'] |
|||
let l:startcols += [l:startcol] |
|||
let l:curitems = l:match['items'] |
|||
|
|||
if l:startcol > l:ctx['col'] |
|||
call asyncomplete#log('core', 's:recompute_pum', 'ignoring due to wrong start col', l:startcol, l:ctx['col']) |
|||
continue |
|||
else |
|||
let l:matches_to_filter[l:source_name] = l:match |
|||
endif |
|||
endfor |
|||
|
|||
let l:startcol = min(l:startcols) |
|||
let l:base = l:ctx['typed'][l:startcol - 1:] " col is 1-indexed, but str 0-indexed |
|||
|
|||
let l:filter_ctx = extend({ |
|||
\ 'base': l:base, |
|||
\ 'startcol': l:startcol, |
|||
\ }, l:ctx) |
|||
|
|||
let l:mode = s:has_complete_info ? complete_info(['mode'])['mode'] : 'unknown' |
|||
if l:mode ==# '' || l:mode ==# 'eval' || l:mode ==# 'unknown' |
|||
let l:Preprocessor = empty(g:asyncomplete_preprocessor) ? function('s:default_preprocessor') : g:asyncomplete_preprocessor[0] |
|||
call l:Preprocessor(l:filter_ctx, l:matches_to_filter) |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:default_preprocessor(options, matches) abort |
|||
let l:items = [] |
|||
let l:startcols = [] |
|||
for [l:source_name, l:matches] in items(a:matches) |
|||
let l:startcol = l:matches['startcol'] |
|||
let l:base = a:options['typed'][l:startcol - 1:] |
|||
for l:item in l:matches['items'] |
|||
if stridx(l:item['word'], l:base) == 0 |
|||
let l:startcols += [l:startcol] |
|||
call add(l:items, l:item) |
|||
endif |
|||
endfor |
|||
endfor |
|||
|
|||
let a:options['startcol'] = min(l:startcols) |
|||
|
|||
call asyncomplete#preprocess_complete(a:options, l:items) |
|||
endfunction |
|||
|
|||
function! asyncomplete#preprocess_complete(ctx, items) |
|||
" TODO: handle cases where this is called asynchronsouly. Currently not supported |
|||
if s:should_skip() | return | endif |
|||
if s:should_skip_popup() | return | endif |
|||
|
|||
call asyncomplete#log('core', 'asyncomplete#preprocess_complete') |
|||
|
|||
if asyncomplete#menu_selected() |
|||
call asyncomplete#log('core', 'asyncomplete#preprocess_complete', 'ignorning pum update due to menu selection') |
|||
return |
|||
endif |
|||
|
|||
if (g:asyncomplete_auto_completeopt == 1) |
|||
setl completeopt=menuone,noinsert,noselect |
|||
endif |
|||
|
|||
call asyncomplete#log('core', 'asyncomplete#preprocess_complete calling complete()', a:ctx['startcol'], a:items) |
|||
call complete(a:ctx['startcol'], a:items) |
|||
endfunction |
|||
|
|||
function! asyncomplete#menu_selected() abort |
|||
" when the popup menu is visible, v:completed_item will be the |
|||
" current_selected item |
|||
" if v:completed_item is empty, no item is selected |
|||
return pumvisible() && !empty(v:completed_item) |
|||
endfunction |
|||
|
|||
function! s:notify_event_to_source(name, event, ctx) abort |
|||
try |
|||
if has_key(s:sources, a:name) |
|||
call s:sources[a:name].on_event(s:sources[a:name], a:ctx, a:event) |
|||
endif |
|||
catch |
|||
call asyncomplete#log('core', 's:notify_event_to_source', 'error', v:exception) |
|||
return |
|||
endtry |
|||
endfunction |
|||
@ -0,0 +1,21 @@ |
|||
" Find a nearest to a `path` parent directory `directoryname` by traversing the |
|||
" filesystem upwards |
|||
function! asyncomplete#utils#find_nearest_parent_directory(path, directoryname) abort |
|||
let l:relative_path = finddir(a:directoryname, a:path . ';') |
|||
|
|||
if !empty(l:relative_path) |
|||
return fnamemodify(l:relative_path, ':p') |
|||
else |
|||
return '' |
|||
endif |
|||
endfunction |
|||
|
|||
if exists('*matchstrpos') |
|||
function! asyncomplete#utils#matchstrpos(expr, pattern) abort |
|||
return matchstrpos(a:expr, a:pattern) |
|||
endfunction |
|||
else |
|||
function! asyncomplete#utils#matchstrpos(expr, pattern) abort |
|||
return [matchstr(a:expr, a:pattern), match(a:expr, a:pattern), matchend(a:expr, a:pattern)] |
|||
endfunction |
|||
endif |
|||
@ -0,0 +1,59 @@ |
|||
let s:callbacks = [] |
|||
|
|||
function! asyncomplete#utils#_on_change#textchangedp#init() abort |
|||
if exists('##TextChangedP') |
|||
call s:setup_if_required() |
|||
return { |
|||
\ 'name': 'TextChangedP', |
|||
\ 'register': function('s:register'), |
|||
\ 'unregister': function('s:unregister'), |
|||
\ } |
|||
else |
|||
return { 'name': 'TextChangedP', 'error': 'Requires vim with TextChangedP support' } |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:setup_if_required() abort |
|||
augroup asyncomplete_utils_on_change_text_changed_p |
|||
autocmd! |
|||
autocmd InsertEnter * call s:on_insert_enter() |
|||
autocmd InsertLeave * call s:on_insert_leave() |
|||
autocmd TextChangedI * call s:on_text_changed_i() |
|||
autocmd TextChangedP * call s:on_text_changed_p() |
|||
augroup END |
|||
endfunction |
|||
|
|||
function! s:register(cb) abort |
|||
call add(s:callbacks , a:cb) |
|||
endfunction |
|||
|
|||
function! s:unregister(obj, cb) abort |
|||
" TODO: remove from s:callbacks |
|||
endfunction |
|||
|
|||
function! s:on_insert_enter() abort |
|||
let s:previous_position = getcurpos() |
|||
endfunction |
|||
|
|||
function! s:on_insert_leave() abort |
|||
unlet s:previous_position |
|||
endfunction |
|||
|
|||
function! s:on_text_changed_i() abort |
|||
call s:maybe_notify_on_change() |
|||
endfunction |
|||
|
|||
function! s:on_text_changed_p() abort |
|||
call s:maybe_notify_on_change() |
|||
endfunction |
|||
|
|||
function! s:maybe_notify_on_change() abort |
|||
" enter to new line or backspace to previous line shouldn't cause change trigger |
|||
let l:previous_position = s:previous_position |
|||
let s:previous_position = getcurpos() |
|||
if l:previous_position[1] ==# getcurpos()[1] |
|||
for l:Cb in s:callbacks |
|||
call l:Cb() |
|||
endfor |
|||
endif |
|||
endfunction |
|||
@ -0,0 +1,83 @@ |
|||
let s:callbacks = [] |
|||
|
|||
let s:change_timer = -1 |
|||
let s:last_tick = [] |
|||
|
|||
function! asyncomplete#utils#_on_change#timer#init() abort |
|||
call s:setup_if_required() |
|||
return { |
|||
\ 'name': 'timer', |
|||
\ 'register': function('s:register'), |
|||
\ 'unregister': function('s:unregister'), |
|||
\ } |
|||
endfunction |
|||
|
|||
function! s:setup_if_required() abort |
|||
augroup asyncomplete_utils_on_change_timer |
|||
autocmd! |
|||
autocmd InsertEnter * call s:on_insert_enter() |
|||
autocmd InsertLeave * call s:on_insert_leave() |
|||
autocmd TextChangedI * call s:on_text_changed_i() |
|||
augroup END |
|||
endfunction |
|||
|
|||
function! s:register(cb) abort |
|||
call add(s:callbacks , a:cb) |
|||
endfunction |
|||
|
|||
function! s:unregister(obj, cb) abort |
|||
" TODO: remove from s:callbacks |
|||
endfunction |
|||
|
|||
function! s:on_insert_enter() abort |
|||
let s:previous_position = getcurpos() |
|||
call s:change_tick_start() |
|||
endfunction |
|||
|
|||
function! s:on_insert_leave() abort |
|||
unlet s:previous_position |
|||
call s:change_tick_stop() |
|||
endfunction |
|||
|
|||
function! s:on_text_changed_i() abort |
|||
call s:check_changes() |
|||
endfunction |
|||
|
|||
function! s:change_tick_start() abort |
|||
if !exists('s:change_timer') |
|||
let s:last_tick = s:change_tick() |
|||
" changes every 30ms, which is 0.03s, it should be fast enough |
|||
let s:change_timer = timer_start(30, function('s:check_changes'), { 'repeat': -1 }) |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:change_tick_stop() abort |
|||
if exists('s:change_timer') |
|||
call timer_stop(s:change_timer) |
|||
unlet s:change_timer |
|||
let s:last_tick = [] |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:check_changes(...) abort |
|||
let l:tick = s:change_tick() |
|||
if l:tick != s:last_tick |
|||
let s:last_tick = l:tick |
|||
call s:maybe_notify_on_change() |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:maybe_notify_on_change() abort |
|||
" enter to new line or backspace to previous line shouldn't cause change trigger |
|||
let l:previous_position = s:previous_position |
|||
let s:previous_position = getcurpos() |
|||
if l:previous_position[1] ==# getcurpos()[1] |
|||
for l:Cb in s:callbacks |
|||
call l:Cb() |
|||
endfor |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:change_tick() abort |
|||
return [b:changedtick, getcurpos()] |
|||
endfunction |
|||
@ -0,0 +1,155 @@ |
|||
*asyncomplete.vim.txt* Async autocompletion for Vim 8 and Neovim. |
|||
*asyncomplete* |
|||
|
|||
|
|||
=============================================================================== |
|||
CONTENTS *asyncomplete-contents* |
|||
|
|||
1. Introduction |asyncomplete-introduction| |
|||
2. Options |asyncomplete-options| |
|||
3. Functions |asyncomplete-functions| |
|||
4. Global vim configuration |asyncomplete-global-config| |
|||
5. Known Issues |asyncomplete-known-issues| |
|||
|
|||
=============================================================================== |
|||
1. Introduction *asyncomplete-introduction* |
|||
|
|||
Async autocompletion for Vim 8 and Neovim with |timers|. |
|||
|
|||
This is inspired by https://github.com/roxma/nvim-complete-manager but written |
|||
in pure Vim Script. |
|||
|
|||
=============================================================================== |
|||
2. Options *asyncomplete-options* |
|||
|
|||
|
|||
g:asyncomplete_auto_popup *g:asyncomplete_auto_popup* |
|||
|
|||
Type: |Number| |
|||
Default: `1` |
|||
|
|||
Automatically show the autocomplete popup menu as you start typing. |
|||
|
|||
g:asyncomplete_log_file *g:asyncomplete_log_file* |
|||
|
|||
Type: |String| |
|||
Default: null |
|||
|
|||
Path to log file. |
|||
|
|||
g:asyncomplete_popup_delay *g:asyncomplete_popup_delay* |
|||
|
|||
Type: |Number| |
|||
Default: 30 |
|||
|
|||
Milliseconds to wait before opening the popup menu |
|||
|
|||
g:asyncomplete_auto_completeopt *g:asyncomplete_auto_completeopt* |
|||
|
|||
Type: |Number| |
|||
Default: 1 |
|||
|
|||
Set default `completeopt` options. These are `menuone,noinsert,noselect`. |
|||
This effectively overwrites what ever the user has in their config file. |
|||
|
|||
Set to 0 to disable. |
|||
|
|||
g:asyncomplete_preprocessor *g:asyncomplete_preprocessor* |
|||
|
|||
Type: |Array| for zero or one |Function| |
|||
Default: [] |
|||
|
|||
Set a function to allow custom filtering or sorting. |
|||
Below example implements removing duplicates. |
|||
|
|||
function! s:my_asyncomplete_preprocessor(options, matches) abort |
|||
let l:visited = {} |
|||
let l:items = [] |
|||
for [l:source_name, l:matches] in items(a:matches) |
|||
for l:item in l:matches['items'] |
|||
if stridx(l:item['word'], a:options['base']) == 0 |
|||
if !has_key(l:visited, l:item['word']) |
|||
call add(l:items, l:item) |
|||
let l:visited[l:item['word']] = 1 |
|||
endif |
|||
endif |
|||
endfor |
|||
endfor |
|||
|
|||
call asyncomplete#preprocess_complete(a:options, l:items) |
|||
endfunction |
|||
|
|||
let g:asyncomplete_preprocessor = [function('s:my_asyncomplete_preprocessor')] |
|||
|
|||
Note: |
|||
asyncomplete#preprocess_complete() must be called synchronously. |
|||
Plans to support async preprocessing will be supported in the future. |
|||
|
|||
context and matches in arguments in preprecessor function should be treated |
|||
as immutable. |
|||
=============================================================================== |
|||
3. Functions *asyncomplete-functions* |
|||
|
|||
asyncomplete#close_popup() *asyncomplete#close_popup()* |
|||
|
|||
Insert selected candidate and close popup menu. |
|||
Following example prevents popup menu from re-opening after insertion. |
|||
> |
|||
inoremap <expr> <C-y> pumvisible() ? asyncomplete#close_popup() : "\<C-y>" |
|||
< |
|||
asyncomplete#cancel_popup() *asyncomplete#cancel_popup()* |
|||
|
|||
Cancel completion and close popup menu. |
|||
Following example prevents popup menu from re-opening after cancellation. |
|||
> |
|||
inoremap <expr> <C-e> pumvisible() ? asyncomplete#cancel_popup() : "\<C-e>" |
|||
< |
|||
asyncomplete#get_source_info({source-name}) *asyncomplete#get_source_info()* |
|||
|
|||
Get the source configuration info as dict. |
|||
Below example implements a priority sort function. |
|||
> |
|||
function! s:sort_by_priority_preprocessor(options, matches) abort |
|||
let l:items = [] |
|||
for [l:source_name, l:matches] in items(a:matches) |
|||
for l:item in l:matches['items'] |
|||
if stridx(l:item['word'], a:options['base']) == 0 |
|||
let l:item['priority'] = |
|||
\ get(asyncomplete#get_source_info(l:source_name),'priority',0) |
|||
call add(l:items, l:item) |
|||
endif |
|||
endfor |
|||
endfor |
|||
|
|||
let l:items = sort(l:items, {a, b -> b['priority'] - a['priority']}) |
|||
|
|||
call asyncomplete#preprocess_complete(a:options, l:items) |
|||
endfunction |
|||
|
|||
let g:asyncomplete_preprocessor = [function('s:sort_by_priority_preprocessor')] |
|||
< |
|||
asyncomplete#get_source_names() *asyncomplete#get_source_names()* |
|||
|
|||
Get the registered source names list. |
|||
|
|||
=============================================================================== |
|||
4. Global vim configuration *asyncomplete-global-config* |
|||
|
|||
If you notice messages like 'Pattern not found' or 'Match 1 of <N>' printed in |
|||
red colour in vim command line and in `:messages` history and you are annoyed |
|||
with them, try setting `shortmess` vim option in your `.vimrc` like so: |
|||
> |
|||
set shortmess+=c |
|||
< |
|||
See `:help shortmess` for details and description. |
|||
|
|||
=============================================================================== |
|||
5. Known Issues *asyncomplete-known-issues* |
|||
|
|||
Builtin complete such as omni func, file func flickers and closes. |
|||
You need vim with patch v8.1.1068. |
|||
https://github.com/vim/vim/commit/fd133323d4e1cc9c0e61c0ce357df4d36ea148e3 |
|||
|
|||
=============================================================================== |
|||
|
|||
vim:tw=78:ts=4:sts=4:sw=4:ft=help:norl: |
|||
@ -0,0 +1,23 @@ |
|||
if exists('g:asyncomplete_loaded') |
|||
finish |
|||
endif |
|||
let g:asyncomplete_loaded = 1 |
|||
|
|||
if get(g:, 'asyncomplete_enable_for_all', 1) |
|||
augroup asyncomplete_enable |
|||
au! |
|||
au BufEnter * if exists('b:asyncomplete_enable') == 0 | call asyncomplete#enable_for_buffer() | endif |
|||
augroup END |
|||
endif |
|||
|
|||
let g:asyncomplete_manager = get(g:, 'asyncomplete_manager', 'asyncomplete#managers#vim#init') |
|||
let g:asyncomplete_change_manager = get(g:, 'asyncomplete_change_manager', ['asyncomplete#utils#_on_change#textchangedp#init', 'asyncomplete#utils#_on_change#timer#init']) |
|||
let g:asyncomplete_triggers = get(g:, 'asyncomplete_triggers', {'*': ['.', '>', ':'] }) |
|||
|
|||
let g:asyncomplete_auto_completeopt = get(g:, 'asyncomplete_auto_completeopt', 1) |
|||
let g:asyncomplete_auto_popup = get(g:, 'asyncomplete_auto_popup', 1) |
|||
let g:asyncomplete_popup_delay = get(g:, 'asyncomplete_popup_delay', 30) |
|||
let g:asyncomplete_log_file = get(g:, 'asyncomplete_log_file', '') |
|||
let g:asyncomplete_preprocessor = get(g:, 'asyncomplete_preprocessor', []) |
|||
|
|||
inoremap <silent> <expr> <Plug>(asyncomplete_force_refresh) asyncomplete#force_refresh() |
|||
@ -0,0 +1 @@ |
|||
/doc/tags |
|||
@ -0,0 +1,7 @@ |
|||
--- |
|||
sudo: required |
|||
services: |
|||
- docker |
|||
language: generic |
|||
script: | |
|||
cd test && ./run-tests |
|||
@ -0,0 +1,10 @@ |
|||
cmdargs: |
|||
# Checking more strictly |
|||
severity: style_problem |
|||
|
|||
policies: |
|||
# Disable a violation |
|||
ProhibitUnnecessaryDoubleQuote: |
|||
enabled: false |
|||
ProhibitImplicitScopeVariable: |
|||
enabled: false |
|||
@ -0,0 +1,27 @@ |
|||
<!-- |
|||
|
|||
Hi, and thanks for reporting an issue with rust.vim. |
|||
|
|||
Details about your environment will help us assist you. |
|||
|
|||
Please edit this template! |
|||
|
|||
--> |
|||
|
|||
* rust.vim version: <!-- Describe if you use a Vim plugin manager, and you |
|||
can use it to tell which version of rust.vim you are running. --> |
|||
|
|||
Steps to reproduce: |
|||
|
|||
<!-- It's best to try to reproduce the issue with the master version of |
|||
rust.vim. The issue may already be fixed! --> |
|||
_?_ |
|||
|
|||
Expected vs. actual behavior: |
|||
|
|||
_?_ |
|||
|
|||
Paste debugging info from the Rust Vim plugin via _one_ of the following |
|||
commands: `:RustInfo`, `:RustInfoToClipboard`, or `:RustInfoToFile <filename>`. |
|||
|
|||
_?_ |
|||
@ -0,0 +1,201 @@ |
|||
Apache License |
|||
Version 2.0, January 2004 |
|||
http://www.apache.org/licenses/ |
|||
|
|||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|||
|
|||
1. Definitions. |
|||
|
|||
"License" shall mean the terms and conditions for use, reproduction, |
|||
and distribution as defined by Sections 1 through 9 of this document. |
|||
|
|||
"Licensor" shall mean the copyright owner or entity authorized by |
|||
the copyright owner that is granting the License. |
|||
|
|||
"Legal Entity" shall mean the union of the acting entity and all |
|||
other entities that control, are controlled by, or are under common |
|||
control with that entity. For the purposes of this definition, |
|||
"control" means (i) the power, direct or indirect, to cause the |
|||
direction or management of such entity, whether by contract or |
|||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|||
outstanding shares, or (iii) beneficial ownership of such entity. |
|||
|
|||
"You" (or "Your") shall mean an individual or Legal Entity |
|||
exercising permissions granted by this License. |
|||
|
|||
"Source" form shall mean the preferred form for making modifications, |
|||
including but not limited to software source code, documentation |
|||
source, and configuration files. |
|||
|
|||
"Object" form shall mean any form resulting from mechanical |
|||
transformation or translation of a Source form, including but |
|||
not limited to compiled object code, generated documentation, |
|||
and conversions to other media types. |
|||
|
|||
"Work" shall mean the work of authorship, whether in Source or |
|||
Object form, made available under the License, as indicated by a |
|||
copyright notice that is included in or attached to the work |
|||
(an example is provided in the Appendix below). |
|||
|
|||
"Derivative Works" shall mean any work, whether in Source or Object |
|||
form, that is based on (or derived from) the Work and for which the |
|||
editorial revisions, annotations, elaborations, or other modifications |
|||
represent, as a whole, an original work of authorship. For the purposes |
|||
of this License, Derivative Works shall not include works that remain |
|||
separable from, or merely link (or bind by name) to the interfaces of, |
|||
the Work and Derivative Works thereof. |
|||
|
|||
"Contribution" shall mean any work of authorship, including |
|||
the original version of the Work and any modifications or additions |
|||
to that Work or Derivative Works thereof, that is intentionally |
|||
submitted to Licensor for inclusion in the Work by the copyright owner |
|||
or by an individual or Legal Entity authorized to submit on behalf of |
|||
the copyright owner. For the purposes of this definition, "submitted" |
|||
means any form of electronic, verbal, or written communication sent |
|||
to the Licensor or its representatives, including but not limited to |
|||
communication on electronic mailing lists, source code control systems, |
|||
and issue tracking systems that are managed by, or on behalf of, the |
|||
Licensor for the purpose of discussing and improving the Work, but |
|||
excluding communication that is conspicuously marked or otherwise |
|||
designated in writing by the copyright owner as "Not a Contribution." |
|||
|
|||
"Contributor" shall mean Licensor and any individual or Legal Entity |
|||
on behalf of whom a Contribution has been received by Licensor and |
|||
subsequently incorporated within the Work. |
|||
|
|||
2. Grant of Copyright License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
copyright license to reproduce, prepare Derivative Works of, |
|||
publicly display, publicly perform, sublicense, and distribute the |
|||
Work and such Derivative Works in Source or Object form. |
|||
|
|||
3. Grant of Patent License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
(except as stated in this section) patent license to make, have made, |
|||
use, offer to sell, sell, import, and otherwise transfer the Work, |
|||
where such license applies only to those patent claims licensable |
|||
by such Contributor that are necessarily infringed by their |
|||
Contribution(s) alone or by combination of their Contribution(s) |
|||
with the Work to which such Contribution(s) was submitted. If You |
|||
institute patent litigation against any entity (including a |
|||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
|||
or a Contribution incorporated within the Work constitutes direct |
|||
or contributory patent infringement, then any patent licenses |
|||
granted to You under this License for that Work shall terminate |
|||
as of the date such litigation is filed. |
|||
|
|||
4. Redistribution. You may reproduce and distribute copies of the |
|||
Work or Derivative Works thereof in any medium, with or without |
|||
modifications, and in Source or Object form, provided that You |
|||
meet the following conditions: |
|||
|
|||
(a) You must give any other recipients of the Work or |
|||
Derivative Works a copy of this License; and |
|||
|
|||
(b) You must cause any modified files to carry prominent notices |
|||
stating that You changed the files; and |
|||
|
|||
(c) You must retain, in the Source form of any Derivative Works |
|||
that You distribute, all copyright, patent, trademark, and |
|||
attribution notices from the Source form of the Work, |
|||
excluding those notices that do not pertain to any part of |
|||
the Derivative Works; and |
|||
|
|||
(d) If the Work includes a "NOTICE" text file as part of its |
|||
distribution, then any Derivative Works that You distribute must |
|||
include a readable copy of the attribution notices contained |
|||
within such NOTICE file, excluding those notices that do not |
|||
pertain to any part of the Derivative Works, in at least one |
|||
of the following places: within a NOTICE text file distributed |
|||
as part of the Derivative Works; within the Source form or |
|||
documentation, if provided along with the Derivative Works; or, |
|||
within a display generated by the Derivative Works, if and |
|||
wherever such third-party notices normally appear. The contents |
|||
of the NOTICE file are for informational purposes only and |
|||
do not modify the License. You may add Your own attribution |
|||
notices within Derivative Works that You distribute, alongside |
|||
or as an addendum to the NOTICE text from the Work, provided |
|||
that such additional attribution notices cannot be construed |
|||
as modifying the License. |
|||
|
|||
You may add Your own copyright statement to Your modifications and |
|||
may provide additional or different license terms and conditions |
|||
for use, reproduction, or distribution of Your modifications, or |
|||
for any such Derivative Works as a whole, provided Your use, |
|||
reproduction, and distribution of the Work otherwise complies with |
|||
the conditions stated in this License. |
|||
|
|||
5. Submission of Contributions. Unless You explicitly state otherwise, |
|||
any Contribution intentionally submitted for inclusion in the Work |
|||
by You to the Licensor shall be under the terms and conditions of |
|||
this License, without any additional terms or conditions. |
|||
Notwithstanding the above, nothing herein shall supersede or modify |
|||
the terms of any separate license agreement you may have executed |
|||
with Licensor regarding such Contributions. |
|||
|
|||
6. Trademarks. This License does not grant permission to use the trade |
|||
names, trademarks, service marks, or product names of the Licensor, |
|||
except as required for reasonable and customary use in describing the |
|||
origin of the Work and reproducing the content of the NOTICE file. |
|||
|
|||
7. Disclaimer of Warranty. Unless required by applicable law or |
|||
agreed to in writing, Licensor provides the Work (and each |
|||
Contributor provides its Contributions) on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
implied, including, without limitation, any warranties or conditions |
|||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
|||
PARTICULAR PURPOSE. You are solely responsible for determining the |
|||
appropriateness of using or redistributing the Work and assume any |
|||
risks associated with Your exercise of permissions under this License. |
|||
|
|||
8. Limitation of Liability. In no event and under no legal theory, |
|||
whether in tort (including negligence), contract, or otherwise, |
|||
unless required by applicable law (such as deliberate and grossly |
|||
negligent acts) or agreed to in writing, shall any Contributor be |
|||
liable to You for damages, including any direct, indirect, special, |
|||
incidental, or consequential damages of any character arising as a |
|||
result of this License or out of the use or inability to use the |
|||
Work (including but not limited to damages for loss of goodwill, |
|||
work stoppage, computer failure or malfunction, or any and all |
|||
other commercial damages or losses), even if such Contributor |
|||
has been advised of the possibility of such damages. |
|||
|
|||
9. Accepting Warranty or Additional Liability. While redistributing |
|||
the Work or Derivative Works thereof, You may choose to offer, |
|||
and charge a fee for, acceptance of support, warranty, indemnity, |
|||
or other liability obligations and/or rights consistent with this |
|||
License. However, in accepting such obligations, You may act only |
|||
on Your own behalf and on Your sole responsibility, not on behalf |
|||
of any other Contributor, and only if You agree to indemnify, |
|||
defend, and hold each Contributor harmless for any liability |
|||
incurred by, or claims asserted against, such Contributor by reason |
|||
of your accepting any such warranty or additional liability. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
APPENDIX: How to apply the Apache License to your work. |
|||
|
|||
To apply the Apache License to your work, attach the following |
|||
boilerplate notice, with the fields enclosed by brackets "[]" |
|||
replaced with your own identifying information. (Don't include |
|||
the brackets!) The text should be enclosed in the appropriate |
|||
comment syntax for the file format. We also recommend that a |
|||
file or class name and description of purpose be included on the |
|||
same "printed page" as the copyright notice for easier |
|||
identification within third-party archives. |
|||
|
|||
Copyright [yyyy] [name of copyright owner] |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
@ -0,0 +1,25 @@ |
|||
Copyright (c) 2015 The Rust Project Developers |
|||
|
|||
Permission is hereby granted, free of charge, to any |
|||
person obtaining a copy of this software and associated |
|||
documentation files (the "Software"), to deal in the |
|||
Software without restriction, including without |
|||
limitation the rights to use, copy, modify, merge, |
|||
publish, distribute, sublicense, and/or sell copies of |
|||
the Software, and to permit persons to whom the Software |
|||
is furnished to do so, subject to the following |
|||
conditions: |
|||
|
|||
The above copyright notice and this permission notice |
|||
shall be included in all copies or substantial portions |
|||
of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF |
|||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
|||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
|||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
|||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR |
|||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|||
DEALINGS IN THE SOFTWARE. |
|||
@ -0,0 +1,109 @@ |
|||
# rust.vim |
|||
|
|||
## Description |
|||
|
|||
This is a Vim plugin that provides [Rust][r] file detection, syntax highlighting, formatting, |
|||
[Syntastic][syn] integration, and more. It requires Vim 8 or higher for full functionality. |
|||
Some things may not work on earlier versions. |
|||
|
|||
## Installation |
|||
|
|||
Use one of the following package managers: |
|||
|
|||
* [Vim8 packages][vim8pack]: |
|||
* `git clone https://github.com/rust-lang/rust.vim ~/.vim/pack/plugins/start/rust.vim` |
|||
* [Vundle][v]: |
|||
* Add `Plugin 'rust-lang/rust.vim'` to `~/.vimrc` |
|||
* `:PluginInstall` or `$ vim +PluginInstall +qall` |
|||
* *Note:* Vundle will not automatically detect Rust files properly if `filetype |
|||
on` is executed before Vundle. Please check the [quickstart][vqs] for more |
|||
details. Errors such as `Not an editor command: RustFmt` may occur if Vundle |
|||
is misconfigured with this plugin. |
|||
* [Pathogen][p]: |
|||
* `git clone --depth=1 https://github.com/rust-lang/rust.vim.git ~/.vim/bundle/rust.vim` |
|||
* [vim-plug][vp]: |
|||
* Add `Plug 'rust-lang/rust.vim'` to `~/.vimrc` |
|||
* `:PlugInstall` or `$ vim +PlugInstall +qall` |
|||
* [dein.vim][d]: |
|||
* Add `call dein#add('rust-lang/rust.vim')` to `~/.vimrc` |
|||
* `:call dein#install()` |
|||
* [NeoBundle][nb]: |
|||
* Add `NeoBundle 'rust-lang/rust.vim'` to `~/.vimrc` |
|||
* Re-open vim or execute `:source ~/.vimrc` |
|||
|
|||
## Features |
|||
|
|||
### Error checking with [Syntastic][syn] |
|||
|
|||
`rust.vim` automatically registers `cargo` as a syntax checker with |
|||
[Syntastic][syn], if nothing else is specified. See `:help rust-syntastic` |
|||
for more details. |
|||
|
|||
### Source browsing with [Tagbar][tgbr] |
|||
|
|||
The installation of Tagbar along with [Universal Ctags][uctags] is recommended |
|||
for a good Tagbar experience. For other kinds of setups, `rust.vim` tries to |
|||
configure Tagbar to some degree. |
|||
|
|||
### Formatting with [rustfmt][rfmt] |
|||
|
|||
The `:RustFmt` command will format your code with |
|||
[rustfmt][rfmt] if installed. |
|||
|
|||
Placing `let g:rustfmt_autosave = 1` in your `~/.vimrc` will |
|||
enable automatic running of `:RustFmt` when you save a buffer. |
|||
|
|||
Do `:help :RustFmt` for further formatting help and customization |
|||
options. |
|||
|
|||
### [Playpen][pp] integration |
|||
|
|||
*Note:* This feature requires [webapi-vim][wav] to be installed. |
|||
|
|||
The `:RustPlay` command will send the current selection, or if |
|||
nothing is selected the current buffer, to the [Rust playpen][pp]. |
|||
|
|||
If you set g:rust_clip_command RustPlay will copy the url to the clipboard. |
|||
|
|||
- Mac: |
|||
|
|||
let g:rust_clip_command = 'pbcopy' |
|||
|
|||
- Linux: |
|||
|
|||
let g:rust_clip_command = 'xclip -selection clipboard' |
|||
|
|||
### Running a test under cursor |
|||
|
|||
In cargo project, the `:RustTest` command will run a test under the cursor. |
|||
This is useful when your project is bigger and running all tests take longer time. |
|||
|
|||
## Help |
|||
|
|||
Further help can be found in the documentation with `:Helptags` then `:help rust`. |
|||
|
|||
Detailed help can be found in the documentation with `:help rust`. |
|||
Helptags (`:help helptags`) need to be generated for this plugin |
|||
in order to navigate the help. Most plugin managers will do this |
|||
automatically, but check their documentation if that is not the case. |
|||
|
|||
## License |
|||
|
|||
Like Rust, rust.vim is primarily distributed under the terms of both the MIT |
|||
license and the Apache License (Version 2.0). See LICENSE-APACHE and |
|||
LICENSE-MIT for details. |
|||
|
|||
[r]: https://www.rust-lang.org |
|||
[v]: https://github.com/gmarik/vundle |
|||
[vqs]: https://github.com/gmarik/vundle#quick-start |
|||
[p]: https://github.com/tpope/vim-pathogen |
|||
[nb]: https://github.com/Shougo/neobundle.vim |
|||
[vp]: https://github.com/junegunn/vim-plug |
|||
[d]: https://github.com/Shougo/dein.vim |
|||
[rfmt]: https://github.com/rust-lang-nursery/rustfmt |
|||
[syn]: https://github.com/scrooloose/syntastic |
|||
[tgbr]: https://github.com/majutsushi/tagbar |
|||
[uctags]: https://ctags.io |
|||
[wav]: https://github.com/mattn/webapi-vim |
|||
[pp]: https://play.rust-lang.org/ |
|||
[vim8pack]: http://vimhelp.appspot.com/repeat.txt.html#packages |
|||
@ -0,0 +1,41 @@ |
|||
scriptencoding utf-8 |
|||
|
|||
if !get(g:, 'rust_conceal', 0) || !has('conceal') || &encoding !=# 'utf-8' |
|||
finish |
|||
endif |
|||
|
|||
" For those who don't want to see `::`... |
|||
if get(g:, 'rust_conceal_mod_path', 0) |
|||
syn match rustNiceOperator "::" conceal cchar=ㆍ |
|||
endif |
|||
|
|||
syn match rustRightArrowHead contained ">" conceal cchar= |
|||
syn match rustRightArrowTail contained "-" conceal cchar=⟶ |
|||
syn match rustNiceOperator "->" contains=rustRightArrowHead,rustRightArrowTail |
|||
|
|||
syn match rustFatRightArrowHead contained ">" conceal cchar= |
|||
syn match rustFatRightArrowTail contained "=" conceal cchar=⟹ |
|||
syn match rustNiceOperator "=>" contains=rustFatRightArrowHead,rustFatRightArrowTail |
|||
|
|||
syn match rustNiceOperator /\<\@!_\(_*\>\)\@=/ conceal cchar=′ |
|||
|
|||
" For those who don't want to see `pub`... |
|||
if get(g:, 'rust_conceal_pub', 0) |
|||
syn match rustPublicSigil contained "pu" conceal cchar=* |
|||
syn match rustPublicRest contained "b" conceal cchar= |
|||
syn match rustNiceOperator "pub " contains=rustPublicSigil,rustPublicRest |
|||
endif |
|||
|
|||
hi link rustNiceOperator Operator |
|||
|
|||
if !get(g:, 'rust_conceal_mod_path', 0) |
|||
hi! link Conceal Operator |
|||
|
|||
augroup rust.vim.after |
|||
autocmd! |
|||
" And keep it after a colorscheme change |
|||
autocmd ColorScheme <buffer> hi! link Conceal Operator |
|||
augroup END |
|||
endif |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,125 @@ |
|||
function! cargo#Load() |
|||
" Utility call to get this script loaded, for debugging |
|||
endfunction |
|||
|
|||
function! cargo#cmd(args) |
|||
" Trim trailing spaces. This is necessary since :terminal command parses |
|||
" trailing spaces as an empty argument. |
|||
let args = substitute(a:args, '\s\+$', '', '') |
|||
if has('terminal') |
|||
let cmd = 'terminal' |
|||
elseif has('nvim') |
|||
let cmd = 'noautocmd new | terminal' |
|||
else |
|||
let cmd = '!' |
|||
endif |
|||
execute cmd 'cargo' args |
|||
endfunction |
|||
|
|||
function! s:nearest_cargo(...) abort |
|||
" If the second argument is not specified, the first argument determines |
|||
" whether we will start from the current directory or the directory of the |
|||
" current buffer, otherwise, we start with the provided path on the |
|||
" second argument. |
|||
|
|||
let l:is_getcwd = get(a:, 1, 0) |
|||
if l:is_getcwd |
|||
let l:starting_path = get(a:, 2, getcwd()) |
|||
else |
|||
let l:starting_path = get(a:, 2, expand('%:p:h')) |
|||
endif |
|||
|
|||
return findfile('Cargo.toml', l:starting_path . ';') |
|||
endfunction |
|||
|
|||
function! cargo#nearestCargo(is_getcwd) abort |
|||
return s:nearest_cargo(a:is_getcwd) |
|||
endfunction |
|||
|
|||
function! cargo#nearestWorkspaceCargo(is_getcwd) abort |
|||
let l:nearest = s:nearest_cargo(a:is_getcwd) |
|||
while l:nearest !=# '' |
|||
for l:line in readfile(l:nearest, '', 0x100) |
|||
if l:line =~# '\V[workspace]' |
|||
return l:nearest |
|||
endif |
|||
endfor |
|||
let l:next = fnamemodify(l:nearest, ':p:h:h') |
|||
let l:nearest = s:nearest_cargo(0, l:next) |
|||
endwhile |
|||
return '' |
|||
endfunction |
|||
|
|||
function! cargo#nearestRootCargo(is_getcwd) abort |
|||
" Try to find a workspace Cargo.toml, and if not found, take the nearest |
|||
" regular Cargo.toml |
|||
let l:workspace_cargo = cargo#nearestWorkspaceCargo(a:is_getcwd) |
|||
if l:workspace_cargo !=# '' |
|||
return l:workspace_cargo |
|||
endif |
|||
return s:nearest_cargo(a:is_getcwd) |
|||
endfunction |
|||
|
|||
|
|||
function! cargo#build(args) |
|||
call cargo#cmd("build " . a:args) |
|||
endfunction |
|||
|
|||
function! cargo#clean(args) |
|||
call cargo#cmd("clean " . a:args) |
|||
endfunction |
|||
|
|||
function! cargo#doc(args) |
|||
call cargo#cmd("doc " . a:args) |
|||
endfunction |
|||
|
|||
function! cargo#new(args) |
|||
call cargo#cmd("new " . a:args) |
|||
cd `=a:args` |
|||
endfunction |
|||
|
|||
function! cargo#init(args) |
|||
call cargo#cmd("init " . a:args) |
|||
endfunction |
|||
|
|||
function! cargo#run(args) |
|||
call cargo#cmd("run " . a:args) |
|||
endfunction |
|||
|
|||
function! cargo#test(args) |
|||
call cargo#cmd("test " . a:args) |
|||
endfunction |
|||
|
|||
function! cargo#bench(args) |
|||
call cargo#cmd("bench " . a:args) |
|||
endfunction |
|||
|
|||
function! cargo#runtarget(args) |
|||
let l:filename = expand('%:p') |
|||
let l:read_manifest = system('cargo read-manifest') |
|||
let l:metadata = json_decode(l:read_manifest) |
|||
let l:targets = get(l:metadata, 'targets', []) |
|||
let l:did_run = 0 |
|||
for l:target in l:targets |
|||
let l:src_path = get(l:target, 'src_path', '') |
|||
let l:kinds = get(l:target, 'kind', []) |
|||
let l:name = get(l:target, 'name', '') |
|||
if l:src_path == l:filename |
|||
if index(l:kinds, 'example') != -1 |
|||
let l:did_run = 1 |
|||
call cargo#run("--example " . shellescape(l:name) . " " . a:args) |
|||
return |
|||
elseif index(l:kinds, 'bin') != -1 |
|||
let l:did_run = 1 |
|||
call cargo#run("--bin " . shellescape(l:name) . " " . a:args) |
|||
return |
|||
endif |
|||
endif |
|||
endfor |
|||
if l:did_run != 1 |
|||
call cargo#run(a:args) |
|||
return |
|||
endif |
|||
endfunction |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,26 @@ |
|||
function! cargo#quickfix#CmdPre() abort |
|||
if &filetype ==# 'rust' && get(b:, 'current_compiler', '') ==# 'cargo' |
|||
" Preserve the current directory, and 'lcd' to the nearest Cargo file. |
|||
let b:rust_compiler_cargo_qf_has_lcd = haslocaldir() |
|||
let b:rust_compiler_cargo_qf_prev_cd = getcwd() |
|||
let b:rust_compiler_cargo_qf_prev_cd_saved = 1 |
|||
let l:nearest = fnamemodify(cargo#nearestRootCargo(0), ':h') |
|||
execute 'lchdir! '.l:nearest |
|||
else |
|||
let b:rust_compiler_cargo_qf_prev_cd_saved = 0 |
|||
endif |
|||
endfunction |
|||
|
|||
function! cargo#quickfix#CmdPost() abort |
|||
if exists("b:rust_compiler_cargo_qf_prev_cd_saved") && b:rust_compiler_cargo_qf_prev_cd_saved |
|||
" Restore the current directory. |
|||
if b:rust_compiler_cargo_qf_has_lcd |
|||
execute 'lchdir! '.b:rust_compiler_cargo_qf_prev_cd |
|||
else |
|||
execute 'chdir! '.b:rust_compiler_cargo_qf_prev_cd |
|||
endif |
|||
let b:rust_compiler_cargo_qf_prev_cd_saved = 0 |
|||
endif |
|||
endfunction |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,563 @@ |
|||
" Description: Helper functions for Rust commands/mappings |
|||
" Last Modified: May 27, 2014 |
|||
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
|||
|
|||
function! rust#Load() |
|||
" Utility call to get this script loaded, for debugging |
|||
endfunction |
|||
|
|||
function! rust#GetConfigVar(name, default) |
|||
" Local buffer variable with same name takes predeence over global |
|||
if has_key(b:, a:name) |
|||
return get(b:, a:name) |
|||
endif |
|||
if has_key(g:, a:name) |
|||
return get(g:, a:name) |
|||
endif |
|||
return a:default |
|||
endfunction |
|||
|
|||
" Include expression {{{1 |
|||
|
|||
function! rust#IncludeExpr(fname) abort |
|||
" Remove leading 'crate::' to deal with 2018 edition style 'use' |
|||
" statements |
|||
let l:fname = substitute(a:fname, '^crate::', '', '') |
|||
|
|||
" Remove trailing colons arising from lines like |
|||
" |
|||
" use foo::{Bar, Baz}; |
|||
let l:fname = substitute(l:fname, ':\+$', '', '') |
|||
|
|||
" Replace '::' with '/' |
|||
let l:fname = substitute(l:fname, '::', '/', 'g') |
|||
|
|||
" When we have |
|||
" |
|||
" use foo::bar::baz; |
|||
" |
|||
" we can't tell whether baz is a module or a function; and we can't tell |
|||
" which modules correspond to files. |
|||
" |
|||
" So we work our way up, trying |
|||
" |
|||
" foo/bar/baz.rs |
|||
" foo/bar.rs |
|||
" foo.rs |
|||
while l:fname !=# '.' |
|||
let l:path = findfile(l:fname) |
|||
if !empty(l:path) |
|||
return l:fname |
|||
endif |
|||
let l:fname = fnamemodify(l:fname, ':h') |
|||
endwhile |
|||
return l:fname |
|||
endfunction |
|||
|
|||
" Jump {{{1 |
|||
|
|||
function! rust#Jump(mode, function) range |
|||
let cnt = v:count1 |
|||
normal! m' |
|||
if a:mode ==# 'v' |
|||
norm! gv |
|||
endif |
|||
let foldenable = &foldenable |
|||
set nofoldenable |
|||
while cnt > 0 |
|||
execute "call <SID>Jump_" . a:function . "()" |
|||
let cnt = cnt - 1 |
|||
endwhile |
|||
let &foldenable = foldenable |
|||
endfunction |
|||
|
|||
function! s:Jump_Back() |
|||
call search('{', 'b') |
|||
keepjumps normal! w99[{ |
|||
endfunction |
|||
|
|||
function! s:Jump_Forward() |
|||
normal! j0 |
|||
call search('{', 'b') |
|||
keepjumps normal! w99[{% |
|||
call search('{') |
|||
endfunction |
|||
|
|||
" Run {{{1 |
|||
|
|||
function! rust#Run(bang, args) |
|||
let args = s:ShellTokenize(a:args) |
|||
if a:bang |
|||
let idx = index(l:args, '--') |
|||
if idx != -1 |
|||
let rustc_args = idx == 0 ? [] : l:args[:idx-1] |
|||
let args = l:args[idx+1:] |
|||
else |
|||
let rustc_args = l:args |
|||
let args = [] |
|||
endif |
|||
else |
|||
let rustc_args = [] |
|||
endif |
|||
|
|||
let b:rust_last_rustc_args = l:rustc_args |
|||
let b:rust_last_args = l:args |
|||
|
|||
call s:WithPath(function("s:Run"), rustc_args, args) |
|||
endfunction |
|||
|
|||
function! s:Run(dict, rustc_args, args) |
|||
let exepath = a:dict.tmpdir.'/'.fnamemodify(a:dict.path, ':t:r') |
|||
if has('win32') |
|||
let exepath .= '.exe' |
|||
endif |
|||
|
|||
let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) |
|||
let rustc_args = [relpath, '-o', exepath] + a:rustc_args |
|||
|
|||
let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" |
|||
|
|||
let pwd = a:dict.istemp ? a:dict.tmpdir : '' |
|||
let output = s:system(pwd, shellescape(rustc) . " " . join(map(rustc_args, 'shellescape(v:val)'))) |
|||
if output !=# '' |
|||
echohl WarningMsg |
|||
echo output |
|||
echohl None |
|||
endif |
|||
if !v:shell_error |
|||
exe '!' . shellescape(exepath) . " " . join(map(a:args, 'shellescape(v:val)')) |
|||
endif |
|||
endfunction |
|||
|
|||
" Expand {{{1 |
|||
|
|||
function! rust#Expand(bang, args) |
|||
let args = s:ShellTokenize(a:args) |
|||
if a:bang && !empty(l:args) |
|||
let pretty = remove(l:args, 0) |
|||
else |
|||
let pretty = "expanded" |
|||
endif |
|||
call s:WithPath(function("s:Expand"), pretty, args) |
|||
endfunction |
|||
|
|||
function! s:Expand(dict, pretty, args) |
|||
try |
|||
let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" |
|||
|
|||
if a:pretty =~? '^\%(everybody_loops$\|flowgraph=\)' |
|||
let flag = '--xpretty' |
|||
else |
|||
let flag = '--pretty' |
|||
endif |
|||
let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) |
|||
let args = [relpath, '-Z', 'unstable-options', l:flag, a:pretty] + a:args |
|||
let pwd = a:dict.istemp ? a:dict.tmpdir : '' |
|||
let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) |
|||
if v:shell_error |
|||
echohl WarningMsg |
|||
echo output |
|||
echohl None |
|||
else |
|||
new |
|||
silent put =output |
|||
1 |
|||
d |
|||
setl filetype=rust |
|||
setl buftype=nofile |
|||
setl bufhidden=hide |
|||
setl noswapfile |
|||
" give the buffer a nice name |
|||
let suffix = 1 |
|||
let basename = fnamemodify(a:dict.path, ':t:r') |
|||
while 1 |
|||
let bufname = basename |
|||
if suffix > 1 | let bufname .= ' ('.suffix.')' | endif |
|||
let bufname .= '.pretty.rs' |
|||
if bufexists(bufname) |
|||
let suffix += 1 |
|||
continue |
|||
endif |
|||
exe 'silent noautocmd keepalt file' fnameescape(bufname) |
|||
break |
|||
endwhile |
|||
endif |
|||
endtry |
|||
endfunction |
|||
|
|||
function! rust#CompleteExpand(lead, line, pos) |
|||
if a:line[: a:pos-1] =~# '^RustExpand!\s*\S*$' |
|||
" first argument and it has a ! |
|||
let list = ["normal", "expanded", "typed", "expanded,identified", "flowgraph=", "everybody_loops"] |
|||
if !empty(a:lead) |
|||
call filter(list, "v:val[:len(a:lead)-1] == a:lead") |
|||
endif |
|||
return list |
|||
endif |
|||
|
|||
return glob(escape(a:lead, "*?[") . '*', 0, 1) |
|||
endfunction |
|||
|
|||
" Emit {{{1 |
|||
|
|||
function! rust#Emit(type, args) |
|||
let args = s:ShellTokenize(a:args) |
|||
call s:WithPath(function("s:Emit"), a:type, args) |
|||
endfunction |
|||
|
|||
function! s:Emit(dict, type, args) |
|||
try |
|||
let output_path = a:dict.tmpdir.'/output' |
|||
|
|||
let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" |
|||
|
|||
let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) |
|||
let args = [relpath, '--emit', a:type, '-o', output_path] + a:args |
|||
let pwd = a:dict.istemp ? a:dict.tmpdir : '' |
|||
let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) |
|||
if output !=# '' |
|||
echohl WarningMsg |
|||
echo output |
|||
echohl None |
|||
endif |
|||
if !v:shell_error |
|||
new |
|||
exe 'silent keepalt read' fnameescape(output_path) |
|||
1 |
|||
d |
|||
if a:type ==# "llvm-ir" |
|||
setl filetype=llvm |
|||
let extension = 'll' |
|||
elseif a:type ==# "asm" |
|||
setl filetype=asm |
|||
let extension = 's' |
|||
endif |
|||
setl buftype=nofile |
|||
setl bufhidden=hide |
|||
setl noswapfile |
|||
if exists('l:extension') |
|||
" give the buffer a nice name |
|||
let suffix = 1 |
|||
let basename = fnamemodify(a:dict.path, ':t:r') |
|||
while 1 |
|||
let bufname = basename |
|||
if suffix > 1 | let bufname .= ' ('.suffix.')' | endif |
|||
let bufname .= '.'.extension |
|||
if bufexists(bufname) |
|||
let suffix += 1 |
|||
continue |
|||
endif |
|||
exe 'silent noautocmd keepalt file' fnameescape(bufname) |
|||
break |
|||
endwhile |
|||
endif |
|||
endif |
|||
endtry |
|||
endfunction |
|||
|
|||
" Utility functions {{{1 |
|||
|
|||
" Invokes func(dict, ...) |
|||
" Where {dict} is a dictionary with the following keys: |
|||
" 'path' - The path to the file |
|||
" 'tmpdir' - The path to a temporary directory that will be deleted when the |
|||
" function returns. |
|||
" 'istemp' - 1 if the path is a file inside of {dict.tmpdir} or 0 otherwise. |
|||
" If {istemp} is 1 then an additional key is provided: |
|||
" 'tmpdir_relpath' - The {path} relative to the {tmpdir}. |
|||
" |
|||
" {dict.path} may be a path to a file inside of {dict.tmpdir} or it may be the |
|||
" existing path of the current buffer. If the path is inside of {dict.tmpdir} |
|||
" then it is guaranteed to have a '.rs' extension. |
|||
function! s:WithPath(func, ...) |
|||
let buf = bufnr('') |
|||
let saved = {} |
|||
let dict = {} |
|||
try |
|||
let saved.write = &write |
|||
set write |
|||
let dict.path = expand('%') |
|||
let pathisempty = empty(dict.path) |
|||
|
|||
" Always create a tmpdir in case the wrapped command wants it |
|||
let dict.tmpdir = tempname() |
|||
call mkdir(dict.tmpdir) |
|||
|
|||
if pathisempty || !saved.write |
|||
let dict.istemp = 1 |
|||
" if we're doing this because of nowrite, preserve the filename |
|||
if !pathisempty |
|||
let filename = expand('%:t:r').".rs" |
|||
else |
|||
let filename = 'unnamed.rs' |
|||
endif |
|||
let dict.tmpdir_relpath = filename |
|||
let dict.path = dict.tmpdir.'/'.filename |
|||
|
|||
let saved.mod = &modified |
|||
set nomodified |
|||
|
|||
silent exe 'keepalt write! ' . fnameescape(dict.path) |
|||
if pathisempty |
|||
silent keepalt 0file |
|||
endif |
|||
else |
|||
let dict.istemp = 0 |
|||
update |
|||
endif |
|||
|
|||
call call(a:func, [dict] + a:000) |
|||
finally |
|||
if bufexists(buf) |
|||
for [opt, value] in items(saved) |
|||
silent call setbufvar(buf, '&'.opt, value) |
|||
unlet value " avoid variable type mismatches |
|||
endfor |
|||
endif |
|||
if has_key(dict, 'tmpdir') | silent call s:RmDir(dict.tmpdir) | endif |
|||
endtry |
|||
endfunction |
|||
|
|||
function! rust#AppendCmdLine(text) |
|||
call setcmdpos(getcmdpos()) |
|||
let cmd = getcmdline() . a:text |
|||
return cmd |
|||
endfunction |
|||
|
|||
" Tokenize the string according to sh parsing rules |
|||
function! s:ShellTokenize(text) |
|||
" states: |
|||
" 0: start of word |
|||
" 1: unquoted |
|||
" 2: unquoted backslash |
|||
" 3: double-quote |
|||
" 4: double-quoted backslash |
|||
" 5: single-quote |
|||
let l:state = 0 |
|||
let l:current = '' |
|||
let l:args = [] |
|||
for c in split(a:text, '\zs') |
|||
if l:state == 0 || l:state == 1 " unquoted |
|||
if l:c ==# ' ' |
|||
if l:state == 0 | continue | endif |
|||
call add(l:args, l:current) |
|||
let l:current = '' |
|||
let l:state = 0 |
|||
elseif l:c ==# '\' |
|||
let l:state = 2 |
|||
elseif l:c ==# '"' |
|||
let l:state = 3 |
|||
elseif l:c ==# "'" |
|||
let l:state = 5 |
|||
else |
|||
let l:current .= l:c |
|||
let l:state = 1 |
|||
endif |
|||
elseif l:state == 2 " unquoted backslash |
|||
if l:c !=# "\n" " can it even be \n? |
|||
let l:current .= l:c |
|||
endif |
|||
let l:state = 1 |
|||
elseif l:state == 3 " double-quote |
|||
if l:c ==# '\' |
|||
let l:state = 4 |
|||
elseif l:c ==# '"' |
|||
let l:state = 1 |
|||
else |
|||
let l:current .= l:c |
|||
endif |
|||
elseif l:state == 4 " double-quoted backslash |
|||
if stridx('$`"\', l:c) >= 0 |
|||
let l:current .= l:c |
|||
elseif l:c ==# "\n" " is this even possible? |
|||
" skip it |
|||
else |
|||
let l:current .= '\'.l:c |
|||
endif |
|||
let l:state = 3 |
|||
elseif l:state == 5 " single-quoted |
|||
if l:c ==# "'" |
|||
let l:state = 1 |
|||
else |
|||
let l:current .= l:c |
|||
endif |
|||
endif |
|||
endfor |
|||
if l:state != 0 |
|||
call add(l:args, l:current) |
|||
endif |
|||
return l:args |
|||
endfunction |
|||
|
|||
function! s:RmDir(path) |
|||
" sanity check; make sure it's not empty, /, or $HOME |
|||
if empty(a:path) |
|||
echoerr 'Attempted to delete empty path' |
|||
return 0 |
|||
elseif a:path ==# '/' || a:path ==# $HOME |
|||
let l:path = expand(a:path) |
|||
if l:path ==# '/' || l:path ==# $HOME |
|||
echoerr 'Attempted to delete protected path: ' . a:path |
|||
return 0 |
|||
endif |
|||
endif |
|||
|
|||
if !isdirectory(a:path) |
|||
return 0 |
|||
endif |
|||
|
|||
" delete() returns 0 when removing file successfully |
|||
return delete(a:path, 'rf') == 0 |
|||
endfunction |
|||
|
|||
" Executes {cmd} with the cwd set to {pwd}, without changing Vim's cwd. |
|||
" If {pwd} is the empty string then it doesn't change the cwd. |
|||
function! s:system(pwd, cmd) |
|||
let cmd = a:cmd |
|||
if !empty(a:pwd) |
|||
let cmd = 'cd ' . shellescape(a:pwd) . ' && ' . cmd |
|||
endif |
|||
return system(cmd) |
|||
endfunction |
|||
|
|||
" Playpen Support {{{1 |
|||
" Parts of gist.vim by Yasuhiro Matsumoto <mattn.jp@gmail.com> reused |
|||
" gist.vim available under the BSD license, available at |
|||
" http://github.com/mattn/gist-vim |
|||
function! s:has_webapi() |
|||
if !exists("*webapi#http#post") |
|||
try |
|||
call webapi#http#post() |
|||
catch |
|||
endtry |
|||
endif |
|||
return exists("*webapi#http#post") |
|||
endfunction |
|||
|
|||
function! rust#Play(count, line1, line2, ...) abort |
|||
redraw |
|||
|
|||
let l:rust_playpen_url = get(g:, 'rust_playpen_url', 'https://play.rust-lang.org/') |
|||
let l:rust_shortener_url = get(g:, 'rust_shortener_url', 'https://is.gd/') |
|||
|
|||
if !s:has_webapi() |
|||
echohl ErrorMsg | echomsg ':RustPlay depends on webapi.vim (https://github.com/mattn/webapi-vim)' | echohl None |
|||
return |
|||
endif |
|||
|
|||
let bufname = bufname('%') |
|||
if a:count < 1 |
|||
let content = join(getline(a:line1, a:line2), "\n") |
|||
else |
|||
let save_regcont = @" |
|||
let save_regtype = getregtype('"') |
|||
silent! normal! gvy |
|||
let content = @" |
|||
call setreg('"', save_regcont, save_regtype) |
|||
endif |
|||
|
|||
let url = l:rust_playpen_url."?code=".webapi#http#encodeURI(content) |
|||
|
|||
if strlen(url) > 5000 |
|||
echohl ErrorMsg | echomsg 'Buffer too large, max 5000 encoded characters ('.strlen(url).')' | echohl None |
|||
return |
|||
endif |
|||
|
|||
let payload = "format=simple&url=".webapi#http#encodeURI(url) |
|||
let res = webapi#http#post(l:rust_shortener_url.'create.php', payload, {}) |
|||
if res.status[0] ==# '2' |
|||
let url = res.content |
|||
endif |
|||
|
|||
let footer = '' |
|||
if exists('g:rust_clip_command') |
|||
call system(g:rust_clip_command, url) |
|||
if !v:shell_error |
|||
let footer = ' (copied to clipboard)' |
|||
endif |
|||
endif |
|||
redraw | echomsg 'Done: '.url.footer |
|||
endfunction |
|||
|
|||
" Run a test under the cursor or all tests {{{1 |
|||
|
|||
" Finds a test function name under the cursor. Returns empty string when a |
|||
" test function is not found. |
|||
function! s:SearchTestFunctionNameUnderCursor() abort |
|||
let cursor_line = line('.') |
|||
|
|||
" Find #[test] attribute |
|||
if search('\m\C#\[test\]', 'bcW') is 0 |
|||
return '' |
|||
endif |
|||
|
|||
" Move to an opening brace of the test function |
|||
let test_func_line = search('\m\C^\s*fn\s\+\h\w*\s*(.\+{$', 'eW') |
|||
if test_func_line is 0 |
|||
return '' |
|||
endif |
|||
|
|||
" Search the end of test function (closing brace) to ensure that the |
|||
" cursor position is within function definition |
|||
normal! % |
|||
if line('.') < cursor_line |
|||
return '' |
|||
endif |
|||
|
|||
return matchstr(getline(test_func_line), '\m\C^\s*fn\s\+\zs\h\w*') |
|||
endfunction |
|||
|
|||
function! rust#Test(mods, winsize, all, options) abort |
|||
let manifest = findfile('Cargo.toml', expand('%:p:h') . ';') |
|||
if manifest ==# '' |
|||
return rust#Run(1, '--test ' . a:options) |
|||
endif |
|||
|
|||
" <count> defaults to 0, but we prefer an empty string |
|||
let winsize = a:winsize ? a:winsize : '' |
|||
|
|||
if has('terminal') |
|||
if has('patch-8.0.910') |
|||
let cmd = printf('%s noautocmd %snew | terminal ++curwin ', a:mods, winsize) |
|||
else |
|||
let cmd = printf('%s terminal ', a:mods) |
|||
endif |
|||
elseif has('nvim') |
|||
let cmd = printf('%s noautocmd %snew | terminal ', a:mods, winsize) |
|||
else |
|||
let cmd = '!' |
|||
let manifest = shellescape(manifest) |
|||
endif |
|||
|
|||
if a:all |
|||
if a:options ==# '' |
|||
execute cmd . 'cargo test --manifest-path' manifest |
|||
else |
|||
execute cmd . 'cargo test --manifest-path' manifest a:options |
|||
endif |
|||
return |
|||
endif |
|||
|
|||
let saved = getpos('.') |
|||
try |
|||
let func_name = s:SearchTestFunctionNameUnderCursor() |
|||
if func_name ==# '' |
|||
echohl ErrorMsg |
|||
echomsg 'No test function was found under the cursor. Please add ! to command if you want to run all tests' |
|||
echohl None |
|||
return |
|||
endif |
|||
if a:options ==# '' |
|||
execute cmd . 'cargo test --manifest-path' manifest func_name |
|||
else |
|||
execute cmd . 'cargo test --manifest-path' manifest func_name a:options |
|||
endif |
|||
return |
|||
finally |
|||
call setpos('.', saved) |
|||
endtry |
|||
endfunction |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,103 @@ |
|||
" For debugging, inspired by https://github.com/w0rp/rust/blob/master/autoload/rust/debugging.vim |
|||
|
|||
let s:global_variable_list = [ |
|||
\ '_rustfmt_autosave_because_of_config', |
|||
\ 'ftplugin_rust_source_path', |
|||
\ 'loaded_syntastic_rust_cargo_checker', |
|||
\ 'loaded_syntastic_rust_filetype', |
|||
\ 'loaded_syntastic_rust_rustc_checker', |
|||
\ 'rust_bang_comment_leader', |
|||
\ 'rust_cargo_avoid_whole_workspace', |
|||
\ 'rust_clip_command', |
|||
\ 'rust_conceal', |
|||
\ 'rust_conceal_mod_path', |
|||
\ 'rust_conceal_pub', |
|||
\ 'rust_fold', |
|||
\ 'rust_last_args', |
|||
\ 'rust_last_rustc_args', |
|||
\ 'rust_original_delimitMate_excluded_regions', |
|||
\ 'rust_playpen_url', |
|||
\ 'rust_prev_delimitMate_quotes', |
|||
\ 'rust_recent_nearest_cargo_tol', |
|||
\ 'rust_recent_root_cargo_toml', |
|||
\ 'rust_recommended_style', |
|||
\ 'rust_set_conceallevel', |
|||
\ 'rust_set_conceallevel=1', |
|||
\ 'rust_set_foldmethod', |
|||
\ 'rust_set_foldmethod=1', |
|||
\ 'rust_shortener_url', |
|||
\ 'rustc_makeprg_no_percent', |
|||
\ 'rustc_path', |
|||
\ 'rustfmt_autosave', |
|||
\ 'rustfmt_autosave_if_config_present', |
|||
\ 'rustfmt_command', |
|||
\ 'rustfmt_emit_files', |
|||
\ 'rustfmt_fail_silently', |
|||
\ 'rustfmt_options', |
|||
\ 'syntastic_extra_filetypes', |
|||
\ 'syntastic_rust_cargo_fname', |
|||
\] |
|||
|
|||
function! s:Echo(message) abort |
|||
execute 'echo a:message' |
|||
endfunction |
|||
|
|||
function! s:EchoGlobalVariables() abort |
|||
for l:key in s:global_variable_list |
|||
if l:key !~# '^_' |
|||
call s:Echo('let g:' . l:key . ' = ' . string(get(g:, l:key, v:null))) |
|||
endif |
|||
|
|||
if has_key(b:, l:key) |
|||
call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key])) |
|||
endif |
|||
endfor |
|||
endfunction |
|||
|
|||
function! rust#debugging#Info() abort |
|||
call cargo#Load() |
|||
call rust#Load() |
|||
call rustfmt#Load() |
|||
call s:Echo('rust.vim Global Variables:') |
|||
call s:Echo('') |
|||
call s:EchoGlobalVariables() |
|||
|
|||
silent let l:output = system(g:rustfmt_command . ' --version') |
|||
echo l:output |
|||
|
|||
let l:rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" |
|||
silent let l:output = system(l:rustc . ' --version') |
|||
echo l:output |
|||
|
|||
silent let l:output = system('cargo --version') |
|||
echo l:output |
|||
|
|||
version |
|||
|
|||
if exists(":SyntasticInfo") |
|||
echo "----" |
|||
echo "Info from Syntastic:" |
|||
execute "SyntasticInfo" |
|||
endif |
|||
endfunction |
|||
|
|||
function! rust#debugging#InfoToClipboard() abort |
|||
redir @" |
|||
silent call rust#debugging#Info() |
|||
redir END |
|||
|
|||
call s:Echo('RustInfo copied to your clipboard') |
|||
endfunction |
|||
|
|||
function! rust#debugging#InfoToFile(filename) abort |
|||
let l:expanded_filename = expand(a:filename) |
|||
|
|||
redir => l:output |
|||
silent call rust#debugging#Info() |
|||
redir END |
|||
|
|||
call writefile(split(l:output, "\n"), l:expanded_filename) |
|||
call s:Echo('RustInfo written to ' . l:expanded_filename) |
|||
endfunction |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,44 @@ |
|||
let s:delimitMate_extra_excluded_regions = ',rustLifetimeCandidate,rustGenericLifetimeCandidate' |
|||
|
|||
" For this buffer, when delimitMate issues the `User delimitMate_map` |
|||
" event in the autocommand system, add the above-defined extra excluded |
|||
" regions to delimitMate's state, if they have not already been added. |
|||
function! rust#delimitmate#onMap() abort |
|||
if &filetype !=# 'rust' |
|||
return |
|||
endif |
|||
|
|||
if get(b:, "delimitMate_quotes") |
|||
let b:rust_prev_delimitMate_quotes = b:delimitMate_quotes |
|||
endif |
|||
let b:delimitMate_quotes = "\" `" |
|||
|
|||
if match(delimitMate#Get("excluded_regions"), |
|||
\ s:delimitMate_extra_excluded_regions) == -1 |
|||
call delimitMate#Set("excluded_regions", |
|||
\delimitMate#Get("excluded_regions").s:delimitMate_extra_excluded_regions) |
|||
endif |
|||
endfunction |
|||
|
|||
" For this buffer, when delimitMate issues the `User delimitMate_unmap` |
|||
" event in the autocommand system, delete the above-defined extra excluded |
|||
" regions from delimitMate's state (the deletion being idempotent and |
|||
" having no effect if the extra excluded regions are not present in the |
|||
" targeted part of delimitMate's state). |
|||
function! rust#delimitmate#onUnmap() abort |
|||
if &filetype !=# 'rust' |
|||
return |
|||
endif |
|||
|
|||
if get(b:, "rust_prev_delimitMate_quotes") |
|||
let b:delimitMate_quotes = b:rust_prev_delimitMate_quotes |
|||
endif |
|||
|
|||
call delimitMate#Set("excluded_regions", substitute( |
|||
\ delimitMate#Get("excluded_regions"), |
|||
\ '\C\V' . s:delimitMate_extra_excluded_regions, |
|||
\ '', 'g')) |
|||
endfunction |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
|
|||
@ -0,0 +1,18 @@ |
|||
" Tagbar support code, for the sake of not automatically overriding its |
|||
" configuration in case Universal Ctags is detected. |
|||
|
|||
let s:ctags_is_uctags = 0 |
|||
let s:checked_ctags = 0 |
|||
|
|||
function! rust#tags#IsUCtags() abort |
|||
if s:checked_ctags == 0 |
|||
let l:ctags_bin = get(g:, 'tagbar_ctags_bin', 'ctags') |
|||
if system(l:ctags_bin.' --version') =~? 'universal ctags' |
|||
let s:ctags_is_uctags = 1 |
|||
endif |
|||
let s:checked_ctags = 1 |
|||
endif |
|||
return s:ctags_is_uctags |
|||
endfunction |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,264 @@ |
|||
" Author: Stephen Sugden <stephen@stephensugden.com> |
|||
" |
|||
" Adapted from https://github.com/fatih/vim-go |
|||
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
|||
|
|||
if !exists("g:rustfmt_autosave") |
|||
let g:rustfmt_autosave = 0 |
|||
endif |
|||
|
|||
if !exists("g:rustfmt_command") |
|||
let g:rustfmt_command = "rustfmt" |
|||
endif |
|||
|
|||
if !exists("g:rustfmt_options") |
|||
let g:rustfmt_options = "" |
|||
endif |
|||
|
|||
if !exists("g:rustfmt_fail_silently") |
|||
let g:rustfmt_fail_silently = 0 |
|||
endif |
|||
|
|||
function! rustfmt#DetectVersion() |
|||
" Save rustfmt '--help' for feature inspection |
|||
silent let s:rustfmt_help = system(g:rustfmt_command . " --help") |
|||
let s:rustfmt_unstable_features = s:rustfmt_help =~# "--unstable-features" |
|||
|
|||
" Build a comparable rustfmt version varible out of its `--version` output: |
|||
silent let l:rustfmt_version_full = system(g:rustfmt_command . " --version") |
|||
let l:rustfmt_version_list = matchlist(l:rustfmt_version_full, |
|||
\ '\vrustfmt ([0-9]+[.][0-9]+[.][0-9]+)') |
|||
if len(l:rustfmt_version_list) < 3 |
|||
let s:rustfmt_version = "0" |
|||
else |
|||
let s:rustfmt_version = l:rustfmt_version_list[1] |
|||
endif |
|||
return s:rustfmt_version |
|||
endfunction |
|||
|
|||
call rustfmt#DetectVersion() |
|||
|
|||
if !exists("g:rustfmt_emit_files") |
|||
let g:rustfmt_emit_files = s:rustfmt_version >= "0.8.2" |
|||
endif |
|||
|
|||
if !exists("g:rustfmt_file_lines") |
|||
let g:rustfmt_file_lines = s:rustfmt_help =~# "--file-lines JSON" |
|||
endif |
|||
|
|||
let s:got_fmt_error = 0 |
|||
|
|||
function! rustfmt#Load() |
|||
" Utility call to get this script loaded, for debugging |
|||
endfunction |
|||
|
|||
function! s:RustfmtWriteMode() |
|||
if g:rustfmt_emit_files |
|||
return "--emit=files" |
|||
else |
|||
return "--write-mode=overwrite" |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:RustfmtConfig() |
|||
let l:rustfmt_toml = findfile('rustfmt.toml', expand('%:p:h') . ';') |
|||
if l:rustfmt_toml !=# '' |
|||
return '--config-path '.l:rustfmt_toml |
|||
endif |
|||
|
|||
let l:_rustfmt_toml = findfile('.rustfmt.toml', expand('%:p:h') . ';') |
|||
if l:_rustfmt_toml !=# '' |
|||
return '--config-path '.l:_rustfmt_toml |
|||
endif |
|||
|
|||
return '' |
|||
endfunction |
|||
|
|||
function! s:RustfmtCommandRange(filename, line1, line2) |
|||
if g:rustfmt_file_lines == 0 |
|||
echo "--file-lines is not supported in the installed `rustfmt` executable" |
|||
return |
|||
endif |
|||
|
|||
let l:arg = {"file": shellescape(a:filename), "range": [a:line1, a:line2]} |
|||
let l:write_mode = s:RustfmtWriteMode() |
|||
let l:rustfmt_config = s:RustfmtConfig() |
|||
|
|||
" FIXME: When --file-lines gets to be stable, add version range checking |
|||
" accordingly. |
|||
let l:unstable_features = s:rustfmt_unstable_features ? '--unstable-features' : '' |
|||
|
|||
let l:cmd = printf("%s %s %s %s %s --file-lines '[%s]' %s", g:rustfmt_command, |
|||
\ l:write_mode, g:rustfmt_options, |
|||
\ l:unstable_features, l:rustfmt_config, |
|||
\ json_encode(l:arg), shellescape(a:filename)) |
|||
return l:cmd |
|||
endfunction |
|||
|
|||
function! s:RustfmtCommand() |
|||
if g:rustfmt_emit_files |
|||
let l:write_mode = "--emit=stdout" |
|||
else |
|||
let l:write_mode = "--write-mode=display" |
|||
endif |
|||
" rustfmt will pick on the right config on its own due to the |
|||
" current directory change. |
|||
return g:rustfmt_command . " ". l:write_mode . " " . g:rustfmt_options |
|||
endfunction |
|||
|
|||
function! s:DeleteLines(start, end) abort |
|||
silent! execute a:start . ',' . a:end . 'delete _' |
|||
endfunction |
|||
|
|||
function! s:RunRustfmt(command, tmpname, from_writepre) |
|||
mkview! |
|||
|
|||
let l:stderr_tmpname = tempname() |
|||
call writefile([], l:stderr_tmpname) |
|||
|
|||
let l:command = a:command . ' 2> ' . l:stderr_tmpname |
|||
|
|||
if a:tmpname ==# '' |
|||
" Rustfmt in stdin/stdout mode |
|||
|
|||
" chdir to the directory of the file |
|||
let l:has_lcd = haslocaldir() |
|||
let l:prev_cd = getcwd() |
|||
execute 'lchdir! '.expand('%:h') |
|||
|
|||
let l:buffer = getline(1, '$') |
|||
if exists("*systemlist") |
|||
silent let out = systemlist(l:command, l:buffer) |
|||
else |
|||
silent let out = split(system(l:command, |
|||
\ join(l:buffer, "\n")), '\r\?\n') |
|||
endif |
|||
else |
|||
if exists("*systemlist") |
|||
silent let out = systemlist(l:command) |
|||
else |
|||
silent let out = split(system(l:command), '\r\?\n') |
|||
endif |
|||
endif |
|||
|
|||
let l:stderr = readfile(l:stderr_tmpname) |
|||
|
|||
call delete(l:stderr_tmpname) |
|||
|
|||
let l:open_lwindow = 0 |
|||
if v:shell_error == 0 |
|||
if a:from_writepre |
|||
" remove undo point caused via BufWritePre |
|||
try | silent undojoin | catch | endtry |
|||
endif |
|||
|
|||
if a:tmpname ==# '' |
|||
let l:content = l:out |
|||
else |
|||
" take the tmpfile's content, this is better than rename |
|||
" because it preserves file modes. |
|||
let l:content = readfile(a:tmpname) |
|||
endif |
|||
|
|||
call s:DeleteLines(len(l:content), line('$')) |
|||
call setline(1, l:content) |
|||
|
|||
" only clear location list if it was previously filled to prevent |
|||
" clobbering other additions |
|||
if s:got_fmt_error |
|||
let s:got_fmt_error = 0 |
|||
call setloclist(0, []) |
|||
let l:open_lwindow = 1 |
|||
endif |
|||
elseif g:rustfmt_fail_silently == 0 && !a:from_writepre |
|||
" otherwise get the errors and put them in the location list |
|||
let l:errors = [] |
|||
|
|||
let l:prev_line = "" |
|||
for l:line in l:stderr |
|||
" error: expected one of `;` or `as`, found `extern` |
|||
" --> src/main.rs:2:1 |
|||
let tokens = matchlist(l:line, '^\s\+-->\s\(.\{-}\):\(\d\+\):\(\d\+\)$') |
|||
if !empty(tokens) |
|||
call add(l:errors, {"filename": @%, |
|||
\"lnum": tokens[2], |
|||
\"col": tokens[3], |
|||
\"text": l:prev_line}) |
|||
endif |
|||
let l:prev_line = l:line |
|||
endfor |
|||
|
|||
if !empty(l:errors) |
|||
call setloclist(0, l:errors, 'r') |
|||
echohl Error | echomsg "rustfmt returned error" | echohl None |
|||
else |
|||
echo "rust.vim: was not able to parse rustfmt messages. Here is the raw output:" |
|||
echo "\n" |
|||
for l:line in l:stderr |
|||
echo l:line |
|||
endfor |
|||
endif |
|||
|
|||
let s:got_fmt_error = 1 |
|||
let l:open_lwindow = 1 |
|||
endif |
|||
|
|||
" Restore the current directory if needed |
|||
if a:tmpname ==# '' |
|||
if l:has_lcd |
|||
execute 'lchdir! '.l:prev_cd |
|||
else |
|||
execute 'chdir! '.l:prev_cd |
|||
endif |
|||
endif |
|||
|
|||
" Open lwindow after we have changed back to the previous directory |
|||
if l:open_lwindow == 1 |
|||
lwindow |
|||
endif |
|||
|
|||
silent! loadview |
|||
endfunction |
|||
|
|||
function! rustfmt#FormatRange(line1, line2) |
|||
let l:tmpname = tempname() |
|||
call writefile(getline(1, '$'), l:tmpname) |
|||
let command = s:RustfmtCommandRange(l:tmpname, a:line1, a:line2) |
|||
call s:RunRustfmt(command, l:tmpname, v:false) |
|||
call delete(l:tmpname) |
|||
endfunction |
|||
|
|||
function! rustfmt#Format() |
|||
call s:RunRustfmt(s:RustfmtCommand(), '', v:false) |
|||
endfunction |
|||
|
|||
function! rustfmt#Cmd() |
|||
" Mainly for debugging |
|||
return s:RustfmtCommand() |
|||
endfunction |
|||
|
|||
function! rustfmt#PreWrite() |
|||
if !filereadable(expand("%@")) |
|||
return |
|||
endif |
|||
if rust#GetConfigVar('rustfmt_autosave_if_config_present', 0) |
|||
if findfile('rustfmt.toml', '.;') !=# '' || findfile('.rustfmt.toml', '.;') !=# '' |
|||
let b:rustfmt_autosave = 1 |
|||
let b:_rustfmt_autosave_because_of_config = 1 |
|||
endif |
|||
else |
|||
if has_key(b:, '_rustfmt_autosave_because_of_config') |
|||
unlet b:_rustfmt_autosave_because_of_config |
|||
unlet b:rustfmt_autosave |
|||
endif |
|||
endif |
|||
|
|||
if !rust#GetConfigVar("rustfmt_autosave", 0) |
|||
return |
|||
endif |
|||
|
|||
call s:RunRustfmt(s:RustfmtCommand(), '', v:true) |
|||
endfunction |
|||
|
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,49 @@ |
|||
" Vim compiler file |
|||
" Compiler: Cargo Compiler |
|||
" Maintainer: Damien Radtke <damienradtke@gmail.com> |
|||
" Latest Revision: 2014 Sep 24 |
|||
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
|||
|
|||
if exists('current_compiler') |
|||
finish |
|||
endif |
|||
runtime compiler/rustc.vim |
|||
let current_compiler = "cargo" |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
if exists(':CompilerSet') != 2 |
|||
command -nargs=* CompilerSet setlocal <args> |
|||
endif |
|||
|
|||
if exists('g:cargo_makeprg_params') |
|||
execute 'CompilerSet makeprg=cargo\ '.escape(g:cargo_makeprg_params, ' \|"').'\ $*' |
|||
else |
|||
CompilerSet makeprg=cargo\ $* |
|||
endif |
|||
|
|||
augroup RustCargoQuickFixHooks |
|||
autocmd! |
|||
autocmd QuickFixCmdPre make call cargo#quickfix#CmdPre() |
|||
autocmd QuickFixCmdPost make call cargo#quickfix#CmdPost() |
|||
augroup END |
|||
|
|||
" Ignore general cargo progress messages |
|||
CompilerSet errorformat+= |
|||
\%-G%\\s%#Downloading%.%#, |
|||
\%-G%\\s%#Compiling%.%#, |
|||
\%-G%\\s%#Finished%.%#, |
|||
\%-G%\\s%#error:\ Could\ not\ compile\ %.%#, |
|||
\%-G%\\s%#To\ learn\ more\\,%.%#, |
|||
\%-Gnote:\ Run\ with\ \`RUST_BACKTRACE=%.%#, |
|||
\%.%#panicked\ at\ \\'%m\\'\\,\ %f:%l:%c |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,57 @@ |
|||
" Vim compiler file |
|||
" Compiler: Rust Compiler |
|||
" Maintainer: Chris Morgan <me@chrismorgan.info> |
|||
" Latest Revision: 2013 Jul 12 |
|||
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
|||
|
|||
if exists("current_compiler") |
|||
finish |
|||
endif |
|||
let current_compiler = "rustc" |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
if exists(":CompilerSet") != 2 |
|||
command -nargs=* CompilerSet setlocal <args> |
|||
endif |
|||
|
|||
if get(g:, 'rustc_makeprg_no_percent', 0) |
|||
CompilerSet makeprg=rustc |
|||
else |
|||
if has('patch-7.4.191') |
|||
CompilerSet makeprg=rustc\ \%:S |
|||
else |
|||
CompilerSet makeprg=rustc\ \% |
|||
endif |
|||
endif |
|||
|
|||
" New errorformat (after nightly 2016/08/10) |
|||
CompilerSet errorformat= |
|||
\%-G, |
|||
\%-Gerror:\ aborting\ %.%#, |
|||
\%-Gerror:\ Could\ not\ compile\ %.%#, |
|||
\%Eerror:\ %m, |
|||
\%Eerror[E%n]:\ %m, |
|||
\%Wwarning:\ %m, |
|||
\%Inote:\ %m, |
|||
\%C\ %#-->\ %f:%l:%c, |
|||
\%E\ \ left:%m,%C\ right:%m\ %f:%l:%c,%Z |
|||
|
|||
" Old errorformat (before nightly 2016/08/10) |
|||
CompilerSet errorformat+= |
|||
\%f:%l:%c:\ %t%*[^:]:\ %m, |
|||
\%f:%l:%c:\ %*\\d:%*\\d\ %t%*[^:]:\ %m, |
|||
\%-G%f:%l\ %s, |
|||
\%-G%*[\ ]^, |
|||
\%-G%*[\ ]^%*[~], |
|||
\%-G%*[\ ]... |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,11 @@ |
|||
--langdef=Rust |
|||
--langmap=Rust:.rs |
|||
--regex-Rust=/^[ \t]*(#\[[^\]]\][ \t]*)*(pub[ \t]+)?(extern[ \t]+)?("[^"]+"[ \t]+)?(unsafe[ \t]+)?fn[ \t]+([a-zA-Z0-9_]+)/\6/f,functions,function definitions/ |
|||
--regex-Rust=/^[ \t]*(pub[ \t]+)?type[ \t]+([a-zA-Z0-9_]+)/\2/T,types,type definitions/ |
|||
--regex-Rust=/^[ \t]*(pub[ \t]+)?enum[ \t]+([a-zA-Z0-9_]+)/\2/g,enum,enumeration names/ |
|||
--regex-Rust=/^[ \t]*(pub[ \t]+)?struct[ \t]+([a-zA-Z0-9_]+)/\2/s,structure names/ |
|||
--regex-Rust=/^[ \t]*(pub[ \t]+)?mod[ \t]+([a-zA-Z0-9_]+)/\2/m,modules,module names/ |
|||
--regex-Rust=/^[ \t]*(pub[ \t]+)?(static|const)[ \t]+([a-zA-Z0-9_]+)/\3/c,consts,static constants/ |
|||
--regex-Rust=/^[ \t]*(pub[ \t]+)?trait[ \t]+([a-zA-Z0-9_]+)/\2/t,traits,traits/ |
|||
--regex-Rust=/^[ \t]*(pub[ \t]+)?impl([ \t\n]*<[^>]*>)?[ \t]+(([a-zA-Z0-9_:]+)[ \t]*(<[^>]*>)?[ \t]+(for)[ \t]+)?([a-zA-Z0-9_]+)/\4 \6 \7/i,impls,trait implementations/ |
|||
--regex-Rust=/^[ \t]*macro_rules![ \t]+([a-zA-Z0-9_]+)/\1/d,macros,macro definitions/ |
|||
@ -0,0 +1,475 @@ |
|||
*ft_rust.txt* Filetype plugin for Rust |
|||
|
|||
============================================================================== |
|||
CONTENTS *rust* |
|||
|
|||
1. Introduction |rust-intro| |
|||
2. Settings |rust-settings| |
|||
3. Commands |rust-commands| |
|||
4. Mappings |rust-mappings| |
|||
|
|||
============================================================================== |
|||
INTRODUCTION *rust-intro* |
|||
|
|||
This plugin provides syntax and supporting functionality for the Rust |
|||
filetype. It requires Vim 8 or higher for full functionality. Some commands |
|||
will not work on earlier versions. |
|||
|
|||
============================================================================== |
|||
SETTINGS *rust-settings* |
|||
|
|||
This plugin has a few variables you can define in your vimrc that change the |
|||
behavior of the plugin. |
|||
|
|||
Some variables can be set buffer local (`:b` prefix), and the buffer local |
|||
will take precedence over the global `g:` counterpart. |
|||
|
|||
*g:rustc_path* |
|||
g:rustc_path~ |
|||
Set this option to the path to rustc for use in the |:RustRun| and |
|||
|:RustExpand| commands. If unset, "rustc" will be located in $PATH: > |
|||
let g:rustc_path = $HOME."/bin/rustc" |
|||
< |
|||
|
|||
*g:rustc_makeprg_no_percent* |
|||
g:rustc_makeprg_no_percent~ |
|||
Set this option to 1 to have 'makeprg' default to "rustc" instead of |
|||
"rustc %": > |
|||
let g:rustc_makeprg_no_percent = 1 |
|||
< |
|||
|
|||
*g:rust_conceal* |
|||
g:rust_conceal~ |
|||
Set this option to turn on the basic |conceal| support: > |
|||
let g:rust_conceal = 1 |
|||
< |
|||
|
|||
*g:rust_conceal_mod_path* |
|||
g:rust_conceal_mod_path~ |
|||
Set this option to turn on |conceal| for the path connecting token |
|||
"::": > |
|||
let g:rust_conceal_mod_path = 1 |
|||
< |
|||
|
|||
*g:rust_conceal_pub* |
|||
g:rust_conceal_pub~ |
|||
Set this option to turn on |conceal| for the "pub" token: > |
|||
let g:rust_conceal_pub = 1 |
|||
< |
|||
|
|||
*g:rust_recommended_style* |
|||
g:rust_recommended_style~ |
|||
Set this option to enable vim indentation and textwidth settings to |
|||
conform to style conventions of the rust standard library (i.e. use 4 |
|||
spaces for indents and sets 'textwidth' to 99). This option is enabled |
|||
by default. To disable it: > |
|||
let g:rust_recommended_style = 0 |
|||
< |
|||
|
|||
*g:rust_fold* |
|||
g:rust_fold~ |
|||
Set this option to turn on |folding|: > |
|||
let g:rust_fold = 1 |
|||
< |
|||
Value Effect ~ |
|||
0 No folding |
|||
1 Braced blocks are folded. All folds are open by |
|||
default. |
|||
2 Braced blocks are folded. 'foldlevel' is left at the |
|||
global value (all folds are closed by default). |
|||
|
|||
*g:rust_bang_comment_leader* |
|||
g:rust_bang_comment_leader~ |
|||
Set this option to 1 to preserve the leader on multi-line doc comments |
|||
using the /*! syntax: > |
|||
let g:rust_bang_comment_leader = 1 |
|||
< |
|||
|
|||
*g:rust_use_custom_ctags_defs* |
|||
g:rust_use_custom_ctags_defs~ |
|||
Set this option to 1 if you have customized ctags definitions for Rust |
|||
and do not wish for those included with rust.vim to be used: > |
|||
let g:rust_use_custom_ctags_defs = 1 |
|||
< |
|||
|
|||
NOTE: rust.vim's built-in definitions are only used for the Tagbar Vim |
|||
plugin, if you have it installed, AND if Universal Ctags is not |
|||
detected. This is because Universal Ctags already has built-in |
|||
support for Rust when used with Tagbar. |
|||
|
|||
Also, note that when using ctags other than Universal Ctags, it is not |
|||
automatically used when generating |tags| files that Vim can use to |
|||
navigate to definitions across different source files. Feel free to |
|||
copy `rust.vim/ctags/rust.ctags` into your own `~/.ctags` if you wish |
|||
to generate |tags| files. |
|||
|
|||
|
|||
*g:ftplugin_rust_source_path* |
|||
g:ftplugin_rust_source_path~ |
|||
Set this option to a path that should be prepended to 'path' for Rust |
|||
source files: > |
|||
let g:ftplugin_rust_source_path = $HOME.'/dev/rust' |
|||
< |
|||
|
|||
*g:rustfmt_command* |
|||
g:rustfmt_command~ |
|||
Set this option to the name of the 'rustfmt' executable in your $PATH. If |
|||
not specified it defaults to 'rustfmt' : > |
|||
let g:rustfmt_command = 'rustfmt' |
|||
< |
|||
*g:rustfmt_autosave* |
|||
g:rustfmt_autosave~ |
|||
Set this option to 1 to run |:RustFmt| automatically when saving a |
|||
buffer. If not specified it defaults to 0 : > |
|||
let g:rustfmt_autosave = 0 |
|||
< |
|||
There is also a buffer-local b:rustfmt_autosave that can be set for |
|||
the same purpose, and can override the global setting. |
|||
|
|||
*g:rustfmt_autosave_if_config_present* |
|||
g:rustfmt_autosave_if_config_present~ |
|||
Set this option to 1 to to have *b:rustfmt_autosave* be set automatically |
|||
if a `rustfmt.toml` file is present in any parent directly leading to |
|||
the file being edited. If not set, default to 0: > |
|||
|
|||
let g:rustfmt_autosave_if_config_present = 0 |
|||
< |
|||
This is useful to have `rustfmt` only execute on save, on projects |
|||
that have `rustfmt.toml` configuration. |
|||
|
|||
There is also a buffer-local b:rustfmt_autosave_if_config_present |
|||
that can be set for the same purpose, which can overrides the global |
|||
setting. |
|||
*g:rustfmt_fail_silently* |
|||
g:rustfmt_fail_silently~ |
|||
Set this option to 1 to prevent 'rustfmt' from populating the |
|||
|location-list| with errors. If not specified it defaults to 0: > |
|||
let g:rustfmt_fail_silently = 0 |
|||
< |
|||
*g:rustfmt_options* |
|||
g:rustfmt_options~ |
|||
Set this option to a string of options to pass to 'rustfmt'. The |
|||
write-mode is already set to 'overwrite'. If not specified it |
|||
defaults to '' : > |
|||
let g:rustfmt_options = '' |
|||
< |
|||
*g:rustfmt_emit_files* |
|||
g:rustfmt_emit_files~ |
|||
If not specified rust.vim tries to detect the right parameter to |
|||
pass to rustfmt based on its reported version. Otherwise, it |
|||
determines whether to run rustfmt with '--emit=files' (when 1 is |
|||
provided) instead of '--write-mode=overwrite'. > |
|||
let g:rustfmt_emit_files = 0 |
|||
|
|||
*g:rust_playpen_url* |
|||
g:rust_playpen_url~ |
|||
Set this option to override the url for the playpen to use: > |
|||
let g:rust_playpen_url = 'https://play.rust-lang.org/' |
|||
< |
|||
|
|||
*g:rust_shortener_url* |
|||
g:rust_shortener_url~ |
|||
Set this option to override the url for the url shortener: > |
|||
let g:rust_shortener_url = 'https://is.gd/' |
|||
< |
|||
|
|||
*g:rust_clip_command* |
|||
g:rust_clip_command~ |
|||
Set this option to the command used in your OS to copy the Rust Play |
|||
url to the clipboard: > |
|||
let g:rust_clip_command = 'xclip -selection clipboard' |
|||
< |
|||
|
|||
*g:cargo_makeprg_params* |
|||
g:cargo_makeprg_params~ |
|||
Set this option to the string of parameters to pass to cargo. If not |
|||
specified it defaults to '$*' : > |
|||
let g:cargo_makeprg_params = 'build' |
|||
< |
|||
|
|||
|
|||
Integration with Syntastic *rust-syntastic* |
|||
-------------------------- |
|||
|
|||
This plugin automatically integrates with the Syntastic checker. There are two |
|||
checkers provided: 'rustc', and 'cargo'. The later invokes 'Cargo' in order to |
|||
build code, and the former delivers a single edited '.rs' file as a compilation |
|||
target directly to the Rust compiler, `rustc`. |
|||
|
|||
Because Cargo is almost exclusively being used for building Rust code these |
|||
days, 'cargo' is the default checker. > |
|||
|
|||
let g:syntastic_rust_checkers = ['cargo'] |
|||
< |
|||
If you would like to change it, you can set `g:syntastic_rust_checkers` to a |
|||
different value. |
|||
*g:rust_cargo_avoid_whole_workspace* |
|||
*b:rust_cargo_avoid_whole_workspace* |
|||
g:rust_cargo_avoid_whole_workspace~ |
|||
When editing a crate that is part of a Cargo workspace, and this |
|||
option is set to 1 (the default), then 'cargo' will be executed |
|||
directly in that crate directory instead of in the workspace |
|||
directory. Setting 0 prevents this behavior - however be aware that if |
|||
you are working in large workspace, Cargo commands may take more time, |
|||
plus the Syntastic error list may include all the crates in the |
|||
workspace. > |
|||
let g:rust_cargo_avoid_whole_workspace = 0 |
|||
< |
|||
*g:rust_cargo_check_all_targets* |
|||
*b:rust_cargo_check_all_targets* |
|||
g:rust_cargo_check_all_targets~ |
|||
When set to 1, the `--all-targets` option will be passed to cargo when |
|||
Syntastic executes it, allowing the linting of all targets under the |
|||
package. |
|||
The default is 0. |
|||
|
|||
*g:rust_cargo_check_all_features* |
|||
*b:rust_cargo_check_all_features* |
|||
g:rust_cargo_check_all_features~ |
|||
When set to 1, the `--all-features` option will be passed to cargo when |
|||
Syntastic executes it, allowing the linting of all features of the |
|||
package. |
|||
The default is 0. |
|||
|
|||
*g:rust_cargo_check_examples* |
|||
*b:rust_cargo_check_examples* |
|||
g:rust_cargo_check_examples~ |
|||
When set to 1, the `--examples` option will be passed to cargo when |
|||
Syntastic executes it, to prevent the exclusion of examples from |
|||
linting. The examples are normally under the `examples/` directory of |
|||
the crate. |
|||
The default is 0. |
|||
|
|||
*g:rust_cargo_check_tests* |
|||
*b:rust_cargo_check_tests* |
|||
g:rust_cargo_check_tests~ |
|||
When set to 1, the `--tests` option will be passed to cargo when |
|||
Syntastic executes it, to prevent the exclusion of tests from linting. |
|||
The tests are normally under the `tests/` directory of the crate. |
|||
The default is 0. |
|||
|
|||
*g:rust_cargo_check_benches* |
|||
*b:rust_cargo_check_benches* |
|||
g:rust_cargo_check_benches~ |
|||
When set to 1, the `--benches` option will be passed to cargo when |
|||
Syntastic executes it. The benches are normally under the `benches/` |
|||
directory of the crate. |
|||
The default is 0. |
|||
|
|||
Integration with auto-pairs *rust-auto-pairs* |
|||
--------------------------- |
|||
|
|||
This plugin automatically configures the auto-pairs plugin not to duplicate |
|||
single quotes, which are used more often for lifetime annotations than for |
|||
single character literals. |
|||
|
|||
*g:rust_keep_autopairs_default* |
|||
g:rust_keep_autopairs_default~ |
|||
|
|||
Don't override auto-pairs default for the Rust filetype. The default |
|||
is 0. |
|||
|
|||
============================================================================== |
|||
COMMANDS *rust-commands* |
|||
|
|||
Invoking Cargo |
|||
-------------- |
|||
|
|||
This plug defines very simple shortcuts for invoking Cargo from with Vim. |
|||
|
|||
:Cargo <args> *:Cargo* |
|||
Runs 'cargo' with the provided arguments. |
|||
|
|||
:Cbuild <args> *:Cbuild* |
|||
Shortcut for 'cargo build`. |
|||
|
|||
:Cclean <args> *:Cclean* |
|||
Shortcut for 'cargo clean`. |
|||
|
|||
:Cdoc <args> *:Cdoc* |
|||
Shortcut for 'cargo doc`. |
|||
|
|||
:Cinit <args> *:Cinit* |
|||
Shortcut for 'cargo init`. |
|||
|
|||
:Crun <args> *:Crun* |
|||
Shortcut for 'cargo run`. |
|||
|
|||
:Ctest <args> *:Ctest* |
|||
Shortcut for 'cargo test`. |
|||
|
|||
:Cupdate <args> *:Cupdate* |
|||
Shortcut for 'cargo update`. |
|||
|
|||
:Cbench <args> *:Cbench* |
|||
Shortcut for 'cargo bench`. |
|||
|
|||
:Csearch <args> *:Csearch* |
|||
Shortcut for 'cargo search`. |
|||
|
|||
:Cpublish <args> *:Cpublish* |
|||
Shortcut for 'cargo publish`. |
|||
|
|||
:Cinstall <args> *:Cinstall* |
|||
Shortcut for 'cargo install`. |
|||
|
|||
:Cruntarget <args> *:Cruntarget* |
|||
Shortcut for 'cargo run --bin' or 'cargo run --example', |
|||
depending on the currently open buffer. |
|||
|
|||
Formatting |
|||
---------- |
|||
|
|||
:RustFmt *:RustFmt* |
|||
Runs |g:rustfmt_command| on the current buffer. If |
|||
|g:rustfmt_options| is set then those will be passed to the |
|||
executable. |
|||
|
|||
If |g:rustfmt_fail_silently| is 0 (the default) then it |
|||
will populate the |location-list| with the errors from |
|||
|g:rustfmt_command|. If |g:rustfmt_fail_silently| is set to 1 |
|||
then it will not populate the |location-list|. |
|||
|
|||
:RustFmtRange *:RustFmtRange* |
|||
Runs |g:rustfmt_command| with selected range. See |
|||
|:RustFmt| for any other information. |
|||
|
|||
|
|||
Playpen integration |
|||
------------------- |
|||
|
|||
:RustPlay *:RustPlay* |
|||
This command will only work if you have web-api.vim installed |
|||
(available at https://github.com/mattn/webapi-vim). It sends the |
|||
current selection, or if nothing is selected, the entirety of the |
|||
current buffer to the Rust playpen, and emits a message with the |
|||
shortened URL to the playpen. |
|||
|
|||
|g:rust_playpen_url| is the base URL to the playpen, by default |
|||
"https://play.rust-lang.org/". |
|||
|
|||
|g:rust_shortener_url| is the base url for the shorterner, by |
|||
default "https://is.gd/" |
|||
|
|||
|g:rust_clip_command| is the command to run to copy the |
|||
playpen url to the clipboard of your system. |
|||
|
|||
Evaulation of a single Rust file |
|||
-------------------------------- |
|||
|
|||
NOTE: These commands are useful only when working with standalone Rust files, |
|||
which is usually not the case for common Rust development. If you wish to |
|||
building Rust crates from with Vim can should use Vim's make, Syntastic, or |
|||
functionality from other plugins. |
|||
|
|||
|
|||
:RustRun [args] *:RustRun* |
|||
:RustRun! [rustc-args] [--] [args] |
|||
Compiles and runs the current file. If it has unsaved changes, |
|||
it will be saved first using |:update|. If the current file is |
|||
an unnamed buffer, it will be written to a temporary file |
|||
first. The compiled binary is always placed in a temporary |
|||
directory, but is run from the current directory. |
|||
|
|||
The arguments given to |:RustRun| will be passed to the |
|||
compiled binary. |
|||
|
|||
If ! is specified, the arguments are passed to rustc instead. |
|||
A "--" argument will separate the rustc arguments from the |
|||
arguments passed to the binary. |
|||
|
|||
If |g:rustc_path| is defined, it is used as the path to rustc. |
|||
Otherwise it is assumed rustc can be found in $PATH. |
|||
|
|||
:RustExpand [args] *:RustExpand* |
|||
:RustExpand! [TYPE] [args] |
|||
Expands the current file using --pretty and displays the |
|||
results in a new split. If the current file has unsaved |
|||
changes, it will be saved first using |:update|. If the |
|||
current file is an unnamed buffer, it will be written to a |
|||
temporary file first. |
|||
|
|||
The arguments given to |:RustExpand| will be passed to rustc. |
|||
This is largely intended for specifying various --cfg |
|||
configurations. |
|||
|
|||
If ! is specified, the first argument is the expansion type to |
|||
pass to rustc --pretty. Otherwise it will default to |
|||
"expanded". |
|||
|
|||
If |g:rustc_path| is defined, it is used as the path to rustc. |
|||
Otherwise it is assumed rustc can be found in $PATH. |
|||
|
|||
:RustEmitIr [args] *:RustEmitIr* |
|||
Compiles the current file to LLVM IR and displays the results |
|||
in a new split. If the current file has unsaved changes, it |
|||
will be saved first using |:update|. If the current file is an |
|||
unnamed buffer, it will be written to a temporary file first. |
|||
|
|||
The arguments given to |:RustEmitIr| will be passed to rustc. |
|||
|
|||
If |g:rustc_path| is defined, it is used as the path to rustc. |
|||
Otherwise it is assumed rustc can be found in $PATH. |
|||
|
|||
:RustEmitAsm [args] *:RustEmitAsm* |
|||
Compiles the current file to assembly and displays the results |
|||
in a new split. If the current file has unsaved changes, it |
|||
will be saved first using |:update|. If the current file is an |
|||
unnamed buffer, it will be written to a temporary file first. |
|||
|
|||
The arguments given to |:RustEmitAsm| will be passed to rustc. |
|||
|
|||
If |g:rustc_path| is defined, it is used as the path to rustc. |
|||
Otherwise it is assumed rustc can be found in $PATH. |
|||
|
|||
|
|||
Running test(s) |
|||
--------------- |
|||
|
|||
:[N]RustTest[!] [options] *:RustTest* |
|||
Runs a test under the cursor when the current buffer is in a |
|||
cargo project with "cargo test" command. If the command did |
|||
not find any test function under the cursor, it stops with an |
|||
error message. |
|||
|
|||
When N is given, adjust the size of the new window to N lines |
|||
or columns. |
|||
|
|||
When ! is given, runs all tests regardless of current cursor |
|||
position. |
|||
|
|||
When [options] is given, it is passed to "cargo" command |
|||
arguments. |
|||
|
|||
When the current buffer is outside cargo project, the command |
|||
runs "rustc --test" command instead of "cargo test" as |
|||
fallback. All tests are run regardless of adding ! since there |
|||
is no way to run specific test function with rustc. [options] |
|||
is passed to "rustc" command arguments in the case. |
|||
|
|||
Takes optional modifiers (see |<mods>|): > |
|||
:tab RustTest |
|||
:belowright 16RustTest |
|||
:leftabove vert 80RustTest |
|||
< |
|||
rust.vim Debugging |
|||
------------------ |
|||
|
|||
:RustInfo *:RustInfo* |
|||
Emits debugging info of the Vim Rust plugin. |
|||
|
|||
:RustInfoToClipboard *:RustInfoClipboard* |
|||
Saves debugging info of the Vim Rust plugin to the default |
|||
register. |
|||
|
|||
:RustInfoToFile [filename] *:RustInfoToFile* |
|||
Saves debugging info of the Vim Rust plugin to the the given |
|||
file, overwritting it. |
|||
|
|||
============================================================================== |
|||
MAPPINGS *rust-mappings* |
|||
|
|||
This plugin defines mappings for |[[| and |]]| to support hanging indents. |
|||
|
|||
============================================================================== |
|||
vim:tw=78:sw=4:noet:ts=8:ft=help:norl: |
|||
@ -0,0 +1,15 @@ |
|||
" vint: -ProhibitAutocmdWithNoGroup |
|||
|
|||
autocmd BufRead,BufNewFile *.rs call s:set_rust_filetype() |
|||
|
|||
if has('patch-8.0.613') |
|||
autocmd BufRead,BufNewFile Cargo.toml setf FALLBACK cfg |
|||
endif |
|||
|
|||
function! s:set_rust_filetype() abort |
|||
if &filetype !=# 'rust' |
|||
set filetype=rust |
|||
endif |
|||
endfunction |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,200 @@ |
|||
" Language: Rust |
|||
" Description: Vim ftplugin for Rust |
|||
" Maintainer: Chris Morgan <me@chrismorgan.info> |
|||
" Last Change: June 08, 2016 |
|||
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
|||
|
|||
if exists("b:did_ftplugin") |
|||
finish |
|||
endif |
|||
let b:did_ftplugin = 1 |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
if get(b:, 'current_compiler', '') ==# '' |
|||
if strlen(findfile('Cargo.toml', '.;')) > 0 |
|||
compiler cargo |
|||
else |
|||
compiler rustc |
|||
endif |
|||
endif |
|||
|
|||
" Variables {{{1 |
|||
|
|||
" The rust source code at present seems to typically omit a leader on /*! |
|||
" comments, so we'll use that as our default, but make it easy to switch. |
|||
" This does not affect indentation at all (I tested it with and without |
|||
" leader), merely whether a leader is inserted by default or not. |
|||
if get(g:, 'rust_bang_comment_leader', 0) |
|||
" Why is the `,s0:/*,mb:\ ,ex:*/` there, you ask? I don't understand why, |
|||
" but without it, */ gets indented one space even if there were no |
|||
" leaders. I'm fairly sure that's a Vim bug. |
|||
setlocal comments=s1:/*,mb:*,ex:*/,s0:/*,mb:\ ,ex:*/,:///,://!,:// |
|||
else |
|||
setlocal comments=s0:/*!,ex:*/,s1:/*,mb:*,ex:*/,:///,://!,:// |
|||
endif |
|||
setlocal commentstring=//%s |
|||
setlocal formatoptions-=t formatoptions+=croqnl |
|||
" j was only added in 7.3.541, so stop complaints about its nonexistence |
|||
silent! setlocal formatoptions+=j |
|||
|
|||
" smartindent will be overridden by indentexpr if filetype indent is on, but |
|||
" otherwise it's better than nothing. |
|||
setlocal smartindent nocindent |
|||
|
|||
if get(g:, 'rust_recommended_style', 1) |
|||
let b:rust_set_style = 1 |
|||
setlocal tabstop=8 shiftwidth=4 softtabstop=4 expandtab |
|||
setlocal textwidth=99 |
|||
endif |
|||
|
|||
setlocal include=\\v^\\s*(pub\\s+)?use\\s+\\zs(\\f\|:)+ |
|||
setlocal includeexpr=rust#IncludeExpr(v:fname) |
|||
|
|||
setlocal suffixesadd=.rs |
|||
|
|||
if exists("g:ftplugin_rust_source_path") |
|||
let &l:path=g:ftplugin_rust_source_path . ',' . &l:path |
|||
endif |
|||
|
|||
if exists("g:loaded_delimitMate") |
|||
if exists("b:delimitMate_excluded_regions") |
|||
let b:rust_original_delimitMate_excluded_regions = b:delimitMate_excluded_regions |
|||
endif |
|||
|
|||
augroup rust.vim.DelimitMate |
|||
autocmd! |
|||
|
|||
autocmd User delimitMate_map :call rust#delimitmate#onMap() |
|||
autocmd User delimitMate_unmap :call rust#delimitmate#onUnmap() |
|||
augroup END |
|||
endif |
|||
|
|||
" Integration with auto-pairs (https://github.com/jiangmiao/auto-pairs) |
|||
if exists("g:AutoPairsLoaded") && !get(g:, 'rust_keep_autopairs_default', 0) |
|||
let b:AutoPairs = {'(':')', '[':']', '{':'}','"':'"', '`':'`'} |
|||
endif |
|||
|
|||
if has("folding") && get(g:, 'rust_fold', 0) |
|||
let b:rust_set_foldmethod=1 |
|||
setlocal foldmethod=syntax |
|||
if g:rust_fold == 2 |
|||
setlocal foldlevel< |
|||
else |
|||
setlocal foldlevel=99 |
|||
endif |
|||
endif |
|||
|
|||
if has('conceal') && get(g:, 'rust_conceal', 0) |
|||
let b:rust_set_conceallevel=1 |
|||
setlocal conceallevel=2 |
|||
endif |
|||
|
|||
" Motion Commands {{{1 |
|||
|
|||
" Bind motion commands to support hanging indents |
|||
nnoremap <silent> <buffer> [[ :call rust#Jump('n', 'Back')<CR> |
|||
nnoremap <silent> <buffer> ]] :call rust#Jump('n', 'Forward')<CR> |
|||
xnoremap <silent> <buffer> [[ :call rust#Jump('v', 'Back')<CR> |
|||
xnoremap <silent> <buffer> ]] :call rust#Jump('v', 'Forward')<CR> |
|||
onoremap <silent> <buffer> [[ :call rust#Jump('o', 'Back')<CR> |
|||
onoremap <silent> <buffer> ]] :call rust#Jump('o', 'Forward')<CR> |
|||
|
|||
" Commands {{{1 |
|||
|
|||
" See |:RustRun| for docs |
|||
command! -nargs=* -complete=file -bang -buffer RustRun call rust#Run(<bang>0, <q-args>) |
|||
|
|||
" See |:RustExpand| for docs |
|||
command! -nargs=* -complete=customlist,rust#CompleteExpand -bang -buffer RustExpand call rust#Expand(<bang>0, <q-args>) |
|||
|
|||
" See |:RustEmitIr| for docs |
|||
command! -nargs=* -buffer RustEmitIr call rust#Emit("llvm-ir", <q-args>) |
|||
|
|||
" See |:RustEmitAsm| for docs |
|||
command! -nargs=* -buffer RustEmitAsm call rust#Emit("asm", <q-args>) |
|||
|
|||
" See |:RustPlay| for docs |
|||
command! -range=% RustPlay :call rust#Play(<count>, <line1>, <line2>, <f-args>) |
|||
|
|||
" See |:RustFmt| for docs |
|||
command! -buffer RustFmt call rustfmt#Format() |
|||
|
|||
" See |:RustFmtRange| for docs |
|||
command! -range -buffer RustFmtRange call rustfmt#FormatRange(<line1>, <line2>) |
|||
|
|||
" See |:RustInfo| for docs |
|||
command! -bar RustInfo call rust#debugging#Info() |
|||
|
|||
" See |:RustInfoToClipboard| for docs |
|||
command! -bar RustInfoToClipboard call rust#debugging#InfoToClipboard() |
|||
|
|||
" See |:RustInfoToFile| for docs |
|||
command! -bar -nargs=1 RustInfoToFile call rust#debugging#InfoToFile(<f-args>) |
|||
|
|||
" See |:RustTest| for docs |
|||
command! -buffer -nargs=* -count -bang RustTest call rust#Test(<q-mods>, <count>, <bang>0, <q-args>) |
|||
|
|||
if !exists("b:rust_last_rustc_args") || !exists("b:rust_last_args") |
|||
let b:rust_last_rustc_args = [] |
|||
let b:rust_last_args = [] |
|||
endif |
|||
|
|||
" Cleanup {{{1 |
|||
|
|||
let b:undo_ftplugin = " |
|||
\ setlocal formatoptions< comments< commentstring< include< includeexpr< suffixesadd< |
|||
\|if exists('b:rust_set_style') |
|||
\|setlocal tabstop< shiftwidth< softtabstop< expandtab< textwidth< |
|||
\|endif |
|||
\|if exists('b:rust_original_delimitMate_excluded_regions') |
|||
\|let b:delimitMate_excluded_regions = b:rust_original_delimitMate_excluded_regions |
|||
\|unlet b:rust_original_delimitMate_excluded_regions |
|||
\|else |
|||
\|unlet! b:delimitMate_excluded_regions |
|||
\|endif |
|||
\|if exists('b:rust_set_foldmethod') |
|||
\|setlocal foldmethod< foldlevel< |
|||
\|unlet b:rust_set_foldmethod |
|||
\|endif |
|||
\|if exists('b:rust_set_conceallevel') |
|||
\|setlocal conceallevel< |
|||
\|unlet b:rust_set_conceallevel |
|||
\|endif |
|||
\|unlet! b:rust_last_rustc_args b:rust_last_args |
|||
\|delcommand RustRun |
|||
\|delcommand RustExpand |
|||
\|delcommand RustEmitIr |
|||
\|delcommand RustEmitAsm |
|||
\|delcommand RustPlay |
|||
\|nunmap <buffer> [[ |
|||
\|nunmap <buffer> ]] |
|||
\|xunmap <buffer> [[ |
|||
\|xunmap <buffer> ]] |
|||
\|ounmap <buffer> [[ |
|||
\|ounmap <buffer> ]] |
|||
\|setlocal matchpairs-=<:> |
|||
\|unlet b:match_skip |
|||
\" |
|||
|
|||
" }}}1 |
|||
|
|||
" Code formatting on save |
|||
augroup rust.vim.PreWrite |
|||
autocmd! |
|||
autocmd BufWritePre *.rs silent! call rustfmt#PreWrite() |
|||
augroup END |
|||
|
|||
setlocal matchpairs+=<:> |
|||
" For matchit.vim (rustArrow stops `Fn() -> X` messing things up) |
|||
let b:match_skip = 's:comment\|string\|rustCharacter\|rustArrow' |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,40 @@ |
|||
" |
|||
" Support for Tagbar -- https://github.com/majutsushi/tagbar |
|||
" |
|||
if !exists(':Tagbar') || rust#tags#IsUCtags() |
|||
finish |
|||
endif |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
if !exists('g:tagbar_type_rust') |
|||
let g:tagbar_type_rust = { |
|||
\ 'ctagstype' : 'rust', |
|||
\ 'kinds' : [ |
|||
\'T:types', |
|||
\'f:functions', |
|||
\'g:enumerations', |
|||
\'s:structures', |
|||
\'m:modules', |
|||
\'c:constants', |
|||
\'t:traits', |
|||
\'i:trait implementations', |
|||
\ ] |
|||
\ } |
|||
endif |
|||
|
|||
" In case you've updated/customized your ~/.ctags and prefer to use it. |
|||
if !get(g:, 'rust_use_custom_ctags_defs', 0) |
|||
let g:tagbar_type_rust.deffile = expand('<sfile>:p:h:h:h') . '/ctags/rust.ctags' |
|||
endif |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,284 @@ |
|||
" Vim indent file |
|||
" Language: Rust |
|||
" Author: Chris Morgan <me@chrismorgan.info> |
|||
" Last Change: 2018 Jan 10 |
|||
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
|||
|
|||
" Only load this indent file when no other was loaded. |
|||
if exists("b:did_indent") |
|||
finish |
|||
endif |
|||
let b:did_indent = 1 |
|||
|
|||
setlocal cindent |
|||
setlocal cinoptions=L0,(s,Ws,J1,j1,m1 |
|||
setlocal cinkeys=0{,0},!^F,o,O,0[,0],0(,0) |
|||
" Don't think cinwords will actually do anything at all... never mind |
|||
setlocal cinwords=for,if,else,while,loop,impl,mod,unsafe,trait,struct,enum,fn,extern,macro |
|||
|
|||
" Some preliminary settings |
|||
setlocal nolisp " Make sure lisp indenting doesn't supersede us |
|||
setlocal autoindent " indentexpr isn't much help otherwise |
|||
" Also do indentkeys, otherwise # gets shoved to column 0 :-/ |
|||
setlocal indentkeys=0{,0},!^F,o,O,0[,0],0(,0) |
|||
|
|||
setlocal indentexpr=GetRustIndent(v:lnum) |
|||
|
|||
" Only define the function once. |
|||
if exists("*GetRustIndent") |
|||
finish |
|||
endif |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
" Come here when loading the script the first time. |
|||
|
|||
function! s:get_line_trimmed(lnum) |
|||
" Get the line and remove a trailing comment. |
|||
" Use syntax highlighting attributes when possible. |
|||
" NOTE: this is not accurate; /* */ or a line continuation could trick it |
|||
let line = getline(a:lnum) |
|||
let line_len = strlen(line) |
|||
if has('syntax_items') |
|||
" If the last character in the line is a comment, do a binary search for |
|||
" the start of the comment. synID() is slow, a linear search would take |
|||
" too long on a long line. |
|||
if synIDattr(synID(a:lnum, line_len, 1), "name") =~? 'Comment\|Todo' |
|||
let min = 1 |
|||
let max = line_len |
|||
while min < max |
|||
let col = (min + max) / 2 |
|||
if synIDattr(synID(a:lnum, col, 1), "name") =~? 'Comment\|Todo' |
|||
let max = col |
|||
else |
|||
let min = col + 1 |
|||
endif |
|||
endwhile |
|||
let line = strpart(line, 0, min - 1) |
|||
endif |
|||
return substitute(line, "\s*$", "", "") |
|||
else |
|||
" Sorry, this is not complete, nor fully correct (e.g. string "//"). |
|||
" Such is life. |
|||
return substitute(line, "\s*//.*$", "", "") |
|||
endif |
|||
endfunction |
|||
|
|||
function! s:is_string_comment(lnum, col) |
|||
if has('syntax_items') |
|||
for id in synstack(a:lnum, a:col) |
|||
let synname = synIDattr(id, "name") |
|||
if synname ==# "rustString" || synname =~# "^rustComment" |
|||
return 1 |
|||
endif |
|||
endfor |
|||
else |
|||
" without syntax, let's not even try |
|||
return 0 |
|||
endif |
|||
endfunction |
|||
|
|||
if exists('*shiftwidth') |
|||
function! s:shiftwidth() |
|||
return shiftwidth() |
|||
endfunc |
|||
else |
|||
function! s:shiftwidth() |
|||
return &shiftwidth |
|||
endfunc |
|||
endif |
|||
|
|||
function GetRustIndent(lnum) |
|||
" Starting assumption: cindent (called at the end) will do it right |
|||
" normally. We just want to fix up a few cases. |
|||
|
|||
let line = getline(a:lnum) |
|||
|
|||
if has('syntax_items') |
|||
let synname = synIDattr(synID(a:lnum, 1, 1), "name") |
|||
if synname ==# "rustString" |
|||
" If the start of the line is in a string, don't change the indent |
|||
return -1 |
|||
elseif synname =~? '\(Comment\|Todo\)' |
|||
\ && line !~# '^\s*/\*' " not /* opening line |
|||
if synname =~? "CommentML" " multi-line |
|||
if line !~# '^\s*\*' && getline(a:lnum - 1) =~# '^\s*/\*' |
|||
" This is (hopefully) the line after a /*, and it has no |
|||
" leader, so the correct indentation is that of the |
|||
" previous line. |
|||
return GetRustIndent(a:lnum - 1) |
|||
endif |
|||
endif |
|||
" If it's in a comment, let cindent take care of it now. This is |
|||
" for cases like "/*" where the next line should start " * ", not |
|||
" "* " as the code below would otherwise cause for module scope |
|||
" Fun fact: " /*\n*\n*/" takes two calls to get right! |
|||
return cindent(a:lnum) |
|||
endif |
|||
endif |
|||
|
|||
" cindent gets second and subsequent match patterns/struct members wrong, |
|||
" as it treats the comma as indicating an unfinished statement:: |
|||
" |
|||
" match a { |
|||
" b => c, |
|||
" d => e, |
|||
" f => g, |
|||
" }; |
|||
|
|||
" Search backwards for the previous non-empty line. |
|||
let prevlinenum = prevnonblank(a:lnum - 1) |
|||
let prevline = s:get_line_trimmed(prevlinenum) |
|||
while prevlinenum > 1 && prevline !~# '[^[:blank:]]' |
|||
let prevlinenum = prevnonblank(prevlinenum - 1) |
|||
let prevline = s:get_line_trimmed(prevlinenum) |
|||
endwhile |
|||
|
|||
" A standalone '{', '}', or 'where' |
|||
let l:standalone_open = line =~# '\V\^\s\*{\s\*\$' |
|||
let l:standalone_close = line =~# '\V\^\s\*}\s\*\$' |
|||
let l:standalone_where = line =~# '\V\^\s\*where\s\*\$' |
|||
if l:standalone_open || l:standalone_close || l:standalone_where |
|||
" ToDo: we can search for more items than 'fn' and 'if'. |
|||
let [l:found_line, l:col, l:submatch] = |
|||
\ searchpos('\<\(fn\)\|\(if\)\>', 'bnWp') |
|||
if l:found_line !=# 0 |
|||
" Now we count the number of '{' and '}' in between the match |
|||
" locations and the current line (there is probably a better |
|||
" way to compute this). |
|||
let l:i = l:found_line |
|||
let l:search_line = strpart(getline(l:i), l:col - 1) |
|||
let l:opens = 0 |
|||
let l:closes = 0 |
|||
while l:i < a:lnum |
|||
let l:search_line2 = substitute(l:search_line, '\V{', '', 'g') |
|||
let l:opens += strlen(l:search_line) - strlen(l:search_line2) |
|||
let l:search_line3 = substitute(l:search_line2, '\V}', '', 'g') |
|||
let l:closes += strlen(l:search_line2) - strlen(l:search_line3) |
|||
let l:i += 1 |
|||
let l:search_line = getline(l:i) |
|||
endwhile |
|||
if l:standalone_open || l:standalone_where |
|||
if l:opens ==# l:closes |
|||
return indent(l:found_line) |
|||
endif |
|||
else |
|||
" Expect to find just one more close than an open |
|||
if l:opens ==# l:closes + 1 |
|||
return indent(l:found_line) |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
|
|||
" A standalone 'where' adds a shift. |
|||
let l:standalone_prevline_where = prevline =~# '\V\^\s\*where\s\*\$' |
|||
if l:standalone_prevline_where |
|||
return indent(prevlinenum) + 4 |
|||
endif |
|||
|
|||
" Handle where clauses nicely: subsequent values should line up nicely. |
|||
if prevline[len(prevline) - 1] ==# "," |
|||
\ && prevline =~# '^\s*where\s' |
|||
return indent(prevlinenum) + 6 |
|||
endif |
|||
|
|||
let l:last_prevline_character = prevline[len(prevline) - 1] |
|||
|
|||
" A line that ends with '.<expr>;' is probably an end of a long list |
|||
" of method operations. |
|||
if prevline =~# '\V\^\s\*.' && l:last_prevline_character ==# ';' |
|||
call cursor(a:lnum - 1, 1) |
|||
let l:scope_start = searchpair('{\|(', '', '}\|)', 'nbW', |
|||
\ 's:is_string_comment(line("."), col("."))') |
|||
if l:scope_start != 0 && l:scope_start < a:lnum |
|||
return indent(l:scope_start) + 4 |
|||
endif |
|||
endif |
|||
|
|||
if l:last_prevline_character ==# "," |
|||
\ && s:get_line_trimmed(a:lnum) !~# '^\s*[\[\]{})]' |
|||
\ && prevline !~# '^\s*fn\s' |
|||
\ && prevline !~# '([^()]\+,$' |
|||
\ && s:get_line_trimmed(a:lnum) !~# '^\s*\S\+\s*=>' |
|||
" Oh ho! The previous line ended in a comma! I bet cindent will try to |
|||
" take this too far... For now, let's normally use the previous line's |
|||
" indent. |
|||
|
|||
" One case where this doesn't work out is where *this* line contains |
|||
" square or curly brackets; then we normally *do* want to be indenting |
|||
" further. |
|||
" |
|||
" Another case where we don't want to is one like a function |
|||
" definition with arguments spread over multiple lines: |
|||
" |
|||
" fn foo(baz: Baz, |
|||
" baz: Baz) // <-- cindent gets this right by itself |
|||
" |
|||
" Another case is similar to the previous, except calling a function |
|||
" instead of defining it, or any conditional expression that leaves |
|||
" an open paren: |
|||
" |
|||
" foo(baz, |
|||
" baz); |
|||
" |
|||
" if baz && (foo || |
|||
" bar) { |
|||
" |
|||
" Another case is when the current line is a new match arm. |
|||
" |
|||
" There are probably other cases where we don't want to do this as |
|||
" well. Add them as needed. |
|||
return indent(prevlinenum) |
|||
endif |
|||
|
|||
if !has("patch-7.4.355") |
|||
" cindent before 7.4.355 doesn't do the module scope well at all; e.g.:: |
|||
" |
|||
" static FOO : &'static [bool] = [ |
|||
" true, |
|||
" false, |
|||
" false, |
|||
" true, |
|||
" ]; |
|||
" |
|||
" uh oh, next statement is indented further! |
|||
|
|||
" Note that this does *not* apply the line continuation pattern properly; |
|||
" that's too hard to do correctly for my liking at present, so I'll just |
|||
" start with these two main cases (square brackets and not returning to |
|||
" column zero) |
|||
|
|||
call cursor(a:lnum, 1) |
|||
if searchpair('{\|(', '', '}\|)', 'nbW', |
|||
\ 's:is_string_comment(line("."), col("."))') == 0 |
|||
if searchpair('\[', '', '\]', 'nbW', |
|||
\ 's:is_string_comment(line("."), col("."))') == 0 |
|||
" Global scope, should be zero |
|||
return 0 |
|||
else |
|||
" At the module scope, inside square brackets only |
|||
"if getline(a:lnum)[0] == ']' || search('\[', '', '\]', 'nW') == a:lnum |
|||
if line =~# "^\\s*]" |
|||
" It's the closing line, dedent it |
|||
return 0 |
|||
else |
|||
return &shiftwidth |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
|
|||
" Fall back on cindent, which does it mostly right |
|||
return cindent(a:lnum) |
|||
endfunction |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,26 @@ |
|||
if exists('g:loaded_rust_vim_plugin_cargo') |
|||
finish |
|||
endif |
|||
let g:loaded_rust_vim_plugin_cargo = 1 |
|||
let s:save_cpo = &cpoptions |
|||
set cpoptions&vim |
|||
|
|||
command! -nargs=+ Cargo call cargo#cmd(<q-args>) |
|||
command! -nargs=* Cbuild call cargo#build(<q-args>) |
|||
command! -nargs=* Cclean call cargo#clean(<q-args>) |
|||
command! -nargs=* Cdoc call cargo#doc(<q-args>) |
|||
command! -nargs=+ Cnew call cargo#new(<q-args>) |
|||
command! -nargs=* Cinit call cargo#init(<q-args>) |
|||
command! -nargs=* Crun call cargo#run(<q-args>) |
|||
command! -nargs=* Ctest call cargo#test(<q-args>) |
|||
command! -nargs=* Cbench call cargo#bench(<q-args>) |
|||
command! -nargs=* Cupdate call cargo#update(<q-args>) |
|||
command! -nargs=* Csearch call cargo#search(<q-args>) |
|||
command! -nargs=* Cpublish call cargo#publish(<q-args>) |
|||
command! -nargs=* Cinstall call cargo#install(<q-args>) |
|||
command! -nargs=* Cruntarget call cargo#runtarget(<q-args>) |
|||
|
|||
let &cpoptions = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,28 @@ |
|||
" Vim syntastic plugin helper |
|||
" Language: Rust |
|||
" Maintainer: Andrew Gallant <jamslam@gmail.com> |
|||
|
|||
if exists('g:loaded_rust_vim') |
|||
finish |
|||
endif |
|||
let g:loaded_rust_vim = 1 |
|||
let s:save_cpo = &cpoptions |
|||
set cpoptions&vim |
|||
|
|||
" This is to let Syntastic know about the Rust filetype. |
|||
" It enables tab completion for the 'SyntasticInfo' command. |
|||
" (This does not actually register the syntax checker.) |
|||
if exists('g:syntastic_extra_filetypes') |
|||
call add(g:syntastic_extra_filetypes, 'rust') |
|||
else |
|||
let g:syntastic_extra_filetypes = ['rust'] |
|||
endif |
|||
|
|||
if !exists('g:syntastic_rust_checkers') |
|||
let g:syntastic_rust_checkers = ['cargo'] |
|||
endif |
|||
|
|||
let &cpoptions = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,363 @@ |
|||
" Vim syntax file |
|||
" Language: Rust |
|||
" Maintainer: Patrick Walton <pcwalton@mozilla.com> |
|||
" Maintainer: Ben Blum <bblum@cs.cmu.edu> |
|||
" Maintainer: Chris Morgan <me@chrismorgan.info> |
|||
" Last Change: Feb 24, 2016 |
|||
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
|||
|
|||
if version < 600 |
|||
syntax clear |
|||
elseif exists("b:current_syntax") |
|||
finish |
|||
endif |
|||
|
|||
" Syntax definitions {{{1 |
|||
" Basic keywords {{{2 |
|||
syn keyword rustConditional match if else |
|||
syn keyword rustRepeat loop while |
|||
" `:syn match` must be used to prioritize highlighting `for` keyword. |
|||
syn match rustRepeat /\<for\>/ |
|||
" Highlight `for` keyword in `impl ... for ... {}` statement. This line must |
|||
" be put after previous `syn match` line to overwrite it. |
|||
syn match rustKeyword /\%(\<impl\>.\+\)\@<=\<for\>/ |
|||
syn keyword rustRepeat in |
|||
syn keyword rustTypedef type nextgroup=rustIdentifier skipwhite skipempty |
|||
syn keyword rustStructure struct enum nextgroup=rustIdentifier skipwhite skipempty |
|||
syn keyword rustUnion union nextgroup=rustIdentifier skipwhite skipempty contained |
|||
syn match rustUnionContextual /\<union\_s\+\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*/ transparent contains=rustUnion |
|||
syn keyword rustOperator as |
|||
syn keyword rustExistential existential nextgroup=rustTypedef skipwhite skipempty contained |
|||
syn match rustExistentialContextual /\<existential\_s\+type/ transparent contains=rustExistential,rustTypedef |
|||
|
|||
syn match rustAssert "\<assert\(\w\)*!" contained |
|||
syn match rustPanic "\<panic\(\w\)*!" contained |
|||
syn match rustAsync "\<async\%(\s\|\n\)\@=" |
|||
syn keyword rustKeyword break |
|||
syn keyword rustKeyword box |
|||
syn keyword rustKeyword continue |
|||
syn keyword rustKeyword crate |
|||
syn keyword rustKeyword extern nextgroup=rustExternCrate,rustObsoleteExternMod skipwhite skipempty |
|||
syn keyword rustKeyword fn nextgroup=rustFuncName skipwhite skipempty |
|||
syn keyword rustKeyword impl let |
|||
syn keyword rustKeyword macro |
|||
syn keyword rustKeyword pub nextgroup=rustPubScope skipwhite skipempty |
|||
syn keyword rustKeyword return |
|||
syn keyword rustKeyword yield |
|||
syn keyword rustSuper super |
|||
syn keyword rustKeyword where |
|||
syn keyword rustUnsafeKeyword unsafe |
|||
syn keyword rustKeyword use nextgroup=rustModPath skipwhite skipempty |
|||
" FIXME: Scoped impl's name is also fallen in this category |
|||
syn keyword rustKeyword mod trait nextgroup=rustIdentifier skipwhite skipempty |
|||
syn keyword rustStorage move mut ref static const |
|||
syn match rustDefault /\<default\ze\_s\+\(impl\|fn\|type\|const\)\>/ |
|||
syn keyword rustAwait await |
|||
syn match rustKeyword /\<try\>!\@!/ display |
|||
|
|||
syn keyword rustPubScopeCrate crate contained |
|||
syn match rustPubScopeDelim /[()]/ contained |
|||
syn match rustPubScope /([^()]*)/ contained contains=rustPubScopeDelim,rustPubScopeCrate,rustSuper,rustModPath,rustModPathSep,rustSelf transparent |
|||
|
|||
syn keyword rustExternCrate crate contained nextgroup=rustIdentifier,rustExternCrateString skipwhite skipempty |
|||
" This is to get the `bar` part of `extern crate "foo" as bar;` highlighting. |
|||
syn match rustExternCrateString /".*"\_s*as/ contained nextgroup=rustIdentifier skipwhite transparent skipempty contains=rustString,rustOperator |
|||
syn keyword rustObsoleteExternMod mod contained nextgroup=rustIdentifier skipwhite skipempty |
|||
|
|||
syn match rustIdentifier contains=rustIdentifierPrime "\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" display contained |
|||
syn match rustFuncName "\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" display contained |
|||
|
|||
syn region rustMacroRepeat matchgroup=rustMacroRepeatDelimiters start="$(" end=")" contains=TOP nextgroup=rustMacroRepeatCount |
|||
syn match rustMacroRepeatCount ".\?[*+]" contained |
|||
syn match rustMacroVariable "$\w\+" |
|||
|
|||
" Reserved (but not yet used) keywords {{{2 |
|||
syn keyword rustReservedKeyword become do priv typeof unsized abstract virtual final override |
|||
|
|||
" Built-in types {{{2 |
|||
syn keyword rustType isize usize char bool u8 u16 u32 u64 u128 f32 |
|||
syn keyword rustType f64 i8 i16 i32 i64 i128 str Self |
|||
|
|||
" Things from the libstd v1 prelude (src/libstd/prelude/v1.rs) {{{2 |
|||
" This section is just straight transformation of the contents of the prelude, |
|||
" to make it easy to update. |
|||
|
|||
" Reexported core operators {{{3 |
|||
syn keyword rustTrait Copy Send Sized Sync |
|||
syn keyword rustTrait Drop Fn FnMut FnOnce |
|||
|
|||
" Reexported functions {{{3 |
|||
" There’s no point in highlighting these; when one writes drop( or drop::< it |
|||
" gets the same highlighting anyway, and if someone writes `let drop = …;` we |
|||
" don’t really want *that* drop to be highlighted. |
|||
"syn keyword rustFunction drop |
|||
|
|||
" Reexported types and traits {{{3 |
|||
syn keyword rustTrait Box |
|||
syn keyword rustTrait ToOwned |
|||
syn keyword rustTrait Clone |
|||
syn keyword rustTrait PartialEq PartialOrd Eq Ord |
|||
syn keyword rustTrait AsRef AsMut Into From |
|||
syn keyword rustTrait Default |
|||
syn keyword rustTrait Iterator Extend IntoIterator |
|||
syn keyword rustTrait DoubleEndedIterator ExactSizeIterator |
|||
syn keyword rustEnum Option |
|||
syn keyword rustEnumVariant Some None |
|||
syn keyword rustEnum Result |
|||
syn keyword rustEnumVariant Ok Err |
|||
syn keyword rustTrait SliceConcatExt |
|||
syn keyword rustTrait String ToString |
|||
syn keyword rustTrait Vec |
|||
|
|||
" Other syntax {{{2 |
|||
syn keyword rustSelf self |
|||
syn keyword rustBoolean true false |
|||
|
|||
" If foo::bar changes to foo.bar, change this ("::" to "\."). |
|||
" If foo::bar changes to Foo::bar, change this (first "\w" to "\u"). |
|||
syn match rustModPath "\w\(\w\)*::[^<]"he=e-3,me=e-3 |
|||
syn match rustModPathSep "::" |
|||
|
|||
syn match rustFuncCall "\w\(\w\)*("he=e-1,me=e-1 |
|||
syn match rustFuncCall "\w\(\w\)*::<"he=e-3,me=e-3 " foo::<T>(); |
|||
|
|||
" This is merely a convention; note also the use of [A-Z], restricting it to |
|||
" latin identifiers rather than the full Unicode uppercase. I have not used |
|||
" [:upper:] as it depends upon 'noignorecase' |
|||
"syn match rustCapsIdent display "[A-Z]\w\(\w\)*" |
|||
|
|||
syn match rustOperator display "\%(+\|-\|/\|*\|=\|\^\|&\||\|!\|>\|<\|%\)=\?" |
|||
" This one isn't *quite* right, as we could have binary-& with a reference |
|||
syn match rustSigil display /&\s\+[&~@*][^)= \t\r\n]/he=e-1,me=e-1 |
|||
syn match rustSigil display /[&~@*][^)= \t\r\n]/he=e-1,me=e-1 |
|||
" This isn't actually correct; a closure with no arguments can be `|| { }`. |
|||
" Last, because the & in && isn't a sigil |
|||
syn match rustOperator display "&&\|||" |
|||
" This is rustArrowCharacter rather than rustArrow for the sake of matchparen, |
|||
" so it skips the ->; see http://stackoverflow.com/a/30309949 for details. |
|||
syn match rustArrowCharacter display "->" |
|||
syn match rustQuestionMark display "?\([a-zA-Z]\+\)\@!" |
|||
|
|||
syn match rustMacro '\w\(\w\)*!' contains=rustAssert,rustPanic |
|||
syn match rustMacro '#\w\(\w\)*' contains=rustAssert,rustPanic |
|||
|
|||
syn match rustEscapeError display contained /\\./ |
|||
syn match rustEscape display contained /\\\([nrt0\\'"]\|x\x\{2}\)/ |
|||
syn match rustEscapeUnicode display contained /\\u{\%(\x_*\)\{1,6}}/ |
|||
syn match rustStringContinuation display contained /\\\n\s*/ |
|||
syn region rustString matchgroup=rustStringDelimiter start=+b"+ skip=+\\\\\|\\"+ end=+"+ contains=rustEscape,rustEscapeError,rustStringContinuation |
|||
syn region rustString matchgroup=rustStringDelimiter start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=rustEscape,rustEscapeUnicode,rustEscapeError,rustStringContinuation,@Spell |
|||
syn region rustString matchgroup=rustStringDelimiter start='b\?r\z(#*\)"' end='"\z1' contains=@Spell |
|||
|
|||
" Match attributes with either arbitrary syntax or special highlighting for |
|||
" derives. We still highlight strings and comments inside of the attribute. |
|||
syn region rustAttribute start="#!\?\[" end="\]" contains=@rustAttributeContents,rustAttributeParenthesizedParens,rustAttributeParenthesizedCurly,rustAttributeParenthesizedBrackets,rustDerive |
|||
syn region rustAttributeParenthesizedParens matchgroup=rustAttribute start="\w\%(\w\)*("rs=e end=")"re=s transparent contained contains=rustAttributeBalancedParens,@rustAttributeContents |
|||
syn region rustAttributeParenthesizedCurly matchgroup=rustAttribute start="\w\%(\w\)*{"rs=e end="}"re=s transparent contained contains=rustAttributeBalancedCurly,@rustAttributeContents |
|||
syn region rustAttributeParenthesizedBrackets matchgroup=rustAttribute start="\w\%(\w\)*\["rs=e end="\]"re=s transparent contained contains=rustAttributeBalancedBrackets,@rustAttributeContents |
|||
syn region rustAttributeBalancedParens matchgroup=rustAttribute start="("rs=e end=")"re=s transparent contained contains=rustAttributeBalancedParens,@rustAttributeContents |
|||
syn region rustAttributeBalancedCurly matchgroup=rustAttribute start="{"rs=e end="}"re=s transparent contained contains=rustAttributeBalancedCurly,@rustAttributeContents |
|||
syn region rustAttributeBalancedBrackets matchgroup=rustAttribute start="\["rs=e end="\]"re=s transparent contained contains=rustAttributeBalancedBrackets,@rustAttributeContents |
|||
syn cluster rustAttributeContents contains=rustString,rustCommentLine,rustCommentBlock,rustCommentLineDocError,rustCommentBlockDocError |
|||
syn region rustDerive start="derive(" end=")" contained contains=rustDeriveTrait |
|||
" This list comes from src/libsyntax/ext/deriving/mod.rs |
|||
" Some are deprecated (Encodable, Decodable) or to be removed after a new snapshot (Show). |
|||
syn keyword rustDeriveTrait contained Clone Hash RustcEncodable RustcDecodable Encodable Decodable PartialEq Eq PartialOrd Ord Rand Show Debug Default FromPrimitive Send Sync Copy |
|||
|
|||
" dyn keyword: It's only a keyword when used inside a type expression, so |
|||
" we make effort here to highlight it only when Rust identifiers follow it |
|||
" (not minding the case of pre-2018 Rust where a path starting with :: can |
|||
" follow). |
|||
" |
|||
" This is so that uses of dyn variable names such as in 'let &dyn = &2' |
|||
" and 'let dyn = 2' will not get highlighted as a keyword. |
|||
syn match rustKeyword "\<dyn\ze\_s\+\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)" contains=rustDynKeyword |
|||
syn keyword rustDynKeyword dyn contained |
|||
|
|||
" Number literals |
|||
syn match rustDecNumber display "\<[0-9][0-9_]*\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" |
|||
syn match rustHexNumber display "\<0x[a-fA-F0-9_]\+\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" |
|||
syn match rustOctNumber display "\<0o[0-7_]\+\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" |
|||
syn match rustBinNumber display "\<0b[01_]\+\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" |
|||
|
|||
" Special case for numbers of the form "1." which are float literals, unless followed by |
|||
" an identifier, which makes them integer literals with a method call or field access, |
|||
" or by another ".", which makes them integer literals followed by the ".." token. |
|||
" (This must go first so the others take precedence.) |
|||
syn match rustFloat display "\<[0-9][0-9_]*\.\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\|\.\)\@!" |
|||
" To mark a number as a normal float, it must have at least one of the three things integral values don't have: |
|||
" a decimal point and more numbers; an exponent; and a type suffix. |
|||
syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\%([eE][+-]\=[0-9_]\+\)\=\(f32\|f64\)\=" |
|||
syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\=\%([eE][+-]\=[0-9_]\+\)\(f32\|f64\)\=" |
|||
syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\=\%([eE][+-]\=[0-9_]\+\)\=\(f32\|f64\)" |
|||
|
|||
" For the benefit of delimitMate |
|||
syn region rustLifetimeCandidate display start=/&'\%(\([^'\\]\|\\\(['nrt0\\\"]\|x\x\{2}\|u{\%(\x_*\)\{1,6}}\)\)'\)\@!/ end=/[[:cntrl:][:space:][:punct:]]\@=\|$/ contains=rustSigil,rustLifetime |
|||
syn region rustGenericRegion display start=/<\%('\|[^[:cntrl:][:space:][:punct:]]\)\@=')\S\@=/ end=/>/ contains=rustGenericLifetimeCandidate |
|||
syn region rustGenericLifetimeCandidate display start=/\%(<\|,\s*\)\@<='/ end=/[[:cntrl:][:space:][:punct:]]\@=\|$/ contains=rustSigil,rustLifetime |
|||
|
|||
"rustLifetime must appear before rustCharacter, or chars will get the lifetime highlighting |
|||
syn match rustLifetime display "\'\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" |
|||
syn match rustLabel display "\'\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*:" |
|||
syn match rustLabel display "\%(\<\%(break\|continue\)\s*\)\@<=\'\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" |
|||
syn match rustCharacterInvalid display contained /b\?'\zs[\n\r\t']\ze'/ |
|||
" The groups negated here add up to 0-255 but nothing else (they do not seem to go beyond ASCII). |
|||
syn match rustCharacterInvalidUnicode display contained /b'\zs[^[:cntrl:][:graph:][:alnum:][:space:]]\ze'/ |
|||
syn match rustCharacter /b'\([^\\]\|\\\(.\|x\x\{2}\)\)'/ contains=rustEscape,rustEscapeError,rustCharacterInvalid,rustCharacterInvalidUnicode |
|||
syn match rustCharacter /'\([^\\]\|\\\(.\|x\x\{2}\|u{\%(\x_*\)\{1,6}}\)\)'/ contains=rustEscape,rustEscapeUnicode,rustEscapeError,rustCharacterInvalid |
|||
|
|||
syn match rustShebang /\%^#![^[].*/ |
|||
syn region rustCommentLine start="//" end="$" contains=rustTodo,@Spell |
|||
syn region rustCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=rustTodo,@Spell |
|||
syn region rustCommentLineDocError start="//\%(//\@!\|!\)" end="$" contains=rustTodo,@Spell contained |
|||
syn region rustCommentBlock matchgroup=rustCommentBlock start="/\*\%(!\|\*[*/]\@!\)\@!" end="\*/" contains=rustTodo,rustCommentBlockNest,@Spell |
|||
syn region rustCommentBlockDoc matchgroup=rustCommentBlockDoc start="/\*\%(!\|\*[*/]\@!\)" end="\*/" contains=rustTodo,rustCommentBlockDocNest,rustCommentBlockDocRustCode,@Spell |
|||
syn region rustCommentBlockDocError matchgroup=rustCommentBlockDocError start="/\*\%(!\|\*[*/]\@!\)" end="\*/" contains=rustTodo,rustCommentBlockDocNestError,@Spell contained |
|||
syn region rustCommentBlockNest matchgroup=rustCommentBlock start="/\*" end="\*/" contains=rustTodo,rustCommentBlockNest,@Spell contained transparent |
|||
syn region rustCommentBlockDocNest matchgroup=rustCommentBlockDoc start="/\*" end="\*/" contains=rustTodo,rustCommentBlockDocNest,@Spell contained transparent |
|||
syn region rustCommentBlockDocNestError matchgroup=rustCommentBlockDocError start="/\*" end="\*/" contains=rustTodo,rustCommentBlockDocNestError,@Spell contained transparent |
|||
|
|||
" FIXME: this is a really ugly and not fully correct implementation. Most |
|||
" importantly, a case like ``/* */*`` should have the final ``*`` not being in |
|||
" a comment, but in practice at present it leaves comments open two levels |
|||
" deep. But as long as you stay away from that particular case, I *believe* |
|||
" the highlighting is correct. Due to the way Vim's syntax engine works |
|||
" (greedy for start matches, unlike Rust's tokeniser which is searching for |
|||
" the earliest-starting match, start or end), I believe this cannot be solved. |
|||
" Oh you who would fix it, don't bother with things like duplicating the Block |
|||
" rules and putting ``\*\@<!`` at the start of them; it makes it worse, as |
|||
" then you must deal with cases like ``/*/**/*/``. And don't try making it |
|||
" worse with ``\%(/\@<!\*\)\@<!``, either... |
|||
|
|||
syn keyword rustTodo contained TODO FIXME XXX NB NOTE |
|||
|
|||
" Folding rules {{{2 |
|||
" Trivial folding rules to begin with. |
|||
" FIXME: use the AST to make really good folding |
|||
syn region rustFoldBraces start="{" end="}" transparent fold |
|||
|
|||
if !exists("b:current_syntax_embed") |
|||
let b:current_syntax_embed = 1 |
|||
syntax include @RustCodeInComment <sfile>:p:h/rust.vim |
|||
unlet b:current_syntax_embed |
|||
|
|||
" Currently regions marked as ```<some-other-syntax> will not get |
|||
" highlighted at all. In the future, we can do as vim-markdown does and |
|||
" highlight with the other syntax. But for now, let's make sure we find |
|||
" the closing block marker, because the rules below won't catch it. |
|||
syn region rustCommentLinesDocNonRustCode matchgroup=rustCommentDocCodeFence start='^\z(\s*//[!/]\s*```\).\+$' end='^\z1$' keepend contains=rustCommentLineDoc |
|||
|
|||
" We borrow the rules from rust’s src/librustdoc/html/markdown.rs, so that |
|||
" we only highlight as Rust what it would perceive as Rust (almost; it’s |
|||
" possible to trick it if you try hard, and indented code blocks aren’t |
|||
" supported because Markdown is a menace to parse and only mad dogs and |
|||
" Englishmen would try to handle that case correctly in this syntax file). |
|||
syn region rustCommentLinesDocRustCode matchgroup=rustCommentDocCodeFence start='^\z(\s*//[!/]\s*```\)[^A-Za-z0-9_-]*\%(\%(should_panic\|no_run\|ignore\|allow_fail\|rust\|test_harness\|compile_fail\|E\d\{4}\|edition201[58]\)\%([^A-Za-z0-9_-]\+\|$\)\)*$' end='^\z1$' keepend contains=@RustCodeInComment,rustCommentLineDocLeader |
|||
syn region rustCommentBlockDocRustCode matchgroup=rustCommentDocCodeFence start='^\z(\%(\s*\*\)\?\s*```\)[^A-Za-z0-9_-]*\%(\%(should_panic\|no_run\|ignore\|allow_fail\|rust\|test_harness\|compile_fail\|E\d\{4}\|edition201[58]\)\%([^A-Za-z0-9_-]\+\|$\)\)*$' end='^\z1$' keepend contains=@RustCodeInComment,rustCommentBlockDocStar |
|||
" Strictly, this may or may not be correct; this code, for example, would |
|||
" mishighlight: |
|||
" |
|||
" /** |
|||
" ```rust |
|||
" println!("{}", 1 |
|||
" * 1); |
|||
" ``` |
|||
" */ |
|||
" |
|||
" … but I don’t care. Balance of probability, and all that. |
|||
syn match rustCommentBlockDocStar /^\s*\*\s\?/ contained |
|||
syn match rustCommentLineDocLeader "^\s*//\%(//\@!\|!\)" contained |
|||
endif |
|||
|
|||
" Default highlighting {{{1 |
|||
hi def link rustDecNumber rustNumber |
|||
hi def link rustHexNumber rustNumber |
|||
hi def link rustOctNumber rustNumber |
|||
hi def link rustBinNumber rustNumber |
|||
hi def link rustIdentifierPrime rustIdentifier |
|||
hi def link rustTrait rustType |
|||
hi def link rustDeriveTrait rustTrait |
|||
|
|||
hi def link rustMacroRepeatCount rustMacroRepeatDelimiters |
|||
hi def link rustMacroRepeatDelimiters Macro |
|||
hi def link rustMacroVariable Define |
|||
hi def link rustSigil StorageClass |
|||
hi def link rustEscape Special |
|||
hi def link rustEscapeUnicode rustEscape |
|||
hi def link rustEscapeError Error |
|||
hi def link rustStringContinuation Special |
|||
hi def link rustString String |
|||
hi def link rustStringDelimiter String |
|||
hi def link rustCharacterInvalid Error |
|||
hi def link rustCharacterInvalidUnicode rustCharacterInvalid |
|||
hi def link rustCharacter Character |
|||
hi def link rustNumber Number |
|||
hi def link rustBoolean Boolean |
|||
hi def link rustEnum rustType |
|||
hi def link rustEnumVariant rustConstant |
|||
hi def link rustConstant Constant |
|||
hi def link rustSelf Constant |
|||
hi def link rustFloat Float |
|||
hi def link rustArrowCharacter rustOperator |
|||
hi def link rustOperator Operator |
|||
hi def link rustKeyword Keyword |
|||
hi def link rustDynKeyword rustKeyword |
|||
hi def link rustTypedef Keyword " More precise is Typedef, but it doesn't feel right for Rust |
|||
hi def link rustStructure Keyword " More precise is Structure |
|||
hi def link rustUnion rustStructure |
|||
hi def link rustExistential rustKeyword |
|||
hi def link rustPubScopeDelim Delimiter |
|||
hi def link rustPubScopeCrate rustKeyword |
|||
hi def link rustSuper rustKeyword |
|||
hi def link rustUnsafeKeyword Exception |
|||
hi def link rustReservedKeyword Error |
|||
hi def link rustRepeat Conditional |
|||
hi def link rustConditional Conditional |
|||
hi def link rustIdentifier Identifier |
|||
hi def link rustCapsIdent rustIdentifier |
|||
hi def link rustModPath Include |
|||
hi def link rustModPathSep Delimiter |
|||
hi def link rustFunction Function |
|||
hi def link rustFuncName Function |
|||
hi def link rustFuncCall Function |
|||
hi def link rustShebang Comment |
|||
hi def link rustCommentLine Comment |
|||
hi def link rustCommentLineDoc SpecialComment |
|||
hi def link rustCommentLineDocLeader rustCommentLineDoc |
|||
hi def link rustCommentLineDocError Error |
|||
hi def link rustCommentBlock rustCommentLine |
|||
hi def link rustCommentBlockDoc rustCommentLineDoc |
|||
hi def link rustCommentBlockDocStar rustCommentBlockDoc |
|||
hi def link rustCommentBlockDocError Error |
|||
hi def link rustCommentDocCodeFence rustCommentLineDoc |
|||
hi def link rustAssert PreCondit |
|||
hi def link rustPanic PreCondit |
|||
hi def link rustMacro Macro |
|||
hi def link rustType Type |
|||
hi def link rustTodo Todo |
|||
hi def link rustAttribute PreProc |
|||
hi def link rustDerive PreProc |
|||
hi def link rustDefault StorageClass |
|||
hi def link rustStorage StorageClass |
|||
hi def link rustObsoleteStorage Error |
|||
hi def link rustLifetime Special |
|||
hi def link rustLabel Label |
|||
hi def link rustExternCrate rustKeyword |
|||
hi def link rustObsoleteExternMod Error |
|||
hi def link rustQuestionMark Special |
|||
hi def link rustAsync rustKeyword |
|||
hi def link rustAwait rustKeyword |
|||
|
|||
" Other Suggestions: |
|||
" hi rustAttribute ctermfg=cyan |
|||
" hi rustDerive ctermfg=cyan |
|||
" hi rustAssert ctermfg=yellow |
|||
" hi rustPanic ctermfg=red |
|||
" hi rustMacro ctermfg=magenta |
|||
|
|||
syn sync minlines=200 |
|||
syn sync maxlines=500 |
|||
|
|||
let b:current_syntax = "rust" |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,93 @@ |
|||
" Vim syntastic plugin |
|||
" Language: Rust |
|||
" Maintainer: Julien Levesy <jlevesy@gmail.com> |
|||
" |
|||
" See for details on how to add an external Syntastic checker: |
|||
" https://github.com/scrooloose/syntastic/wiki/Syntax-Checker-Guide#external |
|||
|
|||
if exists("g:loaded_syntastic_rust_cargo_checker") |
|||
finish |
|||
endif |
|||
|
|||
let g:loaded_syntastic_rust_cargo_checker = 1 |
|||
|
|||
" Force syntastic to call cargo without a specific file name |
|||
let g:syntastic_rust_cargo_fname = "" |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_rust_cargo_IsAvailable() dict |
|||
if exists("*syntastic#util#getVersion") |
|||
echom "rust.vim: version of Syntastic is too old. Needs to be at least 3.7.0." |
|||
return v:false |
|||
endif |
|||
|
|||
return executable(self.getExec()) && |
|||
\ syntastic#util#versionIsAtLeast(self.getVersion(), [0, 16, 0]) |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_rust_cargo_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ "args": "check" }) |
|||
let l:root_cargo_toml = cargo#nearestRootCargo(0) |
|||
let l:nearest_cargo_toml = cargo#nearestCargo(0) |
|||
let b:rust_recent_root_cargo_toml = l:root_cargo_toml |
|||
let b:rust_recent_nearest_cargo_toml = l:nearest_cargo_toml |
|||
|
|||
" All pathname prints are relative to the Cargo.toml of the workspace, if |
|||
" there is a workspace, otherwise they are relative to the Cargo.toml of |
|||
" the single crate. Where to actually execute under these varying |
|||
" circumtances 'cargo' is determined here, and controlled by |
|||
" configuration. |
|||
|
|||
if rust#GetConfigVar('rust_cargo_avoid_whole_workspace', 1) |
|||
if l:root_cargo_toml !=# l:nearest_cargo_toml |
|||
let makeprg = "cd " . fnamemodify(l:nearest_cargo_toml, ":p:h") |
|||
\ . " && " . makeprg |
|||
endif |
|||
else |
|||
let makeprg = "cd " . fnamemodify(l:root_cargo_toml, ":p:h") |
|||
\ . " && " . makeprg |
|||
endif |
|||
|
|||
let l:check_all_targets = rust#GetConfigVar('rust_cargo_check_all_targets', 0) |
|||
let l:check_all_features = rust#GetConfigVar('rust_cargo_check_all_features', 0) |
|||
let l:check_examples = rust#GetConfigVar('rust_cargo_check_examples', 0) |
|||
let l:check_tests = rust#GetConfigVar('rust_cargo_check_tests', 0) |
|||
let l:check_benches = rust#GetConfigVar('rust_cargo_check_benches', 0) |
|||
|
|||
let makeprg = makeprg. ' ' |
|||
\ . (l:check_all_targets ? ' --all-targets' : '') |
|||
\ . (l:check_all_features ? ' --all-features' : '') |
|||
\ . (l:check_benches ? ' --benches' : '') |
|||
\ . (l:check_examples ? ' --examples' : '') |
|||
\ . (l:check_tests ? ' --tests' : '') |
|||
|
|||
" Ignored patterns, and blank lines |
|||
let errorformat = |
|||
\ '%-G,' . |
|||
\ '%-Gerror: aborting %.%#,' . |
|||
\ '%-Gerror: Could not compile %.%#,' |
|||
|
|||
" Meaningful lines (errors, notes, warnings, contextual information) |
|||
let errorformat .= |
|||
\ '%Eerror: %m,' . |
|||
\ '%Eerror[E%n]: %m,' . |
|||
\ '%Wwarning: %m,' . |
|||
\ '%Inote: %m,' . |
|||
\ '%C %#--> %f:%l:%c' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'cwd': fnamemodify(l:root_cargo_toml, ":p:h:."), |
|||
\ 'errorformat': errorformat }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'rust', |
|||
\ 'name': 'cargo'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,54 @@ |
|||
" Vim syntastic plugin |
|||
" Language: Rust |
|||
" Maintainer: Andrew Gallant <jamslam@gmail.com> |
|||
" |
|||
" See for details on how to add an external Syntastic checker: |
|||
" https://github.com/scrooloose/syntastic/wiki/Syntax-Checker-Guide#external |
|||
|
|||
if exists("g:loaded_syntastic_rust_rustc_checker") |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_rust_rustc_checker = 1 |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
function! SyntaxCheckers_rust_rustc_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({}) |
|||
|
|||
" Old errorformat (before nightly 2016/08/10) |
|||
let errorformat = |
|||
\ '%E%f:%l:%c: %\d%#:%\d%# %.%\{-}error:%.%\{-} %m,' . |
|||
\ '%W%f:%l:%c: %\d%#:%\d%# %.%\{-}warning:%.%\{-} %m,' . |
|||
\ '%C%f:%l %m' |
|||
|
|||
" New errorformat (after nightly 2016/08/10) |
|||
let errorformat .= |
|||
\ ',' . |
|||
\ '%-G,' . |
|||
\ '%-Gerror: aborting %.%#,' . |
|||
\ '%-Gerror: Could not compile %.%#,' . |
|||
\ '%Eerror: %m,' . |
|||
\ '%Eerror[E%n]: %m,' . |
|||
\ '%-Gwarning: the option `Z` is unstable %.%#,' . |
|||
\ '%Wwarning: %m,' . |
|||
\ '%Inote: %m,' . |
|||
\ '%C %#--> %f:%l:%c' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'rust', |
|||
\ 'name': 'rustc'}) |
|||
|
|||
" vint: -ProhibitAbbreviationOption |
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
" vint: +ProhibitAbbreviationOption |
|||
|
|||
" vim: set et sw=4 sts=4 ts=8: |
|||
@ -0,0 +1,34 @@ |
|||
# This is brought as reference, to be able to reproduce a new image |
|||
|
|||
FROM alonid/vim-testbed:10 |
|||
|
|||
RUN install_vim -tag v7.4.052 -name vim74-trusty -build \ |
|||
-tag v8.0.1850 -name vim80 -build \ |
|||
-tag v8.1.0105 -name vim81 -build \ |
|||
-tag neovim:v0.1.7 -build \ |
|||
-tag neovim:v0.2.2 -build |
|||
|
|||
ENV PACKAGES="\ |
|||
bash \ |
|||
git \ |
|||
python \ |
|||
python2-pip \ |
|||
curl \ |
|||
" |
|||
|
|||
RUN dnf install -y $PACKAGES |
|||
|
|||
RUN pip install vim-vint==0.3.19 |
|||
|
|||
RUN export HOME=/rust ; mkdir $HOME ; curl https://sh.rustup.rs -sSf | sh -s -- -y |
|||
|
|||
RUN chown vimtest.vimtest -R /rust |
|||
|
|||
RUN (dnf remove -y gcc \*-devel ; \ |
|||
dnf install -y gpm msgpack libvterm libtermkey unibilium ) || true |
|||
RUN dnf clean all |
|||
|
|||
RUN echo "export PATH=~/.cargo/bin:$PATH" >> ~/.bashrc |
|||
|
|||
RUN git clone https://github.com/da-x/vader.vim vader && \ |
|||
cd vader && git checkout v2017-12-26 |
|||
@ -0,0 +1,24 @@ |
|||
Given rust (Some Rust code): |
|||
fn main() { |
|||
println!("Hello World\n") |
|||
} |
|||
|
|||
Execute (RustInfo - call it to see that it works): |
|||
redir => m |
|||
silent RustInfo |
|||
redir END |
|||
Log m |
|||
|
|||
Execute (RustEmitAsm - see that we actually get assembly output): |
|||
silent! w test.rs |
|||
silent! e! test.rs |
|||
redir => m |
|||
silent! RustEmitAsm |
|||
redir END |
|||
AssertEqual 'asm', &filetype |
|||
normal! ggVGy:q<CR> |
|||
AssertEqual 1,(@" =~# '\V.section') |
|||
bd |
|||
call delete('test.rs') |
|||
|
|||
# TODO: a lot more tests |
|||
@ -0,0 +1,292 @@ |
|||
Given rust: |
|||
fn main() { |
|||
let a = 2; |
|||
println!("Hello World\n") |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (very basic indentation result): |
|||
fn main() { |
|||
let a = 2; |
|||
println!("Hello World\n") |
|||
} |
|||
|
|||
############################################ |
|||
# Issue #195 |
|||
|
|||
Given rust: |
|||
fn main() { |
|||
let paths: Vec<_> = ({ |
|||
fs::read_dir("test_data") |
|||
.unwrap() |
|||
.cloned() |
|||
}) |
|||
.collect(); |
|||
|
|||
println!("Hello World\n"); |
|||
} |
|||
|
|||
Do: |
|||
/collect\<cr> |
|||
ostatement();\<ESC>\<ESC> |
|||
|
|||
Expect rust (issue #195): |
|||
fn main() { |
|||
let paths: Vec<_> = ({ |
|||
fs::read_dir("test_data") |
|||
.unwrap() |
|||
.cloned() |
|||
}) |
|||
.collect(); |
|||
statement(); |
|||
|
|||
println!("Hello World\n"); |
|||
} |
|||
|
|||
############################################ |
|||
# Issue #189 |
|||
|
|||
Given rust: |
|||
impl X for { |
|||
pub fn get<Q>(&self, key: &Q) -> Option<Entry<K, V>> |
|||
where |
|||
K: Borrow<Q>, |
|||
Q: Ord + ?Sized, |
|||
{ |
|||
self.inner.get(key).map(Entry::new) |
|||
} |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (issue #189): |
|||
impl X for { |
|||
pub fn get<Q>(&self, key: &Q) -> Option<Entry<K, V>> |
|||
where |
|||
K: Borrow<Q>, |
|||
Q: Ord + ?Sized, |
|||
{ |
|||
self.inner.get(key).map(Entry::new) |
|||
} |
|||
} |
|||
|
|||
############################################ |
|||
# Issue #189b |
|||
|
|||
Given rust: |
|||
impl X for { |
|||
pub fn get<Q>(&self, key: &Q) -> Option<Entry<K, V>> |
|||
where |
|||
K: Borrow<Q>, |
|||
Q: Ord + ?Sized |
|||
{ |
|||
self.inner.get(key).map(Entry::new) |
|||
} |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (issue #189b): |
|||
impl X for { |
|||
pub fn get<Q>(&self, key: &Q) -> Option<Entry<K, V>> |
|||
where |
|||
K: Borrow<Q>, |
|||
Q: Ord + ?Sized |
|||
{ |
|||
self.inner.get(key).map(Entry::new) |
|||
} |
|||
} |
|||
|
|||
############################################ |
|||
# Issue #189c |
|||
|
|||
Given rust: |
|||
impl X for { |
|||
pub fn get<Q>(&self, key: &Q) -> Option<Entry<K, V>> |
|||
where K: Borrow<Q>, Q: Ord + ?Sized |
|||
{ |
|||
self.inner.get(key).map(Entry::new) |
|||
} |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (issue #189b): |
|||
impl X for { |
|||
pub fn get<Q>(&self, key: &Q) -> Option<Entry<K, V>> |
|||
where K: Borrow<Q>, Q: Ord + ?Sized |
|||
{ |
|||
self.inner.get(key).map(Entry::new) |
|||
} |
|||
} |
|||
|
|||
|
|||
############################################ |
|||
# Issue #149 |
|||
|
|||
Given rust: |
|||
fn test() { |
|||
let t = "a |
|||
wah"; |
|||
} |
|||
|
|||
Do: |
|||
/wah\<cr> |
|||
i#\<ESC>\<ESC> |
|||
/;\<cr>o |
|||
statement();\<ESC>\<ESC> |
|||
|
|||
# Disabled |
|||
# Expect rust (issue #149): |
|||
fn test() { |
|||
let t = "a |
|||
#wah"; |
|||
statement(); |
|||
} |
|||
|
|||
|
|||
############################################ |
|||
# Issue #77 |
|||
|
|||
Given rust: |
|||
fn test() { |
|||
} |
|||
|
|||
Do: |
|||
of(x, y,\<CR>z);\<CR> |
|||
f((x, y),\<CR>z);\<CR> |
|||
|
|||
# Disabled |
|||
# Expect rust (issue #77): |
|||
fn test() { |
|||
f(x, y, |
|||
z); |
|||
f((x, y), |
|||
z); |
|||
|
|||
} |
|||
|
|||
############################################ |
|||
# Issue #44 |
|||
|
|||
Given rust: |
|||
fn main() { |
|||
a |
|||
|
|||
let philosophers = vec![ |
|||
Philosopher::new("Judith Butler"), |
|||
Philosopher::new("Gilles Deleuze"), |
|||
Philosopher::new("Karl Marx"), |
|||
Philosopher::new("Emma Goldman"), |
|||
Philosopher::new("Michel Foucault"), |
|||
]; |
|||
} |
|||
|
|||
Do: |
|||
/let\<CR> |
|||
vip= |
|||
|
|||
# Disabled |
|||
# Expect rust (issue #44): |
|||
fn main() { |
|||
a |
|||
|
|||
let philosophers = vec![ |
|||
Philosopher::new("Judith Butler"), |
|||
Philosopher::new("Gilles Deleuze"), |
|||
Philosopher::new("Karl Marx"), |
|||
Philosopher::new("Emma Goldman"), |
|||
Philosopher::new("Michel Foucault"), |
|||
]; |
|||
} |
|||
|
|||
############################################ |
|||
# Issue #5 |
|||
|
|||
Given rust: |
|||
fn f() { |
|||
if x && |
|||
y { |
|||
} |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (issue #5): |
|||
fn f() { |
|||
if x && |
|||
y { |
|||
} |
|||
} |
|||
|
|||
############################################ |
|||
# Issue #366 |
|||
|
|||
Given rust: |
|||
fn f() { |
|||
g(|_| { |
|||
h(); |
|||
}) |
|||
.unwrap(); |
|||
h(); |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (issue #366): |
|||
fn f() { |
|||
g(|_| { |
|||
h(); |
|||
}) |
|||
.unwrap(); |
|||
h(); |
|||
} |
|||
|
|||
Given rust: |
|||
fn f() { |
|||
let a = g(|_| { |
|||
h(); |
|||
}) |
|||
.unwrap(); |
|||
h(); |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (issue #366, variation #2): |
|||
fn f() { |
|||
let a = g(|_| { |
|||
h(); |
|||
}) |
|||
.unwrap(); |
|||
h(); |
|||
} |
|||
|
|||
############################################ |
|||
|
|||
Given rust: |
|||
fn f() { |
|||
let mut state = State::new( |
|||
backend, |
|||
header.clone(), |
|||
).expect("Something"); |
|||
} |
|||
|
|||
Do: |
|||
vip= |
|||
|
|||
Expect rust (Trailing comma in call): |
|||
fn f() { |
|||
let mut state = State::new( |
|||
backend, |
|||
header.clone(), |
|||
).expect("Something"); |
|||
} |
|||
@ -0,0 +1,105 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
import os |
|||
import sys |
|||
|
|||
REPO = "alonid/vim-testbed" |
|||
TAG = "10-rust.vim" |
|||
IMAGE = "%s:%s" % (REPO, TAG) |
|||
|
|||
class Error(Exception): |
|||
pass |
|||
|
|||
def system(cmd, capture=False, ok_fail=False): |
|||
if capture: |
|||
f = os.popen(cmd) |
|||
d = f.read() |
|||
return d |
|||
|
|||
res = os.system(cmd) |
|||
if res != 0: |
|||
if ok_fail: |
|||
return res |
|||
|
|||
raise Error("Error executing: %s" % (cmd, )) |
|||
return 0 |
|||
|
|||
def root(): |
|||
return os.path.dirname(os.path.dirname(os.path.realpath(__file__))) |
|||
|
|||
def prep(): |
|||
d = os.path.join(root(), "test") |
|||
for i in [".cargo", ".rustup", ".multirust"]: |
|||
l = os.path.join(d, i) |
|||
if not os.path.lexists(l): |
|||
os.symlink("/rust/" + i, l) |
|||
|
|||
l = os.path.join(root(), "test/.vimrc") |
|||
if not os.path.lexists(l): |
|||
os.symlink("vimrc", l) |
|||
|
|||
if not os.path.exists(os.path.join(d, ".profile")): |
|||
f = open(os.path.join(d, ".profile"), "w") |
|||
f.write('export PATH="$HOME/.cargo/bin:$PATH"\n') |
|||
f.close() |
|||
|
|||
def docker_run(cmd, interactive=False, ok_fail=False): |
|||
prep() |
|||
d = root() |
|||
params = "-v %s:/testplugin -v %s/test:/home/vimtest" % (d, d) |
|||
params += " -e HOME=/home/vimtest" |
|||
if not interactive: |
|||
params += " -a stderr" |
|||
params += " -e VADER_OUTPUT_FILE=/dev/stderr" |
|||
params += " -u %s" % (os.getuid(), ) |
|||
params += " -w /testplugin" |
|||
if interactive: |
|||
interactive_str = "-it" |
|||
else: |
|||
interactive_str = "" |
|||
return system("docker run %s --rm %s %s %s" % (interactive_str, params, IMAGE, cmd), |
|||
ok_fail=ok_fail) |
|||
|
|||
def image_exists(): |
|||
r = system("docker images -q %s" % (IMAGE, ), capture=True) |
|||
return len(r.strip().splitlines()) >= 1 |
|||
|
|||
def tests_on_docker(): |
|||
res = docker_run("bash -lc 'python /home/vimtest/run-tests inside-docker'", ok_fail=True) |
|||
if res == 0: |
|||
print "Tests OK" |
|||
else: |
|||
print "Tests Failed" |
|||
sys.exit(1) |
|||
|
|||
def inside_docker(): |
|||
res = system("/vim-build/bin/vim80 --not-a-term '+Vader! test/*.vader'", ok_fail=True) |
|||
if res != 0: |
|||
sys.exit(1) |
|||
|
|||
def run_with_vimrc(vimrc): |
|||
res = system("vim -u %s --not-a-term '+Vader! test/*.vader'" % (vimrc, ), ok_fail=True) |
|||
if res != 0: |
|||
sys.exit(1) |
|||
|
|||
def main(): |
|||
if sys.argv[1:] == ["inside-docker"]: |
|||
inside_docker() |
|||
return |
|||
|
|||
if sys.argv[1:2] == ["run-with-vimrc"]: |
|||
run_with_vimrc(sys.argv[2]) |
|||
return |
|||
|
|||
if not image_exists(): |
|||
print "Need to take image from remote" |
|||
system("docker pull %s" % (IMAGE, )) |
|||
|
|||
if "-i" in sys.argv[1:]: |
|||
docker_run("bash -l", interactive=True) |
|||
return |
|||
|
|||
tests_on_docker() |
|||
|
|||
if __name__ == "__main__": |
|||
main() |
|||
@ -0,0 +1,30 @@ |
|||
" vint: -ProhibitSetNoCompatible |
|||
" |
|||
|
|||
set nocompatible |
|||
filetype off |
|||
|
|||
" This script is currently designed to be run from within Docker, the |
|||
" following paths are intrinsic to the container: |
|||
source /rtp.vim |
|||
|
|||
" Paths need prepending (instead of what is originally done |
|||
" in vim-testbed) in order to supersede the rust.vim that is |
|||
" supplied with Vim. |
|||
exec 'set runtimepath=/vader,/testplugin,' . &runtimepath |
|||
cd /testplugin |
|||
|
|||
filetype plugin indent on |
|||
syntax on |
|||
|
|||
set nocompatible |
|||
set tabstop=8 |
|||
set softtabstop=4 |
|||
set shiftwidth=4 |
|||
set expandtab |
|||
set backspace=2 |
|||
set nofoldenable |
|||
set foldmethod=syntax |
|||
set foldlevelstart=10 |
|||
set foldnestmax=10 |
|||
set ttimeoutlen=0 |
|||
@ -0,0 +1,4 @@ |
|||
*~ |
|||
*.swp |
|||
tags |
|||
.DS_Store |
|||
@ -0,0 +1,105 @@ |
|||
# CONTRIBUTING |
|||
- - - |
|||
1\. [Bug reports / GitHub issues](#bugreps) |
|||
2\. [Submitting a patch](#patches) |
|||
3\. [General style notes](#generalstyle) |
|||
4\. [Syntax checker notes](#checkerstyle) |
|||
- - - |
|||
|
|||
<a name="bugreps"></a> |
|||
|
|||
## 1. Bug reports / GitHub issues |
|||
|
|||
Please note that the preferred channel for posting bug reports is the |
|||
[issue tracker at GitHub][bug_tracker]. Reports posted elsewhere are less likely |
|||
to be seen by the core team. |
|||
|
|||
When reporting a bug make sure you search the existing GitHub issues |
|||
for the same/similar issues. If you find one, feel free to add a `+1` |
|||
comment with any additional information that may help us solve the |
|||
issue. |
|||
|
|||
When creating a new issue be sure to state the following: |
|||
|
|||
* steps to reproduce the bug; |
|||
* the version of Vim you are using (run `:ver` to find out); |
|||
* the version of syntastic you are using (see `:SyntasticInfo`). |
|||
|
|||
For syntax checker bugs also state the version of the checker executable |
|||
that you are using. Adding debugging information is typically useful |
|||
too: |
|||
|
|||
* open a file handled by your checker; |
|||
* set `g:syntastic_debug` to 1 or 3; |
|||
* run the checker; |
|||
* copy the output of `:mes`. |
|||
|
|||
<a name="patches"></a> |
|||
|
|||
## 2. Submitting a patch |
|||
|
|||
Before you consider adding features to syntastic, _please_ spend a few minutes |
|||
(re-)reading the latest version of the [manual][manual]. Syntastic is changing |
|||
rapidly at times, and it's possible that some features you want to add exist |
|||
already. |
|||
|
|||
To submit a patch: |
|||
|
|||
* fork the [repo][github] on GitHub; |
|||
* make a [topic branch][branches] and start hacking; |
|||
* submit a pull request based off your topic branch. |
|||
|
|||
Small, focused patches are preferred. |
|||
|
|||
Large changes to the code should be discussed with the core team first. |
|||
Create an issue and explain your plan and see what we say. |
|||
|
|||
Also, make sure to update the manual whenever applicable. Nobody can use |
|||
features that aren't documented. |
|||
|
|||
<a name="generalstyle"></a> |
|||
|
|||
## 3. General style notes |
|||
|
|||
Follow the coding conventions/styles used in the syntastic core: |
|||
|
|||
* use 4 space indents; |
|||
* don't use abbreviated keywords - e.g. use `endfunction`, not `endfun` |
|||
(there's always room for more fun!); |
|||
* don't use `l:` prefixes for variables unless actually required (i.e. |
|||
almost never); |
|||
* code for maintainability; we would rather a function be a couple of |
|||
lines longer and have (for example) some [explaining variables][variables] to |
|||
aid readability. |
|||
|
|||
<a name="checkerstyle"></a> |
|||
|
|||
## 4. Syntax checker notes |
|||
|
|||
Make sure to read the [guide][guide] if you plan to add new syntax checkers. |
|||
|
|||
Use the existing checkers as templates, rather than writing everything |
|||
from scratch. |
|||
|
|||
The preferred style for error format strings is one "clause" per line. |
|||
E.g. (from the `coffee` checker): |
|||
|
|||
```vim |
|||
let errorformat = |
|||
\ '%E%f:%l:%c: %trror: %m,' . |
|||
\ 'Syntax%trror: In %f\, %m on line %l,' . |
|||
\ '%EError: In %f\, Parse error on line %l: %m,' . |
|||
\ '%EError: In %f\, %m on line %l,' . |
|||
\ '%W%f(%l): lint warning: %m,' . |
|||
\ '%W%f(%l): warning: %m,' . |
|||
\ '%E%f(%l): SyntaxError: %m,' . |
|||
\ '%-Z%p^,' . |
|||
\ '%-G%.%#' |
|||
``` |
|||
|
|||
[bug_tracker]: https://github.com/vim-syntastic/syntastic/issues |
|||
[manual]: https://github.com/vim-syntastic/syntastic/blob/master/doc/syntastic.txt |
|||
[github]: https://github.com/vim-syntastic/syntastic |
|||
[branches]: https://github.com/dchelimsky/rspec/wiki/Topic-Branches#using-topic-branches-when-contributing-patches |
|||
[variables]: http://www.refactoring.com/catalog/extractVariable.html |
|||
[guide]: https://github.com/vim-syntastic/syntastic/wiki/Syntax-Checker-Guide |
|||
@ -0,0 +1,13 @@ |
|||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
|||
Version 2, December 2004 |
|||
|
|||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> |
|||
|
|||
Everyone is permitted to copy and distribute verbatim or modified |
|||
copies of this license document, and changing it is allowed as long |
|||
as the name is changed. |
|||
|
|||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
|||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
|||
|
|||
0. You just DO WHAT THE FUCK YOU WANT TO. |
|||
@ -0,0 +1,543 @@ |
|||
, |
|||
/ \,,_ .'| |
|||
,{{| /}}}}/_.' _____________________________________________ |
|||
}}}}` '{{' '. / \ |
|||
{{{{{ _ ;, \ / Ladies and Gentlemen, \ |
|||
,}}}}}} /o`\ ` ;) | | |
|||
{{{{{{ / ( | this is ... | |
|||
}}}}}} | \ | | |
|||
{{{{{{{{ \ \ | | |
|||
}}}}}}}}} '.__ _ | | _____ __ __ _ | |
|||
{{{{{{{{ /`._ (_\ / | / ___/__ ______ / /_____ ______/ /_(_)____ | |
|||
}}}}}}' | //___/ --=: \__ \/ / / / __ \/ __/ __ `/ ___/ __/ / ___/ | |
|||
jgs `{{{{` | '--' | ___/ / /_/ / / / / /_/ /_/ (__ ) /_/ / /__ | |
|||
}}}` | /____/\__, /_/ /_/\__/\__,_/____/\__/_/\___/ | |
|||
| /____/ | |
|||
| / |
|||
\_____________________________________________/ |
|||
|
|||
|
|||
- - - |
|||
1. [Introduction](#introduction) |
|||
2. [Installation](#installation) |
|||
2.1. [Requirements](#requirements) |
|||
2.2. [Installing syntastic with Pathogen](#installpathogen) |
|||
3. [Recommended settings](#settings) |
|||
4. [FAQ](#faq) |
|||
4.1. [I installed syntastic but it isn't reporting any errors...](#faqinfo) |
|||
4.2. [Syntastic supports several checkers for my filetype, how do I tell it which one(s) to use?](#faqcheckers) |
|||
4.3. [How can I run checkers for "foreign" filetypes against the current file?](#faqforeign) |
|||
4.4. [I have enabled multiple checkers for the current filetype. How can I display all errors from all checkers together?](#faqaggregate) |
|||
4.5. [How can I pass additional arguments to a checker?](#faqargs) |
|||
4.6. [I run a checker and the location list is not updated...](#faqloclist) |
|||
4.6. [I run`:lopen` or `:lwindow` and the error window is empty...](#faqloclist) |
|||
4.7. [How can I jump between the different errors without using the location list at the bottom of the window?](#faqlnext) |
|||
4.8. [The error window is closed automatically when I `:quit` the current buffer but not when I `:bdelete` it?](#faqbdelete) |
|||
4.9. [My favourite checker needs to load a configuration file from the project's root rather than the current directory...](#faqconfig) |
|||
4.10. [What is the difference between syntax checkers and style checkers?](#faqstyle) |
|||
4.11. [How can I check scripts written for different versions of Python?](#faqpython) |
|||
4.12. [How can I check scripts written for different versions of Ruby?](#faqruby) |
|||
4.13. [The `perl` checker has stopped working...](#faqperl) |
|||
4.14. [What happened to the `rustc` checker?](#faqrust) |
|||
4.15. [What happened to the `tsc` checker?](#faqtsc) |
|||
4.16. [What happened to the `xcrun` checker?](#faqxcrun) |
|||
5. [Resources](#otherresources) |
|||
|
|||
- - - |
|||
|
|||
<a name="introduction"></a> |
|||
|
|||
## 1\. Introduction |
|||
|
|||
Syntastic is a syntax checking plugin for [Vim][vim] created by |
|||
[Martin Grenfell][scrooloose]. It runs files through external syntax checkers |
|||
and displays any resulting errors to the user. This can be done on demand, or |
|||
automatically as files are saved. If syntax errors are detected, the user is |
|||
notified and is happy because they didn't have to compile their code or execute |
|||
their script to find them. |
|||
|
|||
At the time of this writing, syntastic has checking plugins for ACPI |
|||
Source Language, ActionScript, Ada, Ansible configurations, API Blueprint, |
|||
AppleScript, AsciiDoc, Assembly languages, BEMHTML, Bro, Bourne shell, C, C++, |
|||
C#, Cabal, Chef, CMake, CoffeeScript, Coco, Coq, CSS, Cucumber, CUDA, D, Dart, |
|||
DocBook, Dockerfile, Dust, Elixir, Erlang, eRuby, Fortran, Gentoo metadata, |
|||
GLSL, Go, Haml, Haskell, Haxe, Handlebars, HSS, HTML, Java, JavaScript, JSON, |
|||
JSX, Julia, LESS, Lex, Limbo, LISP, LLVM intermediate language, Lua, Markdown, |
|||
MATLAB, Mercury, NASM, Nix, Objective-C, Objective-C++, OCaml, Perl, Perl |
|||
6, Perl POD, PHP, gettext Portable Object, OS X and iOS property lists, Pug |
|||
(formerly Jade), Puppet, Python, QML, R, Racket, RDF TriG, RDF Turtle, Relax |
|||
NG, reStructuredText, RPM spec, Ruby, SASS/SCSS, Scala, Slim, SML, Solidity, |
|||
Sphinx, SQL, Stylus, Tcl, TeX, Texinfo, Twig, TypeScript, Vala, Verilog, VHDL, |
|||
Vim help, VimL, Vue.js, xHtml, XML, XSLT, XQuery, YACC, YAML, YANG data models, |
|||
YARA rules, z80, Zope page templates, and Zsh. See the [manual][checkers] for |
|||
details about the corresponding supported checkers (`:help syntastic-checkers` |
|||
in Vim). |
|||
|
|||
A number of third-party Vim plugins also provide checkers for syntastic, for |
|||
example: [merlin][merlin], [omnisharp-vim][omnisharp], [rust.vim][rust], |
|||
[syntastic-extras][myint], [syntastic-more][roktas], [tsuquyomi][tsuquyomi], |
|||
[vim-crystal][crystal], [vim-eastwood][eastwood], and [vim-swift][swift]. |
|||
|
|||
Below is a screenshot showing the methods that Syntastic uses to display syntax |
|||
errors. Note that, in practise, you will only have a subset of these methods |
|||
enabled. |
|||
|
|||
![Screenshot 1][screenshot] |
|||
|
|||
1. Errors are loaded into the location list for the corresponding window. |
|||
2. When the cursor is on a line containing an error, the error message is echoed in the command window. |
|||
3. Signs are placed beside lines with errors - note that warnings are displayed in a different color. |
|||
4. There is a configurable statusline flag you can include in your statusline config. |
|||
5. Hover the mouse over a line containing an error and the error message is displayed as a balloon. |
|||
6. (not shown) Highlighting errors with syntax highlighting. Erroneous parts of lines can be highlighted. |
|||
|
|||
<a name="installation"></a> |
|||
|
|||
## 2\. Installation |
|||
|
|||
<a name="requirements"></a> |
|||
|
|||
### 2.1\. Requirements |
|||
|
|||
Syntastic itself has rather relaxed requirements: it doesn't have any external |
|||
dependencies, and it needs a version of [Vim][vim] compiled with a few common |
|||
features: `autocmd`, `eval`, `file_in_path`, `modify_fname`, `quickfix`, |
|||
`reltime`, `statusline`, and `user_commands`. Not all possible combinations of |
|||
features that include the ones above make equal sense on all operating systems, |
|||
but Vim version 7 or later with the "normal", "big", or "huge" feature sets |
|||
should be fine. |
|||
|
|||
Syntastic should work with any modern plugin managers for Vim, such as |
|||
[NeoBundle][neobundle], [Pathogen][pathogen], [Vim-Addon-Manager][vam], |
|||
[Vim-Plug][plug], or [Vundle][vundle]. Instructions for installing syntastic |
|||
with [Pathogen][pathogen] are included below for completeness. |
|||
|
|||
Starting with Vim version 7.4.1486 you can also load syntastic using the |
|||
standard mechanism of packages, without the help of third-party plugin managers |
|||
(see `:help packages` in Vim for details). Beware however that, while support |
|||
for packages has been added in Vim 7.4.1384, the functionality needed by |
|||
syntastic is present only in versions 7.4.1486 and later. |
|||
|
|||
Last but not least: syntastic doesn't know how to do any syntax checks by |
|||
itself. In order to get meaningful results you need to install external |
|||
checkers corresponding to the types of files you use. Please consult the |
|||
[manual][checkers] (`:help syntastic-checkers` in Vim) for a list of supported |
|||
checkers. |
|||
|
|||
<a name="installpathogen"></a> |
|||
|
|||
### 2.2\. Installing syntastic with Pathogen |
|||
|
|||
If you already have [Pathogen][pathogen] working then skip [Step 1](#step1) and go to |
|||
[Step 2](#step2). |
|||
|
|||
<a name="step1"></a> |
|||
|
|||
#### 2.2.1\. Step 1: Install pathogen.vim |
|||
|
|||
First I'll show you how to install Tim Pope's [Pathogen][pathogen] so that it's easy to |
|||
install syntastic. Do this in your terminal so that you get the `pathogen.vim` |
|||
file and the directories it needs: |
|||
```sh |
|||
mkdir -p ~/.vim/autoload ~/.vim/bundle && \ |
|||
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim |
|||
``` |
|||
Next you *need* to add this to your `~/.vimrc`: |
|||
```vim |
|||
execute pathogen#infect() |
|||
``` |
|||
|
|||
<a name="step2"></a> |
|||
|
|||
#### 2.2.2\. Step 2: Install syntastic as a Pathogen bundle |
|||
|
|||
You now have pathogen installed and can put syntastic into `~/.vim/bundle` like |
|||
this: |
|||
```sh |
|||
cd ~/.vim/bundle && \ |
|||
git clone --depth=1 https://github.com/vim-syntastic/syntastic.git |
|||
``` |
|||
Quit vim and start it back up to reload it, then type: |
|||
```vim |
|||
:Helptags |
|||
``` |
|||
If you get an error when you do this, then you probably didn't install |
|||
[Pathogen][pathogen] right. Go back to [Step 1](#step1) and make sure you did the |
|||
following: |
|||
|
|||
1. Created both the `~/.vim/autoload` and `~/.vim/bundle` directories. |
|||
2. Added the `execute pathogen#infect()` line to your `~/.vimrc` file |
|||
3. Did the `git clone` of syntastic inside `~/.vim/bundle` |
|||
4. Have permissions to access all of these directories. |
|||
|
|||
<a name="settings"></a> |
|||
|
|||
## 3\. Recommended settings |
|||
|
|||
Syntastic has numerous options that can be configured, and the defaults |
|||
are not particularly well suitable for new users. It is recommended |
|||
that you start by adding the following lines to your `vimrc` file, and |
|||
return to them after reading the manual (see `:help syntastic` in Vim): |
|||
```vim |
|||
set statusline+=%#warningmsg# |
|||
set statusline+=%{SyntasticStatuslineFlag()} |
|||
set statusline+=%* |
|||
|
|||
let g:syntastic_always_populate_loc_list = 1 |
|||
let g:syntastic_auto_loc_list = 1 |
|||
let g:syntastic_check_on_open = 1 |
|||
let g:syntastic_check_on_wq = 0 |
|||
``` |
|||
|
|||
<a name="faq"></a> |
|||
|
|||
## 4\. FAQ |
|||
|
|||
<a name="faqinfo"></a> |
|||
|
|||
__4.1. Q. I installed syntastic but it isn't reporting any errors...__ |
|||
|
|||
A. The most likely reason is that none of the syntax checkers that it requires |
|||
are installed. For example: by default, python requires either `flake8` or |
|||
`pylint` to be installed and in your `$PATH`. Read the [manual][checkers] |
|||
(`:help syntastic-checkers` in Vim) to find out what executables are |
|||
supported. Note that aliases do not work; the actual executables must be |
|||
available in your `$PATH`. Symbolic links are okay though. You can see |
|||
syntastic's idea of available checkers by running `:SyntasticInfo`. |
|||
|
|||
A second probable reason is that none of the available checkers are |
|||
enabled. Syntastic comes preconfigured with a default list of enabled checkers |
|||
per filetype, but this list is kept short in order to prevent slowing down Vim |
|||
or trying to run conflicting checks. The command `:SyntasticInfo` will show you |
|||
which checkers are enabled. You can tell syntastic which checkers (among the |
|||
available ones) you want to run by setting `g:syntastic_<filetype>_checkers` in |
|||
your `vimrc` (see [below](#faqcheckers)). |
|||
|
|||
A third possible reason is that the `$PATH` seen by syntastic might not be same |
|||
as the `$PATH` in your login shell. Syntastic runs checkers using the shell |
|||
pointed to by Vim's `shell` (or by `g:syntastic_shell`, if set), and that's the |
|||
shell you need to configure to set the proper `$PATH` and environment variables |
|||
for your checkers. You can see syntastic's idea of `$PATH` by running |
|||
```vim |
|||
:echo syntastic#util#system('echo "$PATH"') |
|||
``` |
|||
on UNIX and Mac OS-X systems, or |
|||
```vim |
|||
:echo syntastic#util#system('echo %PATH%') |
|||
``` |
|||
on Windows. |
|||
|
|||
Finally, another reason it could fail is that either the command line options |
|||
or the error output for a syntax checker may have changed. In this case, make |
|||
sure you have the latest version of the syntax checker installed. If it still |
|||
fails then post an [issue][bug_tracker] - or better yet, create a pull request. |
|||
|
|||
<a name="faqcheckers"></a> |
|||
|
|||
__4.2. Q. Syntastic supports several checkers for my filetype, how do I tell it |
|||
which one(s) to use?__ |
|||
|
|||
A. Add a line like this to your `vimrc`: |
|||
```vim |
|||
let g:syntastic_<filetype>_checkers = ['<checker-name>'] |
|||
``` |
|||
|
|||
To see the list of supported checkers for your filetype read the |
|||
[manual][checkers] (`:help syntastic-checkers` in Vim). |
|||
|
|||
For example, Python has the following checkers, among others: `flake8`, |
|||
`pyflakes`, `pylint` and a native `python` checker. To tell syntastic to use |
|||
`pylint`, you would use this setting: |
|||
```vim |
|||
let g:syntastic_python_checkers = ['pylint'] |
|||
``` |
|||
|
|||
Checkers can be chained together like this: |
|||
```vim |
|||
let g:syntastic_php_checkers = ['php', 'phpcs', 'phpmd'] |
|||
``` |
|||
|
|||
This is telling syntastic to run the `php` checker first, and if no errors are |
|||
found, run `phpcs`, and then `phpmd`. |
|||
|
|||
You can also run checkers explicitly by calling `:SyntasticCheck <checker>`. |
|||
For example to run `phpcs` and `phpmd`: |
|||
```vim |
|||
:SyntasticCheck phpcs phpmd |
|||
``` |
|||
|
|||
This works for any checkers available for the current filetype, even if they |
|||
aren't listed in `g:syntastic_<filetype>_checkers`. |
|||
|
|||
<a name="faqforeign"></a> |
|||
|
|||
__4.3. Q. How can I run checkers for "foreign" filetypes against the current |
|||
file?__ |
|||
|
|||
A. You need to qualify the name of the "foreign" checker with the name |
|||
of its filetype. For example to check `tex` files with the checker |
|||
`language_check` (which normally acts only on files of type `text`), you can |
|||
add `text/language_check` to the list fo checkers for `tex`: |
|||
```vim |
|||
let g:syntastic_tex_checkers = ['lacheck', 'text/language_check'] |
|||
``` |
|||
|
|||
This also works with `:SyntasticCheck`, e.g. the following command runs |
|||
`text/language_check` against the current file regardless of the current |
|||
filetype: |
|||
```vim |
|||
:SyntasticCheck text/language_check |
|||
``` |
|||
|
|||
Of course, the checkers specified this way need to be known to syntastic, and |
|||
they need to be shown as available when you run `:SyntasticInfo`. You can't |
|||
just make up a combination of a filetype and a program name and expect it to |
|||
work as a checker. |
|||
|
|||
<a name="faqaggregate"></a> |
|||
|
|||
__4.4. Q. I have enabled multiple checkers for the current filetype. How can I |
|||
display all errors from all checkers together?__ |
|||
|
|||
A. Set `g:syntastic_aggregate_errors` to 1 in your `vimrc`: |
|||
```vim |
|||
let g:syntastic_aggregate_errors = 1 |
|||
``` |
|||
|
|||
See `:help syntastic-aggregating-errors` for more details. |
|||
|
|||
<a name="faqargs"></a> |
|||
|
|||
__4.5. Q. How can I pass additional arguments to a checker?__ |
|||
|
|||
A. In most cases a command line is constructed using an internal function |
|||
named `makeprgBuild()`, which provides a number of options that allow you to |
|||
customise every part of the command that gets run. You can set these options |
|||
using global variables. |
|||
|
|||
The general form of the global `args` variable is |
|||
`syntastic_<filetype>_<checker>_args`. Thus if you wanted to pass |
|||
`--my --args --here` to the Ruby `mri` checker you would add this line to your |
|||
`vimrc`: |
|||
```vim |
|||
let g:syntastic_ruby_mri_args = "--my --args --here" |
|||
``` |
|||
|
|||
See `:help syntastic-checker-options` for more information. |
|||
|
|||
A number of checkers don't use the `makeprgBuild()` function mentioned above, |
|||
or have additional options that can be configured. For these checkers the exact |
|||
list of options should be included in the [manual][checkers] |
|||
(`:help syntastic-checkers` in Vim). |
|||
|
|||
<a name="faqloclist"></a> |
|||
|
|||
__4.6. Q. I run a checker and the location list is not updated...__ |
|||
__4.6. Q. I run`:lopen` or `:lwindow` and the error window is empty...__ |
|||
|
|||
A. By default the location list is changed only when you run the `:Errors` |
|||
command, in order to minimise conflicts with other plugins. If you want the |
|||
location list to always be updated when you run the checkers, add this line to |
|||
your `vimrc`: |
|||
```vim |
|||
let g:syntastic_always_populate_loc_list = 1 |
|||
``` |
|||
|
|||
<a name="faqlnext"></a> |
|||
|
|||
__4.7. Q. How can I jump between the different errors without using the location |
|||
list at the bottom of the window?__ |
|||
|
|||
A. Vim provides several built-in commands for this. See `:help :lnext` and |
|||
`:help :lprevious`. |
|||
|
|||
If you use these commands a lot then you may want to add shortcut mappings to |
|||
your `vimrc`, or install something like [unimpaired][unimpaired], which provides such |
|||
mappings (among other things). |
|||
|
|||
<a name="faqbdelete"></a> |
|||
|
|||
__4.8. Q. The error window is closed automatically when I `:quit` the current buffer |
|||
but not when I `:bdelete` it?__ |
|||
|
|||
A. There is no safe way to handle that situation automatically, but you can |
|||
work around it: |
|||
```vim |
|||
nnoremap <silent> <C-d> :lclose<CR>:bdelete<CR> |
|||
cabbrev <silent> bd <C-r>=(getcmdtype()==#':' && getcmdpos()==1 ? 'lclose\|bdelete' : 'bd')<CR> |
|||
``` |
|||
|
|||
<a name="faqconfig"></a> |
|||
|
|||
__4.9. My favourite checker needs to load a configuration file from the |
|||
project's root rather than the current directory...__ |
|||
|
|||
A. You can set up an `autocmd` to search for the configuration file in the |
|||
current directory and upwards, and add it to the checker's options when found. |
|||
For example for `jscs`: |
|||
|
|||
```vim |
|||
function! FindConfig(prefix, what, where) |
|||
let cfg = findfile(a:what, escape(a:where, ' ') . ';') |
|||
return cfg !=# '' ? ' ' . a:prefix . ' ' . shellescape(cfg) : '' |
|||
endfunction |
|||
|
|||
autocmd FileType javascript let b:syntastic_javascript_jscs_args = |
|||
\ get(g:, 'syntastic_javascript_jscs_args', '') . |
|||
\ FindConfig('-c', '.jscsrc', expand('<afile>:p:h', 1)) |
|||
``` |
|||
|
|||
<a name="faqstyle"></a> |
|||
|
|||
__4.10. Q. What is the difference between syntax checkers and style checkers?__ |
|||
|
|||
A. The errors and warnings they produce are highlighted differently and can |
|||
be filtered by different rules, but otherwise the distinction is pretty much |
|||
arbitrary. There is an ongoing effort to keep things consistent, so you can |
|||
_generally_ expect messages produced by syntax checkers to be _mostly_ related |
|||
to syntax, and messages produced by style checkers to be _mostly_ about style. |
|||
But there can be no formal guarantee that, say, a style checker that runs into |
|||
a syntax error wouldn't die with a fatal message, nor that a syntax checker |
|||
wouldn't give you warnings against using some constructs as being bad practice. |
|||
There is also no guarantee that messages marked as `style` are less severe than |
|||
the ones marked as `syntax` (whatever that might mean). And there are even a |
|||
few Frankenstein checkers (for example `flake8` and `pylama`) that, by their |
|||
nature, produce both kinds of messages. Syntastic is not smart enough to be |
|||
able to sort out these things by itself. |
|||
|
|||
Generally it's more useful to look at this from the perspective of filtering |
|||
unwanted messages, rather than as an indicator of severity levels. The |
|||
distinction between syntax and style is orthogonal to the distinction between |
|||
errors and warnings, and thus you can turn off messages based on level, on |
|||
type, or both. |
|||
|
|||
e.g. To disable all style messages: |
|||
```vim |
|||
let g:syntastic_quiet_messages = { "type": "style" } |
|||
``` |
|||
See `:help syntastic_quiet_messages` for more information. |
|||
|
|||
<a name="faqpython"></a> |
|||
|
|||
__4.11. Q. How can I check scripts written for different versions of Python?__ |
|||
|
|||
A. Install a Python version manager such as [virtualenv][virtualenv] |
|||
or [pyenv][pyenv], activate the environment for the relevant version |
|||
of Python, and install in it the checkers you want to use. Set |
|||
`g:syntastic_python_checkers` accordingly in your `vimrc`, and run [Vim][vim] |
|||
from the virtual environment. |
|||
|
|||
If you're starting Vim from a desktop manager rather than from a terminal you |
|||
might need to write wrapper scripts around your checkers, to activate the |
|||
virtual environment before running the actual checks. Then you'll need to |
|||
point the relevant `g:syntastic_python_<checker>_exec` variables to the wrapper |
|||
scripts. |
|||
|
|||
<a name="faqruby"></a> |
|||
|
|||
__4.12. Q. How can I check scripts written for different versions of Ruby?__ |
|||
|
|||
A. Install a Ruby version manager such as [rvm][rvm] or [rbenv][rbenv], |
|||
activate the relevant version of Ruby, and install in it the checkers you want |
|||
to use. Set `g:syntastic_ruby_checkers` accordingly in your `vimrc`, and run |
|||
[Vim][vim] under the relevant Ruby version. |
|||
|
|||
If you're starting Vim from a desktop manager rather than from a terminal |
|||
and depending on the version manager you use you might need to write wrapper |
|||
scripts around your checkers, to activate the relevant version of Ruby |
|||
before running the actual checks. Then you'll need to point the relevant |
|||
`g:syntastic_ruby_<checker>_exec` variables to the wrapper scripts. |
|||
|
|||
<a name="faqperl"></a> |
|||
|
|||
__4.13. Q. The `perl` checker has stopped working...__ |
|||
|
|||
A. The `perl` checker runs `perl -c` against your file, which in turn |
|||
__executes__ any `BEGIN`, `UNITCHECK`, and `CHECK` blocks, and any `use` |
|||
statements in your file (cf. [perlrun][perlrun]). This is probably fine if you |
|||
wrote the file yourself, but it's a security problem if you're checking |
|||
third-party files. Since there is currently no way to disable this behaviour |
|||
while still producing useful results, the checker is now disabled by default. |
|||
To (re-)enable it, make sure the `g:syntastic_perl_checkers` list includes |
|||
`perl`, and set `g:syntastic_enable_perl_checker` to 1 in your `vimrc`: |
|||
```vim |
|||
let g:syntastic_enable_perl_checker = 1 |
|||
``` |
|||
|
|||
<a name="faqrust"></a> |
|||
|
|||
__4.14. Q. What happened to the `rustc` checker?__ |
|||
|
|||
A. It is now part of the [rust.vim][rust] plugin. If you install this plugin the |
|||
checker should be picked up automatically by syntastic. |
|||
|
|||
<a name="faqtsc"></a> |
|||
|
|||
__4.15. Q. What happened to the `tsc` checker?__ |
|||
|
|||
A. It didn't meet people's expectations and it has been removed. The plugin |
|||
[tsuquyomi][tsuquyomi] comes packaged with a checker for TypeScript. If you |
|||
install this plugin the checker should be picked up automatically by syntastic. |
|||
|
|||
<a name="faqxcrun"></a> |
|||
|
|||
__4.16. Q. What happened to the `xcrun` checker?__ |
|||
|
|||
A. The `xcrun` checker used to have a security problem and it has been removed. |
|||
A better checker for __Swift__ is part of the [vim-swift][swift] plugin. If you |
|||
install this plugin the checker should be picked up automatically by syntastic. |
|||
|
|||
<a name="otherresources"></a> |
|||
|
|||
## 5\. Resources |
|||
|
|||
The preferred place for posting suggestions, reporting bugs, and general |
|||
discussions related to syntastic is the [issue tracker at GitHub][bug_tracker]. |
|||
A guide for writing syntax checkers can be found in the [wiki][guide]. |
|||
There are also a dedicated [google group][google_group], and a |
|||
[syntastic tag at StackOverflow][stack_overflow]. |
|||
|
|||
Syntastic aims to provide a common interface to syntax checkers for as many |
|||
languages as possible. For particular languages, there are, of course, other |
|||
plugins that provide more functionality than syntastic. You might want to take |
|||
a look at [ghcmod-vim][ghcmod], [jedi-vim][jedi], [python-mode][python_mode], [vim-go][vimgo], or |
|||
[YouCompleteMe][ycm]. |
|||
|
|||
[scrooloose]: https://github.com/scrooloose |
|||
[screenshot]: https://github.com/vim-syntastic/syntastic/raw/master/_assets/screenshot_1.png |
|||
|
|||
[bug_tracker]: https://github.com/vim-syntastic/syntastic/issues |
|||
[checkers]: https://github.com/vim-syntastic/syntastic/blob/master/doc/syntastic-checkers.txt |
|||
[crystal]: https://github.com/rhysd/vim-crystal |
|||
[eastwood]: https://github.com/venantius/vim-eastwood |
|||
[ghcmod]: https://github.com/eagletmt/ghcmod-vim |
|||
[google_group]: https://groups.google.com/group/vim-syntastic |
|||
[guide]: https://github.com/vim-syntastic/syntastic/wiki/Syntax-Checker-Guide |
|||
[jedi]: https://github.com/davidhalter/jedi-vim |
|||
[merlin]: https://github.com/the-lambda-church/merlin |
|||
[myint]: https://github.com/myint/syntastic-extras |
|||
[neobundle]: https://github.com/Shougo/neobundle.vim |
|||
[omnisharp]: https://github.com/OmniSharp/omnisharp-vim |
|||
[pathogen]: https://github.com/tpope/vim-pathogen |
|||
[perlrun]: http://perldoc.perl.org/perlrun.html#*-c* |
|||
[plug]: https://github.com/junegunn/vim-plug/ |
|||
[pyenv]: https://github.com/yyuu/pyenv |
|||
[python_mode]: https://github.com/klen/python-mode |
|||
[rbenv]: https://github.com/rbenv/rbenv |
|||
[roktas]: https://github.com/roktas/syntastic-more |
|||
[rust]: https://github.com/rust-lang/rust.vim |
|||
[rvm]: https://rvm.io/ |
|||
[stack_overflow]: http://stackoverflow.com/questions/tagged/syntastic |
|||
[swift]: https://github.com/kballard/vim-swift |
|||
[tsuquyomi]: https://github.com/Quramy/tsuquyomi/ |
|||
[unimpaired]: https://github.com/tpope/vim-unimpaired |
|||
[vam]: https://github.com/MarcWeber/vim-addon-manager |
|||
[vim]: http://www.vim.org/ |
|||
[vimgo]: https://github.com/fatih/vim-go |
|||
[virtualenv]: https://virtualenv.pypa.io/en/stable/ |
|||
[vundle]: https://github.com/gmarik/Vundle.vim |
|||
[ycm]: https://github.com/ycm-core/YouCompleteMe |
|||
|
|||
<!-- |
|||
vim:tw=79:sw=4: |
|||
--> |
|||
|
After Width: | Height: | Size: 90 KiB |
@ -0,0 +1,341 @@ |
|||
if exists('g:loaded_syntastic_c_autoload') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_autoload = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
" Public functions {{{1 |
|||
|
|||
" convenience function to determine the 'null device' parameter |
|||
" based on the current operating system |
|||
function! syntastic#c#NullOutput() abort " {{{2 |
|||
let known_os = has('unix') || has('mac') || syntastic#util#isRunningWindows() |
|||
return known_os ? '-o ' . syntastic#util#DevNull() : '' |
|||
endfunction " }}}2 |
|||
|
|||
" read additional compiler flags from the given configuration file |
|||
" the file format and its parsing mechanism is inspired by clang_complete |
|||
function! syntastic#c#ReadConfig(file) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: looking for', a:file) |
|||
|
|||
" search upwards from the current file's directory |
|||
let config = syntastic#util#findFileInParent(a:file, expand('%:p:h', 1)) |
|||
if config ==# '' |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: file not found') |
|||
return '' |
|||
endif |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: config file:', config) |
|||
if !filereadable(config) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: file unreadable') |
|||
return '' |
|||
endif |
|||
|
|||
" convert filename into absolute path |
|||
let filepath = fnamemodify(config, ':p:h') |
|||
|
|||
" try to read config file |
|||
try |
|||
let lines = readfile(config) |
|||
catch /\m^Vim\%((\a\+)\)\=:E48[45]/ |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, 'ReadConfig: error reading file') |
|||
return '' |
|||
endtry |
|||
|
|||
" filter out empty lines and comments |
|||
call filter(lines, 'v:val !~# ''\v^(\s*#|$)''') |
|||
|
|||
" remove leading and trailing spaces |
|||
call map(lines, 'substitute(v:val, ''\m^\s\+'', "", "")') |
|||
call map(lines, 'substitute(v:val, ''\m\s\+$'', "", "")') |
|||
|
|||
let parameters = [] |
|||
for line in lines |
|||
let matches = matchstr(line, '\m\C^\s*-I\s*\zs.\+') |
|||
if matches !=# '' |
|||
" this one looks like an absolute path |
|||
if match(matches, '\m^\%(/\|\a:\)') != -1 |
|||
call add(parameters, '-I' . matches) |
|||
else |
|||
call add(parameters, '-I' . filepath . syntastic#util#Slash() . matches) |
|||
endif |
|||
else |
|||
call add(parameters, line) |
|||
endif |
|||
endfor |
|||
|
|||
return join(map(parameters, 'syntastic#util#shescape(v:val)')) |
|||
endfunction " }}}2 |
|||
|
|||
" GetLocList() for C-like compilers |
|||
function! syntastic#c#GetLocList(filetype, subchecker, options) abort " {{{2 |
|||
try |
|||
let flags = s:_get_cflags(a:filetype, a:subchecker, a:options) |
|||
catch /\m\C^Syntastic: skip checks$/ |
|||
return [] |
|||
endtry |
|||
|
|||
let makeprg = syntastic#util#shexpand(g:syntastic_{a:filetype}_compiler) . |
|||
\ ' ' . flags . ' ' . syntastic#util#shexpand('%') |
|||
|
|||
let errorformat = s:_get_checker_var('g', a:filetype, a:subchecker, 'errorformat', a:options['errorformat']) |
|||
|
|||
let postprocess = s:_get_checker_var('g', a:filetype, a:subchecker, 'remove_include_errors', 0) ? |
|||
\ ['filterForeignErrors'] : [] |
|||
|
|||
" process makeprg |
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'postprocess': postprocess }) |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private functions {{{1 |
|||
|
|||
" initialize c/cpp syntax checker handlers |
|||
function! s:_init() abort " {{{2 |
|||
let s:handlers = [] |
|||
let s:cflags = {} |
|||
|
|||
call s:_registerHandler('\m\<cairo', 's:_checkPackage', ['cairo', 'cairo']) |
|||
call s:_registerHandler('\m\<freetype', 's:_checkPackage', ['freetype', 'freetype2', 'freetype']) |
|||
call s:_registerHandler('\m\<glade', 's:_checkPackage', ['glade', 'libglade-2.0', 'libglade']) |
|||
call s:_registerHandler('\m\<glib', 's:_checkPackage', ['glib', 'glib-2.0', 'glib']) |
|||
call s:_registerHandler('\m\<gtk', 's:_checkPackage', ['gtk', 'gtk+-2.0', 'gtk+', 'glib-2.0', 'glib']) |
|||
call s:_registerHandler('\m\<libsoup', 's:_checkPackage', ['libsoup', 'libsoup-2.4', 'libsoup-2.2']) |
|||
call s:_registerHandler('\m\<libxml', 's:_checkPackage', ['libxml', 'libxml-2.0', 'libxml']) |
|||
call s:_registerHandler('\m\<pango', 's:_checkPackage', ['pango', 'pango']) |
|||
call s:_registerHandler('\m\<SDL', 's:_checkPackage', ['sdl', 'sdl']) |
|||
call s:_registerHandler('\m\<opengl', 's:_checkPackage', ['opengl', 'gl']) |
|||
call s:_registerHandler('\m\<webkit', 's:_checkPackage', ['webkit', 'webkit-1.0']) |
|||
|
|||
call s:_registerHandler('\m\<php\.h\>', 's:_checkPhp', []) |
|||
call s:_registerHandler('\m\<Python\.h\>', 's:_checkPython', []) |
|||
call s:_registerHandler('\m\<ruby', 's:_checkRuby', []) |
|||
endfunction " }}}2 |
|||
|
|||
" register a handler dictionary object |
|||
function! s:_registerHandler(regex, function, args) abort " {{{2 |
|||
let handler = {} |
|||
let handler['regex'] = a:regex |
|||
let handler['func'] = function(a:function) |
|||
let handler['args'] = a:args |
|||
call add(s:handlers, handler) |
|||
endfunction " }}}2 |
|||
|
|||
" try to find library with 'pkg-config' |
|||
" search possible libraries from first to last given |
|||
" argument until one is found |
|||
function! s:_checkPackage(name, ...) abort " {{{2 |
|||
if executable('pkg-config') |
|||
if !has_key(s:cflags, a:name) |
|||
for pkg in a:000 |
|||
let pkg_flags = syntastic#util#system('pkg-config --cflags ' . pkg) |
|||
" since we cannot necessarily trust the pkg-config exit code |
|||
" we have to check for an error output as well |
|||
if v:shell_error == 0 && pkg_flags !~? 'not found' |
|||
let pkg_flags = ' ' . substitute(pkg_flags, "\n", '', '') |
|||
let s:cflags[a:name] = pkg_flags |
|||
return pkg_flags |
|||
endif |
|||
endfor |
|||
else |
|||
return s:cflags[a:name] |
|||
endif |
|||
endif |
|||
return '' |
|||
endfunction " }}}2 |
|||
|
|||
" try to find PHP includes with 'php-config' |
|||
function! s:_checkPhp() abort " {{{2 |
|||
if executable('php-config') |
|||
if !has_key(s:cflags, 'php') |
|||
let s:cflags['php'] = syntastic#util#system('php-config --includes') |
|||
let s:cflags['php'] = ' ' . substitute(s:cflags['php'], "\n", '', '') |
|||
endif |
|||
return s:cflags['php'] |
|||
endif |
|||
return '' |
|||
endfunction " }}}2 |
|||
|
|||
" try to find the python headers with distutils |
|||
function! s:_checkPython() abort " {{{2 |
|||
if executable('python') |
|||
if !has_key(s:cflags, 'python') |
|||
let s:cflags['python'] = syntastic#util#system('python -c ''from distutils import ' . |
|||
\ 'sysconfig; import sys; sys.stdout.write(sysconfig.get_python_inc())''') |
|||
let s:cflags['python'] = substitute(s:cflags['python'], "\n", '', '') |
|||
let s:cflags['python'] = ' -I' . s:cflags['python'] |
|||
endif |
|||
return s:cflags['python'] |
|||
endif |
|||
return '' |
|||
endfunction " }}}2 |
|||
|
|||
" try to find the ruby headers with 'rbconfig' |
|||
function! s:_checkRuby() abort " {{{2 |
|||
if executable('ruby') |
|||
if !has_key(s:cflags, 'ruby') |
|||
let s:cflags['ruby'] = syntastic#util#system('ruby -r rbconfig -e ' . |
|||
\ '''puts RbConfig::CONFIG["rubyhdrdir"] || RbConfig::CONFIG["archdir"]''') |
|||
let s:cflags['ruby'] = substitute(s:cflags['ruby'], "\n", '', '') |
|||
let s:cflags['ruby'] = ' -I' . s:cflags['ruby'] |
|||
endif |
|||
return s:cflags['ruby'] |
|||
endif |
|||
return '' |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Utilities {{{1 |
|||
|
|||
" resolve checker-related user variables |
|||
function! s:_get_checker_var(scope, filetype, subchecker, name, default) abort " {{{2 |
|||
let prefix = a:scope . ':' . 'syntastic_' |
|||
if exists(prefix . a:filetype . '_' . a:subchecker . '_' . a:name) |
|||
return {a:scope}:syntastic_{a:filetype}_{a:subchecker}_{a:name} |
|||
elseif exists(prefix . a:filetype . '_' . a:name) |
|||
return {a:scope}:syntastic_{a:filetype}_{a:name} |
|||
else |
|||
return a:default |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" resolve user CFLAGS |
|||
function! s:_get_cflags(ft, ck, opts) abort " {{{2 |
|||
" determine whether to parse header files as well |
|||
if has_key(a:opts, 'header_names') && expand('%', 1) =~? a:opts['header_names'] |
|||
if s:_get_checker_var('g', a:ft, a:ck, 'check_header', 0) |
|||
let flags = get(a:opts, 'header_flags', '') . ' -c ' . syntastic#c#NullOutput() |
|||
else |
|||
" checking headers when check_header is unset: bail out |
|||
throw 'Syntastic: skip checks' |
|||
endif |
|||
else |
|||
let flags = get(a:opts, 'main_flags', '') |
|||
endif |
|||
|
|||
let flags .= ' ' . s:_get_checker_var('g', a:ft, a:ck, 'compiler_options', '') . ' ' . s:_get_include_dirs(a:ft) |
|||
|
|||
" check if the user manually set some cflags |
|||
let b_cflags = s:_get_checker_var('b', a:ft, a:ck, 'cflags', '') |
|||
if b_cflags !=# '' |
|||
let flags .= ' ' . b_cflags |
|||
endif |
|||
|
|||
" add optional config file parameters |
|||
let config_file = s:_get_checker_var('b', a:ft, a:ck, 'config_file', s:_get_checker_var('g', a:ft, a:ck, 'config_file', '')) |
|||
let flags .= ' ' . syntastic#c#ReadConfig(config_file) |
|||
|
|||
if b_cflags ==# '' && (a:ft ==# 'c' || a:ft ==# 'cpp') && !s:_get_checker_var('g', a:ft, a:ck, 'no_include_search', 0) |
|||
" refresh the include file search if desired |
|||
if s:_get_checker_var('g', a:ft, a:ck, 'auto_refresh_includes', 0) |
|||
let flags .= ' ' . s:_search_headers() |
|||
else |
|||
" search for header includes if not cached already |
|||
if !exists('b:syntastic_' . a:ft . '_includes') |
|||
let b:syntastic_{a:ft}_includes = s:_search_headers() |
|||
endif |
|||
let flags .= ' ' . b:syntastic_{a:ft}_includes |
|||
endif |
|||
endif |
|||
|
|||
return flags |
|||
endfunction " }}}2 |
|||
|
|||
" get the gcc include directory argument depending on the default |
|||
" includes and the optional user-defined 'g:syntastic_c_include_dirs' |
|||
function! s:_get_include_dirs(filetype) abort " {{{2 |
|||
let include_dirs = [] |
|||
|
|||
if a:filetype =~# '\v^%(c|cpp|objc|objcpp)$' && |
|||
\ (!exists('g:syntastic_'.a:filetype.'_no_default_include_dirs') || |
|||
\ !g:syntastic_{a:filetype}_no_default_include_dirs) |
|||
let include_dirs = copy(s:default_includes) |
|||
endif |
|||
|
|||
if exists('g:syntastic_'.a:filetype.'_include_dirs') |
|||
call extend(include_dirs, g:syntastic_{a:filetype}_include_dirs) |
|||
endif |
|||
|
|||
return join(map(syntastic#util#unique(include_dirs), 'syntastic#util#shescape("-I" . v:val)')) |
|||
endfunction " }}}2 |
|||
|
|||
" search the first 100 lines for include statements that are |
|||
" given in the handlers dictionary |
|||
function! s:_search_headers() abort " {{{2 |
|||
let includes = '' |
|||
let files = [] |
|||
let found = [] |
|||
let lines = filter(getline(1, 100), 'v:val =~# ''\m^\s*#\s*include''') |
|||
|
|||
" search current buffer |
|||
for line in lines |
|||
let file = matchstr(line, '\m"\zs\S\+\ze"') |
|||
if file !=# '' |
|||
call add(files, file) |
|||
continue |
|||
endif |
|||
|
|||
for handler in s:handlers |
|||
if line =~# handler['regex'] |
|||
let includes .= call(handler['func'], handler['args']) |
|||
call add(found, handler['regex']) |
|||
break |
|||
endif |
|||
endfor |
|||
endfor |
|||
|
|||
" search included headers |
|||
for hfile in files |
|||
if hfile !=# '' |
|||
let filename = expand('%:p:h', 1) . syntastic#util#Slash() . hfile |
|||
|
|||
try |
|||
let lines = readfile(filename, '', 100) |
|||
catch /\m^Vim\%((\a\+)\)\=:E484/ |
|||
continue |
|||
endtry |
|||
|
|||
call filter(lines, 'v:val =~# ''\m^\s*#\s*include''') |
|||
|
|||
for handler in s:handlers |
|||
if index(found, handler['regex']) != -1 |
|||
continue |
|||
endif |
|||
|
|||
for line in lines |
|||
if line =~# handler['regex'] |
|||
let includes .= call(handler['func'], handler['args']) |
|||
call add(found, handler['regex']) |
|||
break |
|||
endif |
|||
endfor |
|||
endfor |
|||
endif |
|||
endfor |
|||
|
|||
return includes |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" default include directories |
|||
let s:default_includes = [ |
|||
\ '.', |
|||
\ '..', |
|||
\ 'include', |
|||
\ 'includes', |
|||
\ '..' . syntastic#util#Slash() . 'include', |
|||
\ '..' . syntastic#util#Slash() . 'includes' ] |
|||
|
|||
call s:_init() |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,222 @@ |
|||
if exists('g:loaded_syntastic_log_autoload') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_log_autoload = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
let s:one_time_notices_issued = [] |
|||
|
|||
" Public functions {{{1 |
|||
|
|||
function! syntastic#log#info(msg) abort " {{{2 |
|||
echomsg 'syntastic: info: ' . a:msg |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#log#warn(msg) abort " {{{2 |
|||
echohl WarningMsg |
|||
echomsg 'syntastic: warning: ' . a:msg |
|||
echohl None |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#log#error(msg) abort " {{{2 |
|||
execute 'normal! \<Esc>' |
|||
echohl ErrorMsg |
|||
echomsg 'syntastic: error: ' . a:msg |
|||
echohl None |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#log#oneTimeWarn(msg) abort " {{{2 |
|||
if index(s:one_time_notices_issued, a:msg) >= 0 |
|||
return |
|||
endif |
|||
|
|||
call add(s:one_time_notices_issued, a:msg) |
|||
call syntastic#log#warn(a:msg) |
|||
endfunction " }}}2 |
|||
|
|||
" @vimlint(EVL102, 1, l:OLD_VAR) |
|||
function! syntastic#log#deprecationWarn(old, new, ...) abort " {{{2 |
|||
if exists('g:syntastic_' . a:old) && !exists('g:syntastic_' . a:new) |
|||
let msg = 'variable g:syntastic_' . a:old . ' is deprecated, please use ' |
|||
|
|||
if a:0 |
|||
let OLD_VAR = g:syntastic_{a:old} |
|||
try |
|||
let NEW_VAR = eval(a:1) |
|||
let msg .= 'in its stead: let g:syntastic_' . a:new . ' = ' . string(NEW_VAR) |
|||
let g:syntastic_{a:new} = NEW_VAR |
|||
catch |
|||
let msg .= 'g:syntastic_' . a:new . ' instead' |
|||
endtry |
|||
else |
|||
let msg .= 'g:syntastic_' . a:new . ' instead' |
|||
let g:syntastic_{a:new} = g:syntastic_{a:old} |
|||
endif |
|||
|
|||
call syntastic#log#oneTimeWarn(msg) |
|||
endif |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL102, 0, l:OLD_VAR) |
|||
|
|||
function! syntastic#log#debug(level, msg, ...) abort " {{{2 |
|||
if !s:_isDebugEnabled(a:level) |
|||
return |
|||
endif |
|||
|
|||
let leader = s:_log_timestamp() |
|||
call s:_logRedirect(1) |
|||
|
|||
if a:0 |
|||
" filter out dictionary functions |
|||
echomsg leader . a:msg . ' ' . |
|||
\ strtrans(string(type(a:1) == type({}) || type(a:1) == type([]) ? |
|||
\ filter(copy(a:1), 'type(v:val) != type(function("tr"))') : a:1)) |
|||
else |
|||
echomsg leader . a:msg |
|||
endif |
|||
|
|||
call s:_logRedirect(0) |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#log#debugShowOptions(level, names) abort " {{{2 |
|||
if !s:_isDebugEnabled(a:level) |
|||
return |
|||
endif |
|||
|
|||
let leader = s:_log_timestamp() |
|||
call s:_logRedirect(1) |
|||
|
|||
let vlist = copy(type(a:names) == type('') ? [a:names] : a:names) |
|||
let add_shell = index(vlist, 'shell') >= 0 && &shell !=# syntastic#util#var('shell') |
|||
if !empty(vlist) |
|||
call map(vlist, "'&' . v:val . ' = ' . strtrans(string(eval('&' . v:val))) . (s:_is_modified(v:val) ? ' (!)' : '')") |
|||
if add_shell |
|||
call add(vlist, 'u:shell = ' . strtrans(string(syntastic#util#var('shell'))) . ' (!)') |
|||
endif |
|||
echomsg leader . join(vlist, ', ') |
|||
endif |
|||
call s:_logRedirect(0) |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#log#debugShowVariables(level, names) abort " {{{2 |
|||
if !s:_isDebugEnabled(a:level) |
|||
return |
|||
endif |
|||
|
|||
let leader = s:_log_timestamp() |
|||
call s:_logRedirect(1) |
|||
|
|||
let vlist = type(a:names) == type('') ? [a:names] : a:names |
|||
for name in vlist |
|||
let msg = s:_format_variable(name) |
|||
if msg !=# '' |
|||
echomsg leader . msg |
|||
endif |
|||
endfor |
|||
|
|||
call s:_logRedirect(0) |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#log#debugDump(level) abort " {{{2 |
|||
if !s:_isDebugEnabled(a:level) |
|||
return |
|||
endif |
|||
|
|||
call syntastic#log#debugShowVariables( a:level, sort(keys(g:_SYNTASTIC_DEFAULTS)) ) |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#log#ndebug(level, title, messages) abort " {{{2 |
|||
if s:_isDebugEnabled(a:level) |
|||
return |
|||
endif |
|||
|
|||
call syntastic#log#error(a:title) |
|||
if type(a:messages) == type([]) |
|||
for msg in a:messages |
|||
echomsg msg |
|||
endfor |
|||
else |
|||
echomsg a:messages |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private functions {{{1 |
|||
|
|||
function! s:_isDebugEnabled_smart(level) abort " {{{2 |
|||
return and(g:syntastic_debug, a:level) |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_isDebugEnabled_dumb(level) abort " {{{2 |
|||
" poor man's bit test for bit N, assuming a:level == 2**N |
|||
return (g:syntastic_debug / a:level) % 2 |
|||
endfunction " }}}2 |
|||
|
|||
let s:_isDebugEnabled = function(exists('*and') ? 's:_isDebugEnabled_smart' : 's:_isDebugEnabled_dumb') |
|||
lockvar s:_isDebugEnabled |
|||
|
|||
function! s:_logRedirect(on) abort " {{{2 |
|||
if exists('g:syntastic_debug_file') |
|||
if a:on |
|||
try |
|||
execute 'redir >> ' . fnameescape(expand(g:syntastic_debug_file, 1)) |
|||
catch /\m^Vim\%((\a\+)\)\=:/ |
|||
silent! redir END |
|||
unlet g:syntastic_debug_file |
|||
endtry |
|||
else |
|||
silent! redir END |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Utilities {{{1 |
|||
|
|||
function! s:_log_timestamp_smart() abort " {{{2 |
|||
return printf('syntastic: %f: ', reltimefloat(reltime(g:_SYNTASTIC_START))) |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_log_timestamp_dumb() abort " {{{2 |
|||
return 'syntastic: ' . split(reltimestr(reltime(g:_SYNTASTIC_START)))[0] . ': ' |
|||
endfunction " }}}2 |
|||
|
|||
let s:_log_timestamp = function(has('float') && exists('*reltimefloat') ? 's:_log_timestamp_smart' : 's:_log_timestamp_dumb') |
|||
lockvar s:_log_timestamp |
|||
|
|||
function! s:_format_variable(name) abort " {{{2 |
|||
let vals = [] |
|||
if exists('g:syntastic_' . a:name) |
|||
call add(vals, 'g:syntastic_' . a:name . ' = ' . strtrans(string(g:syntastic_{a:name}))) |
|||
endif |
|||
if exists('b:syntastic_' . a:name) |
|||
call add(vals, 'b:syntastic_' . a:name . ' = ' . strtrans(string(b:syntastic_{a:name}))) |
|||
endif |
|||
|
|||
return join(vals, ', ') |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_is_modified(name) abort " {{{2 |
|||
if !exists('s:option_defaults') |
|||
let s:option_defaults = {} |
|||
endif |
|||
if !has_key(s:option_defaults, a:name) |
|||
let opt_save = eval('&' . a:name) |
|||
execute 'set ' . a:name . '&' |
|||
let s:option_defaults[a:name] = eval('&' . a:name) |
|||
execute 'let &' . a:name . ' = ' . string(opt_save) |
|||
endif |
|||
|
|||
return s:option_defaults[a:name] !=# eval('&' . a:name) |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,84 @@ |
|||
if exists('g:loaded_syntastic_postprocess_autoload') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_postprocess_autoload = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
" Public functions {{{1 |
|||
|
|||
" merge consecutive blanks |
|||
function! syntastic#postprocess#compressWhitespace(errors) abort " {{{2 |
|||
for e in a:errors |
|||
let e['text'] = substitute(e['text'], "\001", '', 'g') |
|||
let e['text'] = substitute(e['text'], '\n', ' ', 'g') |
|||
let e['text'] = substitute(e['text'], '\m\s\{2,}', ' ', 'g') |
|||
let e['text'] = substitute(e['text'], '\m^\s\+', '', '') |
|||
let e['text'] = substitute(e['text'], '\m\s\+$', '', '') |
|||
endfor |
|||
|
|||
return a:errors |
|||
endfunction " }}}2 |
|||
|
|||
" remove spurious CR under Cygwin |
|||
function! syntastic#postprocess#cygwinRemoveCR(errors) abort " {{{2 |
|||
if has('win32unix') |
|||
for e in a:errors |
|||
let e['text'] = substitute(e['text'], '\r', '', 'g') |
|||
endfor |
|||
endif |
|||
|
|||
return a:errors |
|||
endfunction " }}}2 |
|||
|
|||
" decode XML entities |
|||
function! syntastic#postprocess#decodeXMLEntities(errors) abort " {{{2 |
|||
for e in a:errors |
|||
let e['text'] = syntastic#util#decodeXMLEntities(e['text']) |
|||
endfor |
|||
|
|||
return a:errors |
|||
endfunction " }}}2 |
|||
|
|||
" filter out errors referencing other files |
|||
function! syntastic#postprocess#filterForeignErrors(errors) abort " {{{2 |
|||
return filter(copy(a:errors), 'get(v:val, "bufnr") == ' . bufnr('')) |
|||
endfunction " }}}2 |
|||
|
|||
" make sure line numbers are not past end of buffers |
|||
" XXX: this loads all referenced buffers in memory |
|||
function! syntastic#postprocess#guards(errors) abort " {{{2 |
|||
let buffers = syntastic#util#unique(map(filter(copy(a:errors), 'v:val["valid"]'), 'str2nr(v:val["bufnr"])')) |
|||
|
|||
let guards = {} |
|||
for b in buffers |
|||
let guards[b] = len(getbufline(b, 1, '$')) |
|||
endfor |
|||
|
|||
for e in a:errors |
|||
if e['valid'] && e['lnum'] > guards[e['bufnr']] |
|||
let e['lnum'] = guards[e['bufnr']] |
|||
endif |
|||
endfor |
|||
|
|||
return a:errors |
|||
endfunction " }}}2 |
|||
|
|||
" convert error messages from UTF-8 to the current encoding |
|||
function! syntastic#postprocess#iconv(errors) abort " {{{2 |
|||
if has('iconv') && &encoding !=# '' && &encoding !=# 'utf-8' |
|||
for e in a:errors |
|||
let e['text'] = iconv(e['text'], "utf-8", &encoding) |
|||
endfor |
|||
endif |
|||
|
|||
return a:errors |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,809 @@ |
|||
if exists('g:loaded_syntastic_preprocess_autoload') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_preprocess_autoload = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
" Public functions {{{1 |
|||
|
|||
function! syntastic#preprocess#bandit(errors) abort " {{{2 |
|||
let out = [] |
|||
let json = s:_decode_JSON(join(a:errors, '')) |
|||
|
|||
if type(json) == type({}) && has_key(json, 'results') && type(json['results']) == type([]) |
|||
for issue in json['results'] |
|||
if type(issue) == type({}) |
|||
try |
|||
call add(out, |
|||
\ issue['filename'] . ':' . |
|||
\ issue['line_number'] . ':' . |
|||
\ { 'LOW': 'I', 'MEDIUM': 'W', 'HIGH': 'E' }[issue['issue_severity']] . ':' . |
|||
\ issue['test_id'][1:] . ':' . |
|||
\ issue['issue_text'] . |
|||
\ ' [' . issue['test_name'] . '] (confidence: ' . issue['issue_confidence'] . ')') |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker python/bandit: unrecognized error item ' . string(issue)) |
|||
let out = [] |
|||
break |
|||
endtry |
|||
else |
|||
call syntastic#log#warn('checker python/bandit: unrecognized error item ' . string(issue)) |
|||
endif |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker python/bandit: unrecognized error format (crashed checker?)') |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#cabal(errors) abort " {{{2 |
|||
let out = [] |
|||
let star = 0 |
|||
for err in a:errors |
|||
if star |
|||
if err ==# '' |
|||
let star = 0 |
|||
else |
|||
let out[-1] .= ' ' . err |
|||
endif |
|||
else |
|||
call add(out, err) |
|||
if err =~# '\m^*\s' |
|||
let star = 1 |
|||
endif |
|||
endif |
|||
endfor |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#checkstyle(errors) abort " {{{2 |
|||
let out = [] |
|||
let fname = expand('%', 1) |
|||
for err in a:errors |
|||
if match(err, '\m<error\>') > -1 |
|||
let line = str2nr(matchstr(err, '\m\<line="\zs\d\+\ze"')) |
|||
if line == 0 |
|||
continue |
|||
endif |
|||
|
|||
let col = str2nr(matchstr(err, '\m\<column="\zs\d\+\ze"')) |
|||
|
|||
let type = matchstr(err, '\m\<severity="\zs.\ze') |
|||
if type !~? '^[EW]' |
|||
let type = 'E' |
|||
endif |
|||
|
|||
let message = syntastic#util#decodeXMLEntities(matchstr(err, '\m\<message="\zs[^"]\+\ze"')) |
|||
|
|||
call add(out, join([fname, type, line, col, message], ':')) |
|||
elseif match(err, '\m<file name="') > -1 |
|||
let fname = syntastic#util#decodeXMLEntities(matchstr(err, '\v\<file name\="\zs[^"]+\ze"')) |
|||
endif |
|||
endfor |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#cppcheck(errors) abort " {{{2 |
|||
return map(copy(a:errors), 'substitute(v:val, ''\v^\[[^]]+\]\zs( -\> \[[^]]+\])+\ze:'', "", "")') |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#dockerfile_lint(errors) abort " {{{2 |
|||
let out = [] |
|||
let json = s:_decode_JSON(join(a:errors, '')) |
|||
|
|||
if type(json) == type({}) |
|||
try |
|||
let data = json['error']['data'] + json['warn']['data'] + json['info']['data'] |
|||
for e in data |
|||
let type = toupper(e['level'][0]) |
|||
if type ==# 'I' |
|||
let type = 'W' |
|||
let style = 1 |
|||
else |
|||
let style = 0 |
|||
endif |
|||
|
|||
let line = get(e, 'line', 1) |
|||
let message = e['message'] |
|||
if has_key(e, 'description') && e['description'] !=# 'None' |
|||
let message = message . '. ' . e['description'] |
|||
endif |
|||
|
|||
let msg = |
|||
\ type . ':' . |
|||
\ style . ':' . |
|||
\ line . ':' . |
|||
\ message |
|||
call add(out, msg) |
|||
endfor |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker dockerfile/dockerfile_lint: unrecognized error format (crashed checker?)') |
|||
let out = [] |
|||
endtry |
|||
else |
|||
call syntastic#log#warn('checker dockerfile/dockerfile_lint: unrecognized error format (crashed checker?)') |
|||
endif |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#dscanner(errors) abort " {{{2 |
|||
let idx = 0 |
|||
while idx < len(a:errors) && a:errors[idx][0] !=# '{' |
|||
let idx += 1 |
|||
endwhile |
|||
let errs = s:_decode_JSON(join(a:errors[idx :], '')) |
|||
|
|||
let out = [] |
|||
if type(errs) == type({}) && has_key(errs, 'issues') && type(errs['issues']) == type([]) |
|||
for issue in errs['issues'] |
|||
try |
|||
call add(out, |
|||
\ issue['fileName'] . ':' . |
|||
\ issue['line'] . ':' . |
|||
\ issue['column'] . ':' . |
|||
\ issue['message'] . ' [' . issue['key'] . ']') |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker d/dscanner: unrecognized error item ' . string(issue)) |
|||
let out = [] |
|||
break |
|||
endtry |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker d/dscanner: unrecognized error format (crashed checker?)') |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#flow(errors) abort " {{{2 |
|||
let idx = 0 |
|||
while idx < len(a:errors) && a:errors[idx][0] !=# '{' |
|||
let idx += 1 |
|||
endwhile |
|||
let errs = s:_decode_JSON(join(a:errors[idx :], '')) |
|||
|
|||
let out = [] |
|||
if type(errs) == type({}) && has_key(errs, 'errors') && type(errs['errors']) == type([]) |
|||
for e in errs['errors'] |
|||
if type(e) == type({}) && has_key(e, 'message') && type(e['message']) == type([]) && len(e['message']) |
|||
let m = e['message'][0] |
|||
let t = e['message'][1:] |
|||
|
|||
try |
|||
let msg = |
|||
\ m['path'] . ':' . |
|||
\ m['line'] . ':' . |
|||
\ m['start'] . ':' . |
|||
\ (m['line'] ==# m['endline'] && str2nr(m['end']) > 0 ? m['end'] . ':' : '') . |
|||
\ ' ' . m['descr'] |
|||
|
|||
if len(t) |
|||
let msg .= ' ' . join(map(t, |
|||
\ 'v:val["descr"] . " (" . v:val["path"] . ":" . v:val["line"] . ":" . v:val["start"] . ' . |
|||
\ '"," . (v:val["line"] !=# v:val["endline"] ? v:val["endline"] . ":" : "") . ' . |
|||
\ 'v:val["end"] . ")"')) |
|||
endif |
|||
|
|||
let msg = substitute(msg, '\r', '', 'g') |
|||
let msg = substitute(msg, '\n', ' ', 'g') |
|||
|
|||
call add(out, msg) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker javascript/flow: unrecognized error format (crashed checker?)') |
|||
let out = [] |
|||
break |
|||
endtry |
|||
else |
|||
call syntastic#log#warn('checker javascript/flow: unrecognized error format (crashed checker?)') |
|||
let out = [] |
|||
break |
|||
endif |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker javascript/flow: unrecognized error format (crashed checker?)') |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#iconv(errors) abort " {{{2 |
|||
return |
|||
\ has('iconv') && &encoding !=# '' && &encoding !=# 'utf-8' ? |
|||
\ map(a:errors, 'iconv(v:val, "utf-8", &encoding)') : |
|||
\ a:errors |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#jscs(errors) abort " {{{2 |
|||
let errs = join(a:errors, '') |
|||
if errs ==# '' |
|||
return [] |
|||
endif |
|||
|
|||
let json = s:_decode_JSON(errs) |
|||
|
|||
let out = [] |
|||
if type(json) == type({}) |
|||
for fname in keys(json) |
|||
if type(json[fname]) == type([]) |
|||
for e in json[fname] |
|||
try |
|||
let e['message'] = substitute(e['message'], "\n", ' ', 'g') |
|||
cal add(out, fname . ':' . e['line'] . ':' . e['column'] . ':' . e['message']) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker javascript/jscs: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
endtry |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker javascript/jscs: unrecognized error format (crashed checker?)') |
|||
endif |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker javascript/jscs: unrecognized error format (crashed checker?)') |
|||
endif |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#killEmpty(errors) abort " {{{2 |
|||
return filter(copy(a:errors), 'v:val !=# ""') |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#lynt(errors) abort " {{{2 |
|||
let errs = join(a:errors, '') |
|||
if errs ==# '' |
|||
return [] |
|||
endif |
|||
|
|||
let json = s:_decode_JSON(errs) |
|||
|
|||
let out = [] |
|||
if type(json) == type([]) |
|||
for err in json |
|||
if type(err) == type({}) && type(get(err, 'filePath')) == type('') && type(get(err, 'errors')) == type([]) |
|||
let fname = get(err, 'filePath') |
|||
|
|||
for e in get(err, 'errors') |
|||
if type(e) == type({}) |
|||
try |
|||
let line = e['line'] |
|||
let col = e['column'] |
|||
let ecol = line == get(e, 'endLine') ? get(e, 'endColumn') : 0 |
|||
let msg = e['message'] . ' [' . e['ruleName'] . ']' |
|||
|
|||
cal add(out, join([fname, line, col, ecol, msg], ':')) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker javascript/lynt: unrecognized error item ' . string(e)) |
|||
endtry |
|||
else |
|||
call syntastic#log#warn('checker javascript/lynt unrecognized error item ' . string(e)) |
|||
endif |
|||
endfor |
|||
endif |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker javascript/lynt unrecognized error format (crashed checker?)') |
|||
endif |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#perl(errors) abort " {{{2 |
|||
let out = [] |
|||
|
|||
for e in a:errors |
|||
let parts = matchlist(e, '\v^(.*)\sat\s(.{-})\sline\s(\d+)(.*)$') |
|||
if !empty(parts) |
|||
call add(out, parts[2] . ':' . parts[3] . ':' . parts[1] . parts[4]) |
|||
endif |
|||
endfor |
|||
|
|||
return syntastic#util#unique(out) |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#perl6(errors) abort " {{{2 |
|||
if a:errors[0] ==# 'Syntax OK' |
|||
return [] |
|||
endif |
|||
|
|||
let errs = s:_decode_JSON(join(a:errors, '')) |
|||
|
|||
let out = [] |
|||
if type(errs) == type({}) |
|||
try |
|||
for val in values(errs) |
|||
let line = get(val, 'line', 0) |
|||
let pos = get(val, 'pos', 0) |
|||
if pos && has('byte_offset') |
|||
let line_pos = byte2line(pos + 1) |
|||
let column = line_pos > 0 ? pos - line2byte(line_pos) + 2 : 0 |
|||
else |
|||
let column = 0 |
|||
endif |
|||
|
|||
call add(out, join([ |
|||
\ get(val, 'filename', ''), |
|||
\ line, |
|||
\ column, |
|||
\ get(val, 'message', '') ], ':')) |
|||
endfor |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker perl6/perl6: unrecognized error item ' . string(val)) |
|||
let out = [] |
|||
endtry |
|||
else |
|||
call syntastic#log#warn('checker perl6/perl6: unrecognized error format') |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#prospector(errors) abort " {{{2 |
|||
let errs = join(a:errors, '') |
|||
if errs ==# '' |
|||
return [] |
|||
endif |
|||
|
|||
let json = s:_decode_JSON(errs) |
|||
|
|||
let out = [] |
|||
if type(json) == type({}) && has_key(json, 'messages') |
|||
if type(json['messages']) == type([]) |
|||
for e in json['messages'] |
|||
if type(e) == type({}) |
|||
try |
|||
if e['source'] ==# 'pylint' |
|||
let e['location']['character'] += 1 |
|||
endif |
|||
|
|||
let msg = |
|||
\ e['location']['path'] . ':' . |
|||
\ e['location']['line'] . ':' . |
|||
\ e['location']['character'] . ': ' . |
|||
\ e['code'] . ' ' . |
|||
\ e['message'] . ' ' . |
|||
\ '[' . e['source'] . ']' |
|||
|
|||
call add(out, msg) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker python/prospector: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
break |
|||
endtry |
|||
else |
|||
call syntastic#log#warn('checker python/prospector: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
break |
|||
endif |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker python/prospector: unrecognized error format (crashed checker?)') |
|||
endif |
|||
else |
|||
call syntastic#log#warn('checker python/prospector: unrecognized error format (crashed checker?)') |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#rparse(errors) abort " {{{2 |
|||
let errlist = copy(a:errors) |
|||
|
|||
" remove uninteresting lines and handle continuations |
|||
let i = 0 |
|||
while i < len(errlist) |
|||
if i > 0 && errlist[i][:1] ==# ' ' && errlist[i] !~# '\m\s\+\^$' |
|||
let errlist[i-1] .= errlist[i][1:] |
|||
call remove(errlist, i) |
|||
elseif errlist[i] !~# '\m^\(Lint:\|Lint checking:\|Error in\) ' |
|||
call remove(errlist, i) |
|||
else |
|||
let i += 1 |
|||
endif |
|||
endwhile |
|||
|
|||
let out = [] |
|||
let fname = '' |
|||
for e in errlist |
|||
if match(e, '\m^Lint: ') == 0 |
|||
let parts = matchlist(e, '\m^Lint: \(.*\): found on lines \([0-9, ]\+\)\(+\(\d\+\) more\)\=') |
|||
if len(parts) >= 3 |
|||
for line in split(parts[2], '\m,\s*') |
|||
call add(out, 'E:' . fname . ':' . line . ': ' . parts[1]) |
|||
endfor |
|||
endif |
|||
if len(parts) >= 5 && parts[4] !=# '' |
|||
call add(out, 'E:' . fname . ':0: ' . parts[1] . ' - ' . parts[4] . ' messages not shown') |
|||
endif |
|||
elseif match(e, '\m^Lint checking: ') == 0 |
|||
let fname = matchstr(e, '\m^Lint checking: \zs.*') |
|||
elseif match(e, '\m^Error in ') == 0 |
|||
call add(out, substitute(e, '\m^Error in .\+ : .\+\ze:\d\+:\d\+: ', 'E:' . fname, '')) |
|||
endif |
|||
endfor |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#remark_lint(errors) abort " {{{2 |
|||
let out = [] |
|||
let fname = expand('%', 1) |
|||
|
|||
for err in a:errors |
|||
if err =~# '\m^\f\+$' |
|||
let fname = err |
|||
|
|||
elseif err =~# '\v^\s+\d+:\d+\s+%(warning|error)\s.*remark-lint$' |
|||
let parts = matchlist(err, '\v^\s+(\d+):(\d+)\s+([ew])\S+\s+(.{-})\s+(\S+)\s+remark-lint$') |
|||
if len(parts) >6 |
|||
let line = str2nr(parts[1]) |
|||
let col = str2nr(parts[2]) |
|||
let type = parts[3] |
|||
let message = parts[4] . ' [' . parts[5] . ']' |
|||
call add(out, join([fname, type, line, col, message], ':')) |
|||
else |
|||
call syntastic#log#warn('checker markdown/remark_lint: unrecognized error item ' . string(err)) |
|||
endif |
|||
|
|||
elseif err =~# '\v^\s+\d+:\d+-\d+:\d+\s+%(warning|error)\s.*remark-lint$' |
|||
let parts = matchlist(err, '\v^\s+(\d+):(\d+)-(\d+):(\d+)\s+([ew])\S+\s+(.{-})\s+(\S+)\s+remark-lint$') |
|||
if len(parts) >8 |
|||
let line1 = str2nr(parts[1]) |
|||
let col1 = str2nr(parts[2]) |
|||
let line2 = str2nr(parts[3]) |
|||
let col2 = str2nr(parts[4]) - 1 |
|||
let type = parts[5] |
|||
let message = parts[6] . ' [' . parts[7] . ']' |
|||
if line1 == line2 |
|||
call add(out, join([fname, type, line1, col1, col2, message], ':')) |
|||
else |
|||
call add(out, join([fname, type, line1, col1, message], ':')) |
|||
endif |
|||
else |
|||
call syntastic#log#warn('checker markdown/remark_lint: unrecognized error item ' . string(err)) |
|||
endif |
|||
endif |
|||
endfor |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#scss_lint(errors) abort " {{{2 |
|||
let errs = join(a:errors, '') |
|||
if errs ==# '' |
|||
return [] |
|||
endif |
|||
|
|||
let json = s:_decode_JSON(errs) |
|||
|
|||
let out = [] |
|||
if type(json) == type({}) |
|||
for fname in keys(json) |
|||
if type(json[fname]) == type([]) |
|||
for e in json[fname] |
|||
try |
|||
cal add(out, fname . ':' . |
|||
\ e['severity'][0] . ':' . |
|||
\ e['line'] . ':' . |
|||
\ e['column'] . ':' . |
|||
\ e['length'] . ':' . |
|||
\ ( has_key(e, 'linter') ? e['linter'] . ': ' : '' ) . |
|||
\ e['reason']) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker scss/scss_lint: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
endtry |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker scss/scss_lint: unrecognized error format (crashed checker?)') |
|||
endif |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker scss/scss_lint: unrecognized error format (crashed checker?)') |
|||
endif |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#stylelint(errors) abort " {{{2 |
|||
let out = [] |
|||
|
|||
" CssSyntaxError: /path/to/file.css:2:11: Missed semicolon |
|||
let parts = matchlist(a:errors[0], '\v^CssSyntaxError: (.{-1,}):(\d+):(\d+): (.+)') |
|||
if len(parts) > 4 |
|||
call add(out, 'E:' . join(parts[1:4], ':')) |
|||
else |
|||
let errs = s:_decode_JSON(join(a:errors, '')) |
|||
|
|||
let out = [] |
|||
if type(errs) == type([]) && len(errs) == 1 && type(errs[0]) == type({}) && |
|||
\ has_key(errs[0], 'source') && has_key(errs[0], 'warnings') && type(errs[0]['warnings']) == type([]) |
|||
|
|||
for e in errs[0]['warnings'] |
|||
try |
|||
let severity = type(e['severity']) == type(0) ? ['W', 'E'][e['severity']-1] : e['severity'][0] |
|||
let msg = |
|||
\ severity . ':' . |
|||
\ errs[0]['source'] . ':' . |
|||
\ e['line'] . ':' . |
|||
\ e['column'] . ':' . |
|||
\ e['text'] |
|||
call add(out, msg) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker css/stylelint: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
break |
|||
endtry |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker css/stylelint: unrecognized error format (crashed checker?)') |
|||
endif |
|||
endif |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#tern_lint(errors) abort " {{{2 |
|||
let errs = join(a:errors, '') |
|||
let json = s:_decode_JSON(errs) |
|||
|
|||
echomsg string(json) |
|||
let out = [] |
|||
if type(json) == type({}) && has_key(json, 'messages') && type(json['messages']) == type([]) |
|||
for e in json['messages'] |
|||
try |
|||
let line_from = byte2line(e['from'] + 1) |
|||
if line_from > 0 |
|||
let line = line_from |
|||
let column = e['from'] - line2byte(line_from) + 2 |
|||
let line_to = byte2line(e['from'] + 1) |
|||
let hl = line_to == line ? e['to'] - line2byte(line_to) + 1 : 0 |
|||
else |
|||
let line = 0 |
|||
let column = 0 |
|||
let hl = 0 |
|||
endif |
|||
|
|||
if column < 0 |
|||
let column = 0 |
|||
endif |
|||
if hl < 0 |
|||
let hl = 0 |
|||
endif |
|||
|
|||
call add(out, |
|||
\ e['file'] . ':' . |
|||
\ e['severity'][0] . ':' . |
|||
\ line . ':' . |
|||
\ column . ':' . |
|||
\ hl . ':' . |
|||
\ e['message']) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker javascript/tern_lint: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
endtry |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker javascript/tern_lint: unrecognized error format (crashed checker?)') |
|||
endif |
|||
|
|||
echomsg string(out) |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#tslint(errors) abort " {{{2 |
|||
return map(copy(a:errors), 'substitute(v:val, ''\v^((ERROR|WARNING): )?\zs(\([^)]+\))\s(.+)$'', ''\4 \3'', "")') |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#validator(errors) abort " {{{2 |
|||
let out = [] |
|||
for e in a:errors |
|||
let parts = matchlist(e, '\v^"([^"]+)"(.+)') |
|||
if len(parts) >= 3 |
|||
" URL decode, except leave alone any "+" |
|||
let parts[1] = substitute(parts[1], '\m%\(\x\x\)', '\=nr2char("0x".submatch(1))', 'g') |
|||
let parts[1] = substitute(parts[1], '\m\\"', '"', 'g') |
|||
let parts[1] = substitute(parts[1], '\m\\\\', '\\', 'g') |
|||
call add(out, '"' . parts[1] . '"' . parts[2]) |
|||
endif |
|||
endfor |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#vint(errors) abort " {{{2 |
|||
let errs = s:_decode_JSON(join(a:errors, '')) |
|||
|
|||
let out = [] |
|||
if type(errs) == type([]) |
|||
for e in errs |
|||
if type(e) == type({}) |
|||
try |
|||
let msg = |
|||
\ e['file_path'] . ':' . |
|||
\ e['line_number'] . ':' . |
|||
\ e['column_number'] . ':' . |
|||
\ e['severity'][0] . ': ' . |
|||
\ e['description'] . ' (' . |
|||
\ e['policy_name'] . ')' |
|||
|
|||
call add(out, msg) |
|||
catch /\m^Vim\%((\a\+)\)\=:E716/ |
|||
call syntastic#log#warn('checker vim/vint: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
break |
|||
endtry |
|||
else |
|||
call syntastic#log#warn('checker vim/vint: unrecognized error item ' . string(e)) |
|||
let out = [] |
|||
break |
|||
endif |
|||
endfor |
|||
else |
|||
call syntastic#log#warn('checker vim/vint: unrecognized error format (crashed checker?)') |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Workarounds {{{1 |
|||
|
|||
" In errorformat, \ or % following %f make it depend on isfname. The default |
|||
" setting of isfname is crafted to work with completion, rather than general |
|||
" filename matching. The result for syntastic is that filenames containing |
|||
" spaces (or a few other special characters) can't be matched. |
|||
" |
|||
" Fixing isfname to address this problem would depend on the set of legal |
|||
" characters for filenames on the filesystem the project's files lives on. |
|||
" Inferring the kind of filesystem a file lives on, in advance to parsing the |
|||
" file's name, is an interesting problem (think f.i. a file loaded from a VFAT |
|||
" partition, mounted on Linux). A problem syntastic is not prepared to solve. |
|||
" |
|||
" As a result, the functions below exist for the only reason to avoid using |
|||
" things like %f\, in errorformat. |
|||
" |
|||
" References: |
|||
" https://groups.google.com/forum/#!topic/vim_dev/pTKmZmouhio |
|||
" https://vimhelp.appspot.com/quickfix.txt.html#error-file-format |
|||
|
|||
function! syntastic#preprocess#basex(errors) abort " {{{2 |
|||
let out = [] |
|||
let idx = 0 |
|||
while idx < len(a:errors) |
|||
let parts = matchlist(a:errors[idx], '\v^\[\S+\] Stopped at (.+), (\d+)/(\d+):') |
|||
if len(parts) > 3 |
|||
let err = parts[1] . ':' . parts[2] . ':' . parts[3] . ':' |
|||
let parts = matchlist(a:errors[idx+1], '\v^\[(.)\D+(\d+)\] (.+)') |
|||
if len(parts) > 3 |
|||
let err .= (parts[1] ==? 'W' || parts[1] ==? 'E' ? parts[1] : 'E') . ':' . parts[2] . ':' . parts[3] |
|||
call add(out, err) |
|||
let idx +=1 |
|||
endif |
|||
elseif a:errors[idx] =~# '\m^\[' |
|||
" unparseable errors |
|||
call add(out, a:errors[idx]) |
|||
endif |
|||
let idx +=1 |
|||
endwhile |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#bro(errors) abort " {{{2 |
|||
let out = [] |
|||
for e in a:errors |
|||
let parts = matchlist(e, '\v^%(fatal )?(error|warning) in (.{-1,}), line (\d+): (.+)') |
|||
if len(parts) > 4 |
|||
let parts[1] = parts[1][0] |
|||
call add(out, join(parts[1:4], ':')) |
|||
endif |
|||
endfor |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#coffeelint(errors) abort " {{{2 |
|||
let out = [] |
|||
for e in a:errors |
|||
let parts = matchlist(e, '\v^(.{-1,}),(\d+)%(,\d*)?,(error|warn),(.+)') |
|||
if len(parts) > 4 |
|||
let parts[3] = parts[3][0] |
|||
call add(out, join(parts[1:4], ':')) |
|||
endif |
|||
endfor |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#mypy(errors) abort " {{{2 |
|||
let out = [] |
|||
for e in a:errors |
|||
" column numbers |
|||
let parts = matchlist(e, '\v^(.{-1,}):(\d+):(\d+): ([ew])%(rror|arning): (.+)') |
|||
if len(parts) > 5 |
|||
let parts[3] += 1 |
|||
call add(out, join(parts[1:5], ':')) |
|||
continue |
|||
endif |
|||
|
|||
" no column numbers |
|||
let parts = matchlist(e, '\v^(.{-1,}):(\d+): ([ew])%(rror|arning): (.+)') |
|||
if len(parts) > 4 |
|||
call add(out, join(parts[1:4], ':')) |
|||
continue |
|||
endif |
|||
|
|||
" obsolete format |
|||
let parts = matchlist(e, '\v^(.{-1,}), line (\d+): (.+)') |
|||
if len(parts) > 3 |
|||
let parts[4] = parts[3] |
|||
let parts[3] = 'e' |
|||
call add(out, join(parts[1:4], ':')) |
|||
endif |
|||
endfor |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#preprocess#nix(errors) abort " {{{2 |
|||
let out = [] |
|||
for e in a:errors |
|||
let parts = matchlist(e, '\v^(.{-1,}), at (.{-1,}):(\d+):(\d+)$') |
|||
if len(parts) > 4 |
|||
call add(out, join(parts[2:4], ':') . ':' . parts[1]) |
|||
continue |
|||
endif |
|||
|
|||
let parts = matchlist(e, '\v^(.{-1,}) at (.{-1,}), line (\d+):') |
|||
if len(parts) > 3 |
|||
call add(out, parts[2] . ':' . parts[3] . ':' . parts[1]) |
|||
continue |
|||
endif |
|||
|
|||
let parts = matchlist(e, '\v^error: (.{-1,}), in (.{-1,})$') |
|||
if len(parts) > 2 |
|||
call add(out, parts[2] . ':' . parts[1]) |
|||
endif |
|||
endfor |
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private functions {{{1 |
|||
|
|||
" @vimlint(EVL102, 1, l:true) |
|||
" @vimlint(EVL102, 1, l:false) |
|||
" @vimlint(EVL102, 1, l:null) |
|||
function! s:_decode_JSON(json) abort " {{{2 |
|||
if a:json ==# '' |
|||
return [] |
|||
endif |
|||
|
|||
" The following is inspired by https://github.com/MarcWeber/vim-addon-manager and |
|||
" http://stackoverflow.com/questions/17751186/iterating-over-a-string-in-vimscript-or-parse-a-json-file/19105763#19105763 |
|||
" A hat tip to Marc Weber for this trick |
|||
if substitute(a:json, '\v\"%(\\.|[^"\\])*\"|true|false|null|[+-]?\d+%(\.\d+%([Ee][+-]?\d+)?)?', '', 'g') !~# "[^,:{}[\\] \t]" |
|||
" JSON artifacts |
|||
let true = 1 |
|||
let false = 0 |
|||
let null = '' |
|||
|
|||
try |
|||
let object = eval(a:json) |
|||
catch |
|||
" malformed JSON |
|||
let object = '' |
|||
endtry |
|||
else |
|||
let object = '' |
|||
endif |
|||
|
|||
return object |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL102, 0, l:true) |
|||
" @vimlint(EVL102, 0, l:false) |
|||
" @vimlint(EVL102, 0, l:null) |
|||
|
|||
" }}}1 |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,640 @@ |
|||
if exists('g:loaded_syntastic_util_autoload') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_util_autoload = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
" Public functions {{{1 |
|||
|
|||
function! syntastic#util#isRunningWindows() abort " {{{2 |
|||
return has('win16') || has('win32') || has('win64') |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#DevNull() abort " {{{2 |
|||
if syntastic#util#isRunningWindows() |
|||
return 'NUL' |
|||
endif |
|||
return '/dev/null' |
|||
endfunction " }}}2 |
|||
|
|||
" Get directory separator |
|||
function! syntastic#util#Slash() abort " {{{2 |
|||
return (!exists('+shellslash') || &shellslash) ? '/' : '\' |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#CygwinPath(path) abort " {{{2 |
|||
return substitute(syntastic#util#system('cygpath -m ' . syntastic#util#shescape(a:path)), "\n", '', 'g') |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#system(command) abort " {{{2 |
|||
let old_shell = &shell |
|||
let old_lc_messages = $LC_MESSAGES |
|||
let old_lc_all = $LC_ALL |
|||
|
|||
let &shell = syntastic#util#var('shell') |
|||
let $LC_MESSAGES = 'C' |
|||
let $LC_ALL = '' |
|||
|
|||
let crashed = 0 |
|||
let cmd_start = reltime() |
|||
try |
|||
let out = system(a:command) |
|||
catch |
|||
if v:exception =~# '\m^Vim\%((\a\+)\)\=:\%(E145\|E484\|E684\)' |
|||
" XXX re-throwing unmodified v:exception triggers E608 |
|||
throw substitute(v:exception, '.*:\(E145\|E484\|E684\).*', '\1', '') |
|||
endif |
|||
|
|||
let crashed = 1 |
|||
call syntastic#log#error('exception running system(' . string(a:command) . '): ' . v:exception) |
|||
if syntastic#util#isRunningWindows() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, '$TMP = ' . string($TMP) . ', $TEMP = ' . string($TEMP)) |
|||
else |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, '$TERM = ' . string($TERM)) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, '$TMPDIR = ' . string($TMPDIR)) |
|||
endif |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, '$PATH = ' . string($PATH)) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'getcwd() = ' . string(getcwd())) |
|||
call syntastic#log#debugShowOptions(g:_SYNTASTIC_DEBUG_TRACE, g:_SYNTASTIC_SHELL_OPTIONS) |
|||
let out = '' |
|||
endtry |
|||
let cmd_time = split(reltimestr(reltime(cmd_start)))[0] |
|||
|
|||
let $LC_ALL = old_lc_all |
|||
let $LC_MESSAGES = old_lc_messages |
|||
|
|||
let &shell = old_shell |
|||
|
|||
if !crashed && exists('g:_SYNTASTIC_DEBUG_TRACE') |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'system: command run in ' . cmd_time . 's') |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
" Create a temporary directory |
|||
function! syntastic#util#tmpdir() abort " {{{2 |
|||
let tempdir = '' |
|||
|
|||
if (has('unix') || has('mac')) && executable('mktemp') && !has('win32unix') |
|||
" TODO: option "-t" to mktemp(1) is not portable |
|||
let tmp = $TMPDIR !=# '' ? $TMPDIR : $TMP !=# '' ? $TMP : '/tmp' |
|||
let out = split(syntastic#util#system('mktemp -q -d ' . tmp . '/vim-syntastic-' . s:_fuzz() . '-XXXXXXXX'), "\n") |
|||
if v:shell_error == 0 && len(out) == 1 |
|||
let tempdir = out[0] |
|||
endif |
|||
endif |
|||
|
|||
if tempdir ==# '' |
|||
if has('win32') || has('win64') |
|||
let tempdir = $TEMP . syntastic#util#Slash() . 'vim-syntastic-' . s:_fuzz() |
|||
elseif has('win32unix') |
|||
let tempdir = syntastic#util#CygwinPath('/tmp/vim-syntastic-' . s:_fuzz()) |
|||
elseif $TMPDIR !=# '' |
|||
let tempdir = $TMPDIR . '/vim-syntastic-' . s:_fuzz() |
|||
else |
|||
let tempdir = '/tmp/vim-syntastic-' . s:_fuzz() |
|||
endif |
|||
|
|||
try |
|||
call mkdir(tempdir, 'p', 0700) |
|||
catch /\m^Vim\%((\a\+)\)\=:E739/ |
|||
call syntastic#log#error(v:exception) |
|||
let tempdir = '.' |
|||
endtry |
|||
endif |
|||
|
|||
return tempdir |
|||
endfunction " }}}2 |
|||
|
|||
" Recursively remove a directory |
|||
function! syntastic#util#rmrf(what) abort " {{{2 |
|||
" try to make sure we don't delete directories we didn't create |
|||
if a:what !~? 'vim-syntastic-' |
|||
return |
|||
endif |
|||
|
|||
if getftype(a:what) ==# 'dir' |
|||
call s:_delete(a:what, 'rf') |
|||
else |
|||
silent! call delete(a:what) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#getbufvar(buf, name, ...) abort " {{{2 |
|||
return a:0 ? s:_getbufvar(a:buf, a:name, a:1) : getbufvar(a:buf, a:name) |
|||
endfunction " }}}2 |
|||
|
|||
" Search the first 5 lines of the file for a magic number and return a map |
|||
" containing the args and the executable |
|||
" |
|||
" e.g. |
|||
" |
|||
" #!/usr/bin/perl -f -bar |
|||
" |
|||
" returns |
|||
" |
|||
" {'exe': '/usr/bin/perl', 'args': ['-f', '-bar']} |
|||
function! syntastic#util#parseShebang(buf) abort " {{{2 |
|||
for lnum in range(1, 5) |
|||
let line = get(getbufline(a:buf, lnum), 0, '') |
|||
if line =~# '^#!' |
|||
let line = substitute(line, '\v^#!\s*(\S+/env(\s+-\S+)*\s+)?', '', '') |
|||
let exe = matchstr(line, '\m^\S*\ze') |
|||
let args = split(matchstr(line, '\m^\S*\zs.*')) |
|||
return { 'exe': exe, 'args': args } |
|||
endif |
|||
endfor |
|||
|
|||
return { 'exe': '', 'args': [] } |
|||
endfunction " }}}2 |
|||
|
|||
" Get the value of a Vim variable. Allow buffer variables to override global ones. |
|||
function! syntastic#util#bufRawVar(buf, name, ...) abort " {{{2 |
|||
return s:_getbufvar(a:buf, a:name, get(g:, a:name, a:0 ? a:1 : '')) |
|||
endfunction "}}}2 |
|||
|
|||
" Get the value of a syntastic variable. Allow buffer variables to override global ones. |
|||
function! syntastic#util#bufVar(buf, name, ...) abort " {{{2 |
|||
return call('syntastic#util#bufRawVar', [a:buf, 'syntastic_' . a:name] + a:000) |
|||
endfunction "}}}2 |
|||
|
|||
" Get the value of a Vim variable. Allow local variables to override global ones. |
|||
function! syntastic#util#rawVar(name, ...) abort " {{{2 |
|||
return get(b:, a:name, get(g:, a:name, a:0 ? a:1 : '')) |
|||
endfunction " }}}2 |
|||
|
|||
" Get the value of a syntastic variable. Allow local variables to override global ones. |
|||
function! syntastic#util#var(name, ...) abort " {{{2 |
|||
return call('syntastic#util#rawVar', ['syntastic_' . a:name] + a:000) |
|||
endfunction " }}}2 |
|||
|
|||
" Parse a version string. Return an array of version components. |
|||
function! syntastic#util#parseVersion(version, ...) abort " {{{2 |
|||
return map(split(matchstr( a:version, a:0 ? a:1 : '\v^\D*\zs\d+(\.\d+)+\ze' ), '\m\.'), 'str2nr(v:val)') |
|||
endfunction " }}}2 |
|||
|
|||
" Verify that the 'installed' version is at least the 'required' version. |
|||
" |
|||
" 'installed' and 'required' must be arrays. If they have different lengths, |
|||
" the "missing" elements will be assumed to be 0 for the purposes of checking. |
|||
" |
|||
" See http://semver.org for info about version numbers. |
|||
function! syntastic#util#versionIsAtLeast(installed, required) abort " {{{2 |
|||
return syntastic#util#compareLexi(a:installed, a:required) >= 0 |
|||
endfunction " }}}2 |
|||
|
|||
" Almost lexicographic comparison of two lists of integers. :) If lists |
|||
" have different lengths, the "missing" elements are assumed to be 0. |
|||
function! syntastic#util#compareLexi(a, b) abort " {{{2 |
|||
for idx in range(max([len(a:a), len(a:b)])) |
|||
let a_element = str2nr(get(a:a, idx, 0)) |
|||
let b_element = str2nr(get(a:b, idx, 0)) |
|||
if a_element != b_element |
|||
return a_element > b_element ? 1 : -1 |
|||
endif |
|||
endfor |
|||
" still here, thus everything matched |
|||
return 0 |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#screenWidth(str, tabstop) abort " {{{2 |
|||
let chunks = split(a:str, "\t", 1) |
|||
let width = s:_width(chunks[-1]) |
|||
for c in chunks[:-2] |
|||
let cwidth = s:_width(c) |
|||
let width += cwidth + a:tabstop - cwidth % a:tabstop |
|||
endfor |
|||
return width |
|||
endfunction " }}}2 |
|||
|
|||
" Print as much of a:msg as possible without "Press Enter" prompt appearing |
|||
function! syntastic#util#wideMsg(msg) abort " {{{2 |
|||
let old_ruler = &ruler |
|||
let old_showcmd = &showcmd |
|||
|
|||
"This is here because it is possible for some error messages to |
|||
"begin with \n which will cause a "press enter" prompt. |
|||
let msg = substitute(a:msg, "\n", '', 'g') |
|||
|
|||
"convert tabs to spaces so that the tabs count towards the window |
|||
"width as the proper amount of characters |
|||
let chunks = split(msg, "\t", 1) |
|||
let msg = join(map(chunks[:-2], 'v:val . repeat(" ", &tabstop - s:_width(v:val) % &tabstop)'), '') . chunks[-1] |
|||
let msg = strpart(msg, 0, &columns - 1) |
|||
|
|||
set noruler noshowcmd |
|||
call syntastic#util#redraw(0) |
|||
|
|||
echo msg |
|||
|
|||
let &ruler = old_ruler |
|||
let &showcmd = old_showcmd |
|||
endfunction " }}}2 |
|||
|
|||
" Check whether a buffer is loaded, listed, and not hidden |
|||
function! syntastic#util#bufIsActive(buffer) abort " {{{2 |
|||
" convert to number, or hell breaks loose |
|||
let buf = str2nr(a:buffer) |
|||
|
|||
if !bufloaded(buf) || !buflisted(buf) |
|||
return 0 |
|||
endif |
|||
|
|||
" get rid of hidden buffers |
|||
for tab in range(1, tabpagenr('$')) |
|||
if index(tabpagebuflist(tab), buf) >= 0 |
|||
return 1 |
|||
endif |
|||
endfor |
|||
|
|||
return 0 |
|||
endfunction " }}}2 |
|||
|
|||
" Start in directory a:where and walk up the parent folders until it finds a |
|||
" file named a:what; return path to that file |
|||
function! syntastic#util#findFileInParent(what, where) abort " {{{2 |
|||
let old_suffixesadd = &suffixesadd |
|||
let &suffixesadd = '' |
|||
let file = findfile(a:what, escape(a:where, ' ,') . ';') |
|||
let &suffixesadd = old_suffixesadd |
|||
return file |
|||
endfunction " }}}2 |
|||
|
|||
" Start in directory a:where and walk up the parent folders until it finds a |
|||
" file matching a:what; return path to that file |
|||
function! syntastic#util#findGlobInParent(what, where) abort " {{{2 |
|||
let here = fnamemodify(a:where, ':p') |
|||
|
|||
let root = syntastic#util#Slash() |
|||
if syntastic#util#isRunningWindows() && here[1] ==# ':' |
|||
" The drive letter is an ever-green source of fun. That's because |
|||
" we don't care about running syntastic on Amiga these days. ;) |
|||
let root = fnamemodify(root, ':p') |
|||
let root = here[0] . root[1:] |
|||
endif |
|||
|
|||
let old = '' |
|||
while here !=# '' |
|||
try |
|||
" Vim 7.4.279 and later |
|||
let p = globpath(here, a:what, 1, 1) |
|||
catch /\m^Vim\%((\a\+)\)\=:E118/ |
|||
let p = split(globpath(here, a:what, 1), "\n") |
|||
endtry |
|||
|
|||
if !empty(p) |
|||
return fnamemodify(p[0], ':p') |
|||
elseif here ==? root || here ==? old |
|||
break |
|||
endif |
|||
|
|||
let old = here |
|||
|
|||
" we use ':h:h' rather than ':h' since ':p' adds a trailing '/' |
|||
" if 'here' is a directory |
|||
let here = fnamemodify(here, ':p:h:h') |
|||
endwhile |
|||
|
|||
return '' |
|||
endfunction " }}}2 |
|||
|
|||
" Returns the buffer number of a filename |
|||
" @vimlint(EVL104, 1, l:old_shellslash) |
|||
function! syntastic#util#fname2buf(fname) abort " {{{2 |
|||
if exists('+shellslash') |
|||
" bufnr() can't cope with backslashes |
|||
let old_shellslash = &shellslash |
|||
let &shellslash = 1 |
|||
endif |
|||
|
|||
" this is a best-effort attempt to escape file patterns (cf. :h file-pattern) |
|||
" XXX it fails for filenames containing something like \{2,3} |
|||
let buf = -1 |
|||
for md in [':~:.', ':~', ':p'] |
|||
try |
|||
" Older versions of Vim can throw E94 here |
|||
let buf = bufnr('^' . escape(fnamemodify(a:fname, md), '\*?,{}[') . '$') |
|||
catch |
|||
" catch everything |
|||
endtry |
|||
if buf != -1 |
|||
break |
|||
endif |
|||
endfor |
|||
if buf == -1 |
|||
" XXX definitely wrong, but hope is the last thing to die :) |
|||
let buf = bufnr(fnamemodify(a:fname, ':p')) |
|||
endif |
|||
|
|||
if exists('+shellslash') |
|||
let &shellslash = old_shellslash |
|||
endif |
|||
|
|||
return buf |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL104, 0, l:old_shellslash) |
|||
|
|||
" Returns unique elements in a list |
|||
function! syntastic#util#unique(list) abort " {{{2 |
|||
let seen = {} |
|||
let uniques = [] |
|||
for e in a:list |
|||
let k = string(e) |
|||
if !has_key(seen, k) |
|||
let seen[k] = 1 |
|||
call add(uniques, e) |
|||
endif |
|||
endfor |
|||
return uniques |
|||
endfunction " }}}2 |
|||
|
|||
" A less noisy shellescape() |
|||
function! syntastic#util#shescape(string) abort " {{{2 |
|||
return a:string =~# '\m^[A-Za-z0-9_/.-]\+$' ? a:string : shellescape(a:string) |
|||
endfunction " }}}2 |
|||
|
|||
" A less noisy shellescape(expand()) |
|||
function! syntastic#util#shexpand(string, ...) abort " {{{2 |
|||
return syntastic#util#shescape(a:0 ? expand(a:string, a:1) : expand(a:string, 1)) |
|||
endfunction " }}}2 |
|||
|
|||
" Escape arguments |
|||
function! syntastic#util#argsescape(opt) abort " {{{2 |
|||
if type(a:opt) == type('') && a:opt !=# '' |
|||
return [a:opt] |
|||
elseif type(a:opt) == type([]) |
|||
return map(copy(a:opt), 'syntastic#util#shescape(v:val)') |
|||
endif |
|||
|
|||
return [] |
|||
endfunction " }}}2 |
|||
|
|||
" Decode XML entities |
|||
function! syntastic#util#decodeXMLEntities(string) abort " {{{2 |
|||
let str = a:string |
|||
let str = substitute(str, '\m<', '<', 'g') |
|||
let str = substitute(str, '\m>', '>', 'g') |
|||
let str = substitute(str, '\m"', '"', 'g') |
|||
let str = substitute(str, '\m'', "'", 'g') |
|||
let str = substitute(str, '\m&', '\&', 'g') |
|||
return str |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#redraw(full) abort " {{{2 |
|||
if a:full |
|||
redraw! |
|||
else |
|||
redraw |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#dictFilter(errors, filter) abort " {{{2 |
|||
let rules = s:_translateFilter(a:filter) |
|||
" call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, "applying filter:", rules) |
|||
try |
|||
call filter(a:errors, rules) |
|||
catch /\m^Vim\%((\a\+)\)\=:E/ |
|||
let msg = matchstr(v:exception, '\m^Vim\%((\a\+)\)\=:\zs.*') |
|||
call syntastic#log#error('quiet_messages: ' . msg) |
|||
endtry |
|||
endfunction " }}}2 |
|||
|
|||
" Return a [seconds, fractions] list of strings, representing the |
|||
" (hopefully high resolution) time since program start |
|||
function! syntastic#util#stamp() abort " {{{2 |
|||
return split( split(reltimestr(reltime(g:_SYNTASTIC_START)))[0], '\.' ) |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#setLastTick(buf) abort " {{{2 |
|||
call setbufvar(a:buf, 'syntastic_lasttick', getbufvar(a:buf, 'changedtick')) |
|||
endfunction " }}}2 |
|||
|
|||
" Add unique IDs to windows |
|||
function! syntastic#util#setWids() abort " {{{2 |
|||
for tab in range(1, tabpagenr('$')) |
|||
for win in range(1, tabpagewinnr(tab, '$')) |
|||
if gettabwinvar(tab, win, 'syntastic_wid') ==# '' |
|||
call settabwinvar(tab, win, 'syntastic_wid', s:_wid_base . s:_wid_pool) |
|||
let s:_wid_pool += 1 |
|||
endif |
|||
endfor |
|||
endfor |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#str2float(val) abort " {{{2 |
|||
return s:_str2float(a:val) |
|||
endfunction " }}}2 |
|||
|
|||
function! syntastic#util#float2str(val) abort " {{{2 |
|||
return s:_float2str(a:val) |
|||
endfunction " }}}2 |
|||
|
|||
" Crude printf()-like width formatter. Handles wide characters. |
|||
function! syntastic#util#wformat(format, str) abort " {{{2 |
|||
if a:format ==# '' |
|||
return a:str |
|||
endif |
|||
|
|||
echomsg string(a:format) . ', ' . string(a:str) |
|||
let specs = matchlist(a:format, '\v^(-?)(0?)(%([1-9]\d*))?%(\.(\d+))?$') |
|||
if len(specs) < 5 |
|||
return a:str |
|||
endif |
|||
|
|||
let flushleft = specs[1] ==# '-' |
|||
let lpad = specs[2] ==# '0' ? '0' : ' ' |
|||
let minlen = str2nr(specs[3]) |
|||
let maxlen = str2nr(specs[4]) |
|||
let out = substitute(a:str, "\t", ' ', 'g') |
|||
|
|||
if maxlen && s:_width(out) > maxlen |
|||
let chars = filter(split(out, '\zs\ze', 1), 'v:val !=# ""') |
|||
let out = '' |
|||
|
|||
if flushleft |
|||
for c in chars |
|||
if s:_width(out . c) < maxlen |
|||
let out .= c |
|||
else |
|||
let out .= &encoding ==# 'utf-8' && &termencoding ==# 'utf-8' ? "\u2026" : '>' |
|||
break |
|||
endif |
|||
endfor |
|||
else |
|||
call reverse(chars) |
|||
for c in chars |
|||
if s:_width(c . out) < maxlen |
|||
let out = c . out |
|||
else |
|||
let out = (&encoding ==# 'utf-8' && &termencoding ==# 'utf-8' ? "\u2026" : '<') . out |
|||
break |
|||
endif |
|||
endfor |
|||
endif |
|||
endif |
|||
|
|||
if minlen && s:_width(out) < minlen |
|||
if flushleft |
|||
let out .= repeat(' ', minlen - s:_width(out)) |
|||
else |
|||
let out = repeat(lpad, minlen - s:_width(out)) . out |
|||
endif |
|||
endif |
|||
|
|||
return out |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private functions {{{1 |
|||
|
|||
function! s:_translateFilter(filters) abort " {{{2 |
|||
let conditions = [] |
|||
for k in keys(a:filters) |
|||
if type(a:filters[k]) == type([]) |
|||
call extend(conditions, map(copy(a:filters[k]), 's:_translateElement(k, v:val)')) |
|||
else |
|||
call add(conditions, s:_translateElement(k, a:filters[k])) |
|||
endif |
|||
endfor |
|||
|
|||
if conditions == [] |
|||
let conditions = ['1'] |
|||
endif |
|||
return len(conditions) == 1 ? conditions[0] : join(map(conditions, '"(" . v:val . ")"'), ' && ') |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_translateElement(key, term) abort " {{{2 |
|||
let fkey = a:key |
|||
if fkey[0] ==# '!' |
|||
let fkey = fkey[1:] |
|||
let not = 1 |
|||
else |
|||
let not = 0 |
|||
endif |
|||
|
|||
if fkey ==? 'level' |
|||
let op = not ? ' ==? ' : ' !=? ' |
|||
let ret = 'v:val["type"]' . op . string(a:term[0]) |
|||
elseif fkey ==? 'type' |
|||
if a:term ==? 'style' |
|||
let op = not ? ' ==? ' : ' !=? ' |
|||
let ret = 'get(v:val, "subtype", "")' . op . '"style"' |
|||
else |
|||
let op = not ? '!' : '' |
|||
let ret = op . 'has_key(v:val, "subtype")' |
|||
endif |
|||
elseif fkey ==? 'regex' |
|||
let op = not ? ' =~? ' : ' !~? ' |
|||
let ret = 'v:val["text"]' . op . string(a:term) |
|||
elseif fkey ==? 'file' || fkey[:4] ==? 'file:' |
|||
let op = not ? ' =~# ' : ' !~# ' |
|||
let ret = 'bufname(str2nr(v:val["bufnr"]))' |
|||
let mod = fkey[4:] |
|||
if mod !=# '' |
|||
let ret = 'fnamemodify(' . ret . ', ' . string(mod) . ')' |
|||
endif |
|||
let ret .= op . string(a:term) |
|||
else |
|||
call syntastic#log#warn('quiet_messages: ignoring invalid key ' . strtrans(string(fkey))) |
|||
let ret = '1' |
|||
endif |
|||
return ret |
|||
endfunction " }}}2 |
|||
|
|||
" strwidth() was added in Vim 7.3; if it doesn't exist, we use strlen() |
|||
" and hope for the best :) |
|||
let s:_width = function(exists('*strwidth') ? 'strwidth' : 'strlen') |
|||
lockvar s:_width |
|||
|
|||
" @vimlint(EVL103, 1, a:flags) |
|||
function! s:_delete_dumb(what, flags) abort " {{{2 |
|||
if !exists('s:rmrf') |
|||
let s:rmrf = |
|||
\ has('unix') || has('mac') ? 'rm -rf' : |
|||
\ has('win32') || has('win64') ? 'rmdir /S /Q' : |
|||
\ has('win16') || has('win95') || has('dos16') || has('dos32') ? 'deltree /Y' : '' |
|||
endif |
|||
|
|||
if s:rmrf !=# '' |
|||
silent! call syntastic#util#system(s:rmrf . ' ' . syntastic#util#shescape(a:what)) |
|||
else |
|||
call s:_rmrf(a:what) |
|||
endif |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL103, 0, a:flags) |
|||
|
|||
" delete(dir, 'rf') was added in Vim 7.4.1107, but it didn't become usable until 7.4.1128 |
|||
let s:_delete = function(v:version > 704 || (v:version == 704 && has('patch1128')) ? 'delete' : 's:_delete_dumb') |
|||
lockvar s:_delete |
|||
|
|||
function! s:_rmrf(what) abort " {{{2 |
|||
if !exists('s:rmdir') |
|||
let s:rmdir = syntastic#util#shescape(get(g:, 'netrw_localrmdir', 'rmdir')) |
|||
endif |
|||
|
|||
if getftype(a:what) ==# 'dir' |
|||
if filewritable(a:what) != 2 |
|||
return |
|||
endif |
|||
|
|||
try |
|||
" Vim 7.4.279 and later |
|||
let entries = globpath(a:what, '*', 1, 1) |
|||
catch /\m^Vim\%((\a\+)\)\=:E118/ |
|||
let entries = split(globpath(a:what, '*', 1), "\n") |
|||
endtry |
|||
for f in entries |
|||
call s:_rmrf(f) |
|||
endfor |
|||
silent! call syntastic#util#system(s:rmdir . ' ' . syntastic#util#shescape(a:what)) |
|||
else |
|||
silent! call delete(a:what) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
let s:_str2float = function(exists('*str2float') ? 'str2float' : 'str2nr') |
|||
lockvar s:_str2float |
|||
|
|||
function! s:_float2str_smart(val) abort " {{{2 |
|||
return printf('%.1f', a:val) |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_float2str_dumb(val) abort " {{{2 |
|||
return a:val |
|||
endfunction " }}}2 |
|||
|
|||
let s:_float2str = function(has('float') ? 's:_float2str_smart' : 's:_float2str_dumb') |
|||
lockvar s:_float2str |
|||
|
|||
function! s:_getbufvar_dumb(buf, name, ...) abort " {{{2 |
|||
let ret = getbufvar(a:buf, a:name) |
|||
if a:0 && type(ret) == type('') && ret ==# '' |
|||
unlet! ret |
|||
let ret = a:1 |
|||
endif |
|||
return ret |
|||
endfunction "}}}2 |
|||
|
|||
let s:_getbufvar = function(v:version > 703 || (v:version == 703 && has('patch831')) ? 'getbufvar' : 's:_getbufvar_dumb') |
|||
lockvar s:_getbufvar |
|||
|
|||
function! s:_fuzz_dumb() abort " {{{2 |
|||
return 'tmp' |
|||
endfunction " }}}2 |
|||
|
|||
let s:_fuzz = function(exists('*getpid') ? 'getpid' : 's:_fuzz_dumb') |
|||
lockvar s:_fuzz |
|||
|
|||
" }}}1 |
|||
|
|||
let s:_wid_base = 'syntastic_' . s:_fuzz() . '_' . reltimestr(g:_SYNTASTIC_START) . '_' |
|||
let s:_wid_pool = 0 |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,798 @@ |
|||
"============================================================================ |
|||
"File: syntastic.vim |
|||
"Description: Vim plugin for on the fly syntax checking. |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_plugin') || &compatible |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_plugin = 1 |
|||
|
|||
if has('reltime') |
|||
let g:_SYNTASTIC_START = reltime() |
|||
lockvar! g:_SYNTASTIC_START |
|||
endif |
|||
|
|||
let g:_SYNTASTIC_VERSION = '3.10.0-5' |
|||
lockvar g:_SYNTASTIC_VERSION |
|||
|
|||
" Sanity checks {{{1 |
|||
|
|||
if v:version < 700 || (v:version == 700 && !has('patch175')) |
|||
call syntastic#log#error('need Vim version 7.0.175 or later') |
|||
finish |
|||
endif |
|||
|
|||
for s:feature in [ |
|||
\ 'autocmd', |
|||
\ 'eval', |
|||
\ 'file_in_path', |
|||
\ 'modify_fname', |
|||
\ 'quickfix', |
|||
\ 'reltime', |
|||
\ 'statusline', |
|||
\ 'user_commands', |
|||
\ ] |
|||
if !has(s:feature) |
|||
call syntastic#log#error('need Vim compiled with feature ' . s:feature) |
|||
finish |
|||
endif |
|||
endfor |
|||
|
|||
let s:_running_windows = syntastic#util#isRunningWindows() |
|||
lockvar s:_running_windows |
|||
|
|||
if !exists('g:syntastic_shell') |
|||
let g:syntastic_shell = &shell |
|||
endif |
|||
|
|||
if s:_running_windows |
|||
let g:_SYNTASTIC_UNAME = 'Windows' |
|||
elseif executable('uname') |
|||
try |
|||
let g:_SYNTASTIC_UNAME = split(syntastic#util#system('uname'), "\n")[0] |
|||
catch /\m^E145$/ |
|||
call syntastic#log#error("can't run in rvim") |
|||
finish |
|||
catch /\m^E484$/ |
|||
call syntastic#log#error("can't run external programs (misconfigured shell options?)") |
|||
finish |
|||
catch /\m^E684$/ |
|||
let g:_SYNTASTIC_UNAME = 'Unknown' |
|||
endtry |
|||
else |
|||
let g:_SYNTASTIC_UNAME = 'Unknown' |
|||
endif |
|||
lockvar g:_SYNTASTIC_UNAME |
|||
|
|||
" XXX Ugly hack to make g:_SYNTASTIC_UNAME available to :SyntasticInfo without |
|||
" polluting session namespaces |
|||
let g:syntastic_version = |
|||
\ g:_SYNTASTIC_VERSION . |
|||
\ ' (Vim ' . v:version . (has('nvim') ? ', Neovim' : '') . ', ' . |
|||
\ g:_SYNTASTIC_UNAME . |
|||
\ (has('gui') ? ', GUI' : '') . ')' |
|||
lockvar g:syntastic_version |
|||
|
|||
" }}}1 |
|||
|
|||
" Defaults {{{1 |
|||
|
|||
let g:_SYNTASTIC_DEFAULTS = { |
|||
\ 'aggregate_errors': 0, |
|||
\ 'always_populate_loc_list': 0, |
|||
\ 'auto_jump': 0, |
|||
\ 'auto_loc_list': 2, |
|||
\ 'check_on_open': 0, |
|||
\ 'check_on_wq': 1, |
|||
\ 'cursor_columns': 1, |
|||
\ 'debug': 0, |
|||
\ 'echo_current_error': 1, |
|||
\ 'enable_balloons': 1, |
|||
\ 'enable_highlighting': 1, |
|||
\ 'enable_signs': 1, |
|||
\ 'error_symbol': '>>', |
|||
\ 'exit_checks': !(s:_running_windows && syntastic#util#var('shell', &shell) =~? '\m\<cmd\.exe$'), |
|||
\ 'filetype_map': {}, |
|||
\ 'full_redraws': !(has('gui_running') || has('gui_macvim')), |
|||
\ 'id_checkers': 1, |
|||
\ 'ignore_extensions': '\c\v^([gx]?z|lzma|bz2)$', |
|||
\ 'ignore_files': [], |
|||
\ 'loc_list_height': 10, |
|||
\ 'nested_autocommands': 0, |
|||
\ 'quiet_messages': {}, |
|||
\ 'reuse_loc_lists': 1, |
|||
\ 'shell': &shell, |
|||
\ 'sort_aggregated_errors': 1, |
|||
\ 'stl_format': '[Syntax: line:%F (%t)]', |
|||
\ 'style_error_symbol': 'S>', |
|||
\ 'style_warning_symbol': 'S>', |
|||
\ 'warning_symbol': '>>' |
|||
\ } |
|||
lockvar! g:_SYNTASTIC_DEFAULTS |
|||
|
|||
for s:key in keys(g:_SYNTASTIC_DEFAULTS) |
|||
if !exists('g:syntastic_' . s:key) |
|||
let g:syntastic_{s:key} = copy(g:_SYNTASTIC_DEFAULTS[s:key]) |
|||
endif |
|||
endfor |
|||
|
|||
if exists('g:syntastic_quiet_warnings') |
|||
call syntastic#log#oneTimeWarn("variable g:syntastic_quiet_warnings is deprecated, please use let g:syntastic_quiet_messages = {'level': 'warnings'} instead") |
|||
if g:syntastic_quiet_warnings |
|||
let s:quiet_warnings = get(g:syntastic_quiet_messages, 'type', []) |
|||
if type(s:quiet_warnings) != type([]) |
|||
let s:quiet_warnings = [s:quiet_warnings] |
|||
endif |
|||
call add(s:quiet_warnings, 'warnings') |
|||
let g:syntastic_quiet_messages['type'] = s:quiet_warnings |
|||
endif |
|||
endif |
|||
|
|||
" }}}1 |
|||
|
|||
" Debug {{{1 |
|||
|
|||
let g:_SYNTASTIC_SHELL_OPTIONS = [ |
|||
\ 'shell', |
|||
\ 'shellcmdflag', |
|||
\ 'shellpipe', |
|||
\ 'shellquote', |
|||
\ 'shellredir', |
|||
\ 'shelltemp', |
|||
\ 'shellxquote' |
|||
\ ] |
|||
for s:feature in [ |
|||
\ 'autochdir', |
|||
\ 'shellslash', |
|||
\ 'shellxescape', |
|||
\ ] |
|||
|
|||
if exists('+' . s:feature) |
|||
call add(g:_SYNTASTIC_SHELL_OPTIONS, s:feature) |
|||
endif |
|||
endfor |
|||
lockvar! g:_SYNTASTIC_SHELL_OPTIONS |
|||
|
|||
" debug constants |
|||
let g:_SYNTASTIC_DEBUG_TRACE = 1 |
|||
lockvar g:_SYNTASTIC_DEBUG_TRACE |
|||
let g:_SYNTASTIC_DEBUG_LOCLIST = 2 |
|||
lockvar g:_SYNTASTIC_DEBUG_LOCLIST |
|||
let g:_SYNTASTIC_DEBUG_NOTIFICATIONS = 4 |
|||
lockvar g:_SYNTASTIC_DEBUG_NOTIFICATIONS |
|||
let g:_SYNTASTIC_DEBUG_AUTOCOMMANDS = 8 |
|||
lockvar g:_SYNTASTIC_DEBUG_AUTOCOMMANDS |
|||
let g:_SYNTASTIC_DEBUG_VARIABLES = 16 |
|||
lockvar g:_SYNTASTIC_DEBUG_VARIABLES |
|||
let g:_SYNTASTIC_DEBUG_CHECKERS = 32 |
|||
lockvar g:_SYNTASTIC_DEBUG_CHECKERS |
|||
|
|||
" }}}1 |
|||
|
|||
runtime! plugin/syntastic/*.vim |
|||
|
|||
let s:registry = g:SyntasticRegistry.Instance() |
|||
let s:notifiers = g:SyntasticNotifiers.Instance() |
|||
let s:modemap = g:SyntasticModeMap.Instance() |
|||
|
|||
let s:_check_stack = [] |
|||
let s:_quit_pre = [] |
|||
|
|||
" Commands {{{1 |
|||
|
|||
" @vimlint(EVL103, 1, a:cursorPos) |
|||
" @vimlint(EVL103, 1, a:cmdLine) |
|||
" @vimlint(EVL103, 1, a:argLead) |
|||
function! s:CompleteCheckerName(argLead, cmdLine, cursorPos) abort " {{{2 |
|||
let names = [] |
|||
|
|||
let sep_idx = stridx(a:argLead, '/') |
|||
if sep_idx >= 1 |
|||
let ft = a:argLead[: sep_idx-1] |
|||
call extend(names, map( s:registry.getNamesOfAvailableCheckers(ft), 'ft . "/" . v:val' )) |
|||
else |
|||
for ft in s:registry.resolveFiletypes(&filetype) |
|||
call extend(names, s:registry.getNamesOfAvailableCheckers(ft)) |
|||
endfor |
|||
call extend(names, map( copy(s:registry.getKnownFiletypes()), 'v:val . "/"' )) |
|||
endif |
|||
|
|||
return join(names, "\n") |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL103, 0, a:cursorPos) |
|||
" @vimlint(EVL103, 0, a:cmdLine) |
|||
" @vimlint(EVL103, 0, a:argLead) |
|||
|
|||
|
|||
" @vimlint(EVL103, 1, a:cursorPos) |
|||
" @vimlint(EVL103, 1, a:cmdLine) |
|||
" @vimlint(EVL103, 1, a:argLead) |
|||
function! s:CompleteFiletypes(argLead, cmdLine, cursorPos) abort " {{{2 |
|||
return join(s:registry.getKnownFiletypes(), "\n") |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL103, 0, a:cursorPos) |
|||
" @vimlint(EVL103, 0, a:cmdLine) |
|||
" @vimlint(EVL103, 0, a:argLead) |
|||
|
|||
command! -bar -nargs=* -complete=custom,s:CompleteCheckerName SyntasticCheck call SyntasticCheck(<f-args>) |
|||
command! -bar -nargs=? -complete=custom,s:CompleteFiletypes SyntasticInfo call SyntasticInfo(<f-args>) |
|||
command! -bar Errors call SyntasticErrors() |
|||
command! -bar SyntasticReset call SyntasticReset() |
|||
command! -bar SyntasticToggleMode call SyntasticToggleMode() |
|||
command! -bar SyntasticSetLoclist call SyntasticSetLoclist() |
|||
|
|||
command! SyntasticJavacEditClasspath runtime! syntax_checkers/java/*.vim | SyntasticJavacEditClasspath |
|||
command! SyntasticJavacEditConfig runtime! syntax_checkers/java/*.vim | SyntasticJavacEditConfig |
|||
|
|||
" }}}1 |
|||
|
|||
" Public API {{{1 |
|||
|
|||
function! SyntasticCheck(...) abort " {{{2 |
|||
call s:UpdateErrors(bufnr(''), 0, a:000) |
|||
call syntastic#util#redraw(g:syntastic_full_redraws) |
|||
endfunction " }}}2 |
|||
|
|||
function! SyntasticInfo(...) abort " {{{2 |
|||
call s:modemap.modeInfo(a:000) |
|||
call s:registry.echoInfoFor(a:000) |
|||
call s:_explain_skip(a:000) |
|||
call syntastic#log#debugShowOptions(g:_SYNTASTIC_DEBUG_TRACE, g:_SYNTASTIC_SHELL_OPTIONS) |
|||
call syntastic#log#debugDump(g:_SYNTASTIC_DEBUG_VARIABLES) |
|||
endfunction " }}}2 |
|||
|
|||
function! SyntasticErrors() abort " {{{2 |
|||
call g:SyntasticLoclist.current().show() |
|||
endfunction " }}}2 |
|||
|
|||
function! SyntasticReset() abort " {{{2 |
|||
call s:ClearCache(bufnr('')) |
|||
call s:notifiers.refresh(g:SyntasticLoclist.New([])) |
|||
endfunction " }}}2 |
|||
|
|||
function! SyntasticToggleMode() abort " {{{2 |
|||
call s:modemap.toggleMode() |
|||
call s:ClearCache(bufnr('')) |
|||
call s:notifiers.refresh(g:SyntasticLoclist.New([])) |
|||
call s:modemap.echoMode() |
|||
endfunction " }}}2 |
|||
|
|||
function! SyntasticSetLoclist() abort " {{{2 |
|||
call g:SyntasticLoclist.current().setloclist(0) |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Autocommands {{{1 |
|||
|
|||
augroup syntastic |
|||
autocmd! |
|||
autocmd VimEnter * call s:VimEnterHook() |
|||
autocmd BufEnter * call s:BufEnterHook(expand('<afile>', 1)) |
|||
autocmd BufWinEnter * call s:BufWinEnterHook(expand('<afile>', 1)) |
|||
augroup END |
|||
|
|||
if g:syntastic_nested_autocommands |
|||
augroup syntastic |
|||
autocmd BufReadPost * nested call s:BufReadPostHook(expand('<afile>', 1)) |
|||
autocmd BufWritePost * nested call s:BufWritePostHook(expand('<afile>', 1)) |
|||
augroup END |
|||
else |
|||
augroup syntastic |
|||
autocmd BufReadPost * call s:BufReadPostHook(expand('<afile>', 1)) |
|||
autocmd BufWritePost * call s:BufWritePostHook(expand('<afile>', 1)) |
|||
augroup END |
|||
endif |
|||
|
|||
if exists('##QuitPre') |
|||
" QuitPre was added in Vim 7.3.544 |
|||
augroup syntastic |
|||
autocmd QuitPre * call s:QuitPreHook(expand('<afile>', 1)) |
|||
augroup END |
|||
endif |
|||
|
|||
function! s:BufReadPostHook(fname) abort " {{{2 |
|||
let buf = syntastic#util#fname2buf(a:fname) |
|||
if g:syntastic_check_on_open && buf > 0 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_AUTOCOMMANDS, |
|||
\ 'autocmd: BufReadPost, buffer ' . buf . ' = ' . string(a:fname)) |
|||
if index(s:_check_stack, buf) == -1 |
|||
call add(s:_check_stack, buf) |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! s:BufWritePostHook(fname) abort " {{{2 |
|||
let buf = syntastic#util#fname2buf(a:fname) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_AUTOCOMMANDS, |
|||
\ 'autocmd: BufWritePost, buffer ' . buf . ' = ' . string(a:fname)) |
|||
call s:UpdateErrors(buf, 1, []) |
|||
endfunction " }}}2 |
|||
|
|||
function! s:BufEnterHook(fname) abort " {{{2 |
|||
let buf = syntastic#util#fname2buf(a:fname) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_AUTOCOMMANDS, |
|||
\ 'autocmd: BufEnter, buffer ' . buf . ' = ' . string(a:fname) . ', &buftype = ' . string(&buftype)) |
|||
if buf > 0 && getbufvar(buf, '&buftype') ==# '' |
|||
let idx = index(reverse(copy(s:_check_stack)), buf) |
|||
if idx >= 0 |
|||
if !has('vim_starting') |
|||
call remove(s:_check_stack, -idx - 1) |
|||
call s:UpdateErrors(buf, 1, []) |
|||
endif |
|||
elseif &buftype ==# '' |
|||
call s:notifiers.refresh(g:SyntasticLoclist.current()) |
|||
endif |
|||
elseif &buftype ==# 'quickfix' |
|||
" TODO: this is needed because in recent versions of Vim lclose |
|||
" can no longer be called from BufWinLeave |
|||
" TODO: at this point there is no b:syntastic_loclist |
|||
let loclist = filter(copy(getloclist(0)), 'v:val["valid"]') |
|||
let owner = str2nr(getbufvar(buf, 'syntastic_owner_buffer')) |
|||
let buffers = syntastic#util#unique(map(loclist, 'v:val["bufnr"]') + (owner ? [owner] : [])) |
|||
if !empty(get(w:, 'syntastic_loclist_set', [])) && !empty(loclist) && empty(filter( buffers, 'syntastic#util#bufIsActive(v:val)' )) |
|||
call SyntasticLoclistHide() |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! s:BufWinEnterHook(fname) abort " {{{2 |
|||
let buf = syntastic#util#fname2buf(a:fname) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_AUTOCOMMANDS, |
|||
\ 'autocmd: BufWinEnter, buffer ' . buf . ' = ' . string(a:fname) . ', &buftype = ' . string(&buftype)) |
|||
if buf > 0 && getbufvar(buf, '&buftype') ==# '' |
|||
let idx = index(reverse(copy(s:_check_stack)), buf) |
|||
if idx >= 0 && !has('vim_starting') |
|||
call remove(s:_check_stack, -idx - 1) |
|||
call s:UpdateErrors(buf, 1, []) |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! s:VimEnterHook() abort " {{{2 |
|||
let buf = bufnr('') |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_AUTOCOMMANDS, |
|||
\ 'autocmd: VimEnter, buffer ' . buf . ' = ' . string(bufname(buf)) . ', &buftype = ' . string(&buftype)) |
|||
let idx = index(reverse(copy(s:_check_stack)), buf) |
|||
if idx >= 0 && getbufvar(buf, '&buftype') ==# '' |
|||
call remove(s:_check_stack, -idx - 1) |
|||
call s:UpdateErrors(buf, 1, []) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! s:QuitPreHook(fname) abort " {{{2 |
|||
let buf = syntastic#util#fname2buf(a:fname) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_AUTOCOMMANDS, 'autocmd: QuitPre, buffer ' . buf . ' = ' . string(a:fname)) |
|||
|
|||
if !syntastic#util#var('check_on_wq') |
|||
call syntastic#util#setWids() |
|||
call add(s:_quit_pre, buf . '_' . getbufvar(buf, 'changetick') . '_' . w:syntastic_wid) |
|||
endif |
|||
|
|||
if !empty(get(w:, 'syntastic_loclist_set', [])) |
|||
call SyntasticLoclistHide() |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Main {{{1 |
|||
|
|||
"refresh and redraw all the error info for this buf when saving or reading |
|||
function! s:UpdateErrors(buf, auto_invoked, checker_names) abort " {{{2 |
|||
call syntastic#log#debugShowVariables(g:_SYNTASTIC_DEBUG_TRACE, 'version') |
|||
call syntastic#log#debugShowOptions(g:_SYNTASTIC_DEBUG_TRACE, g:_SYNTASTIC_SHELL_OPTIONS) |
|||
call syntastic#log#debugDump(g:_SYNTASTIC_DEBUG_VARIABLES) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'UpdateErrors' . (a:auto_invoked ? ' (auto)' : '') . |
|||
\ ': ' . (len(a:checker_names) ? join(a:checker_names) : 'default checkers')) |
|||
|
|||
call s:modemap.synch() |
|||
|
|||
if s:_skip_file(a:buf) |
|||
return |
|||
endif |
|||
|
|||
let run_checks = !a:auto_invoked || s:modemap.doAutoChecking(a:buf) |
|||
if run_checks |
|||
call s:CacheErrors(a:buf, a:checker_names) |
|||
call syntastic#util#setLastTick(a:buf) |
|||
elseif a:auto_invoked |
|||
return |
|||
endif |
|||
|
|||
let loclist = g:SyntasticLoclist.current(a:buf) |
|||
|
|||
if exists('*SyntasticCheckHook') |
|||
call SyntasticCheckHook(loclist.getRaw()) |
|||
endif |
|||
|
|||
" populate loclist and jump {{{3 |
|||
let do_jump = syntastic#util#var('auto_jump') + 0 |
|||
if do_jump == 2 |
|||
let do_jump = loclist.getFirstError(1) |
|||
elseif do_jump == 3 |
|||
let do_jump = loclist.getFirstError() |
|||
elseif 0 > do_jump || do_jump > 3 |
|||
let do_jump = 0 |
|||
endif |
|||
|
|||
if syntastic#util#var('always_populate_loc_list') || do_jump |
|||
call loclist.setloclist(1) |
|||
if run_checks && do_jump && !loclist.isEmpty() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'loclist: jump') |
|||
execute 'silent! lrewind ' . do_jump |
|||
|
|||
" XXX: Vim doesn't call autocmd commands in a predictible |
|||
" order, which can lead to missing filetype when jumping |
|||
" to a new file; the following is a workaround for the |
|||
" resulting brain damage |
|||
if &filetype ==# '' |
|||
silent! filetype detect |
|||
endif |
|||
endif |
|||
endif |
|||
" }}}3 |
|||
|
|||
call s:notifiers.refresh(loclist) |
|||
endfunction " }}}2 |
|||
|
|||
"clear the loc list for the buffer |
|||
function! s:ClearCache(buf) abort " {{{2 |
|||
let loclist = g:SyntasticLoclist.current(a:buf) |
|||
call s:notifiers.reset(loclist) |
|||
call loclist.destroy() |
|||
endfunction " }}}2 |
|||
|
|||
"detect and cache all syntax errors in this buffer |
|||
function! s:CacheErrors(buf, checker_names) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'CacheErrors: ' . |
|||
\ (len(a:checker_names) ? join(a:checker_names) : 'default checkers')) |
|||
call s:ClearCache(a:buf) |
|||
let newLoclist = g:SyntasticLoclist.New([]) |
|||
call newLoclist.setOwner(a:buf) |
|||
|
|||
if !s:_skip_file(a:buf) |
|||
" debug logging {{{3 |
|||
call syntastic#log#debugShowVariables(g:_SYNTASTIC_DEBUG_TRACE, 'aggregate_errors') |
|||
if syntastic#util#isRunningWindows() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, '$TMP = ' . string($TMP) . ', $TEMP = ' . string($TEMP)) |
|||
else |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, '$TERM = ' . string($TERM)) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, '$TMPDIR = ' . string($TMPDIR)) |
|||
endif |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, '$PATH = ' . string($PATH)) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'getcwd() = ' . string(getcwd())) |
|||
" }}}3 |
|||
|
|||
let clist = s:registry.getCheckers(getbufvar(a:buf, '&filetype'), a:checker_names) |
|||
|
|||
let aggregate_errors = |
|||
\ syntastic#util#var('aggregate_errors') || len(syntastic#util#unique(map(copy(clist), 'v:val.getFiletype()'))) > 1 |
|||
let decorate_errors = aggregate_errors && syntastic#util#var('id_checkers') |
|||
let sort_aggregated_errors = aggregate_errors && syntastic#util#var('sort_aggregated_errors') |
|||
|
|||
let names = [] |
|||
let unavailable_checkers = 0 |
|||
for checker in clist |
|||
let cname = checker.getCName() |
|||
if !checker.isAvailable() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'CacheErrors: Checker ' . cname . ' is not available') |
|||
let unavailable_checkers += 1 |
|||
continue |
|||
endif |
|||
|
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'CacheErrors: Invoking checker: ' . cname) |
|||
|
|||
let loclist = checker.getLocList() |
|||
|
|||
if !loclist.isEmpty() |
|||
if decorate_errors |
|||
call loclist.decorate(cname) |
|||
endif |
|||
call add(names, cname) |
|||
if checker.wantSort() && !sort_aggregated_errors |
|||
call loclist.sort() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'sorted:', loclist) |
|||
endif |
|||
|
|||
call newLoclist.extend(loclist) |
|||
|
|||
if !aggregate_errors |
|||
break |
|||
endif |
|||
endif |
|||
endfor |
|||
|
|||
" set names {{{3 |
|||
if !empty(names) |
|||
if len(syntastic#util#unique(map( copy(names), 'substitute(v:val, "\\m/.*", "", "")' ))) == 1 |
|||
let type = substitute(names[0], '\m/.*', '', '') |
|||
let name = join(map( names, 'substitute(v:val, "\\m.\\{-}/", "", "")' ), ', ') |
|||
call newLoclist.setName( name . ' ('. type . ')' ) |
|||
else |
|||
" checkers from mixed types |
|||
call newLoclist.setName(join(names, ', ')) |
|||
endif |
|||
endif |
|||
" }}}3 |
|||
|
|||
" issue warning about no active checkers {{{3 |
|||
if len(clist) == unavailable_checkers |
|||
if !empty(a:checker_names) |
|||
if len(a:checker_names) == 1 |
|||
call syntastic#log#warn('checker ' . a:checker_names[0] . ' is not available') |
|||
else |
|||
call syntastic#log#warn('checkers ' . join(a:checker_names, ', ') . ' are not available') |
|||
endif |
|||
else |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'CacheErrors: no checkers available for ' . &filetype) |
|||
endif |
|||
endif |
|||
" }}}3 |
|||
|
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'aggregated:', newLoclist) |
|||
if sort_aggregated_errors |
|||
call newLoclist.sort() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'sorted:', newLoclist) |
|||
endif |
|||
endif |
|||
|
|||
call newLoclist.deploy() |
|||
endfunction " }}}2 |
|||
|
|||
"Emulates the :lmake command. Sets up the make environment according to the |
|||
"options given, runs make, resets the environment, returns the location list |
|||
" |
|||
"a:options can contain the following keys: |
|||
" 'makeprg' |
|||
" 'errorformat' |
|||
" |
|||
"The corresponding options are set for the duration of the function call. They |
|||
"are set with :let, so dont escape spaces. |
|||
" |
|||
"a:options may also contain: |
|||
" 'defaults' - a dict containing default values for the returned errors |
|||
" 'subtype' - all errors will be assigned the given subtype |
|||
" 'preprocess' - a function to be applied to the error file before parsing errors |
|||
" 'postprocess' - a list of functions to be applied to the error list |
|||
" 'cwd' - change directory to the given path before running the checker |
|||
" 'env' - environment variables to set before running the checker |
|||
" 'returns' - a list of valid exit codes for the checker |
|||
" @vimlint(EVL102, 1, l:env_save) |
|||
function! SyntasticMake(options) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'SyntasticMake: called with options:', a:options) |
|||
|
|||
" save options and locale env variables {{{3 |
|||
let old_local_errorformat = &l:errorformat |
|||
let old_errorformat = &errorformat |
|||
let old_cwd = getcwd() |
|||
" }}}3 |
|||
|
|||
if has_key(a:options, 'errorformat') |
|||
let &errorformat = a:options['errorformat'] |
|||
set errorformat< |
|||
endif |
|||
|
|||
if has_key(a:options, 'cwd') |
|||
execute 'lcd ' . fnameescape(a:options['cwd']) |
|||
endif |
|||
|
|||
" set environment variables {{{3 |
|||
let env_save = {} |
|||
if has_key(a:options, 'env') && len(a:options['env']) |
|||
for key in keys(a:options['env']) |
|||
if key =~? '\m^[a-z_][a-z0-9_]*$' |
|||
execute 'let env_save[' . string(key) . '] = $' . key |
|||
execute 'let $' . key . ' = ' . string(a:options['env'][key]) |
|||
endif |
|||
endfor |
|||
endif |
|||
" }}}3 |
|||
|
|||
let err_lines = split(syntastic#util#system(a:options['makeprg']), "\n", 1) |
|||
|
|||
" restore environment variables {{{3 |
|||
if len(env_save) |
|||
for key in keys(env_save) |
|||
execute 'let $' . key . ' = ' . string(env_save[key]) |
|||
endfor |
|||
endif |
|||
" }}}3 |
|||
|
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'checker output:', err_lines) |
|||
|
|||
" Does it still make sense to go on? |
|||
let bailout = |
|||
\ syntastic#util#var('exit_checks') && |
|||
\ has_key(a:options, 'returns') && |
|||
\ index(a:options['returns'], v:shell_error) == -1 |
|||
|
|||
if !bailout |
|||
if has_key(a:options, 'Preprocess') |
|||
let err_lines = call(a:options['Preprocess'], [err_lines]) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'preprocess (external):', err_lines) |
|||
elseif has_key(a:options, 'preprocess') |
|||
let err_lines = call('syntastic#preprocess#' . a:options['preprocess'], [err_lines]) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'preprocess:', err_lines) |
|||
endif |
|||
noautocmd lgetexpr err_lines |
|||
|
|||
let errors = deepcopy(getloclist(0)) |
|||
|
|||
if has_key(a:options, 'cwd') |
|||
execute 'lcd ' . fnameescape(old_cwd) |
|||
endif |
|||
|
|||
try |
|||
silent lolder |
|||
catch /\m^Vim\%((\a\+)\)\=:E380/ |
|||
" E380: At bottom of quickfix stack |
|||
call setloclist(0, [], 'r') |
|||
try |
|||
" Vim 7.4.2200 or later |
|||
call setloclist(0, [], 'r', { 'title': '' }) |
|||
catch /\m^Vim\%((\a\+)\)\=:E\%(118\|731\)/ |
|||
" do nothing |
|||
endtry |
|||
catch /\m^Vim\%((\a\+)\)\=:E776/ |
|||
" E776: No location list |
|||
" do nothing |
|||
endtry |
|||
else |
|||
let errors = [] |
|||
endif |
|||
|
|||
" restore options {{{3 |
|||
let &errorformat = old_errorformat |
|||
let &l:errorformat = old_local_errorformat |
|||
" }}}3 |
|||
|
|||
if !s:_running_windows && (s:_os_name() =~? 'FreeBSD' || s:_os_name() =~? 'OpenBSD') |
|||
call syntastic#util#redraw(g:syntastic_full_redraws) |
|||
endif |
|||
|
|||
if bailout |
|||
call syntastic#log#ndebug(g:_SYNTASTIC_DEBUG_LOCLIST, 'checker output:', err_lines) |
|||
throw 'Syntastic: checker error' |
|||
endif |
|||
|
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'raw loclist:', errors) |
|||
|
|||
if has_key(a:options, 'defaults') |
|||
call s:_add_to_errors(errors, a:options['defaults']) |
|||
endif |
|||
|
|||
" Add subtype info if present. |
|||
if has_key(a:options, 'subtype') |
|||
call s:_add_to_errors(errors, { 'subtype': a:options['subtype'] }) |
|||
endif |
|||
|
|||
if has_key(a:options, 'Postprocess') && !empty(a:options['Postprocess']) |
|||
for rule in a:options['Postprocess'] |
|||
let errors = call(rule, [errors]) |
|||
endfor |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'postprocess (external):', errors) |
|||
elseif has_key(a:options, 'postprocess') && !empty(a:options['postprocess']) |
|||
for rule in a:options['postprocess'] |
|||
let errors = call('syntastic#postprocess#' . rule, [errors]) |
|||
endfor |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'postprocess:', errors) |
|||
endif |
|||
|
|||
return errors |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL102, 0, l:env_save) |
|||
|
|||
"return a string representing the state of buffer according to |
|||
"g:syntastic_stl_format |
|||
" |
|||
"return '' if no errors are cached for the buffer |
|||
function! SyntasticStatuslineFlag() abort " {{{2 |
|||
return g:SyntasticLoclist.current().getStatuslineFlag() |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Utilities {{{1 |
|||
|
|||
function! s:_ignore_file(filename) abort " {{{2 |
|||
let fname = fnamemodify(a:filename, ':p') |
|||
for pattern in g:syntastic_ignore_files |
|||
if fname =~# pattern |
|||
return 1 |
|||
endif |
|||
endfor |
|||
return 0 |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_is_quitting(buf) abort " {{{2 |
|||
let quitting = 0 |
|||
if exists('w:syntastic_wid') |
|||
let key = a:buf . '_' . getbufvar(a:buf, 'changetick') . '_' . w:syntastic_wid |
|||
let idx = index(s:_quit_pre, key) |
|||
if idx >= 0 |
|||
call remove(s:_quit_pre, idx) |
|||
let quitting = 1 |
|||
endif |
|||
endif |
|||
|
|||
return quitting |
|||
endfunction " }}}2 |
|||
|
|||
" Skip running in special buffers |
|||
function! s:_skip_file(buf) abort " {{{2 |
|||
let fname = bufname(a:buf) |
|||
let skip = s:_is_quitting(a:buf) || getbufvar(a:buf, 'syntastic_skip_checks') || |
|||
\ (getbufvar(a:buf, '&buftype') !=# '') || !filereadable(fname) || getwinvar(0, '&diff') || |
|||
\ getwinvar(0, '&previewwindow') || s:_ignore_file(fname) || |
|||
\ fnamemodify(fname, ':e') =~? g:syntastic_ignore_extensions |
|||
if skip |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, '_skip_file: skipping checks') |
|||
endif |
|||
return skip |
|||
endfunction " }}}2 |
|||
|
|||
" Explain why checks will be skipped for the current file |
|||
function! s:_explain_skip(filetypes) abort " {{{2 |
|||
let buf = bufnr('') |
|||
if empty(a:filetypes) && s:_skip_file(buf) |
|||
let why = [] |
|||
let fname = bufname(buf) |
|||
let bt = getbufvar(buf, '&buftype') |
|||
|
|||
if s:_is_quitting(buf) |
|||
call add(why, 'quitting buffer') |
|||
endif |
|||
if getbufvar(buf, 'syntastic_skip_checks') |
|||
call add(why, 'b:syntastic_skip_checks set') |
|||
endif |
|||
if bt !=# '' |
|||
call add(why, 'buftype = ' . string(&buftype)) |
|||
endif |
|||
if !filereadable(fname) |
|||
call add(why, 'file not readable / not local') |
|||
endif |
|||
if getwinvar(0, '&diff') |
|||
call add(why, 'diff mode') |
|||
endif |
|||
if getwinvar(0, '&previewwindow') |
|||
call add(why, 'preview window') |
|||
endif |
|||
if s:_ignore_file(fname) |
|||
call add(why, 'filename matching g:syntastic_ignore_files') |
|||
endif |
|||
if fnamemodify(fname, ':e') =~? g:syntastic_ignore_extensions |
|||
call add(why, 'extension matching g:syntastic_ignore_extensions') |
|||
endif |
|||
|
|||
echomsg 'The current file will not be checked (' . join(why, ', ') . ')' |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" Take a list of errors and add default values to them from a:options |
|||
function! s:_add_to_errors(errors, options) abort " {{{2 |
|||
for err in a:errors |
|||
for key in keys(a:options) |
|||
if !has_key(err, key) || empty(err[key]) |
|||
let err[key] = a:options[key] |
|||
endif |
|||
endfor |
|||
endfor |
|||
|
|||
return a:errors |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_os_name() abort " {{{2 |
|||
return g:_SYNTASTIC_UNAME |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,55 @@ |
|||
if exists('g:loaded_syntastic_notifier_autoloclist') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_notifier_autoloclist = 1 |
|||
|
|||
let g:SyntasticAutoloclistNotifier = {} |
|||
|
|||
" Public methods {{{1 |
|||
" |
|||
function! g:SyntasticAutoloclistNotifier.New() abort " {{{2 |
|||
let newObj = copy(self) |
|||
return newObj |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticAutoloclistNotifier.refresh(loclist) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'autoloclist: refresh') |
|||
call g:SyntasticAutoloclistNotifier.AutoToggle(a:loclist) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticAutoloclistNotifier.AutoToggle(loclist) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'autoloclist: toggle') |
|||
let auto_loc_list = syntastic#util#var('auto_loc_list') |
|||
if !a:loclist.isEmpty() |
|||
if auto_loc_list == 1 || auto_loc_list == 3 |
|||
call a:loclist.show() |
|||
endif |
|||
else |
|||
if (auto_loc_list == 1 || auto_loc_list == 2) && !empty(get(w:, 'syntastic_loclist_set', [])) |
|||
try |
|||
" Vim 7.4.2200 or later |
|||
let title = get(getloclist(0, { 'title': 1 }), 'title', ':SyntasticCheck ') |
|||
catch /\m^Vim\%((\a\+)\)\=:E\%(118\|731\)/ |
|||
let title = ':SyntasticCheck ' |
|||
endtry |
|||
|
|||
if strpart(title, 0, 16) ==# ':SyntasticCheck ' |
|||
" TODO: this will close the loc list window if one was opened |
|||
" by something other than syntastic |
|||
call SyntasticLoclistHide() |
|||
|
|||
try |
|||
" Vim 7.4.2200 or later |
|||
call setloclist(0, [], 'r', { 'title': '' }) |
|||
catch /\m^Vim\%((\a\+)\)\=:E\%(118\|731\)/ |
|||
" do nothing |
|||
endtry |
|||
let w:syntastic_loclist_set = [] |
|||
endif |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,58 @@ |
|||
if exists('g:loaded_syntastic_notifier_balloons') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_notifier_balloons = 1 |
|||
|
|||
if !has('balloon_eval') |
|||
let g:syntastic_enable_balloons = 0 |
|||
endif |
|||
|
|||
let g:SyntasticBalloonsNotifier = {} |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticBalloonsNotifier.New() abort " {{{2 |
|||
let newObj = copy(self) |
|||
return newObj |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticBalloonsNotifier.enabled() abort " {{{2 |
|||
return has('balloon_eval') && syntastic#util#var('enable_balloons') |
|||
endfunction " }}}2 |
|||
|
|||
" Update the error balloons |
|||
function! g:SyntasticBalloonsNotifier.refresh(loclist) abort " {{{2 |
|||
unlet! b:syntastic_private_balloons |
|||
if self.enabled() && !a:loclist.isEmpty() |
|||
let b:syntastic_private_balloons = a:loclist.balloons() |
|||
if !empty(b:syntastic_private_balloons) |
|||
set ballooneval balloonexpr=SyntasticBalloonsExprNotifier() |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" Reset the error balloons |
|||
" @vimlint(EVL103, 1, a:loclist) |
|||
function! g:SyntasticBalloonsNotifier.reset(loclist) abort " {{{2 |
|||
if has('balloon_eval') && !empty(get(b:, 'syntastic_private_balloons', {})) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'balloons: reset') |
|||
set noballooneval |
|||
endif |
|||
unlet! b:syntastic_private_balloons |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL103, 0, a:loclist) |
|||
|
|||
" }}}1 |
|||
|
|||
" Private functions {{{1 |
|||
|
|||
function! SyntasticBalloonsExprNotifier() abort " {{{2 |
|||
if !exists('b:syntastic_private_balloons') |
|||
return '' |
|||
endif |
|||
return get(b:syntastic_private_balloons, v:beval_lnum, '') |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,282 @@ |
|||
if exists('g:loaded_syntastic_checker') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_checker = 1 |
|||
|
|||
let g:SyntasticChecker = {} |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticChecker.New(args, ...) abort " {{{2 |
|||
let newObj = copy(self) |
|||
|
|||
let newObj._filetype = a:args['filetype'] |
|||
let newObj._name = a:args['name'] |
|||
|
|||
if a:0 |
|||
" redirected checker |
|||
let newObj._exec_default = get(a:args, 'exec', a:1['_exec_default']) |
|||
|
|||
let filetype = a:1['_filetype'] |
|||
let name = a:1['_name'] |
|||
let prefix = 'SyntaxCheckers_' . filetype . '_' . name . '_' |
|||
|
|||
if exists('g:syntastic_' . filetype . '_' . name . '_sort') && !exists('g:syntastic_' . newObj._filetype . '_' . newObj._name . '_sort') |
|||
let g:syntastic_{newObj._filetype}_{newObj._name}_sort = g:syntastic_{filetype}_{name}_sort |
|||
endif |
|||
|
|||
if has_key(a:args, 'enable') |
|||
let newObj._enable = a:args['enable'] |
|||
elseif has_key(a:1, '_enable') |
|||
let newObj._enable = a:1['_enable'] |
|||
endif |
|||
else |
|||
let newObj._exec_default = get(a:args, 'exec', newObj._name) |
|||
if newObj._exec_default ==# '' |
|||
let newObj._exec_default = '<dummy>' |
|||
endif |
|||
let prefix = 'SyntaxCheckers_' . newObj._filetype . '_' . newObj._name . '_' |
|||
|
|||
if has_key(a:args, 'enable') |
|||
let newObj._enable = a:args['enable'] |
|||
endif |
|||
endif |
|||
|
|||
let newObj._locListFunc = function(prefix . 'GetLocList') |
|||
|
|||
if exists('*' . prefix . 'IsAvailable') |
|||
let newObj._isAvailableFunc = function(prefix . 'IsAvailable') |
|||
else |
|||
let newObj._isAvailableFunc = function('s:_isAvailableDefault') |
|||
endif |
|||
|
|||
if exists('*' . prefix . 'GetHighlightRegex') |
|||
let newObj._highlightRegexFunc = function(prefix . 'GetHighlightRegex') |
|||
endif |
|||
|
|||
return newObj |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getFiletype() abort " {{{2 |
|||
return self._filetype |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getName() abort " {{{2 |
|||
return self._name |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getCName() abort " {{{2 |
|||
return self._filetype . '/' . self._name |
|||
endfunction " }}}2 |
|||
|
|||
" Synchronise _exec with user's setting. Force re-validation if needed. |
|||
" |
|||
" XXX: This function must be called at least once before calling either |
|||
" getExec() or getExecEscaped(). Normally isAvailable() does that for you |
|||
" automatically, but you should keep still this in mind if you change the |
|||
" current checker workflow. |
|||
function! g:SyntasticChecker.syncExec(...) abort " {{{2 |
|||
if a:0 |
|||
let self._exec = a:1 |
|||
else |
|||
let suffix = self._name . '_exec' |
|||
let self._exec = expand( |
|||
\ syntastic#util#var(self._filetype . '_' . suffix, |
|||
\ syntastic#util#var(suffix, self._exec_default)), 1 ) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getExec() abort " {{{2 |
|||
return self._exec |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getExecEscaped() abort " {{{2 |
|||
return syntastic#util#shescape(self._exec) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getLocListRaw() abort " {{{2 |
|||
let checker_start = reltime() |
|||
let name = self.getCName() |
|||
|
|||
if has_key(self, '_enable') |
|||
let status = syntastic#util#var(self._enable, -1) |
|||
if type(status) != type(0) |
|||
call syntastic#log#error('checker ' . name . ': invalid value ' . strtrans(string(status)) . |
|||
\ ' for g:syntastic_' . self._enable . '; try 0 or 1 instead') |
|||
return [] |
|||
endif |
|||
if status < 0 |
|||
call syntastic#log#error('checker ' . name . ': checks disabled for security reasons; ' . |
|||
\ 'set g:syntastic_' . self._enable . ' to 1 to override') |
|||
endif |
|||
if status <= 0 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'getLocList: checker ' . name . ' enabled but not forced') |
|||
return [] |
|||
else |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'getLocList: checker ' . name . ' forced') |
|||
endif |
|||
endif |
|||
|
|||
try |
|||
let list = self._locListFunc() |
|||
if self._exec !=# '' |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, 'getLocList: checker ' . name . ' returned ' . v:shell_error) |
|||
endif |
|||
catch /\m\C^Syntastic: checker error$/ |
|||
let list = [] |
|||
if self._exec !=# '' |
|||
call syntastic#log#error('checker ' . name . ' returned abnormal status ' . v:shell_error) |
|||
else |
|||
call syntastic#log#error('checker ' . name . ' aborted') |
|||
endif |
|||
endtry |
|||
call self._populateHighlightRegexes(list) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, name . ' raw:', list) |
|||
call self._quietMessages(list) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_TRACE, |
|||
\ 'getLocList: checker ' . name . ' run in ' . split(reltimestr(reltime(checker_start)))[0] . 's') |
|||
return list |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getLocList() abort " {{{2 |
|||
return g:SyntasticLoclist.New(self.getLocListRaw()) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.getVersion(...) abort " {{{2 |
|||
if !exists('self._version') |
|||
let command = a:0 ? a:1 : self.getExecEscaped() . ' --version' |
|||
let version_output = syntastic#util#system(command) |
|||
call self.log('getVersion: ' . string(command) . ': ' . |
|||
\ string(split(version_output, "\n", 1)) . |
|||
\ (v:shell_error ? ' (exit code ' . v:shell_error . ')' : '') ) |
|||
let parsed_ver = syntastic#util#parseVersion(version_output) |
|||
if len(parsed_ver) |
|||
call self.setVersion(parsed_ver) |
|||
else |
|||
call syntastic#log#ndebug(g:_SYNTASTIC_DEBUG_LOCLIST, 'checker output:', split(version_output, "\n", 1)) |
|||
call syntastic#log#error("checker " . self.getCName() . ": can't parse version string (abnormal termination?)") |
|||
endif |
|||
endif |
|||
return get(self, '_version', []) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.setVersion(version) abort " {{{2 |
|||
if len(a:version) |
|||
let self._version = copy(a:version) |
|||
call self.log(self.getExec() . ' version =', a:version) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.log(msg, ...) abort " {{{2 |
|||
let leader = self.getCName() . ': ' |
|||
if a:0 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, leader . a:msg, a:1) |
|||
else |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_CHECKERS, leader . a:msg) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.makeprgBuild(opts) abort " {{{2 |
|||
let basename = self._filetype . '_' . self._name . '_' |
|||
|
|||
let parts = [] |
|||
call extend(parts, self._getOpt(a:opts, basename, 'exe', self.getExecEscaped())) |
|||
call extend(parts, self._getOpt(a:opts, basename, 'args', '')) |
|||
call extend(parts, self._getOpt(a:opts, basename, 'fname', syntastic#util#shexpand('%'))) |
|||
call extend(parts, self._getOpt(a:opts, basename, 'post_args', '')) |
|||
call extend(parts, self._getOpt(a:opts, basename, 'tail', '')) |
|||
|
|||
return join(parts) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.isAvailable() abort " {{{2 |
|||
call self.syncExec() |
|||
|
|||
if !has_key(self, '_available') |
|||
let self._available = {} |
|||
endif |
|||
if !has_key(self._available, self._exec) |
|||
let self._available[self._exec] = self._isAvailableFunc() |
|||
endif |
|||
|
|||
return self._available[self._exec] |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.isDisabled() abort " {{{2 |
|||
return has_key(self, '_enable') && syntastic#util#var(self._enable, -1) <= 0 |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker.wantSort() abort " {{{2 |
|||
return syntastic#util#var(self._filetype . '_' . self._name . '_sort', 0) |
|||
endfunction " }}}2 |
|||
|
|||
" This method is no longer used by syntastic. It's here only to maintain |
|||
" backwards compatibility with external checkers which might depend on it. |
|||
function! g:SyntasticChecker.setWantSort(val) abort " {{{2 |
|||
if !exists('g:syntastic_' . self._filetype . '_' . self._name . '_sort') |
|||
let g:syntastic_{self._filetype}_{self._name}_sort = a:val |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private methods {{{1 |
|||
|
|||
function! g:SyntasticChecker._quietMessages(errors) abort " {{{2 |
|||
" wildcard quiet_messages |
|||
let quiet_filters = copy(syntastic#util#var('quiet_messages', {})) |
|||
if type(quiet_filters) != type({}) |
|||
call syntastic#log#warn('ignoring invalid syntastic_quiet_messages') |
|||
unlet quiet_filters |
|||
let quiet_filters = {} |
|||
endif |
|||
|
|||
" per checker quiet_messages |
|||
let name = self._filetype . '_' . self._name |
|||
try |
|||
call extend( quiet_filters, copy(syntastic#util#var(name . '_quiet_messages', {})), 'force' ) |
|||
catch /\m^Vim\%((\a\+)\)\=:E712/ |
|||
call syntastic#log#warn('ignoring invalid syntastic_' . name . '_quiet_messages') |
|||
endtry |
|||
|
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'quiet_messages filter:', quiet_filters) |
|||
|
|||
if !empty(quiet_filters) |
|||
call syntastic#util#dictFilter(a:errors, quiet_filters) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_LOCLIST, 'filtered by quiet_messages:', a:errors) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker._populateHighlightRegexes(errors) abort " {{{2 |
|||
if has_key(self, '_highlightRegexFunc') |
|||
for e in a:errors |
|||
if e['valid'] |
|||
let term = self._highlightRegexFunc(e) |
|||
if term !=# '' |
|||
let e['hl'] = term |
|||
endif |
|||
endif |
|||
endfor |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticChecker._getOpt(opts, basename, name, default) abort " {{{2 |
|||
let ret = [] |
|||
call extend( ret, syntastic#util#argsescape(get(a:opts, a:name . '_before', '')) ) |
|||
call extend( ret, syntastic#util#argsescape(syntastic#util#var( a:basename . a:name, get(a:opts, a:name, a:default) )) ) |
|||
call extend( ret, syntastic#util#argsescape(get(a:opts, a:name . '_after', '')) ) |
|||
|
|||
return ret |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private functions {{{1 |
|||
|
|||
function! s:_isAvailableDefault() dict " {{{2 |
|||
return executable(self.getExec()) |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,138 @@ |
|||
if exists('g:loaded_syntastic_notifier_cursor') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_notifier_cursor = 1 |
|||
|
|||
let g:SyntasticCursorNotifier = {} |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticCursorNotifier.New() abort " {{{2 |
|||
let newObj = copy(self) |
|||
return newObj |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticCursorNotifier.enabled() abort " {{{2 |
|||
return syntastic#util#var('echo_current_error') |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticCursorNotifier.refresh(loclist) abort " {{{2 |
|||
if self.enabled() && !a:loclist.isEmpty() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'cursor: refresh') |
|||
let b:syntastic_private_messages = copy(a:loclist.messages(bufnr(''))) |
|||
let b:syntastic_private_line = -1 |
|||
let b:syntastic_cursor_columns = a:loclist.getCursorColumns() |
|||
autocmd! syntastic CursorMoved |
|||
autocmd syntastic CursorMoved * call SyntasticRefreshCursor() |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" @vimlint(EVL103, 1, a:loclist) |
|||
function! g:SyntasticCursorNotifier.reset(loclist) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'cursor: reset') |
|||
autocmd! syntastic CursorMoved |
|||
unlet! b:syntastic_private_messages |
|||
let b:syntastic_private_line = -1 |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL103, 0, a:loclist) |
|||
|
|||
" }}}1 |
|||
|
|||
" Private functions {{{1 |
|||
|
|||
function! SyntasticRefreshCursor() abort " {{{2 |
|||
if !exists('b:syntastic_private_messages') || empty(b:syntastic_private_messages) |
|||
" file not checked |
|||
return |
|||
endif |
|||
|
|||
if !exists('b:syntastic_private_line') |
|||
let b:syntastic_private_line = -1 |
|||
endif |
|||
let l = line('.') |
|||
let current_messages = get(b:syntastic_private_messages, l, {}) |
|||
|
|||
if !exists('b:syntastic_cursor_columns') |
|||
let b:syntastic_cursor_columns = g:syntastic_cursor_columns |
|||
endif |
|||
|
|||
if b:syntastic_cursor_columns |
|||
let c = virtcol('.') |
|||
if !exists('b:syntastic_private_idx') |
|||
let b:syntastic_private_idx = -1 |
|||
endif |
|||
|
|||
if s:_is_same_index(l, b:syntastic_private_line, c, b:syntastic_private_idx, current_messages) |
|||
return |
|||
else |
|||
let b:syntastic_private_line = l |
|||
endif |
|||
|
|||
if !empty(current_messages) |
|||
let b:syntastic_private_idx = s:_find_index(c, current_messages) |
|||
call syntastic#util#wideMsg(current_messages[b:syntastic_private_idx].text) |
|||
else |
|||
let b:syntastic_private_idx = -1 |
|||
echo |
|||
endif |
|||
else |
|||
if l == b:syntastic_private_line |
|||
return |
|||
endif |
|||
let b:syntastic_private_line = l |
|||
|
|||
if !empty(current_messages) |
|||
call syntastic#util#wideMsg(current_messages[0].text) |
|||
else |
|||
echo |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Utilities {{{1 |
|||
|
|||
function! s:_is_same_index(line, old_line, column, idx, messages) abort " {{{2 |
|||
if a:old_line >= 0 && a:line == a:old_line && a:idx >= 0 |
|||
if len(a:messages) <= 1 |
|||
return 1 |
|||
endif |
|||
|
|||
if a:messages[a:idx].scol <= a:column || a:idx == 0 |
|||
if a:idx == len(a:messages) - 1 || a:column < a:messages[a:idx + 1].scol |
|||
return 1 |
|||
else |
|||
return 0 |
|||
endif |
|||
else |
|||
return 0 |
|||
endif |
|||
else |
|||
return 0 |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_find_index(column, messages) abort " {{{2 |
|||
let max = len(a:messages) - 1 |
|||
if max == 0 |
|||
return 0 |
|||
endif |
|||
let min = 0 |
|||
|
|||
" modified binary search: assign index 0 to columns to the left of the first error |
|||
while min < max - 1 |
|||
let mid = (min + max) / 2 |
|||
if a:column < a:messages[mid].scol |
|||
let max = mid |
|||
else |
|||
let min = mid |
|||
endif |
|||
endwhile |
|||
|
|||
return a:column < a:messages[max].scol ? min : max |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,104 @@ |
|||
if exists('g:loaded_syntastic_notifier_highlighting') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_notifier_highlighting = 1 |
|||
|
|||
" Highlighting requires getmatches introduced in 7.1.040 |
|||
let s:has_highlighting = v:version > 701 || (v:version == 701 && has('patch040')) |
|||
lockvar s:has_highlighting |
|||
|
|||
let g:SyntasticHighlightingNotifier = {} |
|||
|
|||
let s:setup_done = 0 |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticHighlightingNotifier.New() abort " {{{2 |
|||
let newObj = copy(self) |
|||
|
|||
if !s:setup_done |
|||
call self._setup() |
|||
let s:setup_done = 1 |
|||
lockvar s:setup_done |
|||
endif |
|||
|
|||
return newObj |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticHighlightingNotifier.enabled() abort " {{{2 |
|||
return s:has_highlighting && syntastic#util#var('enable_highlighting') |
|||
endfunction " }}}2 |
|||
|
|||
" Sets error highlights in the current window |
|||
function! g:SyntasticHighlightingNotifier.refresh(loclist) abort " {{{2 |
|||
if self.enabled() |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'highlighting: refresh') |
|||
call self._reset() |
|||
let buf = bufnr('') |
|||
let issues = filter(a:loclist.copyRaw(), 'v:val["bufnr"] == buf') |
|||
for item in issues |
|||
let group = 'Syntastic' . get(item, 'subtype', '') . ( item['type'] ==? 'E' ? 'Error' : 'Warning' ) |
|||
|
|||
" The function `Syntastic_{filetype}_{checker}_GetHighlightRegex` is |
|||
" used to override default highlighting. |
|||
if has_key(item, 'hl') |
|||
call matchadd(group, '\%' . item['lnum'] . 'l' . item['hl']) |
|||
elseif get(item, 'col', 0) |
|||
if get(item, 'vcol', 0) |
|||
let lastcol = virtcol([item['lnum'], '$']) |
|||
let coltype = 'v' |
|||
else |
|||
let lastcol = col([item['lnum'], '$']) |
|||
let coltype = 'c' |
|||
endif |
|||
let lcol = min([lastcol, item['col']]) |
|||
|
|||
call matchadd(group, '\%' . item['lnum'] . 'l\%' . lcol . coltype) |
|||
endif |
|||
endfor |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" Remove all error highlights from the window |
|||
" @vimlint(EVL103, 1, a:loclist) |
|||
function! g:SyntasticHighlightingNotifier.reset(loclist) abort " {{{2 |
|||
if s:has_highlighting |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'highlighting: reset') |
|||
call self._reset() |
|||
endif |
|||
endfunction " }}}2 |
|||
" @vimlint(EVL103, 0, a:loclist) |
|||
|
|||
" }}}1 |
|||
|
|||
" Private methods {{{1 |
|||
|
|||
" One time setup: define our own highlighting |
|||
function! g:SyntasticHighlightingNotifier._setup() abort " {{{2 |
|||
if s:has_highlighting |
|||
if !hlexists('SyntasticError') |
|||
highlight link SyntasticError SpellBad |
|||
endif |
|||
if !hlexists('SyntasticWarning') |
|||
highlight link SyntasticWarning SpellCap |
|||
endif |
|||
if !hlexists('SyntasticStyleError') |
|||
highlight link SyntasticStyleError SyntasticError |
|||
endif |
|||
if !hlexists('SyntasticStyleWarning') |
|||
highlight link SyntasticStyleWarning SyntasticWarning |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticHighlightingNotifier._reset() abort " {{{2 |
|||
for match in getmatches() |
|||
if stridx(match['group'], 'Syntastic') == 0 |
|||
call matchdelete(match['id']) |
|||
endif |
|||
endfor |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,445 @@ |
|||
if exists('g:loaded_syntastic_loclist') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_loclist = 1 |
|||
|
|||
let g:SyntasticLoclist = {} |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticLoclist.New(rawLoclist) abort " {{{2 |
|||
let newObj = copy(self) |
|||
|
|||
let llist = filter(copy(a:rawLoclist), 'v:val["valid"]') |
|||
|
|||
for e in llist |
|||
if get(e, 'type', '') ==# '' |
|||
let e['type'] = 'E' |
|||
endif |
|||
endfor |
|||
|
|||
let newObj._rawLoclist = llist |
|||
let newObj._name = '' |
|||
let newObj._owner = bufnr('') |
|||
let newObj._sorted = 0 |
|||
let newObj._columns = g:syntastic_cursor_columns |
|||
|
|||
return newObj |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.current(...) abort " {{{2 |
|||
let buf = a:0 ? a:1 : bufnr('') |
|||
let loclist = syntastic#util#getbufvar(buf, 'syntastic_loclist', {}) |
|||
if type(loclist) != type({}) || empty(loclist) |
|||
unlet! loclist |
|||
let loclist = g:SyntasticLoclist.New([]) |
|||
endif |
|||
return loclist |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.extend(other) abort " {{{2 |
|||
call extend(self._rawLoclist, a:other.copyRaw()) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.sort() abort " {{{2 |
|||
if !self._sorted |
|||
for e in self._rawLoclist |
|||
call s:_set_screen_column(e) |
|||
endfor |
|||
|
|||
call sort(self._rawLoclist, self._columns ? 's:_compare_error_items_by_columns' : 's:_compare_error_items_by_lines') |
|||
|
|||
let self._sorted = 1 |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.isEmpty() abort " {{{2 |
|||
return empty(self._rawLoclist) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.isNewerThan(stamp) abort " {{{2 |
|||
if !exists('self._stamp') |
|||
let self._stamp = [] |
|||
return 0 |
|||
endif |
|||
return syntastic#util#compareLexi(self._stamp, a:stamp) > 0 |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.copyRaw() abort " {{{2 |
|||
return copy(self._rawLoclist) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.getRaw() abort " {{{2 |
|||
return self._rawLoclist |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.getBuffers() abort " {{{2 |
|||
return syntastic#util#unique(map(copy(self._rawLoclist), 'str2nr(v:val["bufnr"])') + [self._owner]) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.getCursorColumns() abort " {{{2 |
|||
return self._columns |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.getStatuslineFlag() abort " {{{2 |
|||
if !exists('self._stl_format') |
|||
let self._stl_format = '' |
|||
endif |
|||
if !exists('self._stl_flag') |
|||
let self._stl_flag = '' |
|||
endif |
|||
|
|||
if g:syntastic_stl_format !=# self._stl_format |
|||
let self._stl_format = g:syntastic_stl_format |
|||
|
|||
if !empty(self._rawLoclist) |
|||
let errors = self.errors() |
|||
let warnings = self.warnings() |
|||
|
|||
let num_errors = len(errors) |
|||
let num_warnings = len(warnings) |
|||
let num_issues = len(self._rawLoclist) |
|||
|
|||
let output = self._stl_format |
|||
|
|||
"hide stuff wrapped in %E(...) unless there are errors |
|||
let output = substitute(output, '\m\C%E{\([^}]*\)}', num_errors ? '\1' : '' , 'g') |
|||
|
|||
"hide stuff wrapped in %W(...) unless there are warnings |
|||
let output = substitute(output, '\m\C%W{\([^}]*\)}', num_warnings ? '\1' : '' , 'g') |
|||
|
|||
"hide stuff wrapped in %B(...) unless there are both errors and warnings |
|||
let output = substitute(output, '\m\C%B{\([^}]*\)}', (num_warnings && num_errors) ? '\1' : '' , 'g') |
|||
|
|||
let flags = { |
|||
\ '%': '%', |
|||
\ 't': num_issues, |
|||
\ 'e': num_errors, |
|||
\ 'w': num_warnings, |
|||
\ 'N': (num_issues ? fnamemodify( bufname(self._rawLoclist[0]['bufnr']), ':t') : ''), |
|||
\ 'P': (num_issues ? fnamemodify( bufname(self._rawLoclist[0]['bufnr']), ':p:~:.') : ''), |
|||
\ 'F': (num_issues ? self._rawLoclist[0]['lnum'] : ''), |
|||
\ 'ne': (num_errors ? fnamemodify( bufname(errors[0]['bufnr']), ':t') : ''), |
|||
\ 'pe': (num_errors ? fnamemodify( bufname(errors[0]['bufnr']), ':p:~:.') : ''), |
|||
\ 'fe': (num_errors ? errors[0]['lnum'] : ''), |
|||
\ 'nw': (num_warnings ? fnamemodify( bufname(warnings[0]['bufnr']), ':t') : ''), |
|||
\ 'pw': (num_warnings ? fnamemodify( bufname(warnings[0]['bufnr']), ':p:~:.') : ''), |
|||
\ 'fw': (num_warnings ? warnings[0]['lnum'] : '') } |
|||
let output = substitute(output, '\v\C\%(-?\d*%(\.\d+)?)([npf][ew]|[NPFtew%])', '\=syntastic#util#wformat(submatch(1), flags[submatch(2)])', 'g') |
|||
|
|||
let self._stl_flag = output |
|||
else |
|||
let self._stl_flag = '' |
|||
endif |
|||
endif |
|||
|
|||
return self._stl_flag |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.getFirstError(...) abort " {{{2 |
|||
let max_issues = len(self._rawLoclist) |
|||
if a:0 && a:1 < max_issues |
|||
let max_issues = a:1 |
|||
endif |
|||
|
|||
for idx in range(max_issues) |
|||
if get(self._rawLoclist[idx], 'type', '') ==? 'E' |
|||
return idx + 1 |
|||
endif |
|||
endfor |
|||
|
|||
return 0 |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.getName() abort " {{{2 |
|||
return len(self._name) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.setName(name) abort " {{{2 |
|||
let self._name = a:name |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.getOwner() abort " {{{2 |
|||
return self._owner |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.setOwner(buffer) abort " {{{2 |
|||
let self._owner = type(a:buffer) == type(0) ? a:buffer : str2nr(a:buffer) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.deploy() abort " {{{2 |
|||
let self._stamp = syntastic#util#stamp() |
|||
for buf in self.getBuffers() |
|||
call setbufvar(buf, 'syntastic_loclist', self) |
|||
endfor |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.destroy() abort " {{{2 |
|||
for buf in self.getBuffers() |
|||
call setbufvar(buf, 'syntastic_loclist', {}) |
|||
endfor |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.decorate(tag) abort " {{{2 |
|||
for e in self._rawLoclist |
|||
let e['text'] .= ' [' . a:tag . ']' |
|||
endfor |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.balloons() abort " {{{2 |
|||
if !exists('self._cachedBalloons') |
|||
let sep = has('balloon_multiline') ? "\n" : ' | ' |
|||
|
|||
let self._cachedBalloons = {} |
|||
for e in self._rawLoclist |
|||
let buf = e['bufnr'] |
|||
|
|||
if !has_key(self._cachedBalloons, buf) |
|||
let self._cachedBalloons[buf] = {} |
|||
endif |
|||
|
|||
if has_key(self._cachedBalloons[buf], e['lnum']) |
|||
let self._cachedBalloons[buf][e['lnum']] .= sep . e['text'] |
|||
else |
|||
let self._cachedBalloons[buf][e['lnum']] = e['text'] |
|||
endif |
|||
endfor |
|||
endif |
|||
|
|||
return get(self._cachedBalloons, bufnr(''), {}) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.errors() abort " {{{2 |
|||
if !exists('self._cachedErrors') |
|||
let self._cachedErrors = self.filter({'type': 'E'}) |
|||
endif |
|||
return self._cachedErrors |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.warnings() abort " {{{2 |
|||
if !exists('self._cachedWarnings') |
|||
let self._cachedWarnings = self.filter({'type': 'W'}) |
|||
endif |
|||
return self._cachedWarnings |
|||
endfunction " }}}2 |
|||
|
|||
" Legacy function. Syntastic no longer calls it, but we keep it |
|||
" around because other plugins (f.i. powerline) depend on it. |
|||
function! g:SyntasticLoclist.hasErrorsOrWarningsToDisplay() abort " {{{2 |
|||
return !self.isEmpty() |
|||
endfunction " }}}2 |
|||
|
|||
" cache used by EchoCurrentError() |
|||
function! g:SyntasticLoclist.messages(buf) abort " {{{2 |
|||
if !exists('self._cachedMessages') |
|||
let self._cachedMessages = {} |
|||
|
|||
let errors = self.errors() + self.warnings() |
|||
for e in errors |
|||
let b = e['bufnr'] |
|||
let l = e['lnum'] |
|||
|
|||
if !has_key(self._cachedMessages, b) |
|||
let self._cachedMessages[b] = {} |
|||
endif |
|||
|
|||
if !has_key(self._cachedMessages[b], l) |
|||
let self._cachedMessages[b][l] = [e] |
|||
elseif self._columns |
|||
call add(self._cachedMessages[b][l], e) |
|||
endif |
|||
endfor |
|||
|
|||
if self._columns |
|||
if !self._sorted |
|||
for b in keys(self._cachedMessages) |
|||
for l in keys(self._cachedMessages[b]) |
|||
if len(self._cachedMessages[b][l]) > 1 |
|||
for e in self._cachedMessages[b][l] |
|||
call s:_set_screen_column(e) |
|||
endfor |
|||
call sort(self._cachedMessages[b][l], 's:_compare_error_items_by_columns') |
|||
endif |
|||
endfor |
|||
endfor |
|||
endif |
|||
|
|||
for b in keys(self._cachedMessages) |
|||
for l in keys(self._cachedMessages[b]) |
|||
call s:_remove_shadowed_items(self._cachedMessages[b][l]) |
|||
endfor |
|||
endfor |
|||
endif |
|||
endif |
|||
|
|||
return get(self._cachedMessages, a:buf, {}) |
|||
endfunction " }}}2 |
|||
|
|||
"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 string comparisons are done with ==? |
|||
function! g:SyntasticLoclist.filter(filters) abort " {{{2 |
|||
let conditions = values(map(copy(a:filters), 's:_translate(v:key, v:val)')) |
|||
let filter = len(conditions) == 1 ? |
|||
\ conditions[0] : join(map(conditions, '"(" . v:val . ")"'), ' && ') |
|||
return filter(copy(self._rawLoclist), filter) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticLoclist.setloclist(new) abort " {{{2 |
|||
if !exists('w:syntastic_loclist_set') |
|||
let w:syntastic_loclist_set = [] |
|||
endif |
|||
if a:new || empty(w:syntastic_loclist_set) || w:syntastic_loclist_set != [self._owner, getbufvar(self._owner, 'changedtick')] |
|||
let replace = !a:new && g:syntastic_reuse_loc_lists && !empty(w:syntastic_loclist_set) |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'loclist: setloclist ' . (replace ? '(replace)' : '(new)')) |
|||
call setloclist(0, self.getRaw(), replace ? 'r' : ' ') |
|||
try |
|||
" Vim 7.4.2200 or later |
|||
call setloclist(0, [], 'r', { 'title': ':SyntasticCheck ' . self._name }) |
|||
catch /\m^Vim\%((\a\+)\)\=:E\%(118\|731\)/ |
|||
" do nothing |
|||
endtry |
|||
call syntastic#util#setLastTick(self._owner) |
|||
let w:syntastic_loclist_set = [self._owner, getbufvar(self._owner, 'syntastic_lasttick')] |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
"display the cached errors for this buf in the location list |
|||
function! g:SyntasticLoclist.show() abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'loclist: show') |
|||
call self.setloclist(0) |
|||
|
|||
if !&previewwindow && !self.isEmpty() |
|||
let num = winnr() |
|||
execute 'lopen ' . syntastic#util#var('loc_list_height') |
|||
if num != winnr() |
|||
execute num . 'wincmd w' |
|||
endif |
|||
|
|||
" try to find the loclist window and set w:quickfix_title |
|||
let errors = getloclist(0) |
|||
for buf in tabpagebuflist() |
|||
if buflisted(buf) && bufloaded(buf) && getbufvar(buf, '&buftype') ==# 'quickfix' |
|||
let win = bufwinnr(buf) |
|||
let title = getwinvar(win, 'quickfix_title') |
|||
|
|||
" TODO: try to make sure we actually own this window; sadly, |
|||
" errors == getloclist(0) is the only somewhat safe way to |
|||
" achieve that |
|||
if strpart(title, 0, 16) ==# ':SyntasticCheck ' || |
|||
\ ( (title ==# '' || title ==# ':setloclist()') && errors == getloclist(0) ) |
|||
call setwinvar(win, 'quickfix_title', ':SyntasticCheck ' . self._name) |
|||
call setbufvar(buf, 'syntastic_owner_buffer', self._owner) |
|||
endif |
|||
endif |
|||
endfor |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Public functions {{{1 |
|||
|
|||
function! SyntasticLoclistHide() abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'loclist: hide') |
|||
silent! lclose |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Utilities {{{1 |
|||
|
|||
function! s:_translate(key, val) abort " {{{2 |
|||
return 'get(v:val, ' . string(a:key) . ', "") ==? ' . string(a:val) |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_set_screen_column(item) abort " {{{2 |
|||
if !has_key(a:item, 'scol') |
|||
let col = get(a:item, 'col', 0) |
|||
if col != 0 && get(a:item, 'vcol', 0) == 0 |
|||
let buf = str2nr(a:item['bufnr']) |
|||
try |
|||
let line = getbufline(buf, a:item['lnum'])[0] |
|||
catch /\m^Vim\%((\a\+)\)\=:E684/ |
|||
let line = '' |
|||
endtry |
|||
let a:item['scol'] = syntastic#util#screenWidth(strpart(line, 0, col), getbufvar(buf, '&tabstop')) |
|||
else |
|||
let a:item['scol'] = col |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_remove_shadowed_items(errors) abort " {{{2 |
|||
" keep only the first message at a given column |
|||
let i = 0 |
|||
while i < len(a:errors) - 1 |
|||
let j = i + 1 |
|||
let dupes = 0 |
|||
while j < len(a:errors) && a:errors[j].scol == a:errors[i].scol |
|||
let dupes = 1 |
|||
let j += 1 |
|||
endwhile |
|||
if dupes |
|||
call remove(a:errors, i + 1, j - 1) |
|||
endif |
|||
let i += 1 |
|||
endwhile |
|||
|
|||
" merge messages with the same text |
|||
let i = 0 |
|||
while i < len(a:errors) - 1 |
|||
let j = i + 1 |
|||
let dupes = 0 |
|||
while j < len(a:errors) && a:errors[j].text == a:errors[i].text |
|||
let dupes = 1 |
|||
let j += 1 |
|||
endwhile |
|||
if dupes |
|||
call remove(a:errors, i + 1, j - 1) |
|||
endif |
|||
let i += 1 |
|||
endwhile |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_compare_error_items_by_columns(a, b) abort " {{{2 |
|||
if a:a['bufnr'] != a:b['bufnr'] |
|||
" group by file |
|||
return a:a['bufnr'] - a:b['bufnr'] |
|||
elseif a:a['lnum'] != a:b['lnum'] |
|||
" sort by line |
|||
return a:a['lnum'] - a:b['lnum'] |
|||
elseif a:a['scol'] != a:b['scol'] |
|||
" sort by screen column |
|||
return a:a['scol'] - a:b['scol'] |
|||
elseif a:a['type'] !=? a:b['type'] |
|||
" errors take precedence over warnings |
|||
return a:a['type'] ==? 'E' ? -1 : 1 |
|||
else |
|||
return 0 |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_compare_error_items_by_lines(a, b) abort " {{{2 |
|||
if a:a['bufnr'] != a:b['bufnr'] |
|||
" group by file |
|||
return a:a['bufnr'] - a:b['bufnr'] |
|||
elseif a:a['lnum'] != a:b['lnum'] |
|||
" sort by line |
|||
return a:a['lnum'] - a:b['lnum'] |
|||
elseif a:a['type'] !=? a:b['type'] |
|||
" errors take precedence over warnings |
|||
return a:a['type'] ==? 'E' ? -1 : 1 |
|||
else |
|||
" sort by screen column |
|||
return a:a['scol'] - a:b['scol'] |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,118 @@ |
|||
if exists('g:loaded_syntastic_modemap') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_modemap = 1 |
|||
|
|||
let g:SyntasticModeMap = {} |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticModeMap.Instance() abort " {{{2 |
|||
if !exists('s:SyntasticModeMapInstance') |
|||
let s:SyntasticModeMapInstance = copy(self) |
|||
call s:SyntasticModeMapInstance.synch() |
|||
endif |
|||
|
|||
return s:SyntasticModeMapInstance |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap.synch() abort " {{{2 |
|||
if exists('g:syntastic_mode_map') |
|||
let self._mode = get(g:syntastic_mode_map, 'mode', 'active') |
|||
let self._activeFiletypes = copy(get(g:syntastic_mode_map, 'active_filetypes', [])) |
|||
let self._passiveFiletypes = copy(get(g:syntastic_mode_map, 'passive_filetypes', [])) |
|||
else |
|||
let self._mode = 'active' |
|||
let self._activeFiletypes = [] |
|||
let self._passiveFiletypes = [] |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap.allowsAutoChecking(filetype) abort " {{{2 |
|||
let registry = g:SyntasticRegistry.Instance() |
|||
let fts = registry.resolveFiletypes(a:filetype) |
|||
|
|||
if self.isPassive() |
|||
return self._isOneFiletypeActive(fts) |
|||
else |
|||
return self._noFiletypesArePassive(fts) |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap.doAutoChecking(buf) abort " {{{2 |
|||
let local_mode = getbufvar(a:buf, 'syntastic_mode') |
|||
if local_mode ==# 'active' || local_mode ==# 'passive' |
|||
return local_mode ==# 'active' |
|||
endif |
|||
|
|||
return self.allowsAutoChecking(getbufvar(a:buf, '&filetype')) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap.isPassive() abort " {{{2 |
|||
return self._mode ==# 'passive' |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap.toggleMode() abort " {{{2 |
|||
call self.synch() |
|||
|
|||
if self._mode ==# 'active' |
|||
let self._mode = 'passive' |
|||
else |
|||
let self._mode = 'active' |
|||
endif |
|||
|
|||
"XXX Changing a global variable. Tsk, tsk... |
|||
if !exists('g:syntastic_mode_map') |
|||
let g:syntastic_mode_map = {} |
|||
endif |
|||
let g:syntastic_mode_map['mode'] = self._mode |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap.echoMode() abort " {{{2 |
|||
echo 'Syntastic: ' . self._mode . ' mode enabled' |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap.modeInfo(filetypes) abort " {{{2 |
|||
echomsg 'Syntastic version: ' . g:syntastic_version |
|||
let type = len(a:filetypes) ? a:filetypes[0] : &filetype |
|||
echomsg 'Info for filetype: ' . type |
|||
|
|||
call self.synch() |
|||
echomsg 'Global mode: ' . self._mode |
|||
if self._mode ==# 'active' |
|||
if len(self._passiveFiletypes) |
|||
let plural = len(self._passiveFiletypes) != 1 ? 's' : '' |
|||
echomsg 'Passive filetype' . plural . ': ' . join(sort(copy(self._passiveFiletypes))) |
|||
endif |
|||
else |
|||
if len(self._activeFiletypes) |
|||
let plural = len(self._activeFiletypes) != 1 ? 's' : '' |
|||
echomsg 'Active filetype' . plural . ': ' . join(sort(copy(self._activeFiletypes))) |
|||
endif |
|||
endif |
|||
echomsg 'Filetype ' . type . ' is ' . (self.allowsAutoChecking(type) ? 'active' : 'passive') |
|||
|
|||
if !len(a:filetypes) |
|||
if exists('b:syntastic_mode') && (b:syntastic_mode ==# 'active' || b:syntastic_mode ==# 'passive') |
|||
echomsg 'Local mode: ' . b:syntastic_mode |
|||
endif |
|||
|
|||
echomsg 'The current file will ' . (self.doAutoChecking(bufnr('')) ? '' : 'not ') . 'be checked automatically' |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private methods {{{1 |
|||
|
|||
function! g:SyntasticModeMap._isOneFiletypeActive(filetypes) abort " {{{2 |
|||
return !empty(filter(copy(a:filetypes), 'index(self._activeFiletypes, v:val) != -1')) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticModeMap._noFiletypesArePassive(filetypes) abort " {{{2 |
|||
return empty(filter(copy(a:filetypes), 'index(self._passiveFiletypes, v:val) != -1')) |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,86 @@ |
|||
if exists('g:loaded_syntastic_notifiers') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_notifiers = 1 |
|||
|
|||
let g:SyntasticNotifiers = {} |
|||
|
|||
let s:_NOTIFIER_TYPES = ['signs', 'balloons', 'highlighting', 'cursor', 'autoloclist'] |
|||
lockvar! s:_NOTIFIER_TYPES |
|||
|
|||
let s:_PERSISTENT_NOTIFIERS = ['signs', 'balloons'] |
|||
lockvar! s:_PERSISTENT_NOTIFIERS |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticNotifiers.Instance() abort " {{{2 |
|||
if !exists('s:SyntasticNotifiersInstance') |
|||
let s:SyntasticNotifiersInstance = copy(self) |
|||
call s:SyntasticNotifiersInstance._initNotifiers() |
|||
endif |
|||
|
|||
return s:SyntasticNotifiersInstance |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticNotifiers.refresh(loclist) abort " {{{2 |
|||
if !syntastic#util#bufIsActive(bufnr('')) || (!a:loclist.isEmpty() && !a:loclist.isNewerThan([])) |
|||
" loclist not fully constructed yet |
|||
return |
|||
endif |
|||
|
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'notifiers: refresh') |
|||
for type in self._enabled_types |
|||
let class = substitute(type, '\m.*', 'Syntastic\u&Notifier', '') |
|||
if !has_key(g:{class}, 'enabled') || self._notifier[type].enabled() |
|||
if index(s:_PERSISTENT_NOTIFIERS, type) > -1 |
|||
" refresh only if loclist has changed since last call |
|||
if !exists('b:syntastic_private_' . type . '_stamp') |
|||
let b:syntastic_private_{type}_stamp = [] |
|||
endif |
|||
if a:loclist.isNewerThan(b:syntastic_private_{type}_stamp) || a:loclist.isEmpty() |
|||
call self._notifier[type].refresh(a:loclist) |
|||
let b:syntastic_private_{type}_stamp = syntastic#util#stamp() |
|||
endif |
|||
else |
|||
call self._notifier[type].refresh(a:loclist) |
|||
endif |
|||
endif |
|||
endfor |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticNotifiers.reset(loclist) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'notifiers: reset') |
|||
for type in self._enabled_types |
|||
let class = substitute(type, '\m.*', 'Syntastic\u&Notifier', '') |
|||
|
|||
" reset notifiers regardless if they are enabled or not, since |
|||
" the user might have disabled them since the last refresh(); |
|||
" notifiers MUST be prepared to deal with reset() when disabled |
|||
if has_key(g:{class}, 'reset') |
|||
call self._notifier[type].reset(a:loclist) |
|||
endif |
|||
|
|||
" also reset stamps |
|||
if index(s:_PERSISTENT_NOTIFIERS, type) > -1 |
|||
let b:syntastic_private_{type}_stamp = [] |
|||
endif |
|||
endfor |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private methods {{{1 |
|||
|
|||
function! g:SyntasticNotifiers._initNotifiers() abort " {{{2 |
|||
let self._notifier = {} |
|||
for type in s:_NOTIFIER_TYPES |
|||
let class = substitute(type, '\m.*', 'Syntastic\u&Notifier', '') |
|||
let self._notifier[type] = g:{class}.New() |
|||
endfor |
|||
|
|||
let self._enabled_types = copy(s:_NOTIFIER_TYPES) |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,445 @@ |
|||
if exists('g:loaded_syntastic_registry') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_registry = 1 |
|||
|
|||
" Initialisation {{{1 |
|||
|
|||
let s:_DEFAULT_CHECKERS = { |
|||
\ 'actionscript': ['mxmlc'], |
|||
\ 'ada': ['gcc'], |
|||
\ 'ansible': ['ansible_lint'], |
|||
\ 'apiblueprint': ['drafter'], |
|||
\ 'applescript': ['osacompile'], |
|||
\ 'asciidoc': ['asciidoc'], |
|||
\ 'asl': ['iasl'], |
|||
\ 'asm': ['gcc'], |
|||
\ 'bro': ['bro'], |
|||
\ 'bemhtml': ['bemhtmllint'], |
|||
\ 'c': ['gcc'], |
|||
\ 'cabal': ['cabal'], |
|||
\ 'chef': ['foodcritic'], |
|||
\ 'cmake': ['cmakelint'], |
|||
\ 'co': ['coco'], |
|||
\ 'cobol': ['cobc'], |
|||
\ 'coffee': ['coffee', 'coffeelint'], |
|||
\ 'coq': ['coqtop'], |
|||
\ 'cpp': ['gcc'], |
|||
\ 'cs': ['mcs'], |
|||
\ 'css': ['csslint'], |
|||
\ 'cucumber': ['cucumber'], |
|||
\ 'cuda': ['nvcc'], |
|||
\ 'd': ['dmd'], |
|||
\ 'dart': ['dartanalyzer'], |
|||
\ 'docbk': ['xmllint'], |
|||
\ 'dockerfile': ['dockerfile_lint'], |
|||
\ 'dustjs': ['swiffer'], |
|||
\ 'elixir': [], |
|||
\ 'erlang': ['escript'], |
|||
\ 'eruby': ['ruby'], |
|||
\ 'fortran': ['gfortran'], |
|||
\ 'glsl': ['cgc'], |
|||
\ 'go': [], |
|||
\ 'haml': ['haml'], |
|||
\ 'handlebars': ['handlebars'], |
|||
\ 'haskell': ['hdevtools', 'hlint'], |
|||
\ 'haxe': ['haxe'], |
|||
\ 'help': [], |
|||
\ 'hss': ['hss'], |
|||
\ 'html': ['tidy'], |
|||
\ 'jade': ['jade_lint'], |
|||
\ 'java': ['javac'], |
|||
\ 'javascript': ['jshint', 'jslint'], |
|||
\ 'json': ['jsonlint', 'jsonval'], |
|||
\ 'julia': [], |
|||
\ 'less': ['lessc'], |
|||
\ 'lex': ['flex'], |
|||
\ 'limbo': ['limbo'], |
|||
\ 'lisp': ['clisp'], |
|||
\ 'llvm': ['llvm'], |
|||
\ 'lua': ['luac'], |
|||
\ 'markdown': ['mdl'], |
|||
\ 'matlab': ['mlint'], |
|||
\ 'mercury': ['mmc'], |
|||
\ 'nasm': ['nasm'], |
|||
\ 'nix': ['nix'], |
|||
\ 'nroff': ['mandoc'], |
|||
\ 'objc': ['gcc'], |
|||
\ 'objcpp': ['gcc'], |
|||
\ 'ocaml': ['camlp4o'], |
|||
\ 'perl': ['perlcritic'], |
|||
\ 'perl6': [], |
|||
\ 'php': ['php', 'phpcs', 'phpmd'], |
|||
\ 'po': ['msgfmt'], |
|||
\ 'pod': ['podchecker'], |
|||
\ 'puppet': ['puppet', 'puppetlint'], |
|||
\ 'pug': ['pug_lint'], |
|||
\ 'python': ['python', 'flake8', 'pylint'], |
|||
\ 'qml': ['qmllint'], |
|||
\ 'r': [], |
|||
\ 'rmd': [], |
|||
\ 'racket': ['racket'], |
|||
\ 'rnc': ['rnv'], |
|||
\ 'rst': ['rst2pseudoxml'], |
|||
\ 'ruby': ['mri'], |
|||
\ 'sass': ['sass'], |
|||
\ 'scala': ['fsc', 'scalac'], |
|||
\ 'scss': ['sass', 'scss_lint'], |
|||
\ 'sh': ['sh', 'shellcheck'], |
|||
\ 'slim': ['slimrb'], |
|||
\ 'sml': ['smlnj'], |
|||
\ 'spec': ['rpmlint'], |
|||
\ 'solidity': ['solc'], |
|||
\ 'sql': ['sqlint'], |
|||
\ 'stylus': ['stylint'], |
|||
\ 'svg': [], |
|||
\ 'tcl': ['nagelfar'], |
|||
\ 'tex': ['lacheck', 'chktex'], |
|||
\ 'texinfo': ['makeinfo'], |
|||
\ 'text': [], |
|||
\ 'trig': ['rapper'], |
|||
\ 'turtle': ['rapper'], |
|||
\ 'twig': ['twiglint'], |
|||
\ 'typescript': [], |
|||
\ 'vala': ['valac'], |
|||
\ 'verilog': ['verilator'], |
|||
\ 'vhdl': ['ghdl'], |
|||
\ 'vim': ['vimlint'], |
|||
\ 'vue': ['pug_lint_vue', 'eslint'], |
|||
\ 'xhtml': ['tidy'], |
|||
\ 'xml': ['xmllint'], |
|||
\ 'xslt': ['xmllint'], |
|||
\ 'xquery': ['basex'], |
|||
\ 'yacc': ['bison'], |
|||
\ 'yaml': ['jsyaml'], |
|||
\ 'yang': ['pyang'], |
|||
\ 'yara': ['yarac'], |
|||
\ 'z80': ['z80syntaxchecker'], |
|||
\ 'zpt': ['zptlint'], |
|||
\ 'zsh': ['zsh'], |
|||
\ } |
|||
lockvar! s:_DEFAULT_CHECKERS |
|||
|
|||
let s:_DEFAULT_FILETYPE_MAP = { |
|||
\ 'gentoo-metadata': 'xml', |
|||
\ 'groff': 'nroff', |
|||
\ 'lhaskell': 'haskell', |
|||
\ 'litcoffee': 'coffee', |
|||
\ 'mail': 'text', |
|||
\ 'mkd': 'markdown', |
|||
\ 'pe-puppet': 'puppet', |
|||
\ 'sgml': 'docbk', |
|||
\ 'sgmllnx': 'docbk', |
|||
\ } |
|||
lockvar! s:_DEFAULT_FILETYPE_MAP |
|||
|
|||
let s:_ECLIM_TYPES = [ |
|||
\ 'c', |
|||
\ 'cpp', |
|||
\ 'html', |
|||
\ 'java', |
|||
\ 'php', |
|||
\ 'python', |
|||
\ 'ruby', |
|||
\ ] |
|||
lockvar! s:_ECLIM_TYPES |
|||
|
|||
let s:_YCM_TYPES = [ |
|||
\ 'c', |
|||
\ 'cpp', |
|||
\ 'objc', |
|||
\ 'objcpp', |
|||
\ ] |
|||
lockvar! s:_YCM_TYPES |
|||
|
|||
let g:SyntasticRegistry = {} |
|||
|
|||
" }}}1 |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
" Note: Handling of filetype aliases: all public methods take aliases as |
|||
" parameters, all private methods take normalized filetypes. Public methods |
|||
" are thus supposed to normalize filetypes before calling private methods. |
|||
|
|||
function! g:SyntasticRegistry.Instance() abort " {{{2 |
|||
if !exists('s:SyntasticRegistryInstance') |
|||
let s:SyntasticRegistryInstance = copy(self) |
|||
let s:SyntasticRegistryInstance._checkerMap = {} |
|||
endif |
|||
|
|||
return s:SyntasticRegistryInstance |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticRegistry.CreateAndRegisterChecker(args) abort " {{{2 |
|||
let registry = g:SyntasticRegistry.Instance() |
|||
|
|||
if has_key(a:args, 'redirect') |
|||
let [ft, name] = split(a:args['redirect'], '/') |
|||
call registry._loadCheckersFor(ft, 1) |
|||
|
|||
let clone = get(registry._checkerMap[ft], name, {}) |
|||
if empty(clone) |
|||
throw 'Syntastic: Checker ' . a:args['redirect'] . ' redirects to unregistered checker ' . ft . '/' . name |
|||
endif |
|||
|
|||
let checker = g:SyntasticChecker.New(a:args, clone) |
|||
else |
|||
let checker = g:SyntasticChecker.New(a:args) |
|||
endif |
|||
call registry._registerChecker(checker) |
|||
endfunction " }}}2 |
|||
|
|||
" Given a list of checker names hints_list, return a map name --> checker. |
|||
" If hints_list is empty, user settings are are used instead. Checkers are |
|||
" not checked for availability (that is, the corresponding IsAvailable() are |
|||
" not run). |
|||
function! g:SyntasticRegistry.getCheckers(ftalias, hints_list) abort " {{{2 |
|||
let ftlist = self.resolveFiletypes(a:ftalias) |
|||
|
|||
let names = |
|||
\ !empty(a:hints_list) ? a:hints_list : |
|||
\ exists('b:syntastic_checkers') ? b:syntastic_checkers : [] |
|||
|
|||
let cnames = [] |
|||
if !empty(names) |
|||
for name in names |
|||
if name !~# '/' |
|||
for ft in ftlist |
|||
call add(cnames, ft . '/' . name) |
|||
endfor |
|||
else |
|||
call add(cnames, name) |
|||
endif |
|||
endfor |
|||
else |
|||
for ft in ftlist |
|||
call self._sanityCheck(ft) |
|||
let defs = |
|||
\ exists('g:syntastic_' . ft . '_checkers') ? g:syntastic_{ft}_checkers : |
|||
\ get(s:_DEFAULT_CHECKERS, ft, []) |
|||
call extend(cnames, map(copy(defs), 'stridx(v:val, "/") < 0 ? ft . "/" . v:val : v:val' )) |
|||
endfor |
|||
endif |
|||
let cnames = syntastic#util#unique(cnames) |
|||
|
|||
for ft in syntastic#util#unique(map( copy(cnames), 'v:val[: stridx(v:val, "/")-1]' )) |
|||
call self._loadCheckersFor(ft, 0) |
|||
endfor |
|||
|
|||
return self._filterCheckersByName(cnames) |
|||
endfunction " }}}2 |
|||
|
|||
" Same as getCheckers(), but keep only the available checkers. This runs the |
|||
" corresponding IsAvailable() functions for all checkers. |
|||
function! g:SyntasticRegistry.getCheckersAvailable(ftalias, hints_list) abort " {{{2 |
|||
return filter(self.getCheckers(a:ftalias, a:hints_list), 'v:val.isAvailable()') |
|||
endfunction " }}}2 |
|||
|
|||
" Same as getCheckers(), but keep only the checkers that are available and |
|||
" disabled. This runs the corresponding IsAvailable() functions for all checkers. |
|||
function! g:SyntasticRegistry.getCheckersDisabled(ftalias, hints_list) abort " {{{2 |
|||
return filter(self.getCheckers(a:ftalias, a:hints_list), 'v:val.isDisabled() && v:val.isAvailable()') |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticRegistry.getKnownFiletypes() abort " {{{2 |
|||
let types = keys(s:_DEFAULT_CHECKERS) |
|||
|
|||
call extend(types, keys(s:_DEFAULT_FILETYPE_MAP)) |
|||
|
|||
if exists('g:syntastic_filetype_map') |
|||
call extend(types, keys(g:syntastic_filetype_map)) |
|||
endif |
|||
|
|||
if exists('g:syntastic_extra_filetypes') && type(g:syntastic_extra_filetypes) == type([]) |
|||
call extend(types, g:syntastic_extra_filetypes) |
|||
endif |
|||
|
|||
return syntastic#util#unique(types) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticRegistry.getNamesOfAvailableCheckers(ftalias) abort " {{{2 |
|||
let ft = s:_normalise_filetype(a:ftalias) |
|||
call self._loadCheckersFor(ft, 0) |
|||
return keys(filter( copy(self._checkerMap[ft]), 'v:val.isAvailable()' )) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticRegistry.resolveFiletypes(ftalias) abort " {{{2 |
|||
return map(split( get(g:syntastic_filetype_map, a:ftalias, a:ftalias), '\m\.' ), 's:_normalise_filetype(v:val)') |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticRegistry.echoInfoFor(ftalias_list) abort " {{{2 |
|||
let ft_list = syntastic#util#unique(self.resolveFiletypes(empty(a:ftalias_list) ? &filetype : a:ftalias_list[0])) |
|||
if len(ft_list) != 1 |
|||
let available = [] |
|||
let active = [] |
|||
let disabled = [] |
|||
|
|||
for ft in ft_list |
|||
call extend(available, map( self.getNamesOfAvailableCheckers(ft), 'ft . "/" . v:val' )) |
|||
call extend(active, map( self.getCheckersAvailable(ft, []), 'ft . "/" . v:val.getName()' )) |
|||
call extend(disabled, map( self.getCheckersDisabled(ft, []), 'ft . "/" . v:val.getName()' )) |
|||
endfor |
|||
else |
|||
let ft = ft_list[0] |
|||
let available = self.getNamesOfAvailableCheckers(ft) |
|||
let active = map(self.getCheckersAvailable(ft, []), 'ft ==# v:val.getFiletype() ? v:val.getName() : v:val.getCName()') |
|||
let disabled = map(self.getCheckersDisabled(ft, []), 'ft ==# v:val.getFiletype() ? v:val.getName() : v:val.getCName()') |
|||
endif |
|||
|
|||
let cnt = len(available) |
|||
let plural = cnt != 1 ? 's' : '' |
|||
let cklist = cnt ? join(sort(available)) : '-' |
|||
echomsg 'Available checker' . plural . ': ' . cklist |
|||
|
|||
let cnt = len(active) |
|||
let plural = cnt != 1 ? 's' : '' |
|||
let cklist = cnt ? join(active) : '-' |
|||
echomsg 'Currently enabled checker' . plural . ': ' . cklist |
|||
|
|||
let cnt = len(disabled) |
|||
let plural = cnt != 1 ? 's' : '' |
|||
if len(disabled) |
|||
let cklist = join(sort(disabled, 's:_compare_checker_names')) |
|||
echomsg 'Checker' . plural . ' disabled for security reasons: ' . cklist |
|||
endif |
|||
|
|||
" Eclim feels entitled to mess with syntastic's variables {{{3 |
|||
if exists(':EclimValidate') && get(g:, 'EclimFileTypeValidate', 1) |
|||
let disabled = filter(copy(ft_list), 's:_disabled_by_eclim(v:val)') |
|||
let cnt = len(disabled) |
|||
if cnt |
|||
let plural = cnt != 1 ? 's' : '' |
|||
let cklist = join(disabled, ', ') |
|||
echomsg 'Checkers for filetype' . plural . ' ' . cklist . ' possibly disabled by Eclim' |
|||
endif |
|||
endif |
|||
" }}}3 |
|||
|
|||
" So does YouCompleteMe {{{3 |
|||
if exists('g:loaded_youcompleteme') && get(g:, 'ycm_show_diagnostics_ui', get(g:, 'ycm_register_as_syntastic_checker', 1)) |
|||
let disabled = filter(copy(ft_list), 's:_disabled_by_ycm(v:val)') |
|||
let cnt = len(disabled) |
|||
if cnt |
|||
let plural = cnt != 1 ? 's' : '' |
|||
let cklist = join(disabled, ', ') |
|||
echomsg 'Checkers for filetype' . plural . ' ' . cklist . ' possibly disabled by YouCompleteMe' |
|||
endif |
|||
endif |
|||
" }}}3 |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private methods {{{1 |
|||
|
|||
function! g:SyntasticRegistry._registerChecker(checker) abort " {{{2 |
|||
let ft = a:checker.getFiletype() |
|||
if !has_key(self._checkerMap, ft) |
|||
let self._checkerMap[ft] = {} |
|||
endif |
|||
|
|||
let name = a:checker.getName() |
|||
if has_key(self._checkerMap[ft], name) |
|||
throw 'Syntastic: Duplicate syntax checker name: ' . ft . '/' . name |
|||
endif |
|||
|
|||
let self._checkerMap[ft][name] = a:checker |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticRegistry._findChecker(cname) abort " {{{2 |
|||
let sep_idx = stridx(a:cname, '/') |
|||
if sep_idx > 0 |
|||
let ft = a:cname[: sep_idx-1] |
|||
let name = a:cname[sep_idx+1 :] |
|||
else |
|||
let ft = &filetype |
|||
let name = a:cname |
|||
endif |
|||
return get(self._checkerMap[ft], name, {}) |
|||
endfunction "}}}2 |
|||
|
|||
function! g:SyntasticRegistry._filterCheckersByName(cnames) abort " {{{2 |
|||
return filter( map(copy(a:cnames), 'self._findChecker(v:val)'), '!empty(v:val)' ) |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticRegistry._loadCheckersFor(filetype, force) abort " {{{2 |
|||
if !a:force && has_key(self._checkerMap, a:filetype) |
|||
return |
|||
endif |
|||
|
|||
execute 'runtime! syntax_checkers/' . a:filetype . '/*.vim' |
|||
|
|||
if !has_key(self._checkerMap, a:filetype) |
|||
let self._checkerMap[a:filetype] = {} |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" Check for obsolete variable g:syntastic_<filetype>_checker |
|||
function! g:SyntasticRegistry._sanityCheck(filetype) abort " {{{2 |
|||
if exists('g:syntastic_' . a:filetype . '_checkers') && |
|||
\ type(g:syntastic_{a:filetype}_checkers) != type([]) |
|||
|
|||
unlet! g:syntastic_{a:filetype}_checkers |
|||
call syntastic#log#error('variable g:syntastic_' . a:filetype . '_checkers has to be a list of strings') |
|||
endif |
|||
|
|||
if exists('g:syntastic_' . a:filetype . '_checker') && |
|||
\ !exists('g:syntastic_' . a:filetype . '_checkers') && |
|||
\ type(g:syntastic_{a:filetype}_checker) == type('') |
|||
|
|||
let g:syntastic_{a:filetype}_checkers = [g:syntastic_{a:filetype}_checker] |
|||
call syntastic#log#oneTimeWarn('variable g:syntastic_' . a:filetype . '_checker is deprecated') |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Utilities {{{1 |
|||
|
|||
"resolve filetype aliases, and replace - with _ otherwise we cant name |
|||
"syntax checker functions legally for filetypes like "gentoo-metadata" |
|||
function! s:_normalise_filetype(ftalias) abort " {{{2 |
|||
let ft = get(s:_DEFAULT_FILETYPE_MAP, a:ftalias, a:ftalias) |
|||
let ft = get(g:syntastic_filetype_map, ft, ft) |
|||
let ft = substitute(ft, '\m-', '_', 'g') |
|||
return ft |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_disabled_by_eclim(filetype) abort " {{{2 |
|||
if index(s:_ECLIM_TYPES, a:filetype) >= 0 |
|||
let lang = toupper(a:filetype[0]) . a:filetype[1:] |
|||
let ft = a:filetype !=# 'cpp' ? lang : 'C' |
|||
return get(g:, 'Eclim' . lang . 'Validate', 1) && !get(g:, 'Eclim' . ft . 'SyntasticEnabled', 0) |
|||
endif |
|||
|
|||
return 0 |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_disabled_by_ycm(filetype) abort " {{{2 |
|||
return index(s:_YCM_TYPES, a:filetype) >= 0 |
|||
endfunction " }}}2 |
|||
|
|||
function! s:_compare_checker_names(a, b) abort " {{{2 |
|||
if a:a ==# a:b |
|||
return 0 |
|||
endif |
|||
|
|||
if stridx(a:a, '/') < 0 |
|||
if stridx(a:b, '/') < 0 |
|||
return a:a < a:b ? -1 : 1 |
|||
else |
|||
return -1 |
|||
endif |
|||
else |
|||
if stridx(a:b, '/') < 0 |
|||
return 1 |
|||
else |
|||
return a:a < a:b ? -1 : 1 |
|||
endif |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,138 @@ |
|||
if exists('g:loaded_syntastic_notifier_signs') || !exists('g:loaded_syntastic_plugin') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_notifier_signs = 1 |
|||
|
|||
" Initialisation {{{1 |
|||
|
|||
" start counting sign ids at 5000, start here to hopefully avoid conflicting |
|||
" with any other code that places signs (not sure if this precaution is |
|||
" actually needed) |
|||
let s:first_sign_id = 5000 |
|||
let s:next_sign_id = s:first_sign_id |
|||
|
|||
let g:SyntasticSignsNotifier = {} |
|||
|
|||
let s:setup_done = 0 |
|||
|
|||
" }}}1 |
|||
|
|||
" Public methods {{{1 |
|||
|
|||
function! g:SyntasticSignsNotifier.New() abort " {{{2 |
|||
let newObj = copy(self) |
|||
return newObj |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticSignsNotifier.enabled() abort " {{{2 |
|||
return has('signs') && syntastic#util#var('enable_signs') |
|||
endfunction " }}}2 |
|||
|
|||
function! g:SyntasticSignsNotifier.refresh(loclist) abort " {{{2 |
|||
call syntastic#log#debug(g:_SYNTASTIC_DEBUG_NOTIFICATIONS, 'signs: refresh') |
|||
|
|||
let old_signs = copy(self._bufSignIds()) |
|||
if self.enabled() |
|||
if !s:setup_done |
|||
call self._setup() |
|||
let s:setup_done = 1 |
|||
lockvar s:setup_done |
|||
endif |
|||
|
|||
call self._signErrors(a:loclist) |
|||
endif |
|||
call self._removeSigns(old_signs) |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" Private methods {{{1 |
|||
|
|||
" One time setup: define our own sign types and highlighting |
|||
function! g:SyntasticSignsNotifier._setup() abort " {{{2 |
|||
if has('signs') |
|||
if !hlexists('SyntasticErrorSign') |
|||
highlight link SyntasticErrorSign error |
|||
endif |
|||
if !hlexists('SyntasticWarningSign') |
|||
highlight link SyntasticWarningSign todo |
|||
endif |
|||
if !hlexists('SyntasticStyleErrorSign') |
|||
highlight link SyntasticStyleErrorSign SyntasticErrorSign |
|||
endif |
|||
if !hlexists('SyntasticStyleWarningSign') |
|||
highlight link SyntasticStyleWarningSign SyntasticWarningSign |
|||
endif |
|||
if !hlexists('SyntasticStyleErrorLine') |
|||
highlight link SyntasticStyleErrorLine SyntasticErrorLine |
|||
endif |
|||
if !hlexists('SyntasticStyleWarningLine') |
|||
highlight link SyntasticStyleWarningLine SyntasticWarningLine |
|||
endif |
|||
|
|||
" define the signs used to display syntax and style errors/warns |
|||
execute 'sign define SyntasticError text=' . g:syntastic_error_symbol . |
|||
\ ' texthl=SyntasticErrorSign linehl=SyntasticErrorLine' |
|||
execute 'sign define SyntasticWarning text=' . g:syntastic_warning_symbol . |
|||
\ ' texthl=SyntasticWarningSign linehl=SyntasticWarningLine' |
|||
execute 'sign define SyntasticStyleError text=' . g:syntastic_style_error_symbol . |
|||
\ ' texthl=SyntasticStyleErrorSign linehl=SyntasticStyleErrorLine' |
|||
execute 'sign define SyntasticStyleWarning text=' . g:syntastic_style_warning_symbol . |
|||
\ ' texthl=SyntasticStyleWarningSign linehl=SyntasticStyleWarningLine' |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" Place signs by all syntax errors in the buffer |
|||
function! g:SyntasticSignsNotifier._signErrors(loclist) abort " {{{2 |
|||
let loclist = a:loclist |
|||
if !loclist.isEmpty() |
|||
|
|||
let buf = bufnr('') |
|||
if !bufloaded(buf) |
|||
" signs can be placed only in loaded buffers |
|||
return |
|||
endif |
|||
|
|||
" errors come first, so that they are not masked by warnings |
|||
let issues = copy(loclist.errors()) |
|||
call extend(issues, loclist.warnings()) |
|||
call filter(issues, 'v:val["bufnr"] == buf') |
|||
let seen = {} |
|||
|
|||
for i in issues |
|||
if i['lnum'] > 0 && !has_key(seen, i['lnum']) |
|||
let seen[i['lnum']] = 1 |
|||
|
|||
let sign_severity = i['type'] ==? 'W' ? 'Warning' : 'Error' |
|||
let sign_subtype = get(i, 'subtype', '') |
|||
let sign_type = 'Syntastic' . sign_subtype . sign_severity |
|||
|
|||
execute 'sign place ' . s:next_sign_id . ' line=' . i['lnum'] . ' name=' . sign_type . ' buffer=' . i['bufnr'] |
|||
call add(self._bufSignIds(), s:next_sign_id) |
|||
let s:next_sign_id += 1 |
|||
endif |
|||
endfor |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" Remove the signs with the given ids from this buffer |
|||
function! g:SyntasticSignsNotifier._removeSigns(ids) abort " {{{2 |
|||
if has('signs') |
|||
for s in reverse(copy(a:ids)) |
|||
execute 'sign unplace ' . s |
|||
call remove(self._bufSignIds(), index(self._bufSignIds(), s)) |
|||
endfor |
|||
endif |
|||
endfunction " }}}2 |
|||
|
|||
" Get all the ids of the SyntaxError signs in the buffer |
|||
function! g:SyntasticSignsNotifier._bufSignIds() abort " {{{2 |
|||
if !exists('b:syntastic_private_sign_ids') |
|||
let b:syntastic_private_sign_ids = [] |
|||
endif |
|||
return b:syntastic_private_sign_ids |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,67 @@ |
|||
"============================================================================ |
|||
"File: mxmlc.vim |
|||
"Description: ActionScript syntax checker - using mxmlc |
|||
"Maintainer: Andy Earnshaw <andyearnshaw@gmail.com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_actionscript_mxmlc_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_actionscript_mxmlc_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_actionscript_mxmlc_GetHighlightRegex(item) |
|||
let term = '' |
|||
|
|||
if match(a:item['text'], '\mvariable ''') > -1 |
|||
let term = matchstr(a:item['text'], '\m''\zs[^'']\+\ze''') |
|||
|
|||
elseif match(a:item['text'], 'expected a definition keyword') > -1 |
|||
let term = matchstr(a:item['text'], '\mnot \zs[^.]\+\ze\.') |
|||
|
|||
elseif match(a:item['text'], '\mundefined \%(property\|method\)') > -1 |
|||
let term = matchstr(a:item['text'], '\mundefined \%(property\|method\) \zs[^. ]\+\ze') |
|||
|
|||
elseif match(a:item['text'], 'could not be found') > -1 |
|||
let term = matchstr(a:item['text'], '\m \zs\S\+\ze could not be found') |
|||
|
|||
elseif match(a:item['text'], 'Type was not found') > -1 |
|||
let term = matchstr(a:item['text'], '\m: \zs[^.]\+\zs\.') |
|||
|
|||
endif |
|||
|
|||
return term !=# '' ? '\V\<' . escape(term, '\') . '\>' : '' |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_actionscript_mxmlc_GetLocList() dict |
|||
call syntastic#log#deprecationWarn('actionscript_mxmlc_conf', 'actionscript_mxmlc_args', |
|||
\ "'-load-config+=' . syntastic#util#shexpand(OLD_VAR)") |
|||
|
|||
let makeprg = self.makeprgBuild({ 'args_after': '-output=' . syntastic#util#DevNull() }) |
|||
|
|||
let errorformat = |
|||
\ '%f(%l): col: %c %trror: %m,' . |
|||
\ '%f(%l): col: %c %tarning: %m,' . |
|||
\ '%f: %trror: %m,' . |
|||
\ '%-G%.%#' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'actionscript', |
|||
\ 'name': 'mxmlc'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,47 @@ |
|||
"============================================================================ |
|||
"File: ada.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: Alfredo Di Napoli <alfredo.dinapoli@gmail.com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_ada_gcc_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_ada_gcc_checker = 1 |
|||
|
|||
if !exists('g:syntastic_ada_compiler_options') |
|||
let g:syntastic_ada_compiler_options = '' |
|||
endif |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_ada_gcc_IsAvailable() dict |
|||
if !exists('g:syntastic_ada_compiler') |
|||
let g:syntastic_ada_compiler = self.getExec() |
|||
endif |
|||
return executable(expand(g:syntastic_ada_compiler, 1)) |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_ada_gcc_GetLocList() dict |
|||
return syntastic#c#GetLocList('ada', 'gcc', { |
|||
\ 'errorformat': |
|||
\ '%-G%f:%s:,' . |
|||
\ '%f:%l:%c: %m,' . |
|||
\ '%f:%l: %m', |
|||
\ 'main_flags': '-c -x ada -gnats -gnatef', |
|||
\ 'header_flags': '-S -x ada -gnats -gnatef', |
|||
\ 'header_names': '\.ads$' }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'ada', |
|||
\ 'name': 'gcc' }) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,55 @@ |
|||
"============================================================================ |
|||
"File: ansible_lint.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: Erik Zaadi <erik.zaadi at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_ansible_ansible_lint_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_ansible_ansible_lint_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_ansible_ansible_lint_IsAvailable() dict |
|||
if !executable(self.getExec()) |
|||
return 0 |
|||
endif |
|||
return syntastic#util#versionIsAtLeast(self.getVersion(), [2, 0, 4]) |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_ansible_ansible_lint_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ 'args_after': '-p' }) |
|||
|
|||
let errorformat = |
|||
\ '%f:%l: [E%n] %m,' . |
|||
\ '%f:%l: [EANSIBLE%n] %m,' . |
|||
\ '%f:%l: [ANSIBLE%n] %m' |
|||
|
|||
let env = syntastic#util#isRunningWindows() ? {} : { 'TERM': 'dumb' } |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'env': env, |
|||
\ 'defaults': {'type': 'E'}, |
|||
\ 'subtype': 'Style', |
|||
\ 'returns': [0, 2] }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'ansible', |
|||
\ 'name': 'ansible_lint', |
|||
\ 'exec': 'ansible-lint'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,66 @@ |
|||
"============================================================================ |
|||
"File: drafter.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: LCD 47 <lcd047 at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_apiblueprint_drafter_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_apiblueprint_drafter_checker = 1 |
|||
|
|||
if !exists('g:syntastic_apiblueprint_drafter_sort') |
|||
let g:syntastic_apiblueprint_drafter_sort = 1 |
|||
endif |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_apiblueprint_drafter_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ 'post_args': '-u -l' }) |
|||
|
|||
let errorformat = |
|||
\ '%trror: (%n) %m,' . |
|||
\ '%tarning: (%n) %m,' . |
|||
\ '%-G%.%#' |
|||
|
|||
let loclist = SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'defaults': {'bufnr': bufnr('')}, |
|||
\ 'returns': [0, 2, 3, 4] }) |
|||
|
|||
for e in loclist |
|||
let matches = matchlist(e['text'], '\v^(.+); line (\d+), column (\d+) - line (\d+), column (\d+)$') |
|||
if len(matches) > 5 |
|||
let e['lnum'] = str2nr(matches[2]) |
|||
let e['col'] = str2nr(matches[3]) |
|||
let e['vcol'] = 0 |
|||
|
|||
if matches[2] == matches[4] |
|||
let e['hl'] = '\%>' . (e['col'] - 1) . 'c\%<' . matches[5] . 'c' |
|||
endif |
|||
|
|||
let e['text'] = matches[1] |
|||
else |
|||
let e['valid'] = 0 |
|||
endif |
|||
endfor |
|||
|
|||
return loclist |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'apiblueprint', |
|||
\ 'name': 'drafter'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,49 @@ |
|||
"============================================================================== |
|||
" FileName: applescript.vim |
|||
" Desc: Syntax checking plugin for syntastic |
|||
" Author: Zhao Cai |
|||
" Email: caizhaoff@gmail.com |
|||
" Version: 0.2.1 |
|||
" Date Created: Thu 09 Sep 2011 10:30:09 AM EST |
|||
" Last Modified: Fri 09 Dec 2011 01:10:24 PM EST |
|||
" |
|||
" History: 0.1.0 - working, but it will run the script everytime to check |
|||
" syntax. Should use osacompile but strangely it does not give |
|||
" errors. |
|||
" |
|||
" 0.2.0 - switch to osacompile, it gives less errors compared |
|||
" with osascript. |
|||
" |
|||
" 0.2.1 - remove g:syntastic_applescript_tempfile. use |
|||
" tempname() instead. |
|||
" |
|||
" License: This program is free software. It comes without any |
|||
" warranty, to the extent permitted by applicable law. You can |
|||
" redistribute it and/or modify it under the terms of the Do What The |
|||
" Fuck You Want To Public License, Version 2, as published by Sam |
|||
" Hocevar. See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_applescript_osacompile_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_applescript_osacompile_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_applescript_osacompile_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ 'args_after': '-o ' . tempname() . '.scpt' }) |
|||
let errorformat = '%f:%l:%m' |
|||
return SyntasticMake({ 'makeprg': makeprg, 'errorformat': errorformat }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'applescript', |
|||
\ 'name': 'osacompile' }) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,47 @@ |
|||
"============================================================================ |
|||
"File: asciidoc.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: LCD 47 <lcd047 at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_asciidoc_asciidoc_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_asciidoc_asciidoc_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_asciidoc_asciidoc_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ 'args_after': syntastic#c#NullOutput() }) |
|||
|
|||
let errorformat = |
|||
\ '%E%\w%\+: %tRROR: %f: line %l: %m,' . |
|||
\ '%E%\w%\+: %tRROR: %f: %m,' . |
|||
\ '%E%\w%\+: FAILED: %f: line %l: %m,' . |
|||
\ '%E%\w%\+: FAILED: %f: %m,' . |
|||
\ '%W%\w%\+: %tARNING: %f: line %l: %m,' . |
|||
\ '%W%\w%\+: %tARNING: %f: %m,' . |
|||
\ '%W%\w%\+: DEPRECATED: %f: line %l: %m,' . |
|||
\ '%W%\w%\+: DEPRECATED: %f: %m' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'returns': [0, 1] }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'asciidoc', |
|||
\ 'name': 'asciidoc'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,23 @@ |
|||
"============================================================================ |
|||
"File: proselint.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: LCD 47 <lcd047 at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_asciidoc_proselint_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_asciidoc_proselint_checker = 1 |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'asciidoc', |
|||
\ 'name': 'proselint', |
|||
\ 'redirect': 'text/proselint'}) |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,59 @@ |
|||
"============================================================================ |
|||
"File: iasl.vim |
|||
"Description: Syntax checking plugin for syntastic using iasl |
|||
"Maintainer: Peter Wu <peter@lekensteyn.nl> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_asl_iasl_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_asl_iasl_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_asl_iasl_GetLocList() dict |
|||
let tmpdir = syntastic#util#tmpdir() . syntastic#util#Slash() |
|||
let makeprg = self.makeprgBuild({ |
|||
\ 'args': '-vi', |
|||
\ 'args_after': ['-p', tmpdir] }) |
|||
|
|||
let errorformat = |
|||
\ '%f(%l) : %trror %n - %m,' . |
|||
\ '%f(%l) : %tarning %n - %m,' . |
|||
\ '%f(%l) : %temark %n - %m,' . |
|||
\ '%f(%l) : %tptimize %n - %m,' . |
|||
\ '%f(%l) : %m' |
|||
|
|||
let loclist = SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'returns': [0, 255] }) |
|||
|
|||
for e in loclist |
|||
if e['type'] =~? 'r' |
|||
let e['type'] = 'W' |
|||
elseif e['type'] =~? 'o' |
|||
let e['type'] = 'W' |
|||
let e['subtype'] = 'Style' |
|||
endif |
|||
endfor |
|||
|
|||
call syntastic#util#rmrf(tmpdir) |
|||
|
|||
return loclist |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'asl', |
|||
\ 'name': 'iasl'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,62 @@ |
|||
"============================================================================ |
|||
"File: gcc.vim |
|||
"Description: Syntax checking for at&t and intel assembly files with gcc |
|||
"Maintainer: Josh Rahm <joshuarahm@gmail.com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_asm_gcc_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_asm_gcc_checker = 1 |
|||
|
|||
if !exists('g:syntastic_asm_compiler_options') |
|||
let g:syntastic_asm_compiler_options = '' |
|||
endif |
|||
|
|||
if !exists('g:syntastic_asm_generic') |
|||
let g:syntastic_asm_generic = 0 |
|||
endif |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_asm_gcc_IsAvailable() dict " {{{1 |
|||
if !exists('g:syntastic_asm_compiler') |
|||
let g:syntastic_asm_compiler = self.getExec() |
|||
endif |
|||
return executable(expand(g:syntastic_asm_compiler, 1)) |
|||
endfunction " }}}1 |
|||
|
|||
function! SyntaxCheckers_asm_gcc_GetLocList() dict " {{{1 |
|||
let buf = bufnr('') |
|||
return syntastic#c#GetLocList('asm', 'gcc', { |
|||
\ 'errorformat': |
|||
\ '%-G%f:%s:,' . |
|||
\ '%f:%l:%c: %trror: %m,' . |
|||
\ '%f:%l:%c: %tarning: %m,' . |
|||
\ '%f:%l: %m', |
|||
\ 'main_flags': '-x assembler -fsyntax-only' . (g:syntastic_asm_generic ? '' : ' -masm=' . s:GetDialect(buf)) }) |
|||
endfunction " }}}1 |
|||
|
|||
" Utilities {{{1 |
|||
|
|||
function! s:GetDialect(buf) " {{{2 |
|||
return syntastic#util#bufVar(a:buf, 'asm_dialect', fnamemodify(bufname(a:buf), ':e') ==? 'asm' ? 'intel' : 'att') |
|||
endfunction " }}}2 |
|||
|
|||
" }}}1 |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'asm', |
|||
\ 'name': 'gcc' }) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,35 @@ |
|||
"============================================================================ |
|||
"File: bemhtmllint.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: Sergej Tatarincev <s.tatarincev at yandex.ua> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_bemhtml_bemhtmllint_checker') |
|||
finish |
|||
endif |
|||
|
|||
let g:loaded_syntastic_bemhtml_bemhtmllint_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function SyntaxCheckers_bemhtml_bemhtmllint_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({}) |
|||
let errorformat = '%f:%l:%c: %m' |
|||
return SyntasticMake({ 'makeprg': makeprg, 'errorformat': errorformat }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'bemhtml', |
|||
\ 'name': 'bemhtmllint', |
|||
\ 'exec': 'bemhtml-lint' }) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,58 @@ |
|||
"============================================================================ |
|||
"File: bro.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: Justin Azoff <justin.azoff@gmail.com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_bro_bro_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_bro_bro_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_bro_bro_GetHighlightRegex(item) |
|||
let term = matchstr(a:item['text'], '\m at or near "\zs[^"]\+\ze"') |
|||
return term !=# '' ? '\V\<' . escape(term, '\') . '\>' : '' |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_bro_bro_IsAvailable() dict |
|||
if !executable(self.getExec()) |
|||
return 0 |
|||
endif |
|||
|
|||
if syntastic#util#system(self.getExecEscaped() . ' --help') !~# '--parse-only' |
|||
call self.log('unknown option "--parse-only"') |
|||
return 0 |
|||
endif |
|||
|
|||
return 1 |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_bro_bro_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ 'args_before': '--parse-only' }) |
|||
|
|||
"example: error in ./foo.bro, line 3: unknown identifier banana, at or near "banana" |
|||
let errorformat = '%t:%f:%l:%m' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'preprocess': 'bro' }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'bro', |
|||
\ 'name': 'bro'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,57 @@ |
|||
"============================================================================ |
|||
"File: avrgcc.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: Karel <karelishere at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_c_avrgcc_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_avrgcc_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
let s:opt_x = { 'c': 'c', 'cpp': 'c++' } |
|||
|
|||
function! SyntaxCheckers_c_avrgcc_GetLocList() dict |
|||
let buf = bufnr('') |
|||
|
|||
let makeprg = self.makeprgBuild({ |
|||
\ 'args_before': syntastic#c#ReadConfig(syntastic#util#bufVar(buf, 'avrgcc_config_file')), |
|||
\ 'args_after': '-x ' . get(s:opt_x, self.getFiletype(), '') . ' -fsyntax-only' }) |
|||
|
|||
let errorformat = |
|||
\ '%-G%f:%s:,' . |
|||
\ '%-G%f:%l: %#error: %#(Each undeclared identifier is reported only%.%#,' . |
|||
\ '%-G%f:%l: %#error: %#for each function it appears%.%#,' . |
|||
\ '%-GIn file included%.%#,' . |
|||
\ '%-G %#from %f:%l\,,' . |
|||
\ '%f:%l:%c: %trror: %m,' . |
|||
\ '%f:%l:%c: %tarning: %m,' . |
|||
\ '%f:%l:%c: %m,' . |
|||
\ '%f:%l: %trror: %m,' . |
|||
\ '%f:%l: %tarning: %m,'. |
|||
\ '%f:%l: %m' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'postprocess': ['compressWhitespace'] }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'c', |
|||
\ 'name': 'avrgcc', |
|||
\ 'exec': 'avr-gcc'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,61 @@ |
|||
"============================================================================ |
|||
"File: checkpatch.vim |
|||
"Description: Syntax checking plugin for syntastic using checkpatch.pl |
|||
"Maintainer: Daniel Walker <dwalker at fifo99 dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_c_checkpatch_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_checkpatch_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_c_checkpatch_IsAvailable() dict |
|||
call syntastic#log#deprecationWarn('c_checker_checkpatch_location', 'c_checkpatch_exec') |
|||
|
|||
if !exists('g:syntastic_c_checkpatch_exec') && !executable(self.getExec()) |
|||
if executable('checkpatch') |
|||
let g:syntastic_c_checkpatch_exec = 'checkpatch' |
|||
elseif executable('./scripts/checkpatch.pl') |
|||
let g:syntastic_c_checkpatch_exec = fnamemodify('./scripts/checkpatch.pl', ':p') |
|||
elseif executable('./scripts/checkpatch') |
|||
let g:syntastic_c_checkpatch_exec = fnamemodify('./scripts/checkpatch', ':p') |
|||
endif |
|||
endif |
|||
|
|||
call self.log('exec =', self.getExec()) |
|||
|
|||
return executable(self.getExec()) |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_c_checkpatch_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ 'args_after': '--no-summary --no-tree --terse --file' }) |
|||
|
|||
let errorformat = |
|||
\ '%W%f:%l: CHECK: %m,' . |
|||
\ '%f:%l: %tARNING: %m,' . |
|||
\ '%f:%l: %tRROR: %m' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'returns': [0, 1], |
|||
\ 'subtype': 'Style' }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'c', |
|||
\ 'name': 'checkpatch', |
|||
\ 'exec': 'checkpatch.pl'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,62 @@ |
|||
"============================================================================ |
|||
"File: clang_check.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: Benjamin Bannier <bbannier at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_c_clang_check_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_clang_check_checker = 1 |
|||
|
|||
if !exists('g:syntastic_c_clang_check_sort') |
|||
let g:syntastic_c_clang_check_sort = 1 |
|||
endif |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_c_clang_check_GetLocList() dict |
|||
let buf = bufnr('') |
|||
|
|||
let makeprg = self.makeprgBuild({ |
|||
\ 'post_args': |
|||
\ '-- ' . |
|||
\ syntastic#c#ReadConfig(syntastic#util#bufVar(buf, 'clang_check_config_file')) . ' ' . |
|||
\ '-fshow-column ' . |
|||
\ '-fshow-source-location ' . |
|||
\ '-fno-caret-diagnostics ' . |
|||
\ '-fno-color-diagnostics ' . |
|||
\ '-fdiagnostics-format=clang' }) |
|||
|
|||
let errorformat = |
|||
\ '%E%f:%l:%c: fatal error: %m,' . |
|||
\ '%E%f:%l:%c: error: %m,' . |
|||
\ '%W%f:%l:%c: warning: %m,' . |
|||
\ '%-G%\m%\%%(LLVM ERROR:%\|No compilation database found%\)%\@!%.%#,' . |
|||
\ '%E%m' |
|||
|
|||
let env = syntastic#util#isRunningWindows() ? {} : { 'TERM': 'dumb' } |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'env': env, |
|||
\ 'defaults': {'bufnr': bufnr('')}, |
|||
\ 'returns': [0, 1] }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'c', |
|||
\ 'name': 'clang_check', |
|||
\ 'exec': 'clang-check'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,62 @@ |
|||
"============================================================================ |
|||
"File: clang_tidy.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: Benjamin Bannier <bbannier at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_c_clang_tidy_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_clang_tidy_checker = 1 |
|||
|
|||
if !exists('g:syntastic_c_clang_tidy_sort') |
|||
let g:syntastic_c_clang_tidy_sort = 1 |
|||
endif |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_c_clang_tidy_GetLocList() dict |
|||
let buf = bufnr('') |
|||
|
|||
let makeprg = self.makeprgBuild({ |
|||
\ 'post_args': |
|||
\ '-- ' . |
|||
\ syntastic#c#ReadConfig(syntastic#util#bufVar(buf, 'clang_tidy_config_file')) . ' ' . |
|||
\ '-fshow-column ' . |
|||
\ '-fshow-source-location ' . |
|||
\ '-fno-caret-diagnostics ' . |
|||
\ '-fno-color-diagnostics ' . |
|||
\ '-fdiagnostics-format=clang' }) |
|||
|
|||
let errorformat = |
|||
\ '%E%f:%l:%c: fatal error: %m,' . |
|||
\ '%E%f:%l:%c: error: %m,' . |
|||
\ '%W%f:%l:%c: warning: %m,' . |
|||
\ '%-G%\m%\%%(LLVM ERROR:%\|No compilation database found%\)%\@!%.%#,' . |
|||
\ '%E%m' |
|||
|
|||
let env = syntastic#util#isRunningWindows() ? {} : { 'TERM': 'dumb' } |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'env': env, |
|||
\ 'defaults': {'bufnr': bufnr('')}, |
|||
\ 'returns': [0, 1] }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'c', |
|||
\ 'name': 'clang_tidy', |
|||
\ 'exec': 'clang-tidy'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,60 @@ |
|||
"============================================================================ |
|||
"File: cppcheck.vim |
|||
"Description: Syntax checking plugin for syntastic using cppcheck.pl |
|||
"Maintainer: LCD 47 <lcd047 at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_c_cppcheck_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_cppcheck_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_c_cppcheck_GetLocList() dict |
|||
let buf = bufnr('') |
|||
|
|||
let makeprg = self.makeprgBuild({ |
|||
\ 'args': syntastic#c#ReadConfig(syntastic#util#bufVar(buf, 'cppcheck_config_file')), |
|||
\ 'args_after': '-q --enable=style' }) |
|||
|
|||
let errorformat = |
|||
\ '[%f:%l]: (%trror) %m,' . |
|||
\ '[%f:%l]: (%tarning) %m,' . |
|||
\ '[%f:%l]: (%ttyle) %m,' . |
|||
\ '[%f:%l]: (%terformance) %m,' . |
|||
\ '[%f:%l]: (%tortability) %m,' . |
|||
\ '[%f:%l]: (%tnformation) %m,' . |
|||
\ '[%f:%l]: (%tnconclusive) %m,' . |
|||
\ '%-G%.%#' |
|||
|
|||
let loclist = SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'preprocess': 'cppcheck', |
|||
\ 'returns': [0] }) |
|||
|
|||
for e in loclist |
|||
if e['type'] =~? '\m^[SPI]' |
|||
let e['type'] = 'w' |
|||
let e['subtype'] = 'Style' |
|||
endif |
|||
endfor |
|||
|
|||
return loclist |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'c', |
|||
\ 'name': 'cppcheck'}) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,40 @@ |
|||
"============================================================================ |
|||
"File: cppclean.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: LCD 47 <lcd047 at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_c_cppclean_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_cppclean_checker = 1 |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_c_cppclean_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({}) |
|||
|
|||
let errorformat = '%f:%l: %m' |
|||
|
|||
return SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'subtype': 'Style', |
|||
\ 'returns': [0, 1] }) |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'c', |
|||
\ 'name': 'cppclean' }) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
@ -0,0 +1,61 @@ |
|||
"============================================================================ |
|||
"File: flawfinder.vim |
|||
"Description: Syntax checking plugin for syntastic |
|||
"Maintainer: LCD 47 <lcd047 at gmail dot com> |
|||
"License: This program is free software. It comes without any warranty, |
|||
" to the extent permitted by applicable law. You can redistribute |
|||
" it and/or modify it under the terms of the Do What The Fuck You |
|||
" Want To Public License, Version 2, as published by Sam Hocevar. |
|||
" See http://sam.zoy.org/wtfpl/COPYING for more details. |
|||
" |
|||
"============================================================================ |
|||
|
|||
if exists('g:loaded_syntastic_c_flawfinder_checker') |
|||
finish |
|||
endif |
|||
let g:loaded_syntastic_c_flawfinder_checker = 1 |
|||
|
|||
if !exists('g:syntastic_c_flawfinder_sort') |
|||
let g:syntastic_c_flawfinder_sort = 1 |
|||
endif |
|||
|
|||
if !exists('g:syntastic_c_flawfinder_thres') |
|||
let g:syntastic_c_flawfinder_thres = 3 |
|||
endif |
|||
|
|||
let s:save_cpo = &cpo |
|||
set cpo&vim |
|||
|
|||
function! SyntaxCheckers_c_flawfinder_GetHighlightRegex(item) |
|||
let term = matchstr(a:item['text'], '\m^(\S\+)\s\+\zs\S\+\ze:') |
|||
return term !=# '' ? '\V\<' . escape(term, '\') . '\>' : '' |
|||
endfunction |
|||
|
|||
function! SyntaxCheckers_c_flawfinder_GetLocList() dict |
|||
let makeprg = self.makeprgBuild({ |
|||
\ 'args_after': '--columns --dataonly --singleline --quiet' }) |
|||
|
|||
let errorformat = '%f:%l:%c: [%n] %m' |
|||
|
|||
let loclist = SyntasticMake({ |
|||
\ 'makeprg': makeprg, |
|||
\ 'errorformat': errorformat, |
|||
\ 'subtype': 'Style', |
|||
\ 'returns': [0] }) |
|||
|
|||
for e in loclist |
|||
let e['type'] = e['nr'] < g:syntastic_{self.getFiletype()}_flawfinder_thres ? 'W' : 'E' |
|||
let e['nr'] = 0 |
|||
endfor |
|||
|
|||
return loclist |
|||
endfunction |
|||
|
|||
call g:SyntasticRegistry.CreateAndRegisterChecker({ |
|||
\ 'filetype': 'c', |
|||
\ 'name': 'flawfinder' }) |
|||
|
|||
let &cpo = s:save_cpo |
|||
unlet s:save_cpo |
|||
|
|||
" vim: set sw=4 sts=4 et fdm=marker: |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue