終于到了我第二喜歡的vim功能了(當然了,最喜歡的是.命令)。我原本計劃在介紹完.命令之後介紹宏,以便讓各位小夥伴們能了解到vim對于重複操作進行的強大的優化。但是由于宏本身跟寄存器息息相關,所以還是忍痛割愛,将它放到寄存器之後。廢話不多說讓我們開始吧。
宏的基本使用我們還是以一個例子進入相應的内容。我要将下面這段代碼
foot = 'foot'
ball = 'ball'
football = foot ball
變為這樣
var foot = 'foot';
var ball = 'ball';
var football = foot ball;
通過分析它這三行其實做的都是同樣的功能,即在每行的行首添加 var 關鍵字,然後在每行的行尾添加分号。我們可以利用之前介紹的.命令或者針對列的可視模式來處理,隻是不管用哪種方法都需要至少兩次操作。
問題先放在這裡,讓我們先了解下什麼是宏,以及怎麼用宏。
宏是存儲在寄存器中的連續的操作指令,以便後續可以對這些指令進行回放。可以使用 q 進行錄制,後面跟寄存器名稱,表示将接下來的操作記錄保存到這個寄存器中。例如使用 qa 表示将接下來的操作保存到 a 這個寄存器中。退出宏的錄制可以直接輸入 q
針對上面的例子,我們可以執行 qa 進行宏的錄制,然後使用 A 在行尾進入插入模式,接着輸入 ; 完成行尾的操作。然後使用 I 進入行首,然後在行首輸入 var 完成這部分的工作。最後使用 q 退出宏的錄制。這樣就将這個宏保存在了a 寄存器.
我們可以使用 :reg a 來查看寄存器的内容。
這個内容完全是我們之前通過鍵盤輸入到vim中的内容,隻是返回到普通模式輸入的是<Esc> 而這個保存的是^[ 因為宏有自己的鍵盤編碼方式,這個方式我覺得不需要特别去查去記,自己就可以從寄存器中查到。
宏錄制完成之後,可以使用 @ 寄存器 來回放寄存器中保存的宏。在回放宏之後可以使用 @@ 來快速回放上一次回放的宏。
到現在各位小夥伴可能已經發現了,它與.命令比較類似,隻能機械地執行之前執行過的内容,它無法做到智能化,例如我在錄制宏的過程中使用了諸如 2w之類的命令,後面在重複的時候很有可能發生錯誤。這就要求我們在使用宏的過程中,盡量規範化光标移動,不要搞這種特例的形式。就像寫代碼不要寫死一樣。
這裡我們還是手動執行了好多次同樣的宏,宏與普通的operator 一行支持前面加數字表示重複,例如2@a 表示重複執行兩次這個宏。上面的例子我們可以稍微做一下修改,即在最後添加一步将光标移動到下一行的操作——j。然後使用這個特性進行重複。
仔細點可以發現,我們執行了3次這個宏,也就是要執行3次j 操作,但是我們是在第二行執行的宏,也就是剩下的行隻允許我們執行一次j 。這裡雖然有問題,但是宏還是正确的對文本進行了修改。
這是因為 vim 宏在 motion 執行失敗之後會終止執行,這個并不是一個 bug,而是一個特性,也就是說利用這個特性我們可以更好地使用宏。例如上述例子中,宏隻執行了一次 j ,第二次執行到j 的時候出錯了,于是就停下來了。這就告訴我們不用關心剩下的操作需要重複多少次,隻需要給出一個足夠大的數,保證已有行能正常進行修改就可以了。
我們再來看一個例子将
1. one
2. two
3. three
4. four
5. five
6. six
7. seven
8. eight
9. nine
10. ten
改為
1) One
2) Two
3) Three
4) Four
5) Five
6) Six
7) Seven
8) Eight
9) Nine
10) Ten
我們可以這麼歸納這個操作,從行首開始找第一個 .,然後執行替換操作将其替換為 )然後找到下一個單詞,将首字母改為大寫。我們可以在宏中執行 0f.r)w~j最後退出。讀一下這段内容, ~之前沒見過吧。之前介紹過,gU和 gu後面可以跟 motion表示将對應範圍的字符轉化為大寫和小寫。g~可以進行大小寫反轉。而這裡的~直接将當前光标所在字符進行反轉。
上述命令我們首先使用 0将光标至于行首,這樣就規範了每行的查找操作。另外這裡由于 10 有兩個字符,所以這裡使用 f 來查找而不僅僅使用 l往後移動一個字符,最後我們不确定. 和單詞之間會不會有空格。所以這裡最好是使用 w 而不是 l 。這些細節體現了我們之前說的要更加規範的移動光标。
宏錄制完了之後,我們可以利用之前介紹的 motion失敗會終止執行的特性,不用數需要處理多少行,直接 10@a(因為第一行已經處理了,所以這裡隻有9行待處理)
以并行的方式執行宏我們将上述例子進行變更
1. one
2. two
3. three
4. four
// do something
5. five
6. six
7. seven
8. eight
9. nine
10. ten
執行上次錄制的宏,發現它在第5行的位置停止了,因為在第5行中未找到 .,所以它終止了,為了繼續運行,需要手動跳過,然後繼續執行。假設我們有多處有注釋,每次遇到問題就停下來,再手工執行,會顯得比較麻煩。為了解決這個問題,我們使用vim提供的另外一種執行宏的方式——以并行的方式執行。
重新錄制宏,與之前相比,隻需要将j這個操作給去除掉。然後使用針對行的可視模式,選中待處理行,然後針對這些選中行來執行宏。我們在這裡來審視一下這兩種方式,并行方式需要提供重複次數,它是第一次執行完了接着執行下一次,下一次的執行依賴于上一次成功的執行。并行則不然,并行是針對選中部分,同時執行一個宏操作。即使中間有錯也不影響其他行的運行。
還是上面的例子,假設在錄制好了宏之後發現我們少了一個j,使用串行話的方式無法順利執行。這種情況下不需要重新錄制宏,隻需要在對應寄存器中添加一條指令。
這裡補充一下寄存器相關知識。在上一篇介紹寄存器的時候我們隻演示了使用小寫字母的寄存器,沒有提到大寫字母的寄存器。根據之前的慣例,大寫字母與小寫字母都可以使用,大寫字母的功能比小寫字母要強,例如大寫的标簽标示全局,小寫的隻能用于單個文件。這裡大寫的寄存器與小寫的寄存器是同一個寄存器,使用大寫時我們可以對寄存器内容進行追加操作。
宏是保存在寄存器中的,q 後面加字母表示宏的内容保存在哪個寄存器中,說到這裡,聰明的你已經反應過來該如何将命令追加到寄存器中了。那就是使用 q 大寫字母。
針對并行操作的例子,假設已經錄好其他操作隻差一個j 了,我們可以使用 qA 進行追加,然後添加 j 操作即可追加前宏的内容如下:
添加完成之後,宏變成了如下内容
後面就可以以串行的方式執行這個宏了
配合文件參數列表使用宏之前介紹過文件參數列表,即使用 :args 可以對文件進行分組,各位小夥伴可能隻知道這個,但是沒找到它的使用場景。也不知道vim提供這個功能有什麼用處。在這裡我們就來看看它的一個使用場景。
我們還是以之前的 neovim 配置文件的工程為例,我要在每個lua文件中添加一行注釋 --this is add by vim macro 。打開一個 lua 文件之後,使用 :args **/*.lua 來将每個 lua 文件加入到參數列表中。然後随意打開一個 lua 文件,在錄制宏的時候執行 ggO<ESC>S--this is add by vim macro 然後退出。這裡還是貫徹了前面說的要是移動更加規範,我們先用 gg 移動到第一行,以便能準确的在首行插入内容。由于在 lua 文件中有注釋的話使用 O 添加一行的時候它會自動添加一個注釋。但是不能确保所有的 lua 文件在行首都有注釋,所以我們先使用 S 删除一行并進入插入模式。當然通過配置也可以取消這個特性,等介紹到文件類型的時候再來讨論這個。
此時文件已經發生了變化,如果我們直接執行宏的話,之前錄制時修改的文件将會兩次執行相同的命令,所以這裡不能保存,可以執行 :edit! 放棄本次修改,或者如果已經修改了的,可以執行u進行回退。
結合之前介紹的在命令模式中執行普通模式的命令,可以使用 :argdo normal @a 。argdo 表示循環對參數列表中的每個文件執行相同的操作。
錄制宏:
添加參數列表:
執行宏
上述的操作方式采用的是并行的執行宏,我們可以對其進行一些修改,讓其支持串行的方式。
還記得之前介紹的怎麼遍曆參數列表嗎,不記得也沒關系。我們可以使用 :next 來訪問下一個,:prev 來訪問上一個。配合之前的命令可以使用 ggO<Esc>S--this is add by vim macro:next。我們無法知道參數列表中到底有多少個文件,但是可以利用失敗即終止這個特性輸入一個足夠大的數字即可,例如 100@a即可。這樣就省去了執行命令模式中命令的相關操作。
對比兩個宏發現我隻需要在之前的宏後面添加一個 :next 指令即可,所以這裡就直接執行了 qA:nextq
對比上面兩種方式發現,并行執行的時候中間某個緩沖區如果出錯并不影響其他緩沖區的執行,這就給我們排查造成了一定的問題,一旦出錯我們不得不打開每一個緩沖區查看執行的結果來找到出錯的位置。而串行則會停在出錯的位置,我們隻要針對出錯的部分做一定的調整,然後繼續執行就好了。而且這個例子中列表參數并不會循環遍曆,也就不用擔心之前修改過的内容又被修改。
編輯宏内容上面我們說到宏是保存在寄存器中的一組操作指令,既然可以利用往寄存器中追加内容的方式往宏中追加指令,那麼是不是我隻要更新了寄存器中的内容,在執行宏的時候命令就會改變呢?如果你能這麼想,那麼恭喜你都會搶答了,而且答對了!
還是以上面那個添加注釋的例子為例,假設我之前忘記了删除新添加的 --,也就是我錄入的宏變成了 ggOthis is add by vim macro 我們會發現在第一行是注釋的文本中它的表現是正常的,但是第一行不是注釋,添加的就是有問題的,例如 nvim-config/lua/config/auto-session.lua。我們發現了這個問題需要對這個宏進行修改。
首先我們需要将 宏從寄存器中放到編輯器中,這就要使用 :put a取出寄存器中的内容,你可能會疑惑為什麼不用 "ap 呢,這是因為 p 命令默認會将寄存器中的内容放到光标所在位置的後面,而 :put 則會直接放到下一行,所以這裡還是放入到當前命令之後要好。接着修改一下這個宏。在對應位置加上 S 這個操作,最後使用 0d$ 從行首粘貼到行尾,注意這裡盡量不要使用 dd,它會連帶着換行符一塊進行粘貼,可能會破壞宏的指令。最後我們可以先删除之前粘貼的一行,再重新執行這個宏
宏是vim提供的很有用的功能,希望我通過本文讓各位小夥伴對它有一個初步的認識,想要用好宏這個強大的工具還是需要花大量的時間去學習研究的。vim這個工具也是常用常學常新的,時不時你就能發現自己當初不知道的内容,就像有小夥伴給我留言給我介紹了一些我之前不知道的命令,在這裡對所有給我留言的小夥伴表示感謝。vim的指令實在太多了,指望我把所有好用的一一介紹,文章的篇幅就顯的太長了,這裡我就不加了,各位小夥伴有什麼好用的方式也可以留言給其他不會的小夥伴一個學習的機會。大家一起共同進步。謝謝大家
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!