我也說說Emacs吧(5) - 基本編輯操作
基本編輯操作
進入編輯模式
標準的emacs用戶是遇不到這一節的,因為默認就可以編輯。但是spacemacs用戶需要先學習一下強大的vi的模式切換功能了。
vi的一個重要特點就是命令特別多,所以一旦學會了效率就非常高。
我們以進入編輯模式為說明一下,大家就清楚了。
有同學說了,切換到編輯模式,隻用一條命令,比如i命令,切過去就是了麼。從功能上說,是這樣的。但是,這樣的效率還不夠高。
vi中是這樣的定義的:
- i:在當前光標之前插入文本
- a: 在當前光標之後插入文本
- A: 在當前行的結尾處插入文本
- I: 在當前行的開始處插入文本
- o: 在光標位置的下一行加一個新行插入文本
- O: 在光標位置的上一行加一個新行插入文本
- s: 刪除光標所在位置的字符,再進行插入
- S: 刪除光標所在行,再進行插入
- R: 用新的字符替換現有的字符
在evil的實現中,i對應的是evil-insert函數:
(defun evil-insert (count &optional vcount skip-empty-lines)
(interactive
(list (prefix-numeric-value current-prefix-arg)
(and (evil-visual-state-p)
(memq (evil-visual-type) '(line block))
(save-excursion
(let ((m (mark)))
;; go to upper-left corner temporarily so
;; `count-lines' yields accurate results
(evil-visual-rotate 'upper-left)
(prog1 (count-lines evil-visual-beginning evil-visual-end)
(set-mark m)))))
(evil-visual-state-p)))
(if (and (called-interactively-p 'any)
(evil-visual-state-p))
(cond
((eq (evil-visual-type) 'line)
(evil-visual-rotate 'upper-left)
(evil-insert-line count vcount))
((eq (evil-visual-type) 'block)
(let ((column (min (evil-column evil-visual-beginning)
(evil-column evil-visual-end))))
(evil-visual-rotate 'upper-left)
(move-to-column column t)
(evil-insert count vcount skip-empty-lines)))
(t
(evil-visual-rotate 'upper-left)
(evil-insert count vcount skip-empty-lines)))
(setq evil-insert-count count
evil-insert-lines nil
evil-insert-vcount (and vcount
(> vcount 1)
(list (line-number-at-pos)
(current-column)
vcount))
evil-insert-skip-empty-lines skip-empty-lines)
(evil-insert-state 1)))
a綁定的是evil-append
A綁定evil-append-line
I綁定evil-insert-line
o綁定evil-open-below
O綁定evil-open-above
s綁定evil-substitute
S綁定evil-change-whole-line
R綁定的是evil-replace-state
從編輯模式退回普通模式的命令的Esc.
標準emacs下的編輯命令
刪除一個字符
Del 刪除上一個字符,對應delete-forward-char函數。
C-d (delete-char) 刪除光標位置字符,不過這個綁定在spacemacs中不支持。
在spacemacs中的正常模式下,可以使用vi的x命令來刪除當前字符,函數是evil-delete-char.
(evil-define-operator evil-delete-char (beg end type register)
:motion evil-forward-char
(interactive "<R><x>")
(evil-delete beg end type register))
刪除一行
下麵我們學習emacs中非常有趣的一個功能。
在vi中,刪除一行非常容易,一條命令dd就可以了。
但是emacs不會這樣的,要想刪除一行,需要三個命令C-a C-k C-k,哈哈
C-k綁定的命令是kill-line,它的作用是,刪除光標至行尾的所有內容,但是不包括換行符。刪除掉這個空行需要再執行一次kill-line.
如果要刪除從光標開始到行首的命令的話,我們可以使用kill-line的負命令,就是把負號做為參數傳給kill-line. 方法是調用negative-argument函數,它綁定的鍵特別多,有C--, A--, C-A--,反正跟減號的組合都是它就是了。
(defun negative-argument (arg)
"Begin a negative numeric argument for the next command.
\\[universal-argument] following digits or minus sign ends the argument."
(interactive "P")
(prefix-command-preserve-state)
(setq prefix-arg (cond ((integerp arg) (- arg))
((eq arg '-) nil)
(t '-)))
(universal-argument--mode))
剪切,複製和粘貼
文本區域的選擇
要想做剪切和複製,第一步要先做文本區域的選擇。
在想要選擇的文本區域的頭部,使用set-mark-command函數來設置標記:
(defun set-mark-command (arg)
(interactive "P")
(cond ((eq transient-mark-mode 'lambda)
(kill-local-variable 'transient-mark-mode))
((eq (car-safe transient-mark-mode) 'only)
(deactivate-mark)))
(cond
((and (consp arg) (> (prefix-numeric-value arg) 4))
(push-mark-command nil))
((not (eq this-command 'set-mark-command))
(if arg
(pop-to-mark-command)
(push-mark-command t)))
((and set-mark-command-repeat-pop
(eq last-command 'pop-global-mark)
(not arg))
(setq this-command 'pop-global-mark)
(pop-global-mark))
((or (and set-mark-command-repeat-pop
(eq last-command 'pop-to-mark-command))
arg)
(setq this-command 'pop-to-mark-command)
(pop-to-mark-command))
((eq last-command 'set-mark-command)
(if (region-active-p)
(progn
(deactivate-mark)
(message "Mark deactivated"))
(activate-mark)
(message "Mark activated")))
(t
(push-mark-command nil))))
set-mark-command綁定在Ctrl-@和Ctrl-空格上。不管是Windows還是MacOS,Ctrl-空格都已被操作係統征用了,可以用C-@,不過這個鍵確實是不好用,需要按Ctrl-Shift-2。
設置了標記之後,將光標移至要選擇的結尾就可以了。
如果想確認下標記的位置,可以用exchange-point-and-mark函數,快捷鍵C-x C-x.
剪切
選中文本塊之後,就可以將其刪除kill-region,然後再到新的位置通過yank命令粘貼出來就好。刪除之後,刪除的結果會存到刪除環中。
在標準emacs下,kill-region綁定在C-w上,但是spacemacs下不支持。可以使用Shift-Delete組合。yank在標準emacs上綁定在C-y上,spacemacs下也不支持,但是可以使用Shift-Insert.
spacemacs是個土洋結合的係統,使用emacs的方式選中文本區域之後,可以使用vi的d命令達到同樣的剪切效果,函數是evil-delete。
不選中文本塊,隻刪除一行的話,命令是dd。
(evil-define-operator evil-delete (beg end type register yank-handler)
(interactive "<R><x><y>")
(unless register
(let ((text (filter-buffer-substring beg end)))
(unless (string-match-p "\n" text)
;; set the small delete register
(evil-set-register ?- text))))
(let ((evil-was-yanked-without-register nil))
(evil-yank beg end type register yank-handler))
(cond
((eq type 'block)
(evil-apply-on-block #'delete-region beg end nil))
((and (eq type 'line)
(= end (point-max))
(or (= beg end)
(/= (char-before end) ?\n))
(/= beg (point-min))
(= (char-before beg) ?\n))
(delete-region (1- beg) end))
(t
(delete-region beg end)))
;; place cursor on beginning of line
(when (and (called-interactively-p 'any)
(eq type 'line))
(evil-first-non-blank)))
複製
複製也是先選中文本區域,然後使用kill-ring-save函數將其複製到刪除環中,最後還是通過yank命令粘貼。它綁定的鍵是A-w,在spacemacs中仍然有效。
也可以使用vi中的y命令,達到同樣的效果,函數是evil-yank. 有沒有覺得用y鍵比A-w鍵要舒服一些?
(evil-define-operator evil-yank (beg end type register yank-handler)
"Saves the characters in motion into the kill-ring."
:move-point nil
:repeat nil
(interactive "<R><x><y>")
(let ((evil-was-yanked-without-register
(and evil-was-yanked-without-register (not register))))
(cond
((and (fboundp 'cua--global-mark-active)
(fboundp 'cua-copy-region-to-global-mark)
(cua--global-mark-active))
(cua-copy-region-to-global-mark beg end))
((eq type 'block)
(evil-yank-rectangle beg end register yank-handler))
((eq type 'line)
(evil-yank-lines beg end register yank-handler))
(t
(evil-yank-characters beg end register yank-handler)))))
如果要複製一行的話,命令是yy.
vi中的粘貼命令是p,函數是evil-paste-after
(evil-define-command evil-paste-after
(count &optional register yank-handler)
"Pastes the latest yanked text behind point.
The return value is the yanked text."
:suppress-operator t
(interactive "P<x>")
(if (evil-visual-state-p)
(evil-visual-paste count register)
(evil-with-undo
(let* ((text (if register
(evil-get-register register)
(current-kill 0)))
(yank-handler (or yank-handler
(when (stringp text)
(car-safe (get-text-property
0 'yank-handler text)))))
(opoint (point)))
(when text
(if (functionp yank-handler)
(let ((evil-paste-count count)
;; for non-interactive use
(this-command #'evil-paste-after))
(insert-for-yank text))
;; no yank-handler, default
(when (vectorp text)
(setq text (evil-vector-to-string text)))
(set-text-properties 0 (length text) nil text)
(unless (eolp) (forward-char))
(push-mark (point) t)
;; TODO: Perhaps it is better to collect a list of all
;; (point . mark) pairs to undo the yanking for COUNT > 1.
;; The reason is that this yanking could very well use
;; `yank-handler'.
(let ((beg (point)))
(dotimes (i (or count 1))
(insert-for-yank text))
(setq evil-last-paste
(list #'evil-paste-after
count
opoint
beg ; beg
(point))) ; end
(evil-set-marker ?\[ beg)
(evil-set-marker ?\] (1- (point)))
(when (evil-normal-state-p)
(evil-move-cursor-back)))))
(when register
(setq evil-last-paste nil))
(and (> (length text) 0) text)))))
p是粘貼到當前光標後,如果要粘貼到當前光標前的話,命令是P,函數是evil-paste-before.
從刪除環中獲取更早的結果
刪除環不隻會保存上一次的結果,如果想要更早的結果,可以通過yank-pop函數來獲取。
vi移動光標方式的補充
上一講講過,可以在正常模式下通過使用0來移動到行首。但是,有時候我們希望跳過行首第一個不是空白符的位置,這時候,就要用"^"命令,函數是evil-first-non-blank.
(evil-define-motion evil-first-non-blank ()
"Move the cursor to the first non-blank character of the current line."
:type exclusive
(evil-narrow-to-line (back-to-indentation)))
vi括號匹配
vi中的%命令,可以匹配跟當前括號配對的另一半括號,函數是evil-jump-item.
vi重複做上一條命令
"."可以執行上一條命令,函數是evil-repeat.
(evil-define-command evil-repeat (count &optional save-point)
"Repeat the last editing command with count replaced by COUNT.
If SAVE-POINT is non-nil, do not move point."
:repeat ignore
:suppress-operator t
(interactive (list current-prefix-arg
(not evil-repeat-move-cursor)))
(cond
((null evil-repeat-ring)
(error "Already executing repeat"))
(save-point
(save-excursion
(evil-repeat count)))
(t
(unwind-protect
(let ((confirm-kill-emacs t)
(kill-buffer-hook
(cons #'(lambda ()
(user-error "Cannot delete buffer in repeat command"))
kill-buffer-hook))
(undo-pointer buffer-undo-list))
(evil-with-single-undo
(setq evil-last-repeat (list (point) count undo-pointer))
(evil-execute-repeat-info-with-count
count (ring-ref evil-repeat-ring 0))))
(evil-normal-state)))))
小結
功能 | 函數 | 鍵綁定 | leader key |
---|---|---|---|
刪除光標處字符 | delete-char | 無 | 無 |
evil-delete-char | x | 無 | |
刪除光標至行尾 | kill-line | C-k | 無 |
傳遞負參數 | negative-argument | C--, A--, C-A-- | 無 |
設置文本塊開始標記 | set-mark-command | C-@ | 無 |
交換光標與文本標記 | exchange-point-and-mark | C-x C-x | 無 |
剪切文本塊到刪除環 | kill-region | Shift-Delete | 無 |
evil-delete | d | 無 | |
複製文本塊到刪除環 | kill-ring-save | A-w | 無 |
evil-yank | y | 無 | |
從刪除環中彈出,即粘貼 | yank | Shift-Insert | 無 |
evil-paste-after | p | 無 | |
evil-paste-before | P | 無 | |
從刪除環中彈出更早的結果 | yank-pop | 無 | 無 |
移至行首第一個非空白符 | evil-first-non-blank | ^ | 無 |
匹配下一個可配對的括號 | evil-jump-item | % | 無 |
再次執行上一次執行的命令 | evil-repeat | . | 無 |
最後更新:2017-06-05 15:01:38