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

69

给定一个包含以下无限重复内容的 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个回答

58

方括号映射 [[, ]], [m, ]m

$VIMRUNTIME/ftplugin/python.vim现在(2018年)重映射了所有在:h ]]:h ]m下记录的Python语言内置映射。这些映射为:

]] 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 还定义了文本对象aCiCaMiM

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 相似但略有修改的动作:

原版 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 文本对象的讨论,请参见what's the fastest way to select a function of Python via VIM?


2016年,这是标准答案。 - shivams
嗨:感谢您的回答。只是很快问一下:第一种方法是否有正确处理修饰符的方式? - clog14
@clog14 抱歉,我不知道答案。如果你所说的第一种方法是指$VIMRUNTIME/ftplugin/python.vim中提供的映射,我建议你在 https://github.com/tpict/vim-ftplugin-python 的上游存储库中提出问题。请提供一个示例文件和对于不同方括号映射所期望行为的描述。 - Hotschke

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 - 与上面相同,但使用行选择

仍然可以在https://web.archive.org/web/20230206092947/https://www.vim.org/scripts/script.php?script_id=30找到。 - undefined

6

如果你设置了set foldmethod=indent,那么移动缩进块就非常容易。例如,如果你在以下代码片段中的def main():行上:

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

if __name__ == "__main__": main()

然后dj接管了整个主函数,可以将其粘贴到其他地方。

0

针对您最后一段的问题,以下脚本定义了一个新的“缩进”文本对象,您可以在其上执行操作。例如,dii会删除与光标所在行相同级别的所有缩进内容。

有关更多信息,请参阅插件文档:http://www.vim.org/scripts/script.php?script_id=3037


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接