Vim - Các phím tắt

các phím tắt tôi dùng trong Vim
Mục lục

Sau phần một của chuỗi bài viết về Vim, hôm nay tôi sẽ nói về các phím tắt tôi dùng, một vài phím tắt tôi học lỏm được trên mạng, các dotfiles của những người đi trước. Vì đây là bài viết thứ hai trong chuỗi nên tôi sẽ không giới thiệu dài dòng nữa, bắt đầu nhé!

1. Cơ bản

Ghi chú

Trong bài viết này sẽ có hai dạng biểu diễn phím tắt bạn nên để ý kỹ:

  1. Giữ cùng lúc nhiều phím: Ctrl+c, Alt+P

  2. Bấm lần lượt, bấm phím này xong buông ra bấm phím tiếp theo: dd, Leaderpv

Đồng thời, phím cC là khác nhau nhé!

1.1. Tải lại

Sau mỗi lần thay đổi file vimrc, không cần phải dùng lệnh :source $MYVIMRC nữa, chỉ cần bấm Leaderr.

noremap <Leader>r :so $MYVIMRC<CR>

1.2. Vimrc

Khi ở bất kỳ đâu trong Vim, chỉ cần bấm Leader, để mở vimrc trong một tab mới.

noremap <Leader>, :tabnew $MYVIMRC<CR>

1.3. Đừng thoát!

Mặc định Vim sẽ thoát ngay lập tức khi bấm Q, dòng này chỉ để tắt chức năng này để không phải thoát nhầm mà không lưu lại gì.

noremap Q <nop>

2. Chỉnh sửa văn bản

2.1. Thay thế toàn tệp

Trong Normal Mode, bấm S để gõ nhanh cú pháp tìm và thay thế trong toàn tệp.

search and replace
nnoremap S :%s//g<Left><Left>

2.2. Dịch chuyển

Khi trong Normal Mode, chọn cả dòng bằng cách bấm V, sau đó dùng phím JK để dời một hoặc nhiều dòng đang chọn lên xuống.

move-selection
vnoremap <silent> J :m'>+<CR>gv=gv
vnoremap <silent> K :m-2<CR>gv=gv

2.3. Thêm dòng trống

Trong Normal Mode:

  • Leadero thêm một dòng trống phía dưới dòng hiện tại.

  • LeaderO thêm một dòng trống phía trên dòng hiện tại.

insert blank
noremap <Leader>o m`o<Esc>``
noremap <Leader>O :call append(line('.')-1, '')<CR>

2.4. Dùng clipboard hệ thống

Phần này hơi phức tạp, bạn có thể bỏ qua phần giải thích, chỉ cần biết là Leadery sẽ copy vào clipboard hệ thống, Leaderp sẽ paste từ clipboard hệ thống.

Giải thích

Mặc định, khi dùng yy, dd…​, Vim sẽ không copy vào clipboard của hệ thống. Nghĩa là khi bạn sao chép một đoạn văn bản trong Vim bằng yy, vào Firefox, Ctrl+v sẽ không dán đoạn bạn đã sao chép. Điều này là bởi mặc định Vim sẽ dùng registers của riêng nó, không dùng chung với clipboard của X server.

Ghi chú
Ví dụ một vài Vim Registers

/ nội dung bạn vừa tìm kiếm

" nội dung bạn vừa x, y…​

+ clipboard của hệ thống

Tìm hiểu tất cả tại đây.

Registers có thể được xem như những cái hộp, nơi Vim sẽ chứa nội dung bạn vừa sao chép hoặc cắt để đặt vào nơi khác. Để gọi tên một register, ta dùng phím "register. Như trong phần ví dụ trên, clipboard của hệ thống Vim sẽ lưu dưới register +.

Ghi chú

Vì thế, để tương tác với clipboard của hệ thống ta phải dùng các tổ hợp phím như sau:

Sao chép cả dòng: "+yy

Cắt từ con trỏ đến cuối dòng: "+D

Dán: "+p

Bạn có thể coi đây như một trở ngại rườm rà, nhưng điều này cho phép ta thao tác với nội dung vừa được sao chép, làm đủ trò với chúng. Nói chung là một tí bất tiện thôi nhưng mở ra bao nhiêu khả năng mới mà các trình chỉnh sửa văn bản khác không thể.

Lưu ý

Lưu ý rằng bạn phải cài bản Vim được build với tùy chọn +clipboard thì mới dùng được tính năng này nhé. Để kiểm tra Vim có hỗ trợ tương tác với clipboard hệ thống không hãy gõ dòng lệnh này vào Vim:

:echo has('clipboard')

Nếu kết quả trả lại là 0 thì Vim của bạn hiện không hỗ trợ thao tác này. Hãy thử gỡ Vim từ terminal và cài lại bản có hỗ trợ bằng lệnh sau:

# Debian
sudo apt purge vim
sudo apt install vim-gtk

# Arch Linux
sudo pacman -Rns vim
sudo pacman -Sy gvim

# Void Linux
sudo xbps-remove -R vim
sudo xbps-install -S gvim
noremap <Leader>y "+y
noremap <Leader>Y "+yy
noremap <Leader>p "+p

2.5. Xóa thật sự

Mặc định, các thao tác yankcut trong Vim sẽ đưa phần văn bản được chọn vào register ". Điều này dẫn đến việc nếu bạn dd hai lần, dòng đầu tiên bạn cắt sẽ bị dòng sau ghi đè, và để xóa dòng hai mà không mất dòng một trong register ", bạn phải xóa nó vào một register khoảng không. Register này được gọi tên với tổ hợp "_.

Vì thế khi lỡ yank hoặc cut một dòng, muốn xóa dòng khác mà không mất dòng ban đầu tôi sẽ dùng các phím tắt Leaderdd để xóa cả dòng,Leaderd$ để xóa từ con trỏ đến cuối dòng,Leaderdw để xóa một từ.

noremap <Leader>dd "_dd
noremap <Leader>d$ "_d$
noremap <Leader>dw "_dw

3. Di chuyển

3.1. Mode toggle

Một phím tắt duy nhất để thay đổi qua lại giữa Insert Mode và Normal Mode. Ctrl+c là phím tắt tôi hay dùng nhất khi thao tác với các phần mềm trong terminal. Thay vì dùng iEsc để thay đổi qua lại, giờ ta chỉ cần một phím tắt rất tiện cho tay trái.

nnoremap <C-c> i
inoremap <C-c> <Esc>

3.2. Từng dòng một

Mặc định của Vim khi dùng jk để di chuyển lên xuống xong Normal Mode sẽ di chuyển theo đoạn trong văn bản, không phải dòng hiển thị trong terminal. Ví dụ có một đoạn văn bản như sau, khi ta bấm phím j, mặc định con trỏ sẽ nhảy xuống dòng số 2.

j

Điều này là hoàn toàn hợp lý vì Vim sẽ dời con trỏ xuống một dòng trong file, vì Vim đang gói (wrap) dòng của chúng ta nên hiển thị trong terminal có vẻ đã xuống dòng nhưng thực tế trong văn bản không hề xuống dòng.

Việc di chuyển như vậy có phần khó khăn khi muốn chỉnh sửa đoạn giữa hoặc cuối của một dòng dài. May mắn thay, phím tắt gj sẽ đưa con trỏ xuống một dòng thay vì một đoạn. Và hầu hết lúc nào tôi cũng di chuyển như vậy nên tôi sẽ gán jk thành gjgk trong cả Normal Mode và Visual Mode.

gj

Ngoài ra thao tác di chuyển theo đoạn như trên cũng có ích trong vài trường hợp, nên việc thay thế j, kthành gj,gk làm mất đi chức năng di chuyển đoạn mặc định của jk. Do đó tôi sẽ thay thế cả hai chiều, nghĩa là gj, gk cũng sẽ thành j, k tương ứng.

nnoremap k gk
nnoremap gk k
nnoremap j gj
nnoremap gj j
vnoremap k gk
vnoremap gk k
vnoremap j gj
vnoremap gj j

3.3. Insert Mode

Phím tắt Ctrl+o trong Insert Mode sẽ cho phép ta thực thi một lệnh (hoặc phím tắt) của Normal Mode mà không cần rời Insert Mode (thật ra là có, chỉ là Vim sẽ tự thoát Insert Mode, thực hiện lệnh và trở về nên người dùng vẫn thấy mình trong Insert Mode). Ví dụ trong Insert Mode, ta ấn Ctrl+o sau đó bấm j, Vim sẽ xuống một dòng như khi ta bấm j trong Normal Mode thay vì nhập ký tự j.

Nhiều lúc trong Insert Mode tôi vẫn muốn di chuyển con trỏ mà không cần dùng phím mũi tên, do đó tôi sẽ gán Ctrl+hjkl tương ứng cho lên xuống trái phải. Có một lưu ý là tôi sẽ không di chuyển từng ký tự như mặc định phím hl làm, thay vào đó tôi sẽ di chuyển từng từ một (word by word) bằng phím bw.

inoremap <C-h> <C-o>b
inoremap <C-j> <C-o>gj
inoremap <C-k> <C-o>gk
inoremap <C-l> <C-o>w

3.4. Cửa sổ

Khi đang chỉnh sửa nhiều văn bản trong các cửa sổ (thường gọi là buffers hoặc windows), bấm Ctrl và tương ứng các phím hjkl để focus vào từng cửa sổ trên màn hình.

focus-buffer
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l

3.5. Luôn ở giữa

Khi tìm kiếm bằng /, dùng nN để di chuyển đến kết quả tìm kiếm tiếp theo, dòng chứa kết quả sẽ luôn ở chính giữa màn hình.

search-center
nnoremap n nzzzv
nnoremap N Nzzzv

Giữ con trỏ luôn ở giữa màn hình khi di chuyển nửa màn hình bằng tổ hợp phím Ctrl+uCtrl+d.

jump-center
nnoremap <C-u> <C-u>zz
nnoremap <C-d> <C-d>zz

3.6. Chẻ ngang dọc

Khi đang có hai cửa sổ trên màn hình, bấm Leaderwv để sang chế độ bổ dọc (vertical split)Leaderwh sẽ chuyển từ bổ dọc thành bổ ngang (horizontal split).

vert-hori
noremap <Leader>wv :wincmd H<CR>
noremap <Leader>wh :wincmd K<CR>

4. Tabs

Ngoài chế độ cửa sổ, Vim còn có chế độ tab. Ví dụ đang chỉnh sửa file a muốn mở tab mới để chỉnh sửa file b bạn có thể dùng lệnh :tabnew b

4.1. Tab kế bên

Khi có nhiều tabs đang mở, phím tắt dưới đây sẽ cho phép bạn di chuyển qua lại giữa các tab theo thứ tự từ trái sang phải bằng phím LeaderTab.

tab-cycle
noremap <Leader><Tab> gt

4.2. Nhảy đến tab

Vì muốn Leader có vai trò tương tự phím Mod của dwm nên tôi gán Leader1-9 thành di chuyển đến tab theo số thứ tự. Chức năng này mặc định Vim không có (hoặc tôi không biết là Vim có) nên tôi phải dùng một hàm loop để gán phím.

tab-num
for i in range(1, 9)
    execute "nnoremap <Leader>" . i . " :" . i . "tabnext<CR>"
endfor

4.3. Tab cuối

Vì đã quen di chuyển các tabs bằng duy nhất tay trái nên việc bấm Leader9 là hơi bất tiện, hoặc là có hơn 9 tabs đang bật, nên tôi sẽ gán Leader` thành di chuyển đến tab cuối cùng (tận cùng phải).

tab-last
noremap <Leader>` :tablast<CR>

5. Kết lại

Như thường lệ, file vimrc của tôi sẽ được công khai tại GitLab. Phần tiếp theo tôi sẽ nói về cách quản lý các plugins.

Ngày 23 tháng 5, 2024

#tech

Tìm kiếm