在vim中更高效地编辑Python文件

70 投票
4 回答
23136 浏览
提问于 2025-04-15 11:46

假设你有一个Python文件,里面的内容是重复的:

def myFunction(a, b, c):
    if a:
        print b
    elif c:
        print 'hello'

我想用我熟悉的vim操作来移动和编辑这个文件。比如说,我想用符号如(、)、[[、]]、{、},或者用像di}这样的命令来删除、复制或修改文本。

在其他编程语言(比如C++、Java、C#等)中,花括号用得很多,所以像di}这样的操作可以很方便地找到匹配的花括号并对那一块内容进行操作。实际上,如果我在上面文本中的'b'字符上,使用vim的di)命令,它会成功删除两个括号之间的文本。

问题出在Python对代码块的识别上。我觉得使用(、)、[[、]]、{、}这些符号的操作几乎都做同样的事情,要么把你带到函数的开始(在def行上方或上面),要么带到函数的结束(在函数最后一行之后)。而且据我所知,没有简单的方法可以告诉vim“选择这个缩进块的所有内容”。在上面的例子中,我希望能在if行的'i'上,输入di},然后它能删除整个if块(直到这个特定函数的结束)。

我相信应该可以让vim根据缩进来进行这样的操作(虽然可能不适用于这个特定的操作,但可以定义一些用户自定义的动作)。你有什么想法可以实现这个吗?

4 个回答

6

当你设置了 set foldmethod=indent 后,移动缩进的代码块就变得非常简单了。比如,如果你在下面这段代码的 def main(): 这一行:

def main():
+-- 35 lines: gps.init()-----------------------------------------------------

if __name__ == "__main__": main()

那么使用 dj 命令就可以选中整个 main 函数,然后你可以把它粘贴到其他地方。

21

python.vim

这个工具让你在Python代码块中移动起来更方便。

快捷键:

  • ]t -- 跳到代码块的开头
  • ]e -- 跳到代码块的结尾
  • ]v -- 选择(可视行模式)代码块
  • ]< -- 将代码块向左移动
  • ]> -- 将代码块向右移动
  • ]# -- 注释所选内容
  • ]u -- 取消注释所选内容
  • ]c -- 选择当前或上一个类
  • ]d -- 选择当前或上一个函数
  • ]<up> -- 跳到上一个缩进相同或更低的行
  • ]<down> -- 跳到下一个缩进相同或更低的行

python_match.vim

这个工具扩展了 % 的功能:

  • % - 在 if/elif/else,try/except/catch,for/continue/break 之间切换
  • g% - 移动到 % 的反方向
  • [% - 移动到当前代码块的开头
  • ]% - 移动到当前代码块的结尾

以上所有操作都可以在普通模式、可视模式和操作待定模式下使用,所以:

  • d]% - 删除到当前代码块的结尾
  • v]%d - 也可以做到这一点,通过可视模式你可以看到正在删除的内容
  • V]%d - 和上面一样,但选择的是整行
59

方括号映射 [[, ]], [m, ]m 及类似的

现在(2018年),在$VIMRUNTIME/ftplugin/python.vim中,所有与python语言相关的内置映射都被重新定义了,这些映射在文档中可以通过 :h ]]:h ]m 找到。具体的映射如下:

]] Jump forward to begin of next toplevel
[[ Jump backwards to begin of current toplevel (if already there, previous toplevel)
]m Jump forward to begin of next method/scope
[m Jump backwords to begin of previous method/scope

][ Jump forward to end of current toplevel
[] Jump backward to end of previous of toplevel
]M Jump forward to end of current method/scope
[M Jump backward to end of previous method/scope

下面的示例代码和注释展示了不同的映射方式。

class Mapping:                              # [[[[
    def __init__(self, iterable):
        pass

    def update(self, iterable):
        pass

    __update = update                       # []

class Reverse:                              # [[ or [m[m
    def __init__(self, data):               # [m
        self.data = data
        self.index = len(data)              # [M

    def __iter__(self):                     # <--- CURSOR
        return self                         # ]M

    def __next__(self):                     # ]m
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]        # ][

class MappingSubclass(Mapping):             # ]] or ]m]m

    def update(self, keys, values):
        pass

这些映射是在以下几个提交中添加和改进的:abd468ed0(2016-09-08),01164a6546b4(2017-11-02),以及7f2e9d7c9cd(2017-11-11)。

如果你还没有这个文件的新版本,可以下载它并放到 ~/.vim/ftplugin/python.vim 这个文件夹里。这个文件夹的优先级高于 $VIMRUNTIME/ftplugin

在这些映射被添加到 $VIMRUNTIME 之前,有一个插件叫 python-mode,它提供了 [[, ]], [M, 和 ]M 的功能。此外,python-mode 还定义了一些文本对象,比如 aC, iC, aM, 和 iM

插件 python-mode

这个vim插件提供了类似于内置功能的移动方式:

2.4 Vim motion ~
                                                                *pymode-motion*

Support Vim motion (See |operator|) for python objects (such as functions,
class and methods).

`C` — means class
`M` — means method or function
                                                            *pymode-motion-keys*

==========  ============================
Key         Command (modes)
==========  ============================
[[          Jump to previous class or function (normal, visual, operator)
]]          Jump to next class or function  (normal, visual, operator)
[M          Jump to previous class or method (normal, visual, operator)
]M          Jump to next class or method (normal, visual, operator)
aC          Select a class. Ex: vaC, daC, yaC, caC (normal, operator)
iC          Select inner class. Ex: viC, diC, yiC, ciC (normal, operator)
aM          Select a function or method. Ex: vaM, daM, yaM, caM (normal, operator)
iM          Select inner func. or method. Ex: viM, diM, yiM, ciM (normal, operator)
==========  ============================

插件 Pythonsense

这个插件提供了类似的移动方式,但稍有不同:

标准的Vim 8.0中的“类”移动(“] ]”,“[ [”等)会找到从第一列开始的代码块,无论这些块是否是类或函数块,而它的方法/函数移动(“[m”,“]m”等)会找到任何缩进级别的所有块,无论这些块是否是类或函数块。相比之下,“Pythonsense”的类移动只会找到所有的类定义,不管它们的缩进级别,而它的方法/函数移动只会找到所有的方法/函数定义,也不管它们的缩进级别。

所有的细节和示例可以在https://github.com/jeetsukumaran/vim-pythonsense#stock-vim-vs-pythonsense-motions找到。此外,这个插件还定义了文本对象 ic/ac(类),if/af(函数),id/ad(文档字符串)。

Neovim & nvim-treesitter-textobjects

对于neovim,你可以使用treesitter和neovim插件 nvim-treesitter-textobjects

文本对象 if/af & ic/ac

这些文本对象不包含在 $VIMRUNTIME/ftplugin/python.vim 中,而是由一些插件提供的:

关于Python的文本对象讨论可以参考在VIM中选择Python函数的最快方法是什么?

撰写回答