Robert Basic's blog

Posts tagged 'vim'

Current Vim setup for PHP development

by Robert Basic on February 10, 2017.

I made some changes to my Vim setup for PHP development recently, so it’s time to write it all down. I’m more than sure that I’ll break it soon and won’t be able to remember all the things I did to have the current setup.

Some new plugins popped up on my radar, I tweaked some older plugins and I even wrote one for PHPStan!

Last year I wrote how I got really good tag support in Vim, so I’ll first expand on that.

Gutentags

Gutentags is probably the plugin that had the biggest impact on my workflow. It made possible many other functionalities and plugins to just work.

First thing I have configured for it is the location for the tag files:

" Where to store tag files
let g:gutentags_cache_dir = '~/.vim/gutentags'

The second is a more “aggressive” excluding of files from generating tags:

let g:gutentags_exclude = ['*.css', '*.html', '*.js', '*.json', '*.xml',
                            \ '*.phar', '*.ini', '*.rst', '*.md',
                            \ '*vendor/*/test*', '*vendor/*/Test*',
                            \ '*vendor/*/fixture*', '*vendor/*/Fixture*',
                            \ '*var/cache*', '*var/log*']

Takes out a lot of rubbish.

I also have the following in the global ~/.ctags configuration file (gutentags uses ctags under the hood to generate the tags):

--PHP-kinds=+cfit-va

This way I get tags for classes, interfaces, functions, namespaces and traits, while variables and aliases are ignored to remove the noise level.

Jump to definition

I paired CtrlP’s CtrlPTag method with gutentags tag files to get jump to definition functionality:

map <silent> <leader>jd :CtrlPTag<cr><C-\>w

My complete CtrlP settings are here.

Current PHP class and method

A combination of three Vim plugins and a PHP phar file gives me the possibility to show in the status bar the current PHP class and method.

Lightline provides the status bar, tagbar to get the current tag, tagbar-phpctags to generate the tags for the current file using phpctags.

Use the following setting to tell tagbar about the phpctags binary:

let g:tagbar_phpctags_bin='~/.vim/phpctags'

This is the tagbar line for lightline:

'tagbar': '%{tagbar#currenttag("[%s]", "", "f")}',

See here how it all fits together.

Now that I wrote it all down, I should take a look if I could use the gutentags tag files for this. Seems like an awful lot of moving parts for a relatively small feature.

PHP namespaces

The vim-php-namespace plugin provides support for inserting use statements in PHP code. It will use the tag files generated by gutentags, there’s no need to set up anything for that.

I have the following mapping to insert the use statements:

function! IPhpInsertUse()
    call PhpInsertUse()
    call feedkeys('a',  'n')
endfunction
autocmd FileType php inoremap <Leader>pnu <Esc>:call IPhpInsertUse()<CR>
autocmd FileType php noremap <Leader>pnu :call PhpInsertUse()<CR>

and this one for expanding classes to get their fully qualified names:

function! IPhpExpandClass()
    call PhpExpandClass()
    call feedkeys('a', 'n')
endfunction
autocmd FileType php inoremap <Leader>pne <Esc>:call IPhpExpandClass()<CR>
autocmd FileType php noremap <Leader>pne :call PhpExpandClass()<CR>

I also let it to automatically sort the namespaces after inserting:

let g:php_namespace_sort_after_insert=1

Linting

ALE is an Asynchronous Lint Engine and it provides linting for Vim 8. It can do linting for a bunch of languages.

My configuration for it is:

let g:ale_linters = {
\   'php': ['php'],
\}
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 0

Lints PHP files on save, in the background. A must have!

A promising completion engine for PHP

php.cd finally provides useful completion for PHP. It’s still rough around the edges, misses a feature or two, but I find it a lot better than any other completion engine I used before. No need to configure anything for it, just follow their installation instructions and that’s it. ^X^O all the things!

PHPStan in Vim

PHPStan is a static analysis tool for PHP and I wrote a small plugin for it, vim-phpstan. It calls phpstan from Vim and populates Vim’s quickfix list with the errors.

For now, the only possible configuration is to set the analyse level:

let g:phpstan_analyse_level = 2

Debugging

Sadly, there is no good PHP debugging client for Vim. Or none that I know of. There are a couple of them out there, but they seem long abandoned. I work on a standalone PHP debugging client, pugdebug, but it has it’s own set of problems as well (packaging on Linux is a nightmare).

Supporting plugins

Other “supporting” plugins are 2072/PHP-Indenting-for-VIm, 2072/vim-syntax-for-PHP, sirver/ultisnips, plugins from the sniphpets organization, ddrscott/vim-side-search, robertbasic/vim-argument-swapper.

I’m pretty happy with the current setup. Do you know maybe of any interesting plugin I’m missing? Let me know!

Happy hackin’!

Tags: ale, completion, gutentags, linting, namespaces, php, phpcd, phpctags, phpstan, plugins, tagbar, vim.
Categories: Development, Programming.

Need help on your PHP projects? Let's talk!

Search and replace in visual selection in Vim

by Robert Basic on January 23, 2017.

The search and replace feature is very powerful in Vim. Just do a :help :s to see all the things it can do.

One thing that always bothered me though, is that when I select something with visual, try to do a search and replace on it, Vim actually does it on the entire line, not just on the selection.

What the…? There must be a way to this, right?

Right. It’s the \%V atom.

Instead of doing :'<,'>s/foo/bar/g to replace foo with bar inside the selection, which will replace all foo occurences with bar on the entire line, the correct way is to use the \%V atom and do :'<,'>s/\%Vfoo/bar/g.

I’m using this approach in the HugoHelperLink fuction of my Vim Hugo Helper plugin.

Happy hackin’!

Tags: replace, search, vim.
Categories: Blablabla, Software.

Need help on your PHP projects? Let's talk!

Force Python version in Vim

by Robert Basic on January 12, 2017.

Vim can be compiled with Python support. Vim can be compiled with both Python 2 and Python 3 support.

At the same time.

But not really.

Vim can have both of them, but use only one at a time. If you start using one version, there is no way to switch to the other one.

The silly thing is that if you simply ask Vim which version does it support, the first one asked and supported is going to be the one loaded and used. Trying to use the other one from that point will result in an error.

if has('python')
elif has('python3')
endif

Guess which one is loaded? Python 2.

Try calling Python 3 and ka-boom!

:py3 print('hello')
E836: This Vim cannot execute :py3 after using :python

Switch the order around:

if has('python3')
elif has('python')
endif

And now? Yup, Python 3.

Why is this ridiculous? Because if you have a bunch of Vim plugins loaded, the first one that asks for a specific Python version wins! Reorder the plugins and suddenly a different Python version is loaded.

Gotta love the software development world.

Luckily, this can also be used to fix the problem itself.

How?

Force one of the Python versions from the top of your .vimrc file:

if has('python3')
endif

Now you can have a little bit of sanity and be sure what Python version is Vim going to use. Of course, doing this might break plugins written solely for Python 2, so do it at your own risk.

Happy hackin’!

Tags: python, vim, vimrc.
Categories: Development, Software.

Need help on your PHP projects? Let's talk!

Editing Vim macros

by Robert Basic on December 15, 2016.

Vim macros are a powerful thing — they let us record keystrokes and play them back later. These macros are recorded to named registers.

One thing I realised about them, is that they can be edited after they have been recorded. This is possible because macros “lives” in the register.

Say, for example, you record a macro of 20+ keystrokes, play it back, only to realize that there’s a single error in the steps. Re-recording the entire macro can be difficult. Instead, paste the contents of that register somewhere, edit it, and then yank it back to that same register.

For a simple example, let’s assume we want to add * around words. We record it to the register a by typing qa (the ^[ is the literal escape character):

bi&^[ea&^[

Play it back with @a and — oh no! that’s not a *, that’s a &!

Vim macro editing to the rescue:

:new # to open a new split
"ap # take the register named "a" and paste from it
:%s/&/*/g # replace all & with *
^v$"ay # jump to start of line, visual mode, jump to end of line, take the named register "a" and yank to it

If we now play back the macro again with @a, we see the *s wrapping the word on which the cursor was, just what we wanted.

Happy hackin’!

Tags: macro, registers, vim.
Categories: Development, Software.

Need help on your PHP projects? Let's talk!

Import custom Python modules in Vim plugins

by Robert Basic on June 16, 2016.

This took me a while to figure out so I’m writing it down for future self and anyone else who needs it.

I started writing a new Vim plugin that will use the Python interface as most of the work will be done there, mostly to keep my sanity.

Having a plugin layout such as:

.
├── lib
│   └── mypymodule
│       └── ham.py
└── plugin
    └── my-vim-plugin.vim

I want to be able to do a

from mypymodule import ham

from within the my-vim-plugin.vim file.

For that to happen the <sfile> command line special comes to rescue. It is the file name of the sourced file in Vim, that is, the file name of the Vim plugin. Using the :p and :h file name modifiers it gives us the full path to the plugin directory of our plugin.

" This will give something like
" /home/robert/projects/my-vim-plugin/plugin
let g:plugin_path = expand('<sfile>:p:h')

And here comes the kicker: the <sfile> needs to be expanded outside of our Vim function where it is used, otherwise the <sfile> points to the path of the file that called the Vim function.

In code, getting the path to the sourced file from within the Vim function would be incorrect:

" ./plugin/my-vim-plugin.vim
function! MyVimPlugin()
python << endpython

import vim

vim.command("let a:plugin_path = expand('<sfile>:p:h')
plugin_path = vim.eval("a:plugin_path")
print plugin_path

endpython
endfunction

because it would end up printing the current working directory from where the MyVimPlugin function is called.

The correct way to do is to get the path to the sourced file outside of the Vim function:

" ./plugin/my-vim-plugin.vim
let g:plugin_path = expand('<sfile>:p:h')

function! MyVimPlugin()
python << endpython

import vim

vim.command("let a:plugin_path = expand('<sfile>:p:h')
plugin_path = vim.eval("a:plugin_path")
print plugin_path

endpython
endfunction

This way the path is set at the time the plugin is sourced and not at the time when function is called.

Finally, to be able to import the mypymodule from the lib, we need to point to the lib directory and add it to the system paths. Complete example:

" ./plugin/my-vim-plugin.vim
let g:plugin_path = expand('<sfile>:p:h')

function! MyVimPlugin()
python << endpython

import os
import sys
import vim

" Get the Vim variable to Python
plugin_path = vim.eval("g:plugin_path")
" Get the absolute path to the lib directory
python_module_path = os.path.abspath('%s/../lib' % (plugin_path))
" Append it to the system paths
sys.path.append(python_module_path)

" And import!
from mypymodule import ham

endpython
endfunction

By the way, here’s the documentation for the Vim Python module.

Happy hackin’!

Tags: plugin, python, vim.
Categories: Development, Programming.

Need help on your PHP projects? Let's talk!