Setting up Vim for PHP and laravel in 2024
Tags: vim, coc, php, laravel
Published: 2024-06-17 02:05:54

I am going to mostly document my own journy of setting up Vim (Not Neovim!) for PHP and laravel. Feel free to follow along.

We're going to use Vim+VimPlug+CoC.nvim, so we need to have nodejs and npm installed. It goes without saying that you should have Vim8+ as well. The current version of Vim as of this writing is Vim-9.1 <br> 

So let's start from a clean `~/.vimrc`

#### Lets place this block, that automatically installs VimPlug, on top of our `~/.vimrc`.
```vim
function! VimplugInstaller()
  let data_dir = has('nvim') ? stdpath('data') . '/site' : '~/.vim'
  if empty(glob(data_dir . '/autoload/plug.vim'))
    silent execute '!curl -fLo '.data_dir.'/autoload/plug.vim --create-dirs  https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
    autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
  endif
endfunction

call VimplugInstaller()
```

#### Followed by several essential plugins:

```vim
call plug#begin()

Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'https://github.com/preservim/tagbar.git'
Plug 'vim-php/phpctags', {'for':'php'}
Plug 'honza/vim-snippets'
Plug 'SirVer/ultisnips'
Plug 'tpope/vim-commentary'
Plug 'sheerun/vim-polyglot'
Plug 'jwalton512/vim-blade'

call plug#end()
```

The most important one, is coc.nvim. The rest are for various enhancements, that we'll go over soon, but before we do, let's define some extensions for coc.nvim and configure the other plugins.

```vim
"===[Coc.nvim]==="
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#_select_confirm() : "\<C-g>u\<CR>"
" use <tab> to trigger completion and navigate to the next complete item
function! CheckBackspace() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

inoremap <silent><expr> <Tab>
      \ coc#pum#visible() ? coc#pum#next(1) :
      \ CheckBackspace() ? "\<Tab>" :
      \ coc#refresh()
inoremap <expr><s-tab> coc#pum#visible() ? coc#pum#prev(1) : "\<c-h>"

" Enhanced <CR> for pairs
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
			\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

" Disable < expansion for pairs
autocmd FileType * let b:coc_pairs_disabled = ["<"]

"===[ Coc-Explorer ]==="
" set up coc-explorer to open in the current directory
let g:coc_explorer_global_mirror = 0
let g:coc_explorer_disable_default_keybindings = 1
let g:coc_explorer_global_root = 'current'
nnoremap <space>e <Cmd>CocCommand explorer<CR>

"===[ Coc Global Extensions ]==="
let g:coc_global_extensions = [
  \ 'coc-html',
  \ 'coc-css',
  \ 'coc-pairs',
  \ 'coc-emmet',
  \ 'coc-explorer',
  \ 'coc-phpactor',
  \ 'coc-blade'
\ ]

" GoTo code navigation
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)

" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
"
" List code actions available for the current buffer
nmap <leader>ca  <Plug>(coc-codeaction)

nnoremap <silent> K :call ShowDocumentation()<CR>
function! ShowDocumentation()
  if CocAction('hasProvider', 'hover')
    call CocActionAsync('doHover')
  else
    call feedkeys('K', 'in')
  endif
endfunction

" Add `:Format` command to format current buffer
command! -nargs=0 Format :call CocActionAsync('format')
" Formatting selected code
vmap <leader>f  <Plug>(coc-format-selected)

" coc-pairs
" disable characters for a specified filetypes
autocmd FileType markdown let b:coc_pairs_disabled = ['txt']
```
The above code, configures tab completion, auto pairs, coc-explorer file explorer and other keybinding to get the most out of coc.nvim.
In the coc global extension section, we define extensions to be installed automatically. I have decided to use phpactor, you may change it for `coc-intelephense`.

#### Lets also configure the snippets:

We can manage our snippets with coc.nvim too, but I choose to use UltiSnips for that.
```vim
"===[ SNIPPETS ]==="
"Use Ctrl j key to trigger the snippets, default was TAB but that conflicts with
"The Completion trigger see :h keycodes to change this to sth else 
"Use Ctrl j and k to move visually within the snippet that was just triggered
"Ctrl l lists the available snippets
let g:UltiSnipsExpandTrigger='<C-j>'
let g:UltiSnipsListSnippets='<C-l>'
let g:UltiSnipsJumpForwardTrigger='<C-j>'
let g:UltiSnipsJumpBackwardTrigger='<C-k>'
let g:copypath_copy_to_unnamed_register = 1
```
Save and quit Vim and open it again and wait for the plugins and extensions to install. <br> 
If you get prompted by an error msg, just press enter and wait for the plugins and the extensions to install.

##### It's better to install the laravel extension manually:
`:CocInstall @yaegassy/coc-laravel`

#### Now lets configure the coc extensions.

Issue the `:CocConfig` command from Vim's command mode and paste the following, inside the file.

```json
{
    "suggest.noselect": true,
	"explorer.icon.enableNerdfont": true,
	"explorer.file.reveal.auto": true
    "languageserver": {
        "phpactor": {
            "command": "phpactor",
            "args": ["language-server"],
            "trace.server": "verbose",
            "filetypes": ["php"]
        }
    }
}
```
If you choose to use a local installation of phpactor, you can just remove `coc-phpactor` with `:CocUninstall coc-phpactor` and modify the `~/.vim/coc-settings.json` to include a path to the phpactor executable and remove the languageserver block entirely.
```json
{
    "phpactor.enable": true,
    "phpactor.path": "/usr/bin/phpactor",
}
```

#### The keybindings:
The only keybindings that you should know, besides the usual Vim keybindings, are:

| Key | Action |
|---|---|
| Tab | completion |
| gd | definition |
| gy | type definition |
| gi | implementation |
| gr | references|
| [g | diagnostics prev |
| ]g | diagnostics next |
| <Leader>ca| code actions |
| K | hover docs |
| <Leader>f (in visual mode) | format code |
| Ctrl-j | Expand Snippet & Move to Next Placeholder |
| Ctrl+k | Move to Prev Placeholder |
| Ctrl-l | List Snippets |
|  Space-e| Open Explorer |

Now we should have an efficient and comfortable setup for working with PHP and laravel.

#### Lets setup several other things for convenience

#### Theme:
Add these plugins to the plugins list and install them with `:PlugInstall`  after sourcing the `~/.vimrc`.

```vim
Plug 'ryanoasis/vim-devicons'
Plug 'catppuccin/vim', { 'as': 'catppuccin' }

```

#### Setup Vim to use the popular catppuccin theme. 

```vim
"===[ Theme ]==="
set background=dark
" Inspect $TERM instead of t_Co
if &term =~ '256color'
	" Enable true (24-bit) colors instead of (8-bit) 256 colors.
	if has('termguicolors')
		let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum"
		let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum"
		let &t_TI = ""
		let &t_TE = ""
		set termguicolors
		set mouse=a
		colorscheme catppuccin_mocha
	endif
	else
		colorscheme catppuccin_mocha
		let &t_TI = ""
		let &t_TE = ""
endif

if has('gui_running')
	set mouse=a
	set guicursor+=a:blinkon0
	set guifont=FiraCodeNerdFont\ 12
	colorscheme catppuccin_mocha
endif

"===[ Curosr Shape ]==="
let &t_SI = "\<Esc>[6 q"
let &t_SR = "\<Esc>[4 q"
let &t_EI = "\<Esc>[2 q"
```

The above block, will make vim use termgui colors (in a termgui capable terminal) and use different cursor shapes for different modes.<br> Make sure your `$TERM` variable reports `xterm-256color`.

In the screenshot below, you can see phpactor, coc-explorer and tagbar at work. <br> I've left an error in the syntax to show you what the inline diagnostics look like.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m4utqvw2yzsuov37xbe3.png)

It would do you well to familiarize yourself with the coc.nvim documentation and commands.<br>
For example, running `:CocDiagnostics` will open a drawer with all the errors found by coc.nvim.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xmpf6v3ehn7qfo75q9bc.png)

Pressing enter on each item will jump you to the line containing the error.

I could not find a decent phpdoc plugin for Vim, but besides that, this setup is very effective.

You can find my full config on github, [Woland's Vim](https://github.com/wolandark/wim/tree/Devel) and my personal `~/.vim` [here](https://github.com/wolandark/.vim).

If you have any notes or questions, please leave a comment. 

# Where to go from here
Start using the setup and tweak it to your needs, little by little. This is not a drop and go config, it's just a kickstarter to put it plainly.

#### Some general notes:
- Familiarize yourself with Vim's buffers. 
- NEVER `:q` Vim, unless you're done for the day
- Learn about `:vimgrep` and `:lvimgrep`
- Integrate fzf, ack, ag, rg into your workflow, (you need to quickly search large projects)


# Possible Q&A

Can I use this with Nvim? 
Yes, you just have to paste all the code into `~/.config/nvim/init.vim`

Why not use Nvim with lspconfig and cmp etc ... ?
Because it takes way longer to configure and the results aren't better than what coc provides with so much ease.