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

Re: [vim] Text object selection for function parameters

Expand Messages
  • A.Politz
    ... With searchpos() this becomes easier than I first thought. assert( cursor at or in front of opening paren ) func! FargsPos() let res = [] let p0 =
    Message 1 of 8 , Aug 30 4:58 PM
    • 0 Attachment
      hermitte@... wrote:

      >
      >
      >d- Another problem I see is related to parameters having embedded commas or
      >balanced braces -- or even ignoring comments. Typically when the type of a
      >formal parameter is a function pointer type, or when the actual parameter is an
      >expression made of a function call.
      >Right now I don't see any neat and efficient way to handle this issue. Did any
      >of you already solved a similar one?
      >
      >
      >
      >Thanks for any input (ergonomical as technical).
      >
      >[1] http://vim.wikia.com/wiki/Indent_text_object
      >
      >
      >
      With searchpos() this becomes easier than I first
      thought.

      "assert("cursor at or in front of opening paren")
      func! FargsPos()
      let res = []
      let p0 = getpos('.')
      "find first paren
      if !search('(','Wc')
      return []
      endif
      call add(res,getpos('.'))
      "goto closing paren
      if searchpair('(','',')','W','Skip()') <= 0
      call setpos('.',p0)
      return []
      endif
      let end = getpos('.')
      "go back to opening paren
      call setpos('.',res[0])
      "search for ',' , while not at the closing paren
      while searchpair('(',',',')','W','Skip()') > 0 && getpos('.') != end
      call add(res,getpos('.'))
      endwhile
      call add( res , end)
      call setpos('.',p0)
      return res " = positions of '(' ',' ... ')'
      endfun

      func! Skip()
      return = synIDattr(synID(line('.'), col('.'), 0),'name') =~?
      'string\|comment\|character'
      endfun


      -ap


      --~--~---------~--~----~------------~-------~--~----~
      You received this message from the "vim_use" maillist.
      For more information, visit http://www.vim.org/maillist.php
      -~----------~----~----~----~------~----~------~--~---
    • hermitte@free.fr
      Hello, A.Politz wrote: [snip script] Your use of searpair() reminded of the second argument I had completly missed. Applying the same
      Message 2 of 8 , Sep 3, 2007
      • 0 Attachment
        Hello,

        "A.Politz" <politza@...> wrote:

        [snip script]

        Your use of searpair() reminded of the second argument I had completly missed.
        Applying the same kind of technique than the one I used in my bracketing system,
        here is the lastest version of the parameters selection mappings.

        It's not yet perfect, but quite functional.

        Thanks a lot!

        ---------- >% ------------
        " Notes:
        " * "i," can't be used to select several parameters with several uses of
        " "i," ; use "a," instead (-> va,a,a,). This is because of single
        " letter parameters.
        " However, "v2i," works perfectly.
        " * Vim7+ only
        " * The following should be resistant to &magic, and other mappings
        onoremap <silent> i, :<c-u>call <sid>SelectParam(1,0)<cr>
        xnoremap <silent> i, :<c-u>call <sid>SelectParam(1,1)<cr><esc>gv
        onoremap <silent> a, :<c-u>call <sid>SelectParam(0,0)<cr>
        xnoremap <silent> a, :<c-u>call <sid>SelectParam(0,1)<cr><esc>gv

        function! s:SelectParam(inner, visual)
        let pos = getpos('.')
        if a:visual ==1 && s:CurChar("'>") =~ '[(,]'
        normal! gvl
        else
        let b = searchpair('\V(\zs','\V,\zs','\V)','bcW','s:Skip()')
        if 0 == b
        throw "Not on a parameter"
        endif
        normal! v
        endif
        let cnt = v:count <= 0 ? 1 : v:count

        while cnt > 0
        let cnt -= 1
        let e = searchpair('\V(', '\V,','\V)', 'W','s:Skip()')
        if 0 == e
        exe "normal! \<esc>"
        call setpos('.', pos)
        throw "Not on a parameter2"
        endif
        if cnt > 0
        normal! l
        endif
        endwhile
        if a:inner == 1
        normal! h
        endif
        endfunction

        function! s:CurChar(char)
        let c = getline(a:char)[col(a:char)-1]
        return c
        endfunction

        func! s:Skip()
        return synIDattr(synID(line('.'), col('.'), 0),'name') =~?
        \ 'string\|comment\|character\|doxygen'
        endfun
        ------------- >% ----------------

        I'll add it into my C&C++ suite, and I guess I'll upload it soon as an
        independant plugin on vim.org.

        --
        Luc Hermitte

        --~--~---------~--~----~------------~-------~--~----~
        You received this message from the "vim_use" maillist.
        For more information, visit http://www.vim.org/maillist.php
        -~----------~----~----~----~------~----~------~--~---
      • A.Politz
        Your use of searpair() reminded of the second argument I had completly missed. Which second argument ? That works already pretty well, nevertheless I made a
        Message 3 of 8 , Sep 3, 2007
        • 0 Attachment
          "Your use of searpair() reminded of the second argument I had completly missed."

          Which second argument ?


          That works already pretty well, nevertheless I made a couple
          comments and attached my own version, if you don't mind. I
          don't think my version is a 100% thing either. I think
          the best approach would be to first define exactly the
          behaviour of each command in any state on any part of the
          arguments, and model a code which satisfies this behaviour.

          What's still missing is :
          [cd], - delete, change till end of arg , like 'dw'

          -ap



          --------------%<---------------------------------------
          " Notes:
          " * "i," can't be used to select several parameters with several uses of
          " "i," ; use "a," instead (-> va,a,a,). This is because of single
          " letter parameters.
          " However, "v2i," works perfectly.
          " * Vim7+ only
          " * The following should be resistant to &magic, and other mappings
          onoremap <silent> i, :<c-u>call <sid>SelectParam(1,0)<cr>
          xnoremap <silent> i, :<c-u>call <sid>SelectParam(1,1)<cr><esc>gv
          onoremap <silent> a, :<c-u>call <sid>SelectParam(0,0)<cr>
          xnoremap <silent> a, :<c-u>call <sid>SelectParam(0,1)<cr><esc>gv

          function! s:SelectParam(inner, visual)
          let pos = getpos('.')
          "Does not work is [(,] is e.g. inside a string
          if a:visual ==1 && s:CurChar("'>") =~ '[(,]'
          "Why 'l' ? searchpair() first moves the cursor anyway.
          "Besides it does not work at the eol.
          normal! gvl
          else
          "regardless of 'magic' '(', ',' and ')' always match itselves.
          let b = searchpair('\V(\zs','\V,\zs','\V)','bcW','s:Skip()')
          if 0 == b
          "I would prefer to simply return, like 'dw' in an empty buffer.
          throw "Not on a parameter"
          endif
          normal! v
          endif
          let cnt = v:count <= 0 ? 1 : v:count

          while cnt > 0
          let cnt -= 1
          let e = searchpair('\V(', '\V,','\V)', 'W','s:Skip()')
          "Like 999d in a buffer with 10 lines, do what is possible.
          if 0 == e
          exe "normal! \<esc>"
          call setpos('.', pos)
          throw "Not on a parameter2"
          endif
          if cnt > 0
          "see 2nd comment
          normal! l
          endif
          endwhile
          if a:inner == 1
          "better : call search('.','b') , which works also at the start of a line
          normal! h
          endif
          endfunction

          function! s:CurChar(char)
          let c = getline(a:char)[col(a:char)-1]
          return c
          endfunction

          func! s:Skip()
          return synIDattr(synID(line('.'), col('.'), 0),'name') =~?
          \ 'string\|comment\|character\|doxygen'
          endfun
          --------------%<---------------------------------------

          --------------%<---------------------------------------
          onoremap <silent> i, :<c-u>call <sid>SelectParam(1,0)<cr>
          xnoremap <silent> i, :<c-u>call <sid>SelectParam(1,1)<cr><esc>gv
          onoremap <silent> a, :<c-u>call <sid>SelectParam(0,0)<cr>
          xnoremap <silent> a, :<c-u>call <sid>SelectParam(0,1)<cr><esc>gv

          function! s:SelectParam(inner, visual)
          if a:visual ==1 && CharAtMark("'>") =~ '[(,]' &&
          !SkipAt(line("'>"),col("'>"))
          normal! gv
          "See the thread 'problem with searchpair()' why this the following
          is necessary
          elseif searchpair('(',',',')','bcW','Skip()') > 0 ||
          searchpair('(',',',')','bW','Skip()') > 0
          call search('.')
          normal! v
          else
          return
          endif
          let cnt = v:count <= 0 ? 1 : v:count

          while cnt > 0
          let cnt -= 1
          if searchpair('(', ',',')', 'W','Skip()') <= 0
          break
          endif
          endwhile
          " Don't include the last closing paren
          if a:inner == 1 || searchpair('(',',',')','n','Skip()') <= 0
          call search('.','b')
          endif
          endfunction

          function! CharAtMark(mark)
          let c = getline(a:mark)[col(a:mark)-1]
          return c
          endfunction

          func! Skip()
          return synIDattr(synID(line('.'), col('.'), 0),'name') =~?
          'special\|string\|comment\|character\|doxygen'
          endfun
          func! SkipAt(l,c)
          return synIDattr(synID(a:l, a:c, 0),'name') =~?
          'special\|string\|comment\|character\|doxygen'
          endfun

          --------------%<---------------------------------------


          --~--~---------~--~----~------------~-------~--~----~
          You received this message from the "vim_use" maillist.
          For more information, visit http://www.vim.org/maillist.php
          -~----------~----~----~----~------~----~------~--~---
        • hermitte@free.fr
          ... The {middle} argument. ... Please comment and fix as you want. They are welcomed. ... So far, most of the small tests I ve done seem fine. However, there
          Message 4 of 8 , Sep 3, 2007
          • 0 Attachment
            "A.Politz" <politza@...> wrote:

            > "Your use of searpair() reminded of the second argument I had completly
            > missed."
            >
            > Which second argument ?

            The {middle} argument.

            > That works already pretty well, nevertheless I made a couple
            > comments and attached my own version, if you don't mind.

            Please comment and fix as you want. They are welcomed.

            > I don't think my version is a 100% thing either. I think
            > the best approach would be to first define exactly the
            > behaviour of each command in any state on any part of the
            > arguments, and model a code which satisfies this behaviour.

            So far, most of the small tests I've done seem fine. However, there are still
            some odd behaviours I'm not sure if they should be perceived as bugs or
            features.

            For instance, in
            Un(Null,fun2(fun3(a,b,g(NULL))),t, titi, , zzz)
            executing "v5a," on the "a" selects from the "a" to the "," preceding the "t".

            > What's still missing is :
            > [cd], - delete, change till end of arg , like 'dw'

            I may be naive, but I think these mappings will be the simple ones.

            > --------------%<---------------------------------------
            > function! s:SelectParam(inner, visual)
            > let pos = getpos('.')
            > "Does not work is [(,] is e.g. inside a string

            Indeed.

            > if a:visual ==1 && s:CurChar("'>") =~ '[(,]'
            > "Why 'l' ? searchpair() first moves the cursor anyway.
            > "Besides it does not work at the eol.
            > normal! gvl

            Indeed you're right. I did a few tests with 'l' before thinking of using 'gv'.

            > [...]
            > if 0 == b
            > "I would prefer to simply return, like 'dw' in an empty buffer.
            > throw "Not on a parameter"

            I'd rather have the mapping insult us than seeing it do strange an unexpected
            things. Seeing a "di," misbehave in interactive mode in not a big issue. Seeing
            a complex plugin misbehave and not understanding why is much more annoying and
            time consuming. As I started working on "i,/a," with the idea of using them in
            complex plugins, aborting the mappings was a "natural" choice.

            > let e = searchpair('\V(', '\V,','\V)', 'W','s:Skip()')
            > "Like 999d in a buffer with 10 lines, do what is possible.

            It's the same idea: See what the mapping does on "vi," when the cursor is on
            "zzz" when there is no closing bracket.

            I perfectly see your point concerning the 999d, but unless we find a parade, I'd
            rather not rely on silent aborts.
            May be in that case we could test whether the abort position is before the saved
            position .... I'll test it later.

            > [....]

            > call search('.')

            Interresing. I've never though about using search() this way. Until now, I've
            relying on ':exe "normal! <left/right>"'. This time I used l/h to "simplify" the
            code, but completly forgot about eol issue.
            I can't remember whether <left>/<right> ignore the EOL, or whether they depend
            on a setting.
            BTW, what could be the benefits of using search() instead of <right>/<left> ?


            Thanks for your improvments.

            --
            Luc Hermitte
            http://hermitte.free.fr/vim/

            --~--~---------~--~----~------------~-------~--~----~
            You received this message from the "vim_use" maillist.
            For more information, visit http://www.vim.org/maillist.php
            -~----------~----~----~----~------~----~------~--~---
          • hermitte@free.fr
            ... I ve just tested it, and it seems to work well. Here is an excerpt of the ... function s:SelectParam(inner,visual) let saved_pos = getpos( . ) . . . while
            Message 5 of 8 , Sep 3, 2007
            • 0 Attachment
              hermitte@... wrote:

              > > let e = searchpair('\V(', '\V,','\V)', 'W','s:Skip()')
              > > "Like 999d in a buffer with 10 lines, do what is possible.
              >
              > It's the same idea: See what the mapping does on "vi," when the cursor
              > is on "zzz" when there is no closing bracket.
              >
              > I perfectly see your point concerning the 999d, but unless we find
              > a parade, I'd rather not rely on silent aborts.
              > May be in that case we could test whether the abort position is
              > before the saved position .... I'll test it later.

              I've just tested it, and it seems to work well. Here is an excerpt of the
              patched function:
              ----------- %< ------------
              function s:SelectParam(inner,visual)
              let saved_pos = getpos('.')
              .
              .
              .
              while cnt > 0
              let cnt -= 1
              if 0 == searchpair('(', ',',')', 'W','s:Skip()')
              if s:IsBefore(getpos('.'), saved_pos)
              " no "vi," when starting from the last parameter
              exe "normal! \<esc>"
              call setpos('.', saved_pos)
              throw (a:visual?'v':'').(a:inner?'i':'a').",: Cursor not on a parameter"
              else
              echomsg (a:visual?'v':'').(a:inner?'i':'a').",: No more parameters"
              " "999di," silently deletes everything till the end
              break
              endif
              endif
              endwhile
              --------------- >% ----------------


              and the new helper function:
              --------------- >% ----------------
              function! s:IsBefore(lhs_pos, rhs_pos)
              if a:lhs_pos[0] != a:rhs_pos[0]
              throw "Postions from incompatible buffers can't be ordered"
              endif
              "1 test lines
              "2 test cols
              let before
              \ = (a:lhs_pos[1] == a:rhs_pos[1])
              \ ? (a:lhs_pos[2] < a:rhs_pos[2])
              \ : (a:lhs_pos[1] < a:rhs_pos[1])
              return before
              endfunction

              --------------- >% ----------------

              --
              Luc Hermitte
              http://hermitte.free.fr/vim/

              --~--~---------~--~----~------------~-------~--~----~
              You received this message from the "vim_use" maillist.
              For more information, visit http://www.vim.org/maillist.php
              -~----------~----~----~----~------~----~------~--~---
            • A.Politz
              ... Right, searchpair() continues the search till it finds the outermost (opening or closing ) parens. It should therefor abort as soon as it finds a paren.
              Message 6 of 8 , Sep 3, 2007
              • 0 Attachment
                hermitte@... wrote:

                >"A.Politz" <politza@...> wrote:
                >
                >
                >
                >>"Your use of searpair() reminded of the second argument I had completly
                >>missed."
                >>
                >>Which second argument ?
                >>
                >>
                >
                >The {middle} argument.
                >
                >
                >
                >>That works already pretty well, nevertheless I made a couple
                >>comments and attached my own version, if you don't mind.
                >>
                >>
                >
                >Please comment and fix as you want. They are welcomed.
                >
                >
                >
                >>I don't think my version is a 100% thing either. I think
                >>the best approach would be to first define exactly the
                >>behaviour of each command in any state on any part of the
                >>arguments, and model a code which satisfies this behaviour.
                >>
                >>
                >
                >So far, most of the small tests I've done seem fine. However, there are still
                >some odd behaviours I'm not sure if they should be perceived as bugs or
                >features.
                >
                >For instance, in
                > Un(Null,fun2(fun3(a,b,g(NULL))),t, titi, , zzz)
                >executing "v5a," on the "a" selects from the "a" to the "," preceding the "t".
                >
                >
                >
                Right, searchpair() continues the search till it finds the
                outermost (opening or closing ) parens. It should therefor
                abort as soon as it finds a paren.

                >>What's still missing is :
                >>[cd], - delete, change till end of arg , like 'dw'
                >>
                >>
                >
                >I may be naive, but I think these mappings will be the simple ones.
                >
                >
                >
                >>--------------%<---------------------------------------
                >>function! s:SelectParam(inner, visual)
                >> let pos = getpos('.')
                >> "Does not work is [(,] is e.g. inside a string
                >>
                >>
                >
                >Indeed.
                >
                >
                >
                >> if a:visual ==1 && s:CurChar("'>") =~ '[(,]'
                >> "Why 'l' ? searchpair() first moves the cursor anyway.
                >> "Besides it does not work at the eol.
                >> normal! gvl
                >>
                >>
                >
                >Indeed you're right. I did a few tests with 'l' before thinking of using 'gv'.
                >
                >
                >
                >>[...]
                >> if 0 == b
                >> "I would prefer to simply return, like 'dw' in an empty buffer.
                >> throw "Not on a parameter"
                >>
                >>
                >
                >I'd rather have the mapping insult us than seeing it do strange an unexpected
                >things. Seeing a "di," misbehave in interactive mode in not a big issue. Seeing
                >a complex plugin misbehave and not understanding why is much more annoying and
                >time consuming. As I started working on "i,/a," with the idea of using them in
                >complex plugins, aborting the mappings was a "natural" choice.
                >
                >
                Firstly : It should never do unexpected things. It would be a bad idea
                to write code which is based on a function with undefined or unknown
                behaviour.
                Assume you are on/in a wellformed argumentlist and based on that
                everything is defined/works as expected.
                I think you have to make this assumption, consider this declaration of
                2 c-functions :

                void f1(a,b, ; //oops forgot closing )
                int f2 c,d); //oops forgot opening (

                With this simple aproach (searchpair()) you have no way of knowing
                that this is a syntax error and should not be processed.

                I am beginning to think that it would be better to implement a function
                which returns concrete positions ( of '(', ',' and ')' s ) which then
                could be processed in anyway you like. Similay to my first aproach.

                >> let e = searchpair('\V(', '\V,','\V)', 'W','s:Skip()')
                >> "Like 999d in a buffer with 10 lines, do what is possible.
                >>
                >>
                >
                >It's the same idea: See what the mapping does on "vi," when the cursor is on
                >"zzz" when there is no closing bracket.
                >
                >I perfectly see your point concerning the 999d, but unless we find a parade, I'd
                >rather not rely on silent aborts.
                >May be in that case we could test whether the abort position is before the saved
                >position .... I'll test it later.
                >
                >
                >
                >>[....]
                >>
                >>
                >
                >
                >
                >> call search('.')
                >>
                >>
                >
                >Interresing. I've never though about using search() this way. Until now, I've
                >relying on ':exe "normal! <left/right>"'. This time I used l/h to "simplify" the
                >code, but completly forgot about eol issue.
                >I can't remember whether <left>/<right> ignore the EOL, or whether they depend
                >on a setting.
                >BTW, what could be the benefits of using search() instead of <right>/<left> ?
                >
                >
                The benefit is that search('.') moves the cursor to the next char
                in the buffer regardless of lines, while <left>/<right>/h/l move
                to the next char in the line.

                >
                >Thanks for your improvments.
                >
                >
                >
                -ap

                --~--~---------~--~----~------------~-------~--~----~
                You received this message from the "vim_use" maillist.
                For more information, visit http://www.vim.org/maillist.php
                -~----------~----~----~----~------~----~------~--~---
              Your message has been successfully submitted and would be delivered to recipients shortly.