diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 025814f72..44eb13f81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,49 +1,105 @@ -# Bug reports / Github issues +# CONTRIBUTING +- - - +1\. [Bug reports / GitHub issues](#bugreps) +2\. [Submitting a patch](#patches) +3\. [General style notes](#generalstyle) +4\. [Syntax checker notes](#checkerstyle) +- - - -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 -additonal information that may help us solve the issue. + + +## 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. -* The version of syntastic you are using. +* 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`. + + + +## 2. Submitting a patch -For syntax checker bugs also state the version of the checker executable that you are using. +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. -# Submitting a patch +To submit a patch: -* Fork the repo on github -* Make a [topic branch](https://github.com/dchelimsky/rspec/wiki/Topic-Branches#using-topic-branches-when-contributing-patches) and start hacking -* Submit a pull request based off your topic branch +* 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. +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. +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. -# General style notes +Also, make sure to update the manual whenever applicable. Nobody can use +features that aren't documented. -Following 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!). -* Dont use `l:` prefixes for variables unless actually required (i.e. almost never). -* Code for maintainabiliy. We would rather a function be a couple of lines longer and have (for example) some [explaining variables](http://www.refactoring.com/catalog/introduceExplainingVariable.html) to aid readability. +## 3. General style notes -# Syntax checker style notes +Follow the coding conventions/styles used in the syntastic core: -The preferred style for error format strings is one "clause" per line. E.g. -(from the coffeelint checker): +* 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. -```viml -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%.%#' + + +## 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 diff --git a/README.markdown b/README.markdown index d46b57d4d..f425e2250 100644 --- a/README.markdown +++ b/README.markdown @@ -17,28 +17,72 @@ \_____________________________________________/ - - -Syntastic is a syntax checking plugin that 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, syntax checking plugins exist for ada, applescript, c, co, -coffee, coq, cpp, cs, css, cucumber, cuda, d, dart, docbk, elixir, erlang, eruby, fortran, -gentoo_metadata, go, haml, haskell, haxe, html, java, javascript, json, less, lisp, lua, matlab, -nasm, objc, ocaml, perl, php, puppet, python, rst, ruby, rust, sass/scss, scala, sh, slim, tcl, tex, -twig, typescript, vala, vhdl, xhtml, xml, xslt, yaml, z80, zpt, zsh - -Screenshot ----------- +- - - +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) + +- - - + + + +## 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 +errors. Note that, in practise, you will only have a subset of these methods enabled. -![Screenshot 1](https://github.com/scrooloose/syntastic/raw/master/_assets/screenshot_1.png) +![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. @@ -47,119 +91,453 @@ enabled. 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. -Installation ------------- + -Installing syntastic is easy but first you need to have the pathogen plugin installed. If you already -have pathogen working then skip Step 1 and go to Step 2. +## 2\. Installation -Step 1: Install pathogen.vim ----------------------------- + -First I'll show you how to install tpope's [pathogen.vim](https://github.com/tpope/vim-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: +### 2.1\. Requirements - mkdir -p ~/.vim/autoload ~/.vim/bundle; \ - curl -so ~/.vim/autoload/pathogen.vim \ - https://raw.github.com/tpope/vim-pathogen/master/autoload/pathogen.vim +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. -Next you *need to add this* to your ~/.vimrc: +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. - call pathogen#infect() +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. -Step 2: Install syntastic as a pathogen bundle ----------------------------------------------- +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. -You now have pathogen installed and can put syntastic into ~/.vim/bundle like this: - + - cd ~/.vim/bundle - git clone https://github.com/scrooloose/syntastic.git +### 2.2\. Installing syntastic with Pathogen -Quit vim and start it back up to reload it, then type: +If you already have [Pathogen][pathogen] working then skip [Step 1](#step1) and go to +[Step 2](#step2). - :Helptags + + +#### 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() +``` -If you get an error when you do this, then you probably didn't install pathogen right. Go back to -step 1 and make sure you did the following: + -1. Created both the ~/.vim/autoload and ~/.vim/bundle directories. -2. Added the "call pathogen#infect()" line to your ~/.vimrc file -3. Did the git clone of syntastic inside ~/.vim/bundle +#### 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. + -Google group ------------- +## 3\. Recommended settings -To get information or make suggestions check out the [google group](https://groups.google.com/group/vim-syntastic). +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 +``` + -FAQ ---- +## 4\. FAQ -__Q. I installed syntastic but it isn't reporting any errors ...__ + -A. The most likely reason is that the syntax checker that it requires isn't installed. For example: python requires either `flake8`, `pyflakes` or `pylint` to be installed and in `$PATH`. To see which executable is required, just look in `syntax_checkers/.vim`. Note that aliases do not work; the actual executable must be available in your `$PATH`. Symbolic links are okay. +__4.1. Q. I installed syntastic but it isn't reporting any errors...__ -Another reason it could fail is that the error output for the 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 create an issue - or better yet, create a pull request. +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`. -__Q. Recently some of my syntax checker options have stopped working...__ +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__checkers` in +your `vimrc` (see [below](#faqcheckers)). -A. The options are still there, they have just been renamed. Recently, almost all syntax checkers were refactored to use the new `syntastic#makeprg#build()` function. This made a lot of the old explicit options redundant - as they are now implied. The new implied options usually have slightly different names to the old options. +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. -e.g. Previously there was `g:syntastic_phpcs_conf`, now you must use `g:syntastic_php_phpcs_args`. +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. -See `:help syntastic-checker-options` for more information. + -__Q. How can I pass additional arguments to a checker?__ +__4.2. Q. Syntastic supports several checkers for my filetype, how do I tell it +which one(s) to use?__ -A. Almost all syntax checkers use the `syntastic#makeprg#build()` function. Those checkers that do can be configured using global variables. The general form of the global args variables are: +A. Add a line like this to your `vimrc`: +```vim +let g:syntastic__checkers = [''] +``` -`syntastic_[filetype]_[subchecker]_args` +To see the list of supported checkers for your filetype read the +[manual][checkers] (`:help syntastic-checkers` in Vim). -So, If you wanted to pass "--my --args --here" to the ruby mri checker you would add this line to your vimrc: +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'] +``` -`let g:syntastic_ruby_mri_args="--my --args --here"` +Checkers can be chained together like this: +```vim +let g:syntastic_php_checkers = ['php', 'phpcs', 'phpmd'] +``` -See `:help syntastic-checker-options` for more information. +This is telling syntastic to run the `php` checker first, and if no errors are +found, run `phpcs`, and then `phpmd`. -__Q. Syntastic supports several checkers for my filetype - how do I tell it which one(s) to use?__ +You can also run checkers explicitly by calling `:SyntasticCheck `. +For example to run `phpcs` and `phpmd`: +```vim +:SyntasticCheck phpcs phpmd +``` -A. Stick a line like this in your vimrc: +This works for any checkers available for the current filetype, even if they +aren't listed in `g:syntastic__checkers`. -`let g:syntastic__checkers=['']` + -To see the list of checkers for your filetype, look in `syntax_checkers//`. +__4.3. Q. How can I run checkers for "foreign" filetypes against the current +file?__ -e.g. Python has the following checkers: `flake8`, `pyflakes`, `pylint` and a native `python` checker. +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'] +``` -To tell syntastic to use `pylint`, you would use this setting: +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 +``` -`let g:syntastic_python_checkers=['pylint']` +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. -Some filetypes, like PHP, have style checkers as well as syntax checkers. These can be chained together like this: + -`let g:syntastic_php_checkers=['php', 'phpcs', 'phpmd']` +__4.4. Q. I have enabled multiple checkers for the current filetype. How can I +display all errors from all checkers together?__ -This is telling syntastic to run the `php` checker first, and if no errors are found, run `phpcs`, and then `phpmd`. +A. Set `g:syntastic_aggregate_errors` to 1 in your `vimrc`: +```vim +let g:syntastic_aggregate_errors = 1 +``` -__Q. How can I jump between the different errors without using the location list at the bottom of the window?__ +See `:help syntastic-aggregating-errors` for more details. -A. Vim provides several built in commands for this. See `:help :lnext` and `:help :lprev`. + -If you use these commands a lot then you may want to add shortcut mappings to your vimrc, or install something like [unimpaired](https://github.com/tpope/vim-unimpaired) - which provides such mappings (among other things). +__4.5. Q. How can I pass additional arguments to a checker?__ -__Q. A syntax checker is giving me unwanted/strange style tips?__ +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. -A. Some filetypes (e.g. php) have style checkers as well as syntax checkers. You can usually configure the options that are passed to the style checkers, or just disable them. Take a look at the syntax checker integration file (e.g. `syntax_checkers/php.vim`) to see what options are available. +The general form of the global `args` variable is +`syntastic___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). -__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: +__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 +``` + + + +__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). + + + +__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 :lclose:bdelete -cabbrev bd lclose\|bdelete +cabbrev bd =(getcmdtype()==#':' && getcmdpos()==1 ? 'lclose\|bdelete' : 'bd') ``` + + + +__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(':p:h', 1)) +``` + + + +__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. + + + +__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__exec` variables to the wrapper +scripts. + + + +__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__exec` variables to the wrapper scripts. + + + +__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 +``` + + + +__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. + + + +__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. + + + +__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. + + + +## 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]: http://valloric.github.io/YouCompleteMe/ + + diff --git a/autoload/syntastic/c.vim b/autoload/syntastic/c.vim index aac6fc38d..c4ce2000e 100644 --- a/autoload/syntastic/c.vim +++ b/autoload/syntastic/c.vim @@ -1,4 +1,4 @@ -if exists("g:loaded_syntastic_c_autoload") +if exists('g:loaded_syntastic_c_autoload') || !exists('g:loaded_syntastic_plugin') finish endif let g:loaded_syntastic_c_autoload = 1 @@ -6,125 +6,285 @@ let g:loaded_syntastic_c_autoload = 1 let s:save_cpo = &cpo set cpo&vim -" initialize c/cpp syntax checker handlers -function! s:Init() - let s:handlers = [] - let s:cflags = {} - - call s:RegHandler('gtk', 'syntastic#c#CheckPKG', - \ ['gtk', 'gtk+-2.0', 'gtk+', 'glib-2.0', 'glib']) - call s:RegHandler('glib', 'syntastic#c#CheckPKG', - \ ['glib', 'glib-2.0', 'glib']) - call s:RegHandler('glade', 'syntastic#c#CheckPKG', - \ ['glade', 'libglade-2.0', 'libglade']) - call s:RegHandler('libsoup', 'syntastic#c#CheckPKG', - \ ['libsoup', 'libsoup-2.4', 'libsoup-2.2']) - call s:RegHandler('webkit', 'syntastic#c#CheckPKG', - \ ['webkit', 'webkit-1.0']) - call s:RegHandler('cairo', 'syntastic#c#CheckPKG', - \ ['cairo', 'cairo']) - call s:RegHandler('pango', 'syntastic#c#CheckPKG', - \ ['pango', 'pango']) - call s:RegHandler('libxml', 'syntastic#c#CheckPKG', - \ ['libxml', 'libxml-2.0', 'libxml']) - call s:RegHandler('freetype', 'syntastic#c#CheckPKG', - \ ['freetype', 'freetype2', 'freetype']) - call s:RegHandler('SDL', 'syntastic#c#CheckPKG', - \ ['sdl', 'sdl']) - call s:RegHandler('opengl', 'syntastic#c#CheckPKG', - \ ['opengl', 'gl']) - call s:RegHandler('ruby', 'syntastic#c#CheckRuby', []) - call s:RegHandler('Python\.h', 'syntastic#c#CheckPython', []) - call s:RegHandler('php\.h', 'syntastic#c#CheckPhp', []) -endfunction - -" default include directories -let s:default_includes = [ '.', '..', 'include', 'includes', - \ '../include', '../includes' ] +" Public functions {{{1 " convenience function to determine the 'null device' parameter " based on the current operating system -function! syntastic#c#GetNullDevice() - if has('win32') - return '-o nul' - elseif has('unix') || has('mac') - return '-o /dev/null' - endif - return '' -endfunction +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 -" get the gcc include directory argument depending on the default -" includes and the optional user-defined 'g:syntastic_c_include_dirs' -function! syntastic#c#GetIncludeDirs(filetype) - let include_dirs = [] +" 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) - if !exists('g:syntastic_'.a:filetype.'_no_default_include_dirs') || - \ !g:syntastic_{a:filetype}_no_default_include_dirs - let include_dirs = copy(s:default_includes) + " 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 - - if exists('g:syntastic_'.a:filetype.'_include_dirs') - call extend(include_dirs, g:syntastic_{a:filetype}_include_dirs) + 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 - return join(map(syntastic#util#unique(include_dirs), '"-I" . v:val'), ' ') -endfunction - -" 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) - " search in the current file's directory upwards - let config = findfile(a:file, '.;') - if config == '' || !filereadable(config) | return '' | endif - " convert filename into absolute path - let filepath = substitute(fnamemodify(config, ':p:h'), '\', '/', 'g') + let filepath = fnamemodify(config, ':p:h') " try to read config file try - let lines = map(readfile(config), - \ 'substitute(v:val, ''\'', ''/'', ''g'')') - catch /E484/ + 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 = matchlist(line, '\C^\s*-I\s*\(\S\+\)') - if matches != [] && matches[1] != '' + let matches = matchstr(line, '\m\C^\s*-I\s*\zs.\+') + if matches !=# '' " this one looks like an absolute path - if match(matches[1], '^\%(/\|\a:\)') != -1 - call add(parameters, '-I' . matches[1]) + if match(matches, '\m^\%(/\|\a:\)') != -1 + call add(parameters, '-I' . matches) else - call add(parameters, '-I' . filepath . '/' . matches[1]) + call add(parameters, '-I' . filepath . syntastic#util#Slash() . matches) endif else call add(parameters, line) endif endfor - return join(parameters, ' ') -endfunction + 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\', 's:_checkPhp', []) + call s:_registerHandler('\m\', 's:_checkPython', []) + call s:_registerHandler('\m\' + 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: diff --git a/autoload/syntastic/makeprg.vim b/autoload/syntastic/makeprg.vim deleted file mode 100644 index 59311f7db..000000000 --- a/autoload/syntastic/makeprg.vim +++ /dev/null @@ -1,45 +0,0 @@ -if exists("g:loaded_syntastic_makeprg_autoload") - finish -endif -let g:loaded_syntastic_makeprg_autoload = 1 - -"Returns a makeprg of the form -" -"[exe] [args] [filename] [post_args] [tail] -" -"A (made up) example: -" ruby -a -b -c test_file.rb --more --args > /tmp/output -" -"To generate this you would call: -" -" let makeprg = syntastic#makeprg#build({ -" \ 'exe': 'ruby', -" \ 'args': '-a -b -c', -" \ 'post_args': '--more --args', -" \ 'tail': '> /tmp/output', -" \ 'subchecker': 'mri' }) -" -"Note that the current filename is added by default - but can be overridden by -"passing in an 'fname' arg. -" -"All options can be overriden by the user with global variables - even when -"not specified by the checker in syntastic#makeprg#build(). -" -"E.g. They could override the checker exe with -" -" let g:syntastic_ruby_mri_exe="another_ruby_checker_exe.rb" -" -"The general form of the override option is: -" syntastic_[filetype]_[subchecker]_[option-name] -" -function! syntastic#makeprg#build(opts) - let builder = g:SyntasticMakeprgBuilder.New( - \ get(a:opts, 'exe', ''), - \ get(a:opts, 'args', ''), - \ get(a:opts, 'fname', ''), - \ get(a:opts, 'post_args', ''), - \ get(a:opts, 'tail', ''), - \ get(a:opts, 'subchecker', '') ) - - return builder.makeprg() -endfunction diff --git a/autoload/syntastic/postprocess.vim b/autoload/syntastic/postprocess.vim index 188a28a42..c69f3978a 100644 --- a/autoload/syntastic/postprocess.vim +++ b/autoload/syntastic/postprocess.vim @@ -1,4 +1,4 @@ -if exists("g:loaded_syntastic_postprocess_autoload") +if exists('g:loaded_syntastic_postprocess_autoload') || !exists('g:loaded_syntastic_plugin') finish endif let g:loaded_syntastic_postprocess_autoload = 1 @@ -6,53 +6,79 @@ let g:loaded_syntastic_postprocess_autoload = 1 let s:save_cpo = &cpo set cpo&vim -function! s:compareErrorItems(a, b) - if a:a['bufnr'] != a:b['bufnr'] - " group by files - return a:a['bufnr'] - a:b['bufnr'] - elseif a:a['lnum'] != a:b['lnum'] - 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 - return get(a:a, 'col') - get(a:b, 'col') - endif -endfunction - -" natural sort -function! syntastic#postprocess#sort(errors) - return sort(a:errors, 's:compareErrorItems') -endfunction - -function syntastic#postprocess#compressWhitespace(errors) - let llist = [] +" 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'], '\s\{2,}', ' ', 'g') - call add(llist, e) + 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 llist -endfunction + return a:errors +endfunction " }}}2 " remove spurious CR under Cygwin -function! syntastic#postprocess#cygwinRemoveCR(errors) +function! syntastic#postprocess#cygwinRemoveCR(errors) abort " {{{2 if has('win32unix') - let llist = [] - for e in a:errors let e['text'] = substitute(e['text'], '\r', '', 'g') - call add(llist, e) endfor - else - let llist = a:errors endif - return llist -endfunction + 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 et sts=4 sw=4: + +" vim: set sw=4 sts=4 et fdm=marker: diff --git a/autoload/syntastic/preprocess.vim b/autoload/syntastic/preprocess.vim new file mode 100644 index 000000000..e27acf107 --- /dev/null +++ b/autoload/syntastic/preprocess.vim @@ -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') > -1 + let line = str2nr(matchstr(err, '\m\ \[[^]]+\])+\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: diff --git a/autoload/syntastic/util.vim b/autoload/syntastic/util.vim index e68f6c95f..8a17fe644 100644 --- a/autoload/syntastic/util.vim +++ b/autoload/syntastic/util.vim @@ -1,4 +1,4 @@ -if exists("g:loaded_syntastic_util_autoload") +if exists('g:loaded_syntastic_util_autoload') || !exists('g:loaded_syntastic_plugin') finish endif let g:loaded_syntastic_util_autoload = 1 @@ -6,100 +6,231 @@ let g:loaded_syntastic_util_autoload = 1 let s:save_cpo = &cpo set cpo&vim -if !exists("g:syntastic_debug") - let g:syntastic_debug = 0 -endif +" Public functions {{{1 -let s:deprecationNoticesIssued = [] +function! syntastic#util#isRunningWindows() abort " {{{2 + return has('win16') || has('win32') || has('win64') +endfunction " }}}2 -function! syntastic#util#DevNull() - if has('win32') +function! syntastic#util#DevNull() abort " {{{2 + if syntastic#util#isRunningWindows() return 'NUL' endif return '/dev/null' -endfunction +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 + 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 -"search the first 5 lines of the file for a magic number and return a map -"containing the args and the executable + 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. +" e.g. " -"#!/usr/bin/perl -f -bar +" #!/usr/bin/perl -f -bar " -"returns +" returns " -"{'exe': '/usr/bin/perl', 'args': ['-f', '-bar']} -function! syntastic#util#parseShebang() - for lnum in range(1,5) - let line = getline(lnum) - - if line =~ '^#!' - let exe = matchstr(line, '^#!\s*\zs[^ \t]*') - let args = split(matchstr(line, '^#!\s*[^ \t]*\zs.*')) - return {'exe': exe, 'args': args} +" {'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 + return { 'exe': '', 'args': [] } +endfunction " }}}2 -" Verify that the 'installed' version is at the 'required' version, if not -" better. -" -" 'installed' and 'required' must be arrays. Only the -" first three elements (major, minor, patch) are looked at. +" 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. " -" Either array may be less than three elements. The "missing" elements -" will be assumed to be '0' for the purposes of checking. +" '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) - for index in [0,1,2] - if len(a:installed) <= index - let installed_element = 0 - else - let installed_element = a:installed[index] - endif - if len(a:required) <= index - let required_element = 0 - else - let required_element = a:required[index] - endif - if installed_element != required_element - return installed_element > required_element +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 - " Everything matched, so it is at least the required version. - return 1 -endfunction + " 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) +" 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 - "convert tabs to spaces so that the tabs count towards the window width - "as the proper amount of characters - let msg = substitute(a:msg, "\t", repeat(" ", &tabstop), "g") - let msg = strpart(msg, 0, winwidth(0)-1) + "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') - "This is here because it is possible for some error messages to begin with - "\n which will cause a "press enter" prompt. I have noticed this in the - "javascript:jshint checker and have been unable to figure out why it - "happens - let msg = substitute(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 - redraw + call syntastic#util#redraw(0) echo msg - let &ruler=old_ruler - let &showcmd=old_showcmd -endfunction + 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) +function! syntastic#util#bufIsActive(buffer) abort " {{{2 " convert to number, or hell breaks loose let buf = str2nr(a:buffer) @@ -115,32 +246,390 @@ function! syntastic#util#bufIsActive(buffer) endfor return 0 -endfunction +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) +function! syntastic#util#unique(list) abort " {{{2 let seen = {} + let uniques = [] for e in a:list - let seen[e] = 1 + let k = string(e) + if !has_key(seen, k) + let seen[k] = 1 + call add(uniques, e) + endif endfor - return copy(keys(seen)) -endfunction + 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 -function! syntastic#util#debug(msg) - if g:syntastic_debug - echomsg "syntastic: debug: " . a:msg +" 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 -endfunction -function! syntastic#util#deprecationWarn(msg) - if index(s:deprecationNoticesIssued, a:msg) >= 0 - return + 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 - call add(s:deprecationNoticesIssued, a:msg) - echomsg "syntastic: warning: " . a:msg -endfunction +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 et sts=4 sw=4: + +" vim: set sw=4 sts=4 et fdm=marker: diff --git a/doc/syntastic-checkers.txt b/doc/syntastic-checkers.txt new file mode 100644 index 000000000..7453201e0 --- /dev/null +++ b/doc/syntastic-checkers.txt @@ -0,0 +1,8313 @@ +*syntastic-checkers.txt* Syntastic checkers +*syntastic-checkers* + +============================================================================== +SYNTAX CHECKERS BY LANGUAGE *syntastic-checkers-lang* + +|syntastic| comes with checkers for the following languages: + + ACPI Source Language.....................|syntastic-checkers-asl| + ActionScript.............................|syntastic-checkers-actionscript| + Ada......................................|syntastic-checkers-ada| + Ansible..................................|syntastic-checkers-ansible| + API Blueprint............................|syntastic-checkers-apiblueprint| + AppleScript..............................|syntastic-checkers-applescript| + AsciiDoc.................................|syntastic-checkers-asciidoc| + Assembly Languages.......................|syntastic-checkers-asm| + + BEMHTML..................................|syntastic-checkers-bemhtml| + Bro......................................|syntastic-checkers-bro| + + C........................................|syntastic-checkers-c| + C#.......................................|syntastic-checkers-cs| + C++......................................|syntastic-checkers-cpp| + Cabal....................................|syntastic-checkers-cabal| + Chef.....................................|syntastic-checkers-chef| + CMake....................................|syntastic-checkers-cmake| + COBOL....................................|syntastic-checkers-cobol| + Coco.....................................|syntastic-checkers-co| + CoffeeScript.............................|syntastic-checkers-coffee| + Coq......................................|syntastic-checkers-coq| + CSS......................................|syntastic-checkers-css| + Cucumber.................................|syntastic-checkers-cucumber| + CUDA.....................................|syntastic-checkers-cuda| + + D........................................|syntastic-checkers-d| + Dart.....................................|syntastic-checkers-dart| + DocBook..................................|syntastic-checkers-docbk| + Dockerfile...............................|syntastic-checkers-dockerfile| + Dust.....................................|syntastic-checkers-dustjs| + + Elixir...................................|syntastic-checkers-elixir| + Erlang...................................|syntastic-checkers-erlang| + eRuby....................................|syntastic-checkers-eruby| + + Fortran..................................|syntastic-checkers-fortran| + + Gentoo Metadata..........................|syntastic-checkers-gentoo| + Gettext PO...............................|syntastic-checkers-po| + GLSL.....................................|syntastic-checkers-glsl| + Go.......................................|syntastic-checkers-go| + + Haml.....................................|syntastic-checkers-haml| + Handlebars...............................|syntastic-checkers-handlebars| + Haskell..................................|syntastic-checkers-haskell| + Haxe.....................................|syntastic-checkers-haxe| + HSS......................................|syntastic-checkers-hss| + HTML.....................................|syntastic-checkers-html| + + Java.....................................|syntastic-checkers-java| + JavaScript...............................|syntastic-checkers-javascript| + JSON.....................................|syntastic-checkers-json| + Julia....................................|syntastic-checkers-julia| + + LESS.....................................|syntastic-checkers-less| + Lex......................................|syntastic-checkers-lex| + Limbo....................................|syntastic-checkers-limbo| + LISP.....................................|syntastic-checkers-lisp| + LLVM.....................................|syntastic-checkers-llvm| + Lua......................................|syntastic-checkers-lua| + + Markdown.................................|syntastic-checkers-markdown| + MATLAB...................................|syntastic-checkers-matlab| + Mercury..................................|syntastic-checkers-mercury| + + NASM.....................................|syntastic-checkers-nasm| + Nix......................................|syntastic-checkers-nix| + nroff....................................|syntastic-checkers-nroff| + + Objective-C..............................|syntastic-checkers-objc| + Objective-C++............................|syntastic-checkers-objcpp| + OCaml....................................|syntastic-checkers-ocaml| + + Perl.....................................|syntastic-checkers-perl| + Perl 6...................................|syntastic-checkers-perl6| + PHP......................................|syntastic-checkers-php| + POD......................................|syntastic-checkers-pod| + Pug (formerly Jade)......................|syntastic-checkers-pug| + Puppet...................................|syntastic-checkers-puppet| + Python...................................|syntastic-checkers-python| + + QML......................................|syntastic-checkers-qml| + + R........................................|syntastic-checkers-r| + R Markdown...............................|syntastic-checkers-rmd| + Racket...................................|syntastic-checkers-racket| + Relax NG.................................|syntastic-checkers-rnc| + reStructuredText.........................|syntastic-checkers-rst| + RPM spec.................................|syntastic-checkers-spec| + Ruby.....................................|syntastic-checkers-ruby| + + SASS.....................................|syntastic-checkers-sass| + Scala....................................|syntastic-checkers-scala| + SCSS.....................................|syntastic-checkers-scss| + Sh.......................................|syntastic-checkers-sh| + Slim.....................................|syntastic-checkers-slim| + SML......................................|syntastic-checkers-sml| + Solidity.................................|syntastic-checkers-solidity| + SQL......................................|syntastic-checkers-sql| + Stylus...................................|syntastic-checkers-stylus| + SVG......................................|syntastic-checkers-svg| + + Tcl......................................|syntastic-checkers-tcl| + TeX......................................|syntastic-checkers-tex| + Texinfo..................................|syntastic-checkers-texinfo| + Text.....................................|syntastic-checkers-text| + Turtle...................................|syntastic-checkers-turtle| + TriG.....................................|syntastic-checkers-trig| + Twig.....................................|syntastic-checkers-twig| + TypeScript...............................|syntastic-checkers-typescript| + + Vala.....................................|syntastic-checkers-vala| + Verilog..................................|syntastic-checkers-verilog| + VHDL.....................................|syntastic-checkers-vhdl| + Vim help.................................|syntastic-checkers-help| + VimL.....................................|syntastic-checkers-vim| + Vue.js...................................|syntastic-checkers-vue| + + xHTML....................................|syntastic-checkers-xhtml| + XML......................................|syntastic-checkers-xml| + XQuery...................................|syntastic-checkers-xquery| + XSLT.....................................|syntastic-checkers-xslt| + + YACC.....................................|syntastic-checkers-yacc| + YAML.....................................|syntastic-checkers-yaml| + YANG.....................................|syntastic-checkers-yang| + YARA.....................................|syntastic-checkers-yara| + + Z80......................................|syntastic-checkers-z80| + Zope Page Templates......................|syntastic-checkers-zpt| + Zsh......................................|syntastic-checkers-zsh| + +Third-party checkers are available for additional languages. + +============================================================================== +SYNTAX CHECKERS FOR ACPI SOURCE LANGUAGE *syntastic-checkers-asl* + +The following checkers are available for the ACPI Source Language (filetype +"asl"): + + 1. iasl.....................|syntastic-asl-iasl| + +------------------------------------------------------------------------------ +1. iasl *syntastic-asl-iasl* + +Name: iasl +Maintainer: Peter Wu + +"iasl" is a compiler/decompiler for ACPI Source Language (ASL) and ACPI +Machine Language (AML). See the project's page for details: + + https://acpica.org/ + +Checker options~ + +This checker is initialised using the "makeprgBuild()" function and thus it +accepts the standard options described at |syntastic-config-makeprg|. + +Note~ + +You probably also need a plugin to set |filetype| for ASL files, such as +"vim-acpi-asl": + + https://github.com/martinlroth/vim-acpi-asl + +============================================================================== +SYNTAX CHECKERS FOR ACTIONSCRIPT *syntastic-checkers-actionscript* + +The following checkers are available for ActionScript (filetype +"actionscript"): + + 1. mxmlc....................|syntastic-actionscript-mxmlc| + +------------------------------------------------------------------------------ +1. mxmlc *syntastic-actionscript-mxmlc* + +Name: mxmlc +Maintainer: Andy Earnshaw + +"mxmlc" is a compiler for ActionScript. See Apache Flex for details: + + http://flex.apache.org/ + +Checker options~ + +This checker is initialised using the "makeprgBuild()" function and thus it +accepts the standard options described at |syntastic-config-makeprg|. + +============================================================================== +SYNTAX CHECKERS FOR ADA *syntastic-checkers-ada* + +The following checkers are available for Ada (filetype "ada"): + + 1. GCC......................|syntastic-ada-gcc| + +------------------------------------------------------------------------------ +1. GCC *syntastic-ada-gcc* + +Name: gcc +Maintainer: Alfredo Di Napoli + +Checker options~ + *'g:syntastic_ada_compiler'* +Type: string +Default: "gcc" +Compiler executable. + + *'g:syntastic_ada_errorformat'* +Type: string +Default: unset +Override for the default |'errorformat'|. + + *'g:syntastic_ada_remove_include_errors'* +Type: boolean +Default: 0 +By default, errors in files included from the file being checked are shown. +Set this variable to 1 to remove messages about errors in included files. +Please note that this means syntastic will silently abort checks if there are +fatal errors in one of the included files. + + *'g:syntastic_ada_compiler_options'* +Type: string +Default: unset +Compilation flags (such as defines or include directories) to be passed to the +linter. + + *'g:syntastic_ada_config_file'* +Type: string +Default: unset +File containing additional compilation flags to be passed to the linter, one +option per line (cf. |syntastic-config-files|). + + *'g:syntastic_ada_include_dirs'* +Type: array of strings +Default: [] +Include directories to be passed to the linter, in addition to the above +compilation flags. You can set it like this: > + let g:syntastic_ada_include_dirs = ["includes", "headers"] +< +and the corresponding "-Iincludes -Iheaders" will be added to the compilation +flags. + + *'b:syntastic_ada_cflags'* +Type: string +Default: unset +Buffer-local variable. Additional compilation flags specific to the current +buffer. + + *'g:syntastic_ada_check_header'* +Type: boolean +Default: 0 +If the current file is a header (namely if its extension is "ads"), all checks +are silently skipped. You can force syntastic to check header files by +setting the above variable to 1. + +Note~ + +This checker doesn't call the "makeprgBuild()" function, and thus it ignores +the usual 'g:syntastic_ada_gcc_