Loading ...
Sorry, an error occurred while loading the content.

Place a sign on each line between two regexps

Expand Messages
  • esquifit
    Hi I want to place a sign (:he signs) next to each line between two regexps Example Beginning with this file context 1 context 2 start line à line à end
    Message 1 of 5 , Oct 22, 2013
    • 0 Attachment
      Hi

      I want to place a sign (:he signs) next to each line between two regexps

      Example

      Beginning with this file

      context 1
      context 2
      start
      line …
      line …
      end
      context 3
      context 4
      context 5
      start
      line …
      line …
      end
      context 6

      the goal is the following

      context 1
      context 3
      start
      >> line …
      >> line …
      end
      context 3
      context 4
      context 5
      start
      >> line …
      >> line …
      end
      context 6

      Assuming the sign is called Foo, the way to place it at line 123 is

      :sign place 1 name=Foo line=123 file=/my/file

      My first attempt was something like this:

      g:/start/+1;/end/-1 exe 'sign place 1 name='.line('.').' file='.expand('%:p')

      But this yielded a E481. Apparently exe doesn’t support address ranges.

      Next thing I attempted was using macros:
      let @q=':sign place 1 name=CIInsert line= =line(".")^M file= =expand("%:p")^M^M'

      With this definition, echo @q yields:
      :sign place 1 name=Foo line=^R=line(".")^M file=^R=expand("%:p")^M^M
      and this produces the desired result when I @q on any line.

      Using this in combination with an address range:
      g:/start/+1;/end/-1 @q
      only puts a sign next to the last line of each region. This suggest I'm misinterpreting the way g://;// works. I also tried with ',' and space between the start and end patters, no difference.

      Any idea?

      --
      --
      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 the Google Groups "vim_use" group.
      To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@....
      For more options, visit https://groups.google.com/groups/opt_out.
    • Ben Fritz
      ... You are correct, exe does not support ranges. But you re also using your g command wrong. See below. ... I think you are very confused how the :g command
      Message 2 of 5 , Oct 22, 2013
      • 0 Attachment
        On Tuesday, October 22, 2013 8:04:19 AM UTC-5, esquifit wrote:
        > My first attempt was something like this:
        >
        > g:/start/+1;/end/-1 exe 'sign place 1 name='.line('.').' file='.expand('%:p')
        >
        > But this yielded a E481. Apparently exe doesn’t support address ranges.
        >

        You are correct, exe does not support ranges. But you're also using your
        g command wrong. See below.

        > Next thing I attempted was using macros:
        >
        > let @q=':sign place 1 name=CIInsert line= =line(".")^M file= =expand("%:p")^M^M'
        >
        >
        >
        > With this definition, echo @q yields:
        >
        > :sign place 1 name=Foo line=^R=line(".")^M file=^R=expand("%:p")^M^M
        >
        > and this produces the desired result when I @q on any line.
        >
        >
        >
        > Using this in combination with an address range:
        >
        > g:/start/+1;/end/-1 @q
        >
        > only puts a sign next to the last line of each region. This suggest
        > I'm misinterpreting the way g://;// works.

        I think you are very confused how the :g command works. In this last
        command, I think you're also confused about the difference between
        normal mode and command-line mode.

        I THINK you are trying to execute your macro q as if in normal mode, on
        all lines between a "start" and an "end".

        The :g command takes a SINGLE pattern, and on all lines matching that
        pattern, it will execute a SINGLE ex (command-line) command. The g,
        pattern, and command are all separated with a delimiter. The delimiter
        is the same between all of them.

        So to start with, writing @q as the command does not do what you want.
        If you want @q to act like you typed it in normal mode, you need to use
        the ex command, :normal! @q

        Next, you're trying to use a start and end pattern with the :g command.
        But you can only use one pattern.

        If you had ONE group you wanted to execute your code on, you could use a
        range with the :g command. For example,

        :/start/+1,/end/-1g#^#normal! @q

        In this case, the start and end patterns make a range for the :g
        command. This command will run on the next start...end group after the
        cursor only, but will execute on all lines inside that group. I used the
        '#' character as a delimiter in this case for the :g command, which will
        match every line within the range, because I just used '^' as the
        pattern which matches the beginning of any line. Finally, the normal!
        command will run your q macro.

        But you have multiple start...end blocks. So instead, I'd suggest
        running the :g command on the entire buffer. You should run the command
        on lines matching "start" and the command should be created in a way to
        affect all lines of interest relative to that "start" line. In this
        case, you could do it like this:

        :g#start#+1,/end/-1normal! @q

        Again I use # for the delimiter for the :g command. The :g command will
        run a command on every line matching "start" because that is the pattern
        I give it. The command it runs is the same :normal command, but it will
        execute on every line in a range. The range is defined to run starting
        from "+1": i.e. from the next line after the one the :g command is
        currently running on; i.e. the first line below "start". The range is
        defined to run ending at /end/-1, i.e. the last line above the next
        occurrence of "end".

        Note that so far I've been using your second approach, to run a recorded
        macro. I don't actually like this approach, I think your :exe approach
        is better. But as you discovered, :exe does not take a range so you
        can't just drop it in as a replacement for :normal! above. My first try
        was chaining together :g commands, but I guess that's not allowed. There
        may be a better way, but at this point I would just do an explicit loop:

        :g#start#let line=line('.')+1 | while getline(line)!~'end' | exe 'sign place 1 name=Foo line='.line.' file='.expand('%:p') | let line+=1 | endwhile

        Or maybe a normal command with a range:

        :g#start#+1,/end/-1normal! :exe 'sign place 1 name=Foo line='.line('.').' file='.expand('%:p')^M

        Or, make a function:

        function! PlaceSigns(linenum)
        let line=a:linenum+1
        while getline(line)!~'end'
        exe 'sign place 1 name=Foo line='.line.' file='.expand('%:p')
        let line+=1
        endwhile
        endfun

        :g#start#call PlaceSigns(line('.'))

        --
        --
        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 the Google Groups "vim_use" group.
        To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@....
        For more options, visit https://groups.google.com/groups/opt_out.
      • esquifit
        ... [...] ... Thanks for the detail explanation. Apart from my mistake of forgetting the normal before @q, the paragraph above was enlightening for me.
        Message 3 of 5 , Oct 22, 2013
        • 0 Attachment
          On Tuesday, October 22, 2013 5:57:58 PM UTC+2, Ben Fritz wrote:
          > On Tuesday, October 22, 2013 8:04:19 AM UTC-5, esquifit wrote:
          [...]
          >
          > :g#start#+1,/end/-1normal! @q
          >
          > Again I use # for the delimiter for the :g command. The :g command will
          > run a command on every line matching "start" because that is the pattern
          > I give it. The command it runs is the same :normal command, but it will
          > execute on every line in a range. The range is defined to run starting
          > from "+1": i.e. from the next line after the one the :g command is
          > currently running on; i.e. the first line below "start".

          Thanks for the detail explanation. Apart from my mistake of forgetting the 'normal' before @q, the paragraph above was enlightening for me. Empirically I knew that, generally speaking,

          :g/pat1/;/pat2/cmd

          works and I was misled thinking that the 'g' modified the behaviour or the
          range /pat1/;/pat2/ to which the cmd applies so that all regions delimited
          between those patters would be taken into account (instead of only the first
          one, which is what happens if you don't use 'g'). As I know understand from
          your explanation, the proper way to look at it is:

          Wrong interpretation
          :g modifier
          /start/+1,/end/-1 range
          normal! @q command
          Correct interpretation
          :g/start/ global line selector
          +1,/end/-1 range
          normal! @q command


          > :g#start#let line=line('.')+1 | while getline(line)!~'end' | exe 'sign
          > place 1 name=Foo line='.line.' file='.expand('%:p') | let line+=1 | endwhile

          I won't argue the merits of this line. I just don't like it. Not for a quick
          one-time hack.

          >
          > Or maybe a normal command with a range:
          >
          > :g#start#+1,/end/-1normal! :exe 'sign place 1 name=Foo line='.line('.').'
          > file='.expand('%:p')^M

          This is indeed similar to what I originally intended. The 'normal :exe'
          does the trick of avoiding the E418 about ranges not supported by exe, but
          for some reason the whole line doesn't work. Even a simpler thing like

          :g/START/;/END/normal :exe 'echo '.line('.')

          doesn't yield any result.

          > Or, make a function:
          >
          > function! PlaceSigns(linenum)
          > let line=a:linenum+1
          > while getline(line)!~'end'
          > exe 'sign place 1 name=Foo line='.line.' file='.expand('%:p')
          > let line+=1
          > endwhile
          > endfun
          >
          > :g#start#call PlaceSigns(line('.'))

          I also thought of this but I didn't know how to pass the line number as a
          parameter. I have still to test it.

          Thanks again for your insight and the time you put into the answer.

          --
          --
          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 the Google Groups "vim_use" group.
          To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@....
          For more options, visit https://groups.google.com/groups/opt_out.
        • Ben Fritz
          ... Did you include a literal carriage return at the end, by typing CTRL-V and then CTRL-M or Enter to get Vim to display ^M? I m seeing a weird pattern for
          Message 4 of 5 , Oct 22, 2013
          • 0 Attachment
            On Tuesday, October 22, 2013 3:46:09 PM UTC-5, esquifit wrote:
            > On Tuesday, October 22, 2013 5:57:58 PM UTC+2, Ben Fritz wrote:
            > >
            > > Or maybe a normal command with a range:
            > >
            > > :g#start#+1,/end/-1normal! :exe 'sign place 1 name=Foo line='.line('.').'
            > > file='.expand('%:p')^M
            >
            > This is indeed similar to what I originally intended. The 'normal :exe'
            > does the trick of avoiding the E418 about ranges not supported by exe, but
            > for some reason the whole line doesn't work. Even a simpler thing like
            >
            > :g/START/;/END/normal :exe 'echo '.line('.')
            >
            > doesn't yield any result.
            >

            Did you include a literal carriage return at the end, by typing CTRL-V and then CTRL-M or Enter to get Vim to display ^M?

            I'm seeing a weird pattern for the :g command again in your simplified example, I'm not going to test whether it works anyway.

            --
            --
            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 the Google Groups "vim_use" group.
            To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@....
            For more options, visit https://groups.google.com/groups/opt_out.
          • esquifit
            ... That s was the problem, thanks. ... which you can also write as ... This executes the following command on all lines matching /START/. On those lines, the
            Message 5 of 5 , Oct 24, 2013
            • 0 Attachment
              On Wednesday, October 23, 2013 4:23:29 AM UTC+2, Ben Fritz wrote:
              > Did you include a literal carriage return at the end, by typing CTRL-V and
              > then CTRL-M or Enter to get Vim to display ^M?

              That's was the problem, thanks.

              > I'm seeing a weird pattern for the :g command again in your simplified
              > example, I'm not going to test whether it works anyway.

              No weird pattern here, it is the same as before:

              :g/START/;/END/normal :exe 'echo '.line('.')^M
              which you can also write as
              :g#START#;/END/normal :exe 'echo '.line('.')^M

              This executes the following command on all lines matching /START/. On those
              lines, the command is 'further restricted' to the range
              ;/END/
              which stands for 'all lines between the line containing the cursor and the
              next match of /END/'

              This kind of range (omitting the first component) is not explicitly
              documented in the vim help, but it works just fine.


              --
              --
              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 the Google Groups "vim_use" group.
              To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@....
              For more options, visit https://groups.google.com/groups/opt_out.
            Your message has been successfully submitted and would be delivered to recipients shortly.