user55400
user55400

Reputation: 4029

Deleting "blocks" of text in vim

I have files that look like the following:

>>Start-pattern
some text
some more text
>>Start-pattern
some other text

some more other text
>>Start-pattern
[and so on]

What I would like to have is a mapping that allows me to delete the current "block" of text. Where "block" is defined as all lines ranging from ">>Start-pattern" until before the next ">>Start-pattern" line.

What I have is the following:

nnoremap ,d $:?^>>Start-pattern?,/^>>Start-pattern/-1delete<CR>

that works unless I'm standing on the last "block" in a file.

How can I get this map (or a completely different one that does the same thing) to work on all "blocks" in the file?

Upvotes: 2

Views: 662

Answers (4)

user55400
user55400

Reputation: 4029

Thanks a lot for the nice answers, guys!

In the end I went for the most maintainable/robust one. Building on Whaledawg's answer and taking Luc Hermitte's comment into account I ended up with:

fun! DelBlock(pat)
  let startLine = search(a:pat, "nbcW")
  let endLine = search(a:pat, "nW")

  if startLine == 0
     echo "Block '".a:pat."' not found"
     return
  end

  if endLine == 0
     let endLine = line("$")
  else
     let endLine = endLine-1
  end

  execute startLine.","endLine."delete"
endfunction

nmap ,d :call DelBlock("^>>Start-pattern")<CR>

Upvotes: 2

Brian Carper
Brian Carper

Reputation: 72926

You can do it with a single regex:

nnoremap ,d :1,.s/\v%(%(.*%#&(\>\>Start-pattern))%(\1@!\_.)+)\|%((\>\>Start-pattern)%(\2@!\_.)*%#%(\2@!\_.)+)//

Whether you should is another question...

Upvotes: 6

Whaledawg
Whaledawg

Reputation: 4286

I think, once again, we're pushing the boundaries of regular expressions past what they are good at. They are good at swapping one string with another, it's a side effect that you can delete text with them.

Instead why not take advantage of the charming scripting language vim provides. It's easier and like a trip on the way-back machine. If you add the following function to your .vimrc file, it will do what you want and be more reusable/flexible:

fun! DelBlock(thePattern)
  let origLineNumber = line(".")
  let lineNumber = origLineNumber

  let lineString = getline(lineNumber)
  while lineString !~ a:thePattern
    let lineNumber = lineNumber - 1
    if lineNumber < 0
      echo "Function not found :/"
    endif
    let lineString = getline(lineNumber)
  endwhile
  let startLine = lineNumber

  let lastLineInFile = line("$")
  let lineNumber = origLineNumber + 1
  let lineString = getline(lineNumber)
  while lineNumber != lastLineInFile && lineString !~ a:thePattern
    let lineNumber = lineNumber + 1
    let lineString = getline(lineNumber)
  endwhile
  let endLine = lineNumber
  if endLine != lastLineInFile
    let endLine = endLine - 1
  end

  execute startLine.","endLine."d"
endfunction

map ,d :call DelBlock("^>>Start-pattern")<CR>

Upvotes: 7

rampion
rampion

Reputation: 89043

Here's a possible hack.

nnoremap ,d maGo>>Start-pattern<Esc>'a$?^>>Start-pattern<CR>dNmaGdd'a

 ma                     - mark your current position
 G                      - go to the end of the file
 o>>Start-pattern<Esc>  - append your pattern line
 'a                     - go back to your prior position
 $                      - go to the end of the current line
 ?^>>Start-pattern<CR>  - find the previous occurrence of the pattern
 dN                     - delete until the next occurence
 ma                     - mark your current position
 G                      - go to the end of the file
 dd                     - delete the last line of the file
 'a                     - return to your previous position

I originally tried to get \%$ (end of file) to work, but I couldn't get the deletion to include the last character in the file. So I tried this way instead.

I've done some testing, and it seems to work for me (intermediate blocks and last block).

Upvotes: 3

Related Questions