├── LICENSE ├── README.md ├── VERSION ├── autoload └── Phpqa.vim └── plugin ├── Phpqa.vim └── python └── codecoverage.vim /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Jon Cairns 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP QA Tools For Vim 2 | 3 | This is a plugin for Vim that integrates PHP quality checking tools, to allow you to code to a particular standard and easily spot errors and violations. 4 | 5 | It uses PHP linter to check for syntax errors, and integrates with [PHP Code Sniffer][1] and [PHP Mess Detector][2] to check for coding standard violations, and shows code coverage from clover XML files. 6 | 7 | ### Quick Guide 8 | 9 | The plugin is configured by default to automatically run the QA tools when a PHP file is saved. Therefore, save a file and the linter will run. If there is a syntax error, the offending line will be highlighted. Plus, a [quickfix][3] window opens to show the error and it's position in the file. 10 | 11 | If there are no syntax errors, PHP Code Sniffer and Mess Detector will run. These will require some configuration to fit your needs, which you can read about under the "Configuration" heading. The output of these two commands are combined, and the file is highlighted with the occurences. Again, a quickfix window opens, showing the violations and allowing you to navigate through them. 12 | 13 | You can toggle markers with the following commands (in command mode): 14 | 15 | ```vim 16 | qa " Show/hide code sniffer and mess detector violations 17 | qc " Show/hide code coverage markers 18 | ``` 19 | 20 | What's the `` key? It's likely to be either `\` or `,`, but you can set it from the command line or in your *.vimrc* file using: 21 | 22 | ```vim 23 | let mapleader="@" 24 | ``` 25 | 26 | or whatever you want it to be. 27 | 28 | You can also run each command separately on demand: 29 | 30 | - `:Php` - check for syntax errors 31 | - `:Phpcs` - run code sniffer 32 | - `:Phpmd` - run mess detector (will ask for a rule XML file if not set) 33 | - `:Phpcc` - show code coverage (will ask for a clover XML file if not set) 34 | 35 | ### Code Coverage 36 | 37 | If you generate clover code coverage reports with your tests, you can toggle markers to show which lines are covered and which aren't. You can run the command once using `Phpcc` as shown above, or you can configure it to load the markers every time you open a new file - see the configuration section for more information. 38 | 39 | You can also toggle code coverage (to stop it automatically running on opening a file) with: 40 | 41 | ```vim 42 | qc 43 | ``` 44 | 45 | ### Installation 46 | 47 | Installation is easy-peasy if you're using [Vundle][4]. Just add this to your *.vimrc* file: 48 | 49 | ```vim 50 | Bundle 'joonty/vim-phpqa.git' 51 | ``` 52 | and run `vim +BundleInstall +qall` from a terminal. 53 | 54 | If you aren't using vundle, you will have to extract the files in each folder to the correct folder in *.vim/*. 55 | 56 | **Note:** your Vim installation must be compiled with *signs* for this plugin to work. If you want to use code coverage support you will need to have Vim compiled with Python. 57 | 58 | ### Configuration 59 | 60 | Each command has it's own configuration settings, which allow you to get the functionality you want. 61 | 62 | #### PHP, Mess Detector and Codesniffer 63 | 64 | PHP mess detector needs a ruleset XML file (see the [mess detector website][2] for more information) to run, which you will be prompted for the first time the command runs. However, it's much easier to just specify it in your *.vimrc* file: 65 | 66 | ```vim 67 | let g:phpqa_messdetector_ruleset = "/path/to/phpmd.xml" 68 | ``` 69 | 70 | For PHP code sniffer, you can pass arguments to the command line binary (run `phpcs --help` to see a list). For example: 71 | 72 | ```vim 73 | " Set the codesniffer args 74 | let g:phpqa_codesniffer_args = "--standard=Zend" 75 | ``` 76 | 77 | However, **don't** set the `--report=` argument, as it won't work! 78 | 79 | For all the commands, you can override the executable: 80 | 81 | ```vim 82 | " PHP executable (default = "php") 83 | let g:phpqa_php_cmd='/path/to/php' 84 | 85 | " PHP Code Sniffer binary (default = "phpcs") 86 | let g:phpqa_codesniffer_cmd='/path/to/phpcs' 87 | 88 | " PHP Mess Detector binary (default = "phpmd") 89 | let g:phpqa_messdetector_cmd='/path/to/phpmd' 90 | ``` 91 | However, you don't need to do this if the commands `php`, `phpcs` and `phpmd` are can be found in your `$PATH` environment variable. 92 | 93 | And you can stop them running automatically: 94 | 95 | ```vim 96 | " Don't run messdetector on save (default = 1) 97 | let g:phpqa_messdetector_autorun = 0 98 | 99 | " Don't run codesniffer on save (default = 1) 100 | let g:phpqa_codesniffer_autorun = 0 101 | 102 | " Show code coverage on load (default = 0) 103 | let g:phpqa_codecoverage_autorun = 1 104 | ``` 105 | 106 | By default, the location list window will open when mess detector/codesniffer violations are found. You can stop this happening by setting this option: 107 | 108 | ```vim 109 | " Stop the location list opening automatically 110 | let g:phpqa_open_loc = 0 111 | ``` 112 | 113 | #### Code Coverage Configuration 114 | 115 | For code coverage, you can specify a clover XML file to stop the prompt appearing each time, and choose to only show markers for lines that aren't covered - by default both covered and uncovered code have markers. Not showing covered line markers is likely to speed things up noticeably when moving between files. 116 | 117 | ```vim 118 | " Clover code coverage XML file 119 | let g:phpqa_codecoverage_file = "/path/to/clover.xml" 120 | " Show markers for lines that ARE covered by tests (default = 1) 121 | let g:phpqa_codecoverage_showcovered = 0 122 | ``` 123 | 124 | ### Acknowlegements 125 | 126 | This plugin **originally** reused and modified a lot of the code from the Vim [QuickHigh plugin][5], written by Brian Medley. Although this plugin no longer uses any of that code, my thanks goes to Brian for the work that's gone into that script. 127 | 128 | ### License 129 | 130 | This plugin is released under the [MIT License][6]. 131 | 132 | 133 | [1]: http://pear.php.net/package/PHP_CodeSniffer/redirected 134 | [2]: http://phpmd.org/ 135 | [3]: http://vimdoc.sourceforge.net/htmldoc/quickfix.html 136 | [4]: https://github.com/gmarik/vundle 137 | [5]: http://www.vim.org/scripts/script.php?script_id=124 138 | [6]: https://github.com/joonty/vim-phpqa/raw/master/LICENSE 139 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.7.0 2 | -------------------------------------------------------------------------------- /autoload/Phpqa.vim: -------------------------------------------------------------------------------- 1 | " Description: 2 | " Vim plugin that uses PHP qa tools and highlights the current file with 3 | " syntax errors and coding standard violations. 4 | " 5 | " License: 6 | " MIT (https://raw.githubusercontent.com/joonty/vim-phpqa/master/LICENSE) 7 | " 8 | " Authors: 9 | " Jon Cairns 10 | " 11 | 12 | " ------------------------------------------------------------------------------ 13 | " Exit when already loaded (or "compatible" mode set) 14 | if exists("g:loaded_phpqa") || &cp 15 | finish 16 | endif 17 | let g:loaded_phpqa= 1 18 | let s:keepcpo = &cpo 19 | set cpo&vim 20 | let s:lineid = 9012 21 | let s:numsigns = 0 22 | let g:phpqa_num_cc_signs = 0 23 | 24 | "============================================================================= 25 | " PRIVATE FUNCTIONS {{{1 26 | 27 | " 28 | " Description: 29 | " This function adds signs to the given buffer according to the contents of 30 | " the error list.line. 31 | " 32 | function! s:AddSigns(buffer) 33 | for line in getloclist(0) 34 | if line.lnum == 0 35 | continue 36 | endif 37 | if has_key(g:phpqa_sign_type_map,line.type) 38 | let l:name=g:phpqa_sign_type_map[line.type] 39 | else 40 | let l:name="GenericError" 41 | endif 42 | exec "sign place ".s:lineid." line=".line.lnum." name=".l:name." buffer=".a:buffer 43 | let s:numsigns = s:numsigns + 1 44 | endfor 45 | endfunction 46 | 47 | function! s:RemoveSigns() 48 | while s:numsigns > 0 49 | exec "sign unplace ".s:lineid 50 | let s:numsigns = s:numsigns - 1 51 | endwhile 52 | endfunction 53 | 54 | " Remove code coverage markers 55 | function s:RemoveCodeCoverageSigns() 56 | while 0 != g:phpqa_num_cc_signs 57 | sign unplace 4783 58 | let g:phpqa_num_cc_signs = g:phpqa_num_cc_signs - 1 59 | endwhile 60 | endfunction 61 | 62 | " Combine error lists for codesniffer and messdetector 63 | function s:CombineLists(phpcs_list,phpmd_list) 64 | 65 | if 0 != len(a:phpcs_list) 66 | let k = 0 67 | for val in a:phpcs_list 68 | let a:phpcs_list[k] = g:phpqa_codesniffer_type . " ". a:phpcs_list[k]." ".g:phpqa_codesniffer_append 69 | let k = k+1 70 | endfor 71 | endif 72 | if 0 != len(a:phpmd_list) 73 | let k = 0 74 | for val in a:phpmd_list 75 | let a:phpmd_list[k] = g:phpqa_messdetector_type." ".a:phpmd_list[k]." ".g:phpqa_messdetector_append 76 | let k = k+1 77 | endfor 78 | endif 79 | return a:phpcs_list + a:phpmd_list 80 | endf 81 | "1}}} 82 | "============================================================================= 83 | 84 | "============================================================================= 85 | " GLOBAL FUNCTIONS {{{1 86 | 87 | " Run the PHP linter to check for syntax errors 88 | function! Phpqa#PhpLint() 89 | if &filetype == "php" 90 | if 0 != len(g:phpqa_php_cmd) 91 | let l:bufNo = bufnr('%') 92 | call s:RemoveSigns() 93 | let l:php_output=system(g:phpqa_php_cmd." -l ".@%." 1>/dev/null") 94 | let l:php_list=split(l:php_output, "\n") 95 | 96 | if 0 != v:shell_error && match(l:php_list[0],"No syntax errors") == -1 97 | let l:php_list[0] = "P ".l:php_list[0] 98 | set errorformat=%t\ %m\ in\ %f\ on\ line\ %l 99 | lexpr l:php_list[0] 100 | call s:AddSigns(l:bufNo) 101 | if g:phpqa_open_loc 102 | lope 103 | endif 104 | return 1 105 | else 106 | if 1 == g:phpqa_verbose 107 | echohl Error | echo "No syntax errors" | echohl None 108 | endif 109 | lgete [] 110 | lcl 111 | endif 112 | elseif 1 == g:phpqa_verbose 113 | echohl Error | echo "PHP binary set to empty, not running lint" | echohl None 114 | endif 115 | endif 116 | return 0 117 | endfunction 118 | 119 | " Run PHP code sniffer. 120 | function! Phpqa#PhpCodeSniffer() 121 | if @% == "" 122 | echohl Error | echo "Invalid buffer (are you in the error window?)" |echohl None 123 | return [] 124 | endif 125 | " Run codesniffer if the command hasn't been unset 126 | if 0 != len(g:phpqa_codesniffer_cmd) 127 | let l:phpcs_output=system(g:phpqa_codesniffer_cmd." ".g:phpqa_codesniffer_args." --report=emacs ".@%) 128 | let l:phpcs_list=split(l:phpcs_output, "\n") 129 | else 130 | let l:phpcs_list = [] 131 | if 1 == g:phpqa_verbose 132 | echohl Error | echo "PHPCS binary set to empty, not running codesniffer" | echohl None 133 | endif 134 | endif 135 | return l:phpcs_list 136 | endf 137 | 138 | " Run mess detector. 139 | " 140 | " The user is required to specify a ruleset XML file if they haven't already. 141 | function! Phpqa#PhpMessDetector() 142 | if @% == "" 143 | echohl Error | echo "Invalid buffer (are you in the error window?)" |echohl None 144 | return [] 145 | endif 146 | " Run messdetector if the command hasn't been unset 147 | if 0 != len(g:phpqa_messdetector_cmd) 148 | let file_tmp = "" 149 | while 0 == len(g:phpqa_messdetector_ruleset) 150 | let file_tmp = input("Please specify a mess detector ruleset file, or built in rule: ",file_tmp) 151 | let g:phpqa_messdetector_ruleset = file_tmp 152 | endwhile 153 | let l:phpmd_output=system(g:phpqa_messdetector_cmd." ".@%." text ".g:phpqa_messdetector_ruleset) 154 | let l:phpmd_list=split(l:phpmd_output, "\n") 155 | else 156 | let l:phpmd_list = [] 157 | echohl Error | echo "PHPMD binary set to empty, not running mess detector" | echohl None 158 | endif 159 | return l:phpmd_list 160 | endf 161 | 162 | " Run Code Sniffer and Mess Detector. 163 | function! Phpqa#PhpQaTools(runcs,runmd) 164 | let l:bufNo = bufnr('%') 165 | call s:RemoveSigns() 166 | 167 | if 1 == a:runcs 168 | let l:phpcs_list=Phpqa#PhpCodeSniffer() 169 | else 170 | let l:phpcs_list = [] 171 | endif 172 | 173 | if 1 == a:runmd 174 | let l:phpmd_list = Phpqa#PhpMessDetector() 175 | else 176 | let l:phpmd_list = [] 177 | endif 178 | 179 | let error_list=s:CombineLists(l:phpcs_list,l:phpmd_list) 180 | if 0 != len(error_list) 181 | set errorformat=%t\ %f:%l:%c:\ %m,%t\ %f:%l\ %m 182 | lgete error_list 183 | call s:AddSigns(l:bufNo) 184 | if g:phpqa_open_loc 185 | lope 186 | endif 187 | else 188 | lgete [] 189 | lcl 190 | endif 191 | endf 192 | 193 | " Toggle the code coverage markers. 194 | " 195 | " If the command has been run, remove the signs. Otherwise run it with 196 | " Phpqa#PhpCodeCoverage() 197 | function! Phpqa#CodeCoverageToggle() 198 | if 0 != g:phpqa_num_cc_signs 199 | let g:phpqa_codecoverage_autorun = 0 200 | call s:RemoveCodeCoverageSigns() 201 | else 202 | let g:phpqa_codecoverage_autorun = 1 203 | call Phpqa#PhpCodeCoverage() 204 | endif 205 | 206 | endf 207 | 208 | function! Phpqa#QAToolsToggle() 209 | if g:phpqa_run_on_write == 1 210 | call s:RemoveSigns() 211 | let g:phpqa_run_on_write = 0 212 | echohl Error | echo "PHP QA tools won't run automatically on save" | echohl None 213 | else 214 | let g:phpqa_run_on_write = 1 215 | echohl Error | echo "PHP QA tools has been enabled" | echohl None 216 | endif 217 | endf 218 | 219 | 220 | " Run code coverage, and ask the user for the coverage file if not specified 221 | function! Phpqa#PhpCodeCoverage() 222 | call s:RemoveCodeCoverageSigns() 223 | 224 | let file_tmp = "" 225 | while 0 == len(g:phpqa_codecoverage_file) 226 | let file_tmp = resolve(expand(input("Please specify a clover code coverage XML file (leave blank to cancel): ",file_tmp,"file"))) 227 | if filereadable(file_tmp) 228 | let g:phpqa_codecoverage_file = file_tmp 229 | elseif file_tmp == "" 230 | echo "Cancelled" 231 | break 232 | else 233 | echohl Error |echo "Not a valid or readable file"|echohl None 234 | endif 235 | endwhile 236 | if filereadable(g:phpqa_codecoverage_file) 237 | call AddCodeCoverageSigns(g:phpqa_codecoverage_file) 238 | endif 239 | endf 240 | " }}}1 241 | "============================================================================= 242 | -------------------------------------------------------------------------------- /plugin/Phpqa.vim: -------------------------------------------------------------------------------- 1 | "------------------------------------------------- 2 | " PHP QA tools for Vim {{{ 3 | " 4 | " Description: 5 | " Vim plugin that uses PHP qa tools and highlights the current file with 6 | " syntax errors and coding standard violations. 7 | " 8 | " License: 9 | " MIT (https://raw.githubusercontent.com/joonty/vim-phpqa/master/LICENSE) 10 | " 11 | " Authors: 12 | " Jon Cairns 13 | " 14 | " }}} 15 | "------------------------------------------------- 16 | 17 | " Disable plugin if php isn't available 18 | if !executable("php") 19 | finish 20 | endif 21 | 22 | if exists("g:phpqa_check") 23 | finish 24 | endif 25 | 26 | if 0 == has("signs") 27 | echohl ErrorMsg | echo "I'm sorry, phpqa needs a vim with +signs." | echohl None 28 | finish 29 | endif 30 | 31 | let $CUR_DIRECTORY=expand(":p:h") 32 | source $CUR_DIRECTORY/python/codecoverage.vim 33 | 34 | let g:phpqa_check = 1 35 | 36 | " Give more feedback about commands 37 | let g:phpqa_verbose = 0 38 | 39 | " PHP binary 40 | if !exists("g:phpqa_php_cmd") 41 | let g:phpqa_php_cmd='php' 42 | endif 43 | 44 | " PHPCS binary (PHP_CodeSniffer) 45 | if !exists("g:phpqa_codesniffer_cmd") 46 | let g:phpqa_codesniffer_cmd='phpcs' 47 | endif 48 | 49 | " Arguments to pass to code sniffer, e.g standard name 50 | if !exists("g:phpqa_codesniffer_args") 51 | let g:phpqa_codesniffer_args="" 52 | endif 53 | 54 | " PHPMD binary (mess detector) 55 | if !exists("g:phpqa_messdetector_cmd") 56 | let g:phpqa_messdetector_cmd='phpmd' 57 | endif 58 | 59 | " Rule set built-in or XML file for mess detector, comma separated 60 | if !exists("g:phpqa_messdetector_ruleset") 61 | let g:phpqa_messdetector_ruleset="codesize,unusedcode,naming" 62 | endif 63 | 64 | " Clover code coverage file 65 | if !exists("g:phpqa_codecoverage_file") 66 | let g:phpqa_codecoverage_file = "" 67 | endif 68 | 69 | " Whether to automatically show code coverage on file load 70 | if !exists("g:phpqa_codecoverage_autorun") 71 | let g:phpqa_codecoverage_autorun = 0 72 | endif 73 | 74 | " Whether to show signs for covered code (or only not covered) 75 | " It may speed things up to turn this off 76 | if !exists("g:phpqa_codecoverage_showcovered") 77 | let g:phpqa_codecoverage_showcovered = 1 78 | endif 79 | 80 | " Whether to show signs for covered code (or only not covered) 81 | " It may speed things up to turn this off 82 | if !exists("g:phpqa_codecoverage_regex") 83 | let g:phpqa_codecoverage_showcovered = 1 84 | endif 85 | 86 | " Whether to automatically run codesniffer when saving a file 87 | if !exists("g:phpqa_codesniffer_autorun") 88 | let g:phpqa_codesniffer_autorun = 1 89 | endif 90 | 91 | " Whether to automatically run messdetector when saving a file 92 | if !exists("g:phpqa_messdetector_autorun") 93 | let g:phpqa_messdetector_autorun = 1 94 | endif 95 | 96 | " Whether qa tools should run on buffer write 97 | if !exists("g:phpqa_run_on_write") 98 | let g:phpqa_run_on_write = 1 99 | endif 100 | 101 | " Whether to open the location list automatically with CodeSniffer/Mess 102 | " detector violations 103 | if !exists("g:phpqa_open_loc") 104 | let g:phpqa_open_loc = 1 105 | endif 106 | 107 | 108 | " Run all QA tools 109 | function! PhpqaRunAll() 110 | if &filetype == 'php' 111 | " Check syntax valid before running others 112 | let retval=Phpqa#PhpLint() 113 | if 0 == retval && 1 == g:phpqa_run_on_write 114 | call Phpqa#PhpQaTools(g:phpqa_codesniffer_autorun,g:phpqa_messdetector_autorun) 115 | endif 116 | endif 117 | endf 118 | 119 | " Run code coverage 120 | function! PhpqaRunCodeCoverage() 121 | if &filetype == 'php' 122 | if "" != g:phpqa_codecoverage_file && 1 == g:phpqa_codecoverage_autorun 123 | call Phpqa#PhpCodeCoverage() 124 | endif 125 | endif 126 | endf 127 | 128 | if !hasmapto('CodeCoverageToggle', 'n') 129 | nmap qc CodeCoverageToggle 130 | endif 131 | nnoremap