Skip to Content

How I use Vim

Posted on

vimrc.png

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

2021-10-14 23.39.49.gif

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

2021-10-14 23.50.33.gif

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 8020, 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à: gfAlternate 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à: amim tương ứng với a methodin 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)

2021-10-15 00.40.13.gif

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>

2021-10-15 01.04.17.gif

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

2021-10-15 01.13.36.gif

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()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:

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.

comments powered by Disqus