How to append lines when iterating over a range.

59 views
Skip to first unread message

Eric Siegel

unread,
Aug 7, 2013, 9:58:55 PM8/7/13
to vim...@googlegroups.com
I want to convert lines like this:

value1|value2|value3

into:

def foo(x="value1"
y="value2"
z="value3")

But I ran into a problem when trying to apply this function to a range. The range gets confused because I am appending additional lines. I solved this problem by using the range keyword and using a:firstline, a:lastline, and a for loop that skips every four lines.

Something like this:
This is the incomplete version because the actual problem I'm trying to solve is slightly different, but the crux of the problem is still the same.

function! ReFormat() range
let num_lines = a:lastline - a:firstline + 1

for linenum in range(a:firstline, a:firstline + (num_lines - 1) * 4, 4)
let line = getline(linenum)
...
endfor
endfunction

Is there not some helper method that knows how to append using ranges. My coworker said that in emacs he's able to get the current cursor position. In vim we can get the current line '.', but if we could get the cursor position as the range iterates, we wouldn't have to worry about the number of lines appended.

Thanks for reading my rambling,
Eric

Tim Chase

unread,
Aug 7, 2013, 10:49:15 PM8/7/13
to vim...@googlegroups.com
On 2013-08-07 18:58, Eric Siegel wrote:
> I want to convert lines like this:
>
> value1|value2|value3
>
> into:
>
> def foo(x="value1"
> y="value2"
> z="value3")
>
> But I ran into a problem when trying to apply this function to a
> range. The range gets confused because I am appending additional
> lines.
[snip]
> Is there not some helper method that knows how to append using
> ranges

I know that using a :g command notes the first/last lines of the
range specified as well as marking the already-processed lines. You
don't provide the exact specifications of your transformation (are
there always 3 items separated by pipes? does "foo" ever change?
does indentation change based on a changing function-name?)

However, a simple transformation can be done with

:%s/\(.*\)|\(.*\)|\(.*\)/def foo(x="\1"\r y="\2"\r
z="\3")

which should work for any range you pass it (in the above example,
the whole file, but could just as easily be any Ex range).

-tim




Eric Siegel

unread,
Aug 8, 2013, 12:40:11 AM8/8/13
to vim...@googlegroups.com
I know that I could use the substitute command, but it would be a bit unwieldy.
The "foo" part doesn't change, and the number of parts is fixed, but the "value" sections go through transformations.

I can also use a macro, but that too suffers from the same problem.

What do you mean that the global command marks the already processed lines?

John Little

unread,
Aug 8, 2013, 7:50:48 AM8/8/13
to vim...@googlegroups.com
I suggest using a substitute that calls a function using \= (see :help sub-replace).

For example:

function! ReFormat(stuff)
let l = split(a:stuff, '|')
let x = l[0]
let y = l[1]
let z = l[2]
return 'def foo(x="' . x . '"' . "\n"
\ . ' y="' . y . '"' . "\n"
\ . ' z="' . z . '")'
endfunction

:%s/\w\+|\w\+|\w\+/\=ReFormat(submatch(0))

If the pattern that finds the lines to process is different to that which identifies the text within the line, use :global, say

:g/whatever/s/\w\+|\w\+|\w\+/\=ReFormat(submatch(0))

Using a function that processes a range is hard IMO.

Regards, John Little

Eric Siegel

unread,
Aug 8, 2013, 10:28:52 AM8/8/13
to vim...@googlegroups.com
It's good to hear that other people think range with text insertion is difficult. Looks like I'll just have to write another helper function.

I do like the use of "\=Reformat()". That's pretty clean.

Eric

Ben Fritz

unread,
Aug 8, 2013, 11:21:30 AM8/8/13
to vim...@googlegroups.com
On Wednesday, August 7, 2013 8:58:55 PM UTC-5, Eric Siegel wrote:
> My coworker said that in emacs he's able to get the current cursor position. In vim we can get the current line '.', but if we could get the cursor position as the range iterates, we wouldn't have to worry about the number of lines appended.
>

I'm not sure whether you still need the answer to this question, but:

:help getpos()

Also:

:help line()
:help col()
:help virtcol()

Any of these functions can return information about the cursor position.

Maybe you already know about these and I misunderstand your question? You mentioned using '.' to get the current line, by which you might mean line('.'), but if that's the case I'm not sure what your problem is because that will give you the cursor line.

Eric Siegel

unread,
Aug 8, 2013, 1:42:12 PM8/8/13
to vim...@googlegroups.com
Ben,

Hopefully this doesn't get sent twice, but it seems that my last response was lost in the ether.

I don't think that those functions help.  Here is a simplified version of my problem.

Say we want to convert these lines:

line1
line2
line3

Into this:

line1
some
appended
lines
line2
some
appended
lines
line3
some
appended
lines

My first attempt at the function looks like this:

function! Example()
   call append(line('.'), ["some", "appended", "lines"])
endfunction

The problem is that this won't work for ranges.

If you try this command:

1,3call Example()

You get this output:

line1
some
some
some
appended
lines
appended
lines
appended
lines
line2
line3

The problem is that range iterates over line numbers, but I'm changing the content of those lines.

The solution that I came up with that works with ranges is this:

function! Example() range
   let num_lines = a:lastline - a:firstline + 1

   for linenum in range(a:firstline, a:firstline + (num_lines - 1) * 4, 4)
      call append(linenum, ["some", "appended", "lines"])
   endfor
endfunction

It works, but is by no means elegant.  I have to specify the number my start and end lines and how much to stride by.

This is why using substitute, as John suggests, is probably the best option, assuming that things don't get too complicated.

Hope this explains things,

Eric




--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to a topic in the Google Groups "vim_use" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/vim_use/BSej2dwukdA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to vim_use+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



Tim Chase

unread,
Aug 8, 2013, 1:50:22 PM8/8/13
to vim...@googlegroups.com, siege...@gmail.com
Which can take advantage of the aforementioned line-marking that :g
does by then doing

:1,3g/^/call Example()

-tim



Reply all
Reply to author
Forward
0 new messages