How do I visual select a calculation backwards?

后端 未结 2 1857
予麋鹿
予麋鹿 2021-01-16 07:40

I would like to visual select backwards a calculation p.e.

200 + 3 This is my text -300 +2 + (9*3)
                        |-------------|*

This is text 0,2         


        
2条回答
  •  温柔的废话
    2021-01-16 08:26

    A regex that comes close in pure vim is

    \v\c\s*\zs(\s{-}(((sqrt|log|sin|cos|tan|exp)?\(.{-}\))|(-?[0-9,.]+(e-?[0-9]+)?)|([-+*/%^]+)))+(\s*\=?)?\s*
    

    There are limitations: subexpressions (including function arguments) aren't parsed. You'd need to use a proper grammar parser to do that, and I don't recommend doing that in pure vim1

    Operator Mapping

    To enable using this a bit like text-objects, use something like this in your $MYVIMRC:

    func! DetectExpr(flag)
        let regex = '\v\c\s*\zs(\s{-}(((sqrt|log|sin|cos|tan|exp)?\(.{-}\))|(-?[0-9,.]+(e-?[0-9]+)?)|([-+*/%^]+)))+(\s*\=?)?\s*' 
        return searchpos(regex, a:flag . 'ncW', line('.'))
    endf
    
    func! PositionLessThanEqual(a, b)
        "echo 'a: ' . string(a:a)
        "echo 'b: ' . string(a:b)
        if (a:a[0] == a:b[0])
            return (a:a[1] <= a:b[1]) ? 1 : 0
        else
            return (a:a[0] <= a:b[0]) ? 1 : 0
        endif
    endf
    
    func! SelectExpr(mustthrow)
        let cpos  = getpos(".")
        let cpos  = [cpos[1], cpos[2]] " use only [lnum,col] elements
        let begin = DetectExpr('b')
        if ( ((begin[0] == 0) && (begin[1] == 0))
          \ || !PositionLessThanEqual(begin, cpos) )
            if (a:mustthrow)
                throw "Cursor not inside a valid expression"
            else
                "echoerr "not satisfied: " . string(begin) . " < " . string(cpos)
            endif
            return 0
        endif
        "echo "satisfied: " . string(begin) . " < " . string(cpos)
    
        call setpos('.', [0, begin[0], begin[1], 0])
        let end = DetectExpr('e')
        if ( ((end[0] == 0) || (end[1] == 0))
          \ || !PositionLessThanEqual(cpos,  end) )
            call setpos('.', [0, cpos[0], cpos[1], 0])
            if (a:mustthrow)
                throw "Cursor not inside a valid expression"
            else
                "echoerr "not satisfied: " . string(begin) . " < " . string(cpos) . " < " . string(end) 
            endif
            return 0
        endif
        "echo "satisfied: " . string(begin) . " < " . string(cpos) . " < " . string(end) 
    
        norm! v
        call setpos('.', [0, end[0],   end[1],   0])
        return 1
    endf
    
    silent! unmap X
    silent! unmap 
    
    xnoremap X :call SelectExpr(0)
    onoremap X :call SelectExpr(0)
    

    Now you can operator on the nearest expression around (or after) the cursor position:

    • vX - [v]isually select e[X]pression
    • dX - [d]elete current e[X]pression
    • yX - [y]ank current e[X]pression
    • "ayX - id. to register a

    As a trick, use the following to arrive at the exact ascii art from the OP (using virtualedit for the purpose of the demo):

    Insert mode mapping

    In response to the chat:

    " if you want trailing spaces/equal sign to be eaten:
    imap  :let @e="""edX=substitute(@e, '^\v(.{-})(\s*\=?)?\s*$', '\=string(eval(submatch(1)))', '')
    
    " but I'm assuming you wanted them preserved:
    imap  :let @e="""edX=substitute(@e, '^\v(.{-})(\s*\=?\s*)?$', '\=string(eval(submatch(1))) . submatch(2)', '')
    

    allows you to hit Alt-. during insert mode and the current expression gets replaced with it's evaluation. The cursor ends up at the end of the result in insert mode.

    200 + 3 This is my text -300 +2 + (9*3)
    
    This is text 0.25 + 2.000 + sqrt(15/1.5)
    

    Tested by pressing Alt-. in insert 3 times:

    203 This is my text -271
    
    This is text 5.412278
    

    For Fun: ascii art

    vXoyoEsc`<jPvXr-r|e.

    To easily test it yourself:

    :let @q="vXoyo\x1b`

    Now you can @q anywhere and it will ascii-decorate the nearest expression :)

    200 + 3 = 203 -300 +2 + (9*3) =
    |-------|*
              |-------------------|*
    
    200 + 3 = 203 -300 +2 + (9*3)
              |-----------------|*
    |-------|*
    
    This is text 0,25 + 2.000 + sqrt(15/1.5)
                 |-------------------------|*
    

    1 consider using Vim's python integration to do such parsing

提交回复
热议问题