简体   繁体   中英

How can I delete a YAML section in Vim with a macro/mapping/function?

The YAML sections are started with ^--- , and don't have any ending delimiter. Example file:

--- a header
data:
  key: value
  key2: value
--- another header
data:
  name: 

The ideal approach would be to use operator-pending mappings (see a. and b. ), so you could delete, change, or yank a section. My partly working solution "around ---" mapping is:

:onoremap a- :<c-u>execute "normal! ?^--- \r:nohlsearch\rVNk"<cr>

This works well for middle sections, but fails on the last section of the file because it can't find a subsequent header match. I can modify the search so it'll find the end of the file in addition to the header text (searching for /^--- \\|\\%$ or /\\v^--- |%$ ), but that misses the last line of the file. The search should go up a line to not select the subsequent header, but if we reached the end of the file, we must not go up.

An alternative approach would be to use the /search/offset functionality, but I don't know whether there's a way to group the search to find pattern\\|end where the offset is applied to a successful pattern match but not end . Since "go one line above pattern" is /pattern/-1 , I hoped something like the following would be possible (this is simplified syntax):

/(/header/-1)|end

Is this possible, or would I need to write a function to change the selection? I don't know vimscript yet, so any pointers in that direction would also be appreciated.

Try this:

source this function:

function! GimmeRange() abort
    let pat = '^--- \|\%$'
    let start = search(pat, 'b')
    let end = search(pat)
    let end = end == line('$')? end:end-1
    return ":\<c-u>exec 'normal! ".start.'GV'.( end-start )."+'\<CR>"
endfunction

create this map:

onoremap <expr> a-  GimmeRange()  

Do your test with the omapping a-

A simple mapping:

:map £ mdG:set paste<CR>o---<Esc>:set nopaste<CR>'d$/---<CR>NedddnGdd
  • md : set d mark on current line (which is espected to belong to the section to be deleted

  • G : go to end of file

  • :set paste<CR> : temporarily disable autoindent ( <CR> is obtained with CTRL-V CTRL-M)

  • o---<Esc> : Add a new last line composed of --- ( <Esc> is obtained with CTRL-V ESC)

  • :set nopaste<CR> : restore previous autoindent setting

  • 'd$ : go to end of line of d mark

  • /---<CR>N : go to beginning of section to be deleted

  • dddn : delete section header then section body

  • Gdd : delete last line (the one with --- )

Using a function as Kent suggested is cleaner and more maintainable, but I was able to modify the original mapping and will post it here for completeness.

igemnace gave me some pointers on the #vim IRC chat. The crucial piece is that I can search for a line-ending in the file (not a literal newline) by including \\n in the search. Next, I need to change the motion characters since the match starts one line earlier. We need to go down a line from the upper match (j), and we don't need to go up (k) from the lower match. Finally, the search match region needs to be modified with \\zs so if the cursor is on the line before the header, vim doesn't think the cursor is overlapping the match. That would cause the editor to delete the wrong section in that case. The logic is:

  • search backwards for .*\\zs\\n--- \\|\\%$
  • go down a line
  • visual select until the next LOWER match

After all the escaping, that becomes:

:onoremap a- :<c-u>execute "normal! ?.*\\\zs\\n--- \\\|\\\%$\r:nohlsearch\rjVN"<cr>

But I didn't want the mapping to wipe out my search buffer, so I saved and restored it:

:onoremap a- :<c-u>let savedSearch=@/\|execute "normal! ?.*\\\zs\\n--- \\\|\\\%$\rjVN"\|let @/=savedSearch<cr>

Note that this solution doesn't work when the file begins with a section header, but YAML never does. This assumption is not portable to other data languages, so it's another reason why writing a function is a better solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM