12

How can I move X bytes forward, starting from the current cursor location (including line breaks)?

[count]go could be used to move forward X bytes from the start of the buffer. I tried Shift + V, G, [count]go (assuming that [count]go would start counting from the begin of my selection), but unfortunately that did not work either because go only starts counting from the begin of the buffer.

I have also tried :set rulerformat=%o to display the current byte offset (as suggested by Jumping to a byte offset, and displaying position as byte offset), added the numbers in my head and finally issued [count]go. This works, but it is not very practical...

1
  • If everything is on one line, you can use Xl (where X is the number of characters) or the number of characters followed by Right arrow.
    – Lekensteyn
    Commented Jul 6, 2015 at 13:35

2 Answers 2

12

This search moves 40 chars (not bytes, though) forward:

/\_.\{40}/e

by searching for exactly 40 chars (\{40}) of any kind, including newline (\_.), and placing the cursor at the end of the search (/e). See: http://vimregex.com/#Non-Greedy, :help search-offset and :help \_

Also, see :h 23.4 for binary editing.


Update: Based on this answer, here's a function that jumps to byte offset:

let s:last_jump_bytes = 0

function! JumpTo(byte_nr)
    let crt_byte = line2byte(line('.')) + col('.')
    if (a:byte_nr == 0)
        let dst_byte = crt_byte + s:last_jump_bytes
    else
        let dst_byte = crt_byte + a:byte_nr
        let s:last_jump_bytes = a:byte_nr
    endif
    let dst_line = byte2line(dst_byte)
    let dst_col = dst_byte -line2byte(dst_line)
    "remove next line if you don't want to record this for `Ctrl-O`
    execute "normal " . dst_line . "G"
    call setpos('.', [0, dst_line, dst_col])
endfunction

command! -nargs=1 JumpToOffset :call JumpTo(<f-args>)

" silly mapping to Ctrl-C (demo)
nnoremap <expr> <silent> <c-c> ":<c-u>call JumpTo(" . v:count . ")<cr>"

Can be used like this:

:JumpToOffset 400

or typing the mapped keyboard mapping, with a count:

40CTRL-C

If you don't use a count, the previous count number is re-used. So you can do: 40CTRL-C CTRL-C CTRL-C 30CTRL-C CTRL-C to jump 40, 40, 40, 30, 30 bytes etc.

Hit Ctrl-O to jump back (see comments inside the function).

12
  • Thanks for the answer. I'm really looking for moving forward X bytes, not chars though. Could you explain what your search pattern does, possibly with references to documentation?
    – Rob W
    Commented Jul 6, 2015 at 14:31
  • Done. It is also possible to automate the [count]go process though in a vim function (read current byte offset, add desired number, go there).
    – VanLaser
    Commented Jul 6, 2015 at 15:02
  • ... added automated function and command.
    – VanLaser
    Commented Jul 6, 2015 at 16:57
  • Thanks for the update, this starts to look good! There are two small differences between [count]go and your method: 1) [count]go adds an item to the jump list, so I can use Ctrl+O to quickly jump back to my previous position. 2) [count]go can be used without :, is it possible to introduce a new [count]GO that does your thing. Could you edit your answer to also match that behavior of go?
    – Rob W
    Commented Jul 6, 2015 at 17:08
  • edited for both points
    – VanLaser
    Commented Jul 6, 2015 at 17:19
10

I ended up using the following solution, which implements the logic from my question.

  • [count]GO to move [count] bytes forward.
  • [count]Go to move [count] bytes backwards.

Add this to your .vimrc:

function! JumpToByte(byte_nr)
    " See https://vi.stackexchange.com/a/3911/2720 for the byte counting bug
    let crt_byte = line2byte(line('.')) + col('.') - 1
    if version < 781 && &l:binary == 1 && &l:eol == 0
        let crt_byte += 1
        let crt_byte += line('.') == 1
    endif
    let dst_byte = crt_byte + a:byte_nr
    execute "normal " . dst_byte . "go"
endfunction
nnoremap <expr> <silent> GO ":<c-u>call JumpToByte(" . v:count . ")<cr>"
nnoremap <expr> <silent> Go ":<c-u>call JumpToByte(-" . v:count . ")<cr>"

Thanks to VanLaser for his initial implementation, which put me in the right direction.

5
  • One question, where exactly do you need this functionality? (BTW I ended up combining your solution with reusing the previous count if none is provided).
    – VanLaser
    Commented Jul 6, 2015 at 20:38
  • 1
    @VanLaser I was reading the raw contents of a PDF file, in order to get a better understanding of the PDF file format. A PDF file consists of many objects, and many of these objects have a length prefix. Being able to jump X bytes ahead was useful for debugging. And before you ask why I'm editing raw PDF files: I am developing a new feature for PDF.js which requires a deeper knowledge of the PDF file format.
    – Rob W
    Commented Jul 6, 2015 at 20:44
  • Thanks for the answer (and good luck)! The combined version is here, if interested: pastebin.com/7sVyiA85
    – VanLaser
    Commented Jul 6, 2015 at 20:55
  • @VanLaser I've updated my answer with a final version. It turns out that your original line counting method was fine, but there was a bug in Vim. I've submitted a patch which got accepted, so in the latest version of Vim, your answer will also work as intended.
    – Rob W
    Commented Jul 12, 2015 at 21:25
  • You seem to have done some real digging there (going to the source) - congratulations!
    – VanLaser
    Commented Jul 12, 2015 at 21:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.