如何使用Vim跳转到XML元素的下一个兄弟元素?

时间:2022-11-12 19:18:57

I'm editing a number of XML files now that have some kind of structure like:

我正在编辑一些XML文件,这些文件具有如下结构:

<Parent>
  <Child name="Fred">
    Some content
  </Child>
  <Child name="George" value="other content" />
  ...
</Parent>

In other words, the content of some parent element will be a sequence of child elements (with the same name in this case). As examples, think of a list of items for sale in a catalogue, or even a sequence of <xsl:template> in an <xsl:stylesheet>.

换句话说,某个父元素的内容将是子元素的序列(在本例中名称相同)。作为示例,请考虑目录中要出售的项目列表,甚至是

What I would like to do is map some key sequence in vim to go to the next (or previous) child element when I am within the parent element, and ideally to be able to prefix this with a count (so that I can "go to the fifth next child, for example). Ideally, I will need to be outside the content of the Child element for this to make sense, so that inside the Child element I can jump between Grandchildren with the same map. I looked online for plugins/solutions to this and haven't found anything.

我想做的是在vim中一些关键序列映射到进入下一个(或之前)当我在父元素的子元素,和理想情况下能够前缀计数(这样我就能“去第五下一个孩子,例如)。理想情况下,我需要不在子元素的内容中,这样才能有意义,以便在子元素中,我可以使用相同的映射在子代元素之间跳转。我在网上找了插件/解决方案,但什么也没找到。

What I can currently do is vat<Esc>j in the case where the next Child is on the immediate next line after the close of this Child (similarly vato<Esc>k for preceding Child). However, I have a few problems with this:

我目前可以做的是vat j,如果下一个子节点在该子节点结束后的下一个子节点上(类似的,vato k)。然而,我有一些问题:

  1. I can't prefix this with a count; doing so before the mapping causes weird behaviour (see :help v, prefixing v with count doesn't do what I'd like here); doing so inside the mapping (e.g. v2at) selects successively 'higher' enclosing elements.
  2. 我不能用计数来给它加上前缀;在映射导致奇怪行为之前这样做(参见:help v,在v前面加上count,这不是我想要的);在映射(例如v2at)中这样做,将依次选择“更高”的包围元素。
  3. This won't work if the sequence of Child elements don't follow directly after each other, each opening tag following on the immediate next line from the preceding closing tag.
  4. 如果子元素的序列不直接跟随在一起,每个开始标记都跟随在前面结束标记的下一行,那么这将不起作用。
  5. If I do this on the last Child of a Parent, I'll go to the closing tag of the parent (or some of its text content if it exists, or whatever)
  6. 如果我在父进程的最后一个子节点上执行这个操作,那么我将访问父进程的结束标记(或者它的一些文本内容,如果它存在,或者其他什么)
  7. Clobbers the previously selected region
  8. 破坏先前选定的区域

Number 2 is not hugely critical - I can probably enforce correct formatting on the files I'm editing, and can use xmllint to do this easily. I still would prefer a more 'semantic' approach for elegance and robustness, if possible. Number 3 is really not a big deal, it would just be a nice bonus if I could either stay where I am, or go to the first Child, when on the last Child. Number 4 has yet to be an issue at all for me. Maybe it will interfere with a plugin, or my work in the future, but whatever, certainly not critical.

第2点不是非常关键——我可能可以对正在编辑的文件强制执行正确的格式,并且可以使用xmllint轻松实现这一点。如果可能的话,我还是希望采用更“语义化”的方法来实现优雅性和健壮性。第三个孩子并不是什么大问题,如果我可以呆在我现在的地方,或者在最后一个孩子的时候去找第一个孩子,那就更好了。对我来说,4号根本就不是问题。也许它会干扰一个插件,或者我将来的工作,但是无论如何,肯定不是关键。

Number 1 is the critical one though, as I frequently find myself wanting to go so many Children down. Currently, I'm looking for the Child I want to go to, glancing at my relativenumber for that line, then going that many lines down (or up). This is

第一个问题很关键,因为我经常发现自己想让这么多孩子失望。目前,我正在寻找我想要去的那个孩子,看了看我的亲戚,看了看那条线,然后往下(或向上)走了那么多行。这是

  • Annoying, and not obvious to the way I think about the content of the XML
  • 这很烦人,我对XML内容的理解也不明显
  • Not easily scriptable/macroable, at least not robustly
  • 不容易脚本化/宏化,至少不健壮

Another possible solution would be to do a search for Child and then go [count]n or [count]N. This has a few issues as well:

另一种可能的解决办法是寻找孩子,然后去[count]n或[count]n。这也有一些问题:

  1. Either I have to think of and type the name Child myself, which is entirely not programmable; or I do something like vato<Esc>l* which can't be prefixed by a count in the way I'd want
  2. 要么我自己去想并输入名字子元素,这是完全不可编程的;或者我做一些像vato l*的事情,它不能以我想要的方式以计数作为前缀
  3. Breaks when nesting Child elements inside each other (has use cases), splitting Child elements across separate Parent elements, when the string "Child" appears anywhere other than as a Child element etc.
  4. 当将子元素嵌套在彼此内部(有用例)时,当字符串“子”出现在除子元素之外的任何地方时(有用例),当将子元素分散到不同的父元素时,就会中断。
  5. Clobbers the previous search; this is a nuisance when I'm using the search to highlight something currently relevant, or still want to be able to jump to the next search item after jumping to the next element.
  6. 前面的搜索方法;当我使用搜索来突出显示当前相关的内容时,或者仍然想在跳转到下一个元素后跳转到下一个搜索项时,这是很麻烦的。

Ideally I would create some kind of function+command+mapping that would handle all of this in a robust, intelligent manner. Then I could just mindlessly use my command while editing, and have no headaches trying to record a macro that works by jumping between elements, etc. However, I am still quite new to vimscripting and am not quite sure how to begin.

理想情况下,我将创建某种函数+命令+映射,以一种健壮、智能的方式处理所有这些。然后,我就可以在编辑时随意地使用命令,并且不需要费力地记录一个可以在元素之间跳转的宏,等等。

3 个解决方案

#1


2  

If you need this motion often, it makes sense to create a custom motion for it. With my CountJump plugin, this is very easy:

如果您经常需要这个动作,那么为它创建一个自定义动作是有意义的。使用我的CountJump插件,这很容易:

call CountJump#Motion#MakeBracketMotion('<buffer>', '', '', '<Child', '<Child.*\zs/>\|</Child>', 0)

This will modify the [[ [] etc. motions (you can also choose other mappings), and supports a count, too. (Put this into e.g. ~/.vim/ftplugin/xml_motions.vim.)

这将修改[[]等动作(您也可以选择其他映射),并支持计数。(把这个放到~/.vim/ftplugin/xml_motions.vim中)

#2


3  

They obviously don't satisfy all your requirements but these crude commands may help…

它们显然不能满足您的所有需求,但是这些粗糙的命令可能会有所帮助……

/^\s\{<C-r>=indent(".")<CR>}<\w\+\s<CR>
?^\s\{<C-r>=indent(".")<CR>}<\w\+\s<CR>

Jump forward/backward to the next XML tag at the same indentation level.

在同一缩进级别上向前/向后跳转到下一个XML标记。

#3


1  

I would probably just adjust the folding (you can turn on XML folding with let g:xml_syntax_folding = 1 and setlocal foldmethod=syntax) so that the child elements are all folded (e.g. zMzr). Then, navigation to childs is a simple "N line(s) up/down" with j / k.

我可能只需要调整折叠(您可以使用let g: xml_syntax_折叠= 1和setlocal foldmethod=syntax来打开XML折叠),以便子元素都被折叠(例如zMzr)。然后,导航到childs是一个简单的“N行(s)向上/向下”,带有j / k。

#1


2  

If you need this motion often, it makes sense to create a custom motion for it. With my CountJump plugin, this is very easy:

如果您经常需要这个动作,那么为它创建一个自定义动作是有意义的。使用我的CountJump插件,这很容易:

call CountJump#Motion#MakeBracketMotion('<buffer>', '', '', '<Child', '<Child.*\zs/>\|</Child>', 0)

This will modify the [[ [] etc. motions (you can also choose other mappings), and supports a count, too. (Put this into e.g. ~/.vim/ftplugin/xml_motions.vim.)

这将修改[[]等动作(您也可以选择其他映射),并支持计数。(把这个放到~/.vim/ftplugin/xml_motions.vim中)

#2


3  

They obviously don't satisfy all your requirements but these crude commands may help…

它们显然不能满足您的所有需求,但是这些粗糙的命令可能会有所帮助……

/^\s\{<C-r>=indent(".")<CR>}<\w\+\s<CR>
?^\s\{<C-r>=indent(".")<CR>}<\w\+\s<CR>

Jump forward/backward to the next XML tag at the same indentation level.

在同一缩进级别上向前/向后跳转到下一个XML标记。

#3


1  

I would probably just adjust the folding (you can turn on XML folding with let g:xml_syntax_folding = 1 and setlocal foldmethod=syntax) so that the child elements are all folded (e.g. zMzr). Then, navigation to childs is a simple "N line(s) up/down" with j / k.

我可能只需要调整折叠(您可以使用let g: xml_syntax_折叠= 1和setlocal foldmethod=syntax来打开XML折叠),以便子元素都被折叠(例如zMzr)。然后,导航到childs是一个简单的“N行(s)向上/向下”,带有j / k。