Как я могу перейти к следующему родному элементу XML с помощью Vim?

Теперь я редактирую несколько XML файлов, которые имеют некоторую структуру вроде:

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

Другими словами, содержимое некоторого родительского элемента будет представлять собой последовательность дочерних элементов (с таким же именем в этом случае). В качестве примера подумайте о списке предметов для продажи в каталоге или даже о последовательности <xsl:template> в <xsl:stylesheet>.

То, что я хотел бы сделать, это сопоставить некоторую последовательность клавиш в vim, чтобы перейти к следующему (или предыдущему) дочернему элементу, когда я вхожу в родительский элемент, и в идеале, чтобы иметь возможность префикс этого с подсчетом (чтобы я может, например, "перейти к пятому следующему ребенку" ). В идеале мне нужно будет находиться вне содержимого элемента Child, чтобы это имело смысл, так что внутри элемента Child я могу прыгать между внуками с той же картой. смотрел онлайн для плагинов/решений для этого и ничего не нашел.

В настоящее время я могу сделать vat<Esc>j в том случае, когда следующий ребенок находится на следующей следующей строке после закрытия этого Ребенка (аналогично vato<Esc>k для предыдущего ребенка). Однако у меня есть несколько проблем с этим:

  • Я не могу префикс это с помощью count; делая это до того, как отображение вызывает странное поведение (см. :help v, префикс v с подсчетом не делает то, что я хотел бы здесь); делая это внутри отображения (например, v2at) последовательно выбирает "более высокие" окружающие элементы.
  • Это не будет работать, если последовательность дочерних элементов не будет следовать сразу друг за другом, каждый из которых будет открываться в следующей следующей строке из предыдущего закрывающего тега.
  • Если я делаю это на последнем дочернем элементе родителя, я перейду к закрывающему тегу родителя (или некоторого его текстового содержимого, если он существует, или что-то еще)
  • Снимает ранее выбранную область

Номер 2 не имеет особого значения - я могу, вероятно, обеспечить правильное форматирование файлов, которые я редактирую, и могу использовать xmllint для этого легко. Я по-прежнему предпочитаю более "семантический" подход к элегантности и надежности, если это возможно. Номер 3 действительно не имеет большого значения, это было бы просто отличным бонусом, если бы я мог остаться там, где я есть, или пойти к первому Ребенку, когда на последнем Ребенке. Номер 4 все еще должен быть проблемой для меня. Возможно, это будет мешать плагину или моей работе в будущем, но что угодно, конечно, не критично.

Номер 1 является критическим, хотя, как я часто нахожу, что хочу пойти так много детей вниз. В настоящее время я ищу Ребенка, к которому я хочу пойти, взглянув на мой релятивируемый номер для этой линии, затем перейдя к этому множеству строк (или вверх). Это

  • Раздражающе, и не очевидно, как я думаю о содержании XML
  • Нелегко записывать сценарии/макросъемки, по крайней мере, не надежно

Другим возможным решением было бы выполнить поиск Child, а затем перейти [count]n или [count]n. Это также имеет несколько проблем:

  • Либо я должен думать, и называть имя Child сам, что полностью не программируется; или я делаю что-то вроде vato<Esc>l*, которое не может быть префиксным счетчиком так, как хотелось бы
  • Разрывы при вложении элементов Child внутри друг друга (имеют прецеденты), разделение элементов Child на отдельные элементы Parent, когда строка "Child" появляется где угодно, кроме как Child и т.д.
  • Скрывает предыдущий поиск; это неприятность, когда я использую поиск, чтобы выделить что-то актуальное в данный момент, или все же хочу, чтобы можно было перейти к следующему элементу поиска после перехода к следующему элементу.

В идеале я бы создал какую-то функцию + команду + сопоставление, которая бы справлялась со всем этим надежным, интеллектуальным образом. Тогда я мог просто бездумно использовать мою команду во время редактирования и не имею головных болей, пытающихся записать макрос, который работает, перепрыгивая между элементами и т.д. Однако я все еще совершенно новичок в vimscripting и не совсем уверен, как начать.

Ответ 1

Если вам нужно это движение часто, имеет смысл создать для него настраиваемое движение. С моим плагином CountJump это очень просто:

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

Это изменит движение [[ [] и т.д. (вы также можете выбрать другие сопоставления) и также поддерживает подсчет. (Поместите это, например, ~/.vim/ftplugin/xml_motions.vim.)

Ответ 2

Они явно не удовлетворяют всем вашим требованиям, но эти грубые команды могут помочь...

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

Переход вперед/назад к следующему тегу XML на том же уровне отступов.

Ответ 3

Я бы, скорее всего, просто отрегулировал складку (вы можете включить фальцовку XML с помощью let g:xml_syntax_folding = 1 и setlocal foldmethod=syntax), чтобы дочерние элементы были свернуты (например, zMzr). Затем навигация по дочерним элементам является простой "нитью (вверх)" с помощью j/k.