How I use Vim
Tản mạn
Đã 5 năm kể từ ngày đầu tiên mình viết 1 bài về Vim: How I meet Vim. Sau 1 quãng thời gian khá lâu sử dụng Vim, mình có thể khẳng định rằng Vim đã thay đổi mình rất nhiều, cả về tốc độ edit text at speed of thought lẫn mindset cần có khi lập trình. Và cho đến tận bây giờ, Vim vẫn không ngừng làm mình bất ngờ vì những điều nó có thể làm được.
Thời gian gần đây, mình vẫn nhận được thông báo từ Viblo vì bài viết Kinh nghiệm khi làm việc với Vim được upvote/bookmark, nên mình nghĩ mình nên chia sẻ thêm 1 vài điều gì đó. Và mình quyết định dọn dẹp lại file .vimrc
hiện tại, chỉ giữ lại những gì mình nghĩ là cần thiết để áp dụng trong workflow hàng ngày của mình.
Bài viết tổng hợp 1 số Tips & Tricks mình hay dùng trong quá trình làm việc. Hy vọng có thể giúp ích cho các bạn :)
Things I want to share
Hiện tại mình đang chia .vimrc
thành 5 phần chính:
- Base setup: Config cơ bản cho Vim (set nocompatible, filetype, shell, mouse, …)
- Plugins: Những plugins mình đang sử dụng.
- Appearance
- Custom Mappings & Actions: Phần này gồm những mappings của mình
- Custom Functions
1. Theme
Sau 5 năm sử dụng vim-railscasts theme, mình quyết định chuyển qua theme gruvbox. Lý do chỉ là vì nhiều người dùng theme này quá, và mình thì muốn đổi gió 1 chút :v
Plug 'morhetz/gruvbox'
colorscheme gruvbox
set background=dark
2. Settings, Mappings & Actions
2.1 Share clipboard between Vim and OS
Mặc định thì những gì bạn copy từ ứng dụng khác (system clipboard) sẽ không paste trực tiếp vào VIM ở normal mode được. Và những gì bạn copy trong VIM cũng không paste trực tiếp sang ứng dụng khác (Google Chrome chẳng hạn) được. Config này giúp share clipboard giữa Vim & OS.
set clipboard^=unnamed,unnamedplus
Source: Accessing the system clipboard
2.2 Set number and relative number
Set number để mình nhìn được số dòng trong file. Relative number để mình nhìn số dòng dễ hơn khi muốn di chuyển lên/xuống x dòng bằng cách sử dụng: 20j
hoặc 20k
set number " Line number are good
set relativenumber " Relative number to quickly run command
2.3 Resizing windows
Config giúp resize pane, windows trong vim.
"\\ Resizing Windows
nnoremap <Leader><Leader>= <C-w>=
nnoremap <Leader><Leader><UP> <C-W>_
nnoremap <Leader><Leader><DOWN> <C-W>=
nnoremap <Leader><Leader><LEFT> <C-W>\|
nnoremap <Leader><Leader><RIGHT> <C-W>\|
nnoremap <Leader><Leader>k <C-w>+10
nnoremap <Leader><Leader>j <C-w>-10
nnoremap <Leader><Leader>h <C-w><10
nnoremap <Leader><Leader>l <C-w>>10
2.4 Swap lines
" Move a line of text using Leader+[jk]
nmap <Leader>j mz:m+<cr>`z
nmap <Leader>k mz:m-2<cr>`z
vmap <Leader>j :m'>+<cr>`<my`>mzgv`yo`z
vmap <Leader>k :m'<-2<cr>`>my`<mzgv`yo`z
2.5 Recall command in history
Ví dụ bạn đã từng gõ lệnh :e ~/Desktop/test.txt
, sau đó thực hiện 1 loạt các command như :w
, :q
, … Bây giờ nếu muốn edit lại file test.txt
, bạn chỉ cần gõ :e ~/D
và gõ Control + p
, Vim sẽ tự tìm command match với những bì bạn gõ trong history.
"\\ Use C-p and C-n for calling command in history
cnoremap <C-p> <Up>
cnoremap <C-n> <Down>
2.6 Delete trailing space
Thêm config này để tự động xóa trailing space, tránh bị đồng đội comment khi gửi PR nhé :v
"\\ Delete all trailing space when saving file
autocmd BufWritePre * :%s/\s\+$//e
2.7 Edit a temp file
Đôi lúc mình muốn tạo 1 temp file để take note, sau đó lưu lại file nếu cần, còn không thì chỉ take note rồi xóa file đó đi. Mình thường sử dụng mapping này:
"\\ Edit a temporary file
nnoremap <Leader><Leader>tp :exec "e " . tempname()<cr>
1 file trong thư mục /tmp
sẽ được tạo (với hệ điều hành MacOS sẽ là thư mục khác). Sau đó nếu mình muốn lưu lại file này thì sẽ dùng :w ~/Desktop/abc.txt
chẳng hạn. Còn nếu không cần lưu lại thì mình cứ save quit thôi, OS sẽ tự động dọn dẹp giúp mình. :D
3. Plugins
3.1 vim-rails
Plug 'tpope/vim-rails'
Nếu bạn theo nguyên tắc 80⁄20, và muốn chọn ra 20% features mang lại 80% hiệu quả của vim-rails thì mình xin giới thiệu 2 tính năng hay nhất, đó là: gf
và Alternate and Related Files
gf
cho phép bạn jumping giữa các file. Ví dụ con trỏ của bạn đang ở vị trí * trong Pos*t.first
, nếu bạn gõ gf
thì Vim sẽ tự động nhảy tới models/post.rb
. Nếu muốn mở trong cửa sổ khác, bạn có thể dùng: C-w + f
. Tương tự cho các action jump giữa file views, test, partial, …
(* là vị trí hiện tại của con trỏ)
Pos*t.first
app/models/post.rb
has_many :c*omments
app/models/comment.rb
link_to 'Home', :controller => 'bl*og'
app/controllers/blog_controller.rb
<%= render 'sh*ared/sidebar' %>
app/views/shared/_sidebar.html.erb
<%= stylesheet_link_tag 'appl*ication' %>
app/assets/stylesheets/application.css
class BlogController < Applica*tionController
app/controllers/application_controller.rb
class ApplicationController < ActionCont*roller::Base
.../action_controller/base.rb
fixtures :pos*ts
test/fixtures/posts.yml
layout :pri*nt
app/views/layouts/print.html.erb
<%= link_to "New", new_comme*nt_path %>
app/controllers/comments_controller.rb (jumps to def new)
Feature thứ 2 là Alternate and Related Files
. Ví dụ bạn đang ở trong model app/models/article.rb
. Nếu bạn gõ command :AV
thì Vim sẽ mở ra 1 split pane để edit Alternate file
của article.rb
file, đó là file unit test của model này spec/models/article_spec.rb
. Tương tự cho Related file (với command :RV
).
Current file | Alternate file | Related file |
---|---|---|
model | unit test | schema definition |
controller (in method) | functional test | template (view) |
template (view) | functional test | controller (jump to method) |
migration | previous migration | next migration |
database.yml | database.example.yml | environments/*.rb |
Ngoài ra, nếu dự án của bạn sử dụng request spec thay vì controller spec thì bạn có thể add thêm config này để dạy vim-rails cách tìm file spec tương ứng:
"------------------------------------------------------
"\\ Rails.vim
let g:rails_projections = {
\ "app/controllers/*_controller.rb": {
\ "test": [
\ "spec/controllers/{}_controller_spec.rb",
\ "spec/requests/{}_spec.rb"
\ ],
\ },
\ "spec/requests/*_spec.rb": {
\ "alternate": [
\ "app/controllers/{}_controller.rb",
\ ],
\ }}
Read more with :h rails
3.2 vim-ruby
Plug 'vim-ruby/vim-ruby'
Plugin này giúp indent, highlight, … cho các file ruby. Ngoài ra mình mới phát hiện thêm 1 điều nữa là plugin này còn cung cấp 1 loại text object nữa, đó là: am
và im
tương ứng với a method
và in method
. Như thế chúng ta có thể sử dụng cam
- change a method, hoặc cim
- change in method.
3.3 nerdtree
Plug 'scrooloose/nerdtree'
Trước giờ mình dùng NERDTree khá nhiều (chủ yếu là để cho nó trong giống với editor khác như VSCode :v). Thao tác thêm/sửa/xóa folder/files cũng khá tiện. Lần sửa lại file .vimrc
này, mình quyết định đổi lại mapping của NERDTree, thay mapping Toggle nerdtree từ F5
về thành -
.
Ngoài ra mình cũng phát hiện thêm 1 command mới là: :NERDTreeFind
, giúp mình mở locate current file trên NERDTree.
"------------------------------------------------------
"\\ NERDTree
map - :NERDTreeToggle<CR>
map ] :NERDTreeFind<CR>
3.4 ag
Plug 'rking/ag.vim'
Mình dùng the silver searcher, và plugin này giúp search content trong project.
Nhu cầu dùng của mình thì có: Search từ khóa trong toàn bộ project. Search từ khóa chỉ trong các file ruby. Search 1 từ vừa được copy.
Đối với phần Search 1 từ vừa được copy, trong normal mode, mình gõ yw
để copy từ, sau đó gõ <Space><Space>d
để search từ đó, đỡ phải gõ <Space>g từ-khóa
để search.
"------------------------------------------------------
"\\ Ag
map <Leader>g :Ag<Space>
"\\ Search in project with word in clipboard
nmap <Leader><Leader>d :Ag <c-r>"<CR>
"\\ Search with specific file type
nmap <Leader><Leader>v :Ag --ruby<Space>
3.5 Custom snippets
Như trong bài này, mình có khuyên mọi người nên tự viết snippets cho riêng mình. Nhưng cách ngày đó mình chọn là tạo thêm file vào trong thư mục của vim-snippets
, như thế không ổn khi mình cài đặt trên máy khác. Sau đó mình đã tìm được cách khác đơn giản hơn. Mình tự tạo 1 repo my-vim-snippets, sau đó add vào .vimrc
như 1 plugin
"\\ Snippets
Plug 'SirVer/ultisnips'
Plug 'ttuan/my-vim-snippets'
Lưu ý là bạn vẫn cần Ultisnips
nhé :D
3.6 vim-easy-motion
Plug 'easymotion/vim-easymotion' " Moving around
Really nice plugin. Mình sử dụng plugin này rất nhiều (ngoài ra mình cũng dùng vimium với tính năng tương tự nhưng là cho Chrome)
3.7 vim-fugitive
Plug 'tpope/vim-fugitive'
Sử dụng plugin này bạn có thể thao tác với Git mà không cần phải chuyển sang terminal. =)))
Higly recommended bạn xem video này: Using Vim: Vim + Git - Fugitive Part 1 - Usage and Merge Conflicts để thấy công dụng của vim-fugitive
:v
"------------------------------------------------------
"\\ Vim Fugitive
nmap <Leader>gs :G<CR>
nnoremap <Leader>bl :Git blame<CR>
nmap <Leader>gf :diffget //2<CR>
nmap <Leader>gj :diffget //3<CR>
Read more:
3.8 fzf
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'
fzf quả nổi tiếng và mình không có gì để lăng xê hơn =)) Dưới đây là các mapping của mình khi dùng fzf.
Riêng đối với phần search theo tag, mình sử dụng thêm ctags
kèm với git hook để trigger, sinh ra file tags
. FZF sẽ dựa vào file tags
này để find/jump to definition.
"------------------------------------------------------
"\\ FZF
nnoremap <silent> <C-f> :Files<CR>
nnoremap <C-p> :GFiles<CR>
nnoremap <silent> <C-s> :Snippets<CR>
nnoremap <Leader>fg :GFiles?<CR>
nnoremap <Leader>fn :Buffers<CR>
nnoremap <Leader>fc :Commits<CR>
nnoremap <Leader>fm :Marks<CR>
nnoremap <Leader>fC :BCommits<CR> " Git commits for current buffer
nnoremap <Leader>ft :Tags<CR>
let g:fzf_layout = { 'down': '40%' }
3.9 vim + tmux
"\\ Vim + Tmux
Plug 'christoomey/vim-tmux-navigator' " Moving between tmux & vim: https://gist.github.com/mislav/5189704
Plug 'benmills/vimux'
Refer: Tips & tricks khi làm việc với Vim và Tmux
Vim ❤️ Tmux =))
vimux
là plugin giúp mình không phải nhảy từ vim sang tmux pane. Mình thường dùng vimux
để:
- Run test cho current file
- Run test under cursor
- bundle install (sau khi add gem mới thì sẽ chạy bundle)
- Run command bất kì ở terminal
"------------------------------------------------------
"\\ Vmux - Run, Forrest, Run
nnoremap <silent> <Leader>vl :VimuxRunLastCommand<CR>
" Run rspec file
nnoremap <silent> <Leader>rs :call VimuxRunCommand("clear; rspec " . bufname("%"))<CR>
" Run rspec test case
nnoremap <silent> <Leader>rt :call VimuxRunCommand("clear; rspec " . bufname("%") . ":" . line("."))<CR>
" Run bundle install
nnoremap <silent> <Leader>bi :call VimuxRunCommand("clear; bundle install")<CR>
3.10 Tabular
Plug 'godlygeek/tabular' " Text filtering and alignment
Và extension cuối cùng mình muốn giới thiệu tới bạn đó là tabular
. Mình thường dùng tabluar
để align nội dung sao in ra nhìn cho đẹp mắt :v
"------------------------------------------------------
"\\ Tabular
if exists(":Tabularize")
nmap <Leader>a= :Tabularize /=<CR>
vmap <Leader>a= :Tabularize /=<CR>
nmap <Leader>a: :Tabularize /:\zs<CR>
vmap <Leader>a: :Tabularize /:\zs<CR>
endif
Read more: http://vimcasts.org/episodes/aligning-text-with-tabular-vim/
4. Functions
Nếu được giới thiệu functions mình hay dùng nhất, thì mình sẽ nói về GoGithubLine()
và GoGithubCommit()
. Đây là 2 functions mình viết để giúp lấy link Github, sau đó gửi qua chat để trao đổi dễ hơn.
Function trong .vimrc
"\\ Go to current line in github
function! GoGithubLine()
let current_file = @%
let current_line = line('.')
execute 'Silent goGithubLine' current_file current_line
endfunction
nnoremap <Leader><Leader>ggl :call GoGithubLine()<cr>
"\\ Go to Github with commit hash
function! GoGithubCommit()
let commit_hash = expand("<cword>")
execute 'Silent goGithubCommit' commit_hash
endfunction
nnoremap <Leader><Leader>ggc :call GoGithubCommit()<cr>
2 functions này sẽ lấy ra tên file, line hoặc git commit, sau đó mình truyền tham số này vào 2 hàm bên zsh.
# ~/.zshenv
# Non interactive app will load source in this file. So I move this function here to Vim can using it
goGithubLine() {
file_name=$1
line=$2
repo_url=$(git config --get remote.upstream.url)
repo_name=(${=repo_url//:/ }) # Zsh split string to arr T.T
rn=(${=repo_name[2]//./ })
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome "https://github.com/${rn[1]}/blob/develop/${file_name}#L${line}"
}
goGithubCommit() {
commit_hash=$1
repo_url=$(git config --get remote.upstream.url)
repo_name=(${=repo_url//:/ }) # Zsh split string to arr T.T
rn=(${=repo_name[2]//./ })
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome "https://github.com/${rn[1]}/commit/${commit_hash}"
}
5. Others
Ngoài ra, trong quá trình dọn dẹp .vimrc
, mình còn học thêm được 1 vài điều:
- Vim list changes to current file
- Different between wq and x
- Vim indent inline comments
- Use Ctrl + ] to jump to definition
- How to debug Vim mappings
Tổng kết
Trên đây là các tips, tricks mình hay dùng vơi Vim trong công việc hàng ngày. Hy vọng có thể giúp ích được cho các bạn.
Bạn cũng có thể xem full nội dung .vimrc
của mình ở ttuan/dotfiles
Và nếu các bạn có những tips/tricks hữu ích khác, hãy chia sẻ/ góp ý cho mình với nhé :D Cám ơn các bạn đã đọc bài viết.