[7.4 Vim 宏在 arglist 中的应用](#7.4 Vim 宏在 arglist 中的应用)
写在前面
本篇为第六章自学笔记,主要介绍了正则表达式和 Vim 宏录制的基础知识,并结合几个典型应用场景进行了演示,包括变量名的批量重命名、Python 代码模块的完整重构等。但相较于 JetBrains 家族的成熟 IDE 工具生态,Vim 在代码重构领域仍然稍显稚嫩,暂时还没有一统江湖的杀手锏级别的通用工具,不过这方面的进展仍然非常值得关注。
上述方案虽然完成了既定目标,但无法提前获知需要替换的文件列表。要想提前了解需要替换哪些文件,可以使用命令 :vimgrep /\<Egg\>/ **/*.py,然后执行 :copen + Enter 查看匹配到的文件列表:
【图 6.8 利用 vimgrep + copen 命令提前获知需要替换的文件列表】
其他实用替换技巧:
:%s/<[^>]*>//g:批量删除文档中的所有 HTML 标记;
:%s#//.*$##:删除单行注释(以 // 开头)。
7 Vim 宏的应用
关于 Vim 宏的基础知识与用法,可完全参考《Vim Masterclass 》专栏 第 15 篇笔记,这里仅梳理具体演示案例。
7.1 Vim 宏在代码重构中的应用
需要重构的源码文件如下(Chapter06/welcome.py):
python复制代码
#!/usr/bin/python
from kitchen import bacon, egg, sausage
import random
INGREDIENTS = [egg.Egg(), bacon.Bacon(), sausage.Sausage()]
def prepare_ingredient(ingredient):
has_spam = random.choice([True, False])
if isinstance(ingredient, egg.Egg) and has_spam:
return 'spam eggs'
if isinstance(ingredient, bacon.Bacon) and has_spam:
return 'bacon and spam'
if isinstance(ingredient, sausage.Sausage) and has_spam:
return 'spam sausage'
return ingredient.name
def main():
print('Scene: A cafe. A man and his wife enter.')
print('Man: Well, what\'ve you got?')
menu = []
for ingredient in INGREDIENTS:
menu.append(prepare_ingredient(ingredient))
print('Waitress: Well, there\'s', ', '.join(menu))
if __name__ == '__main__':
main()
重构目标:改造 L8 至 L16 的多重 if 分支判定逻辑。
总思路:将各分支的返回值重构为一个父类方法的返回值,再让各子类在继承父类时重写该方法,从而彻底消除 if 判定。
以下是具体实现步骤:
先在父类新增一个成员属性 custom_spam_name,然后修改 prepare 方法:
python复制代码
# Chapter06/solution/ingredient.py
class Ingredient(object):
def __init__(self, name):
self.name = name
self.custom_spam_name = None
def prepare(self, with_spam=True):
"""Might or might not add spam to the ingredient."""
if with_spam:
return self.custom_spam_name or 'spam ' + self.name
return self.name
# Chapter06/kitchen/egg.py
from kitchen import ingredient
class Egg(ingredient.Ingredient):
def __init__(self):
self.name = 'egg'
self.custom_spam_name = 'spam eggs'
# Chapter06/kitchen/bacon.py
from kitchen import ingredient
class Bacon(ingredient.Ingredient):
def __init__(self):
self.name = 'bacon'
self.custom_spam_name = 'bacon and spam'
# Chapter06/kitchen/sausage.py
from kitchen import ingredient
class Sausage(ingredient.Ingredient):
def __init__(self):
self.name = 'sausage'
self.custom_spam_name = 'spam sausage'
最后完成对 welcome.py 的重构(L8 到 L10):
python复制代码
#!/usr/bin/python
from kitchen import bacon, egg, sausage
import random
INGREDIENTS = [egg.Egg(), bacon.Bacon(), sausage.Sausage()]
def prepare_ingredient(ingredient):
has_spam = random.choice([True, False])
return ingredient.prepare(with_spam=has_spam)
def main():
print('Scene: A cafe. A man and his wife enter.')
print('Man: Well, what\'ve you got?')
menu = []
for ingredient in INGREDIENTS:
menu.append(prepare_ingredient(ingredient))
print('Waitress: Well, there\'s', ', '.join(menu))
if __name__ == '__main__':
main()
书中演示的 Vim 宏重构操作,其实是通过录制宏 "a,将原来的多重 if 判定逻辑(光标初始定位到第一个 if 处):
python复制代码
def prepare_ingredient(ingredient):
has_spam = random.choice([True, False])
if isinstance(ingredient, egg.Egg) and has_spam:
return 'spam eggs'
if isinstance(ingredient, bacon.Bacon) and has_spam:
return 'bacon and spam'
if isinstance(ingredient, sausage.Sausage) and has_spam:
return 'spam sausage'
return ingredient.name
利用 :argdo 命令可以实现对多个文件批量执行宏命令,格式为(假如宏代码位于寄存器 "a 内):
python复制代码
:arg **/*.py
:argdo execute ":normal @a" | update
后记
尽管 Vim 在代码重构方面还没有公认的高效处理模式和适用于所有语言环境的通用插件,但相关进展仍然非常值得关注。这就好比 DeepSeek 和 OpenAI 的竞合关系,一旦 Vim 诞生了专门用于代码重构的通用插件,完全开源的吸引力也许很快就会让 JetBrains 这样的 IDE 霸主迅速跌落神坛。