0

My final goal is to split a text on sentence delimiters. However it shouldn't be split in sentences, but in chunks ca. 1000 lines and plus the rest of the current sentence. But in the experiment below I use only 2 lines for simplicity.

Consider the following text in a VIM buffer:

line 1. line 1. line 1.
line 2. line 2. line 2.
line 3. line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.

After execution of:

:2 | exec 'normal! )hvgg' | exec "'<,'>w /tmp/part1.txt" | exec 'normal! gvd'

The result is as expected. Buffer contains:

line 2. line 2.
line 3. line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.

But after the next run

:2 | exec 'normal! )hvgg' | exec "'<,'>w /tmp/part2.txt" | exec 'normal! gvd'

buffer contains:

line 2.
line 3. line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.

What's going on here? Why doesn't it move to the second line which is line 3. line 3. line 3. ?

But I would expect the following text remain in the buffer

line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.
4

The root of the problem is the gv at the end. It is unnecessary since the visual selection persists from the previous part of the command. Worse, it's not harmless because when you do gv when you're already in Visual mode the current and previous Visual area are exchanged. Meaning the previous selection is reselected!

Even though the previous selection was deleted the selection is applied to the same area...or at least what remains of it. In this case that would be the first of the remaining line 2. sentences.

To demonstrate, watch what happens when you enter this twice: 2G)hvgggvd. This is exactly what's happening on the command line, minus the write to disk, but with 100% Normal mode commands.

So anyways we just need to lose the gv and this will work:

:2 | exec 'normal! )hvgg' | exec "'<,'>w! /tmp/part1.txt" | exec 'normal! d'

Beyond that there is a lot of noise in this command that can be cleaned up. Two of the execs and accompanying quotes aren't necessary:

:2 | exec 'norm! )hvgg' | '<,'>w! /tmp/part1.txt | norm! d

Update: In response to one of your comments this...

'<,'>

is a linewise operation that selects from the first line of the last selected visual area to the last line of the same area. To get character wise ranges you need to use back-tics like this:

`<,`>

However, this won't work with :w...it's a linewise operation only.

I'd point out that this is outside the scope of your question which asks why particular text is being deleted....so you might want to ask a separate question for characterwise write to file.

Update 2: Here's a totally different approach to your problem...

Create a macro with the following command

:let @q = ")hs\<CR>\<ESC>k:let @a=@a+1\<CR>:1,.w /tmp/part\<C-R>a.txt\<CR>dgg"
  • Run this command: :let @a = 0.
  • Put the cursor on a sentence and, in Normal mode, run the macro with @q

This will behave the way you describe: the sentence and everything above it will be written to a file named /tmp/part1.txt and the saved text will be deleted. Now move to any other sentence and do @q again and this time the same thing will happen but it will write to /tmp/part2.txt. Each time you run the macro the file name will auto-increment. You can reset the index again with :let @a = 0.

Like the previous update this is not addressing the actual question asked but because I'm nice I'm providing some extra help. (And OP still won't accept my answer.)

  • I get "E20: Mark not set" when I run your last command for the first time after opening the file. However after I re-run it (pressing up-arrow button and then ENTER) it works, but only once. The file contains expected number of lines. But all the subsequent executions put only one line into the file. – ka3ak Feb 17 '18 at 15:51
  • I use ":1000..." at the begin of the command. The rest is copied from your command. The first run creates a file '/tmp/part1.txt' with 1000 lines. All the subsequent runs overwrite the file, but it contains only one line after that. – ka3ak Feb 17 '18 at 16:06
  • E492: Not an editor command: <,>w! /tmp/part1.txt | norm! d There should be backtick characters before < and > which aren't displayed for some reason in the comment. – ka3ak Feb 17 '18 at 16:08
  • You can read it for yourself at :h '< and :h '>. I think you might need to create another question for this separate issue if you can't get it working. Oh, and read :h mark-motions to see difference between back and forward tics. – B Layer Feb 17 '18 at 16:10
  • Ok. But it doesn't explain why there is only one line in the file instead of at least 1000. – ka3ak Feb 17 '18 at 16:21
-1

I've finally come to the accepted solution. Thanks to @B Layer for cooperation (e.g. mentioning that w command only writes full lines to a file). I've upvoted your answer.

:let i=1
:1000 | exec 'normal! )hvggd' | exec 'if i<10 | let num="0" . i | else | let num=i | endif' | call writefile(split(@@, "\n", 1), '/tmp/test' . num . '.txt') | let i=i+1

After that repeat the command by pressing @: if the last chunk is shorter than 1000 lines you'll get an error. Just save the rest manually.

After 5 executions I had as expected:

$ wc -l /tmp/test*.txt
  1001 /tmp/test01.txt
  1000 /tmp/test02.txt
  1001 /tmp/test03.txt
  1006 /tmp/test04.txt
  1001 /tmp/test05.txt

For those who want to use it I'll shortly explain the key steps:

:1000

Move to the line 1000 (chunks should be at least 1000 lines long)

exec 'normal! )hvggd'

Move to the begin of the next sentence and one char back so that the first char of the next sentence isn't deleted afterwards. To work properly on sentences which are spread over multiple lines whichwrap=h,l should be set, which isn't default as far as I know. Select everything from the current cursor position to the begin and delete it. The deleted content will be saved in the register.

call writefile(split(@@, "\n", 1), '/tmp/test' . num . '.txt')

Write the register contents (the chunk) to a file

I proceeded from the assumption that there won't be more than 99 chunks. If you need more, adjust the part related to padding as needed.

  • I'm glad you found something that works for the larger problem you were trying to address but I worry that future readers will be confused because this does not answer the question being asked. Your title is "vim doesn't move to expected line" and except for a bit of background at the beginning the entire post is a demonstration of the problem followed by these two questions (the only questions in the whole post): "What's going on here?" and "Why doesn't it move to the second line which is line 3. line 3. line 3.?" – B Layer Feb 18 '18 at 18:33
  • ... [cont] Yes you mention a "final goal" at the start but you don't explicitly ask for a solution to it and you don't provide enough information to solve that anyways before devoting the vast majority of the post to one specific issue. – B Layer Feb 18 '18 at 18:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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