tags:

views:

239

answers:

4

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?

+2  A: 

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).

rampion
Nice trick working around the problem at the end of the file by inserting the pattern! I had one problem, though, with deleting a last block: I have "autoindent" set and then the "o>>Start-pattern<Esc>" bit (potentially) puts the >>Start-pattern with indent. There's a workaround by doing "o0<C-d>>>Start-pattern<Esc>".
+5  A: 

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>
Whaledawg
In the past I did play with getline to search stuff around, and it's far too slow. Prefer to simply use search(). First backwards, then forward.
Luc Hermitte
+4  A: 
Brian Carper
Nice! that `\|` should be a `|`, though (since it's very magic)
rampion
I'd have thought so too, but it's actually so the mapping doesn't eat the bar. (:h map_bar)
Brian Carper
*shudder* - but does its work flawlessly! I did not understand it completely, though, even after reading up lots of interesting vim regex features.
A: 

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>