How do I visual select a calculation backwards?

孤者浪人 提交于 2020-01-11 07:22:07

问题


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,25 + 2.000 + sqrt(15/1.5)
             |-------------------------|*

The reason is that I will use it in insert mode. After writing a calculation I want to select the calculation (using a map) and put the results of the calculation in the text.

What the regex must do is:
- select from the cursor (see * in above example) backwards to the start of the calculation
(including \/-+*:.,^).
- the calculation can start only with log/sqrt/abs/round/ceil/floor/sin/cos/tan or with a positive or negative number
- the calculation can also start at the beginning of the line but it never goes back to a previous line

I tried in all ways but could not find the correct regex.
I noted that backward searching is different then forward searching.

Can someone help me?

Edit
Forgot to mention that it must include also the '=' if there is one and if the '=' is before the cursor or if there is only space between the cursor and '='.
It must not include other '=' signs.

200 + 3 = 203 -300 +2 + (9*3) =
          |-------------------|<SPACES>*

200 + 3 = 203 -300 +2 + (9*3)
          |-----------------|<SPACES>*

* = where the cursor is


回答1:


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 <M-.>

xnoremap <silent>X :<C-u>call SelectExpr(0)<CR>
onoremap <silent>X :<C-u>call SelectExpr(0)<CR>

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 <M-.> <C-o>:let @e=""<CR><C-o>"edX<C-r>=substitute(@e, '^\v(.{-})(\s*\=?)?\s*$', '\=string(eval(submatch(1)))', '')<CR>

" but I'm assuming you wanted them preserved:
imap <M-.> <C-o>:let @e=""<CR><C-o>"edX<C-r>=substitute(@e, '^\v(.{-})(\s*\=?\s*)?$', '\=string(eval(submatch(1))) . submatch(2)', '')<CR>

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`<jPvXr-r|e.a*\x1b"
:set virtualedit=all

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




回答2:


This seems quite a complicated task after all to achieve with regex, so if you can avoid it in any way, try to do so.

I've created a regex that works for a few examples - give it a try and see if it does the trick:

^(?:[A-Za-z]|\s)+((?:[^A-Za-z]+)?(?:log|sqrt|abs|round|ceil|floor|sin|cos|tan)[^A-Za-z]+)(?:[A-Za-z]|\s)*$

The part that you are interested in should be in the first matching group.

Let me know if you need an explanation.

EDIT:

^ - match the beginning of a line

(?:[A-Za-z]|\s)+ - match everything that's a letter or a space once or more

match and capture the following 3:
((?:[^A-Za-z]+)? - match everything that's NOT a letter (i.e. in your case numbers or operators)

(?:log|sqrt|abs|round|ceil|floor|sin|cos|tan) - match one of your keywords

[^A-Za-z]+) - match everything that's NOT a letter (i.e. in your case numbers or operators)

(?:[A-Za-z]|\s)* - match everything that's a letter or a space zero or more times

$ - match the end of the line



来源:https://stackoverflow.com/questions/10494321/how-do-i-visual-select-a-calculation-backwards

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!