2

I have created a vmap text object for selecting the text of a single LaTeX \item:

vmap im ?\\item<CR>o/\\item\\|\\end{itemize}<CR>b$

But this has the annoying feature that I lose my current search term. I have read that search terms are restored when the search happens inside a function call, so I wanted to convert the map to just call a function that would do the searches:

function! ItemizeTextObject()
  ?\\item
  normal o
  /\\item|\\end{itemize}
  normal b$
endfunction

vmap in :call ItemizeTextObject()<CR>

Unfortunately, this does not work: I get an error ("Pattern not found: \item|\end{itemize}"), no text at all is selected, and a new line is inserted below the line my cursor is on. I tried several variations of this, and had no success.

I think the basic problem is that I have to preserve the visual mode when calling the function (the o in my command should switch to the other end of the selection, but it inserts a new line instead), but I don't know how.

Update:

I try to get the following behaviour: In a text like this:

\begin{itemize}
  \item lorem ipsum...
  \item piece of text I want to select,
   the *CURSOR* is here, and there is more text
   that follows
  \item lorem ipsum...
\end{itemize}

I want to hit vin, and then the text block in the middle should be selected:

  \item piece of text I want to select,
   the *CURSOR* is here, and there is more text
   that follows

that means the text from the beginning of the previous \item, until but not including the next \item or \end{itemize}.

daniel kullmann
  • 13,653
  • 8
  • 51
  • 67

1 Answers1

2

I've used the doc on operatorfunc to come up with the following, which should be (close to) what you want1:

function! ItemizeTextObject(type, ...)
    let sel_save = &selection
    let &selection = "inclusive"
    let reg_save = @@

    if a:0  " Invoked from Visual mode, use '< and '> marks.
        silent! 1,+1?\\item
        norm v | " use V for linewise visual mode
        "" use V for linewise visual mode:
        "norm V
        silent! /\\item\|\\end{itemize}
    "elseif a:type == 'line'
    "elseif a:type == 'block'
    else
       silent! 1,+1?\\item
       norm v
       silent! /\\item
    endif

    norm b$

    let &selection = sel_save
    let @@ = reg_save
endfunction

silent! unmap in

xnoremap <silent> in :<C-U>call ItemizeTextObject(visualmode(), 1)<CR>

If you want the mapping in both visual and select modes, you should use vnoremap

Notes of things to address:

  • you can now implement the motion from another mode (fill in the branches in the function)
  • if wrapscan is on, no search should wrap (perhaps temporarily set nowrapscan?)
  • you might want to make the operator repeating so you can extend the selection by saying vininin (see https://stackoverflow.com/a/7292271/85371 for an example)
  • it looks like you want 'linewise' behaviour (due to b$?)
    • consider using norm V (see comment)

Edit I compared the behaviour with this simple mapping:

xnoremap <silent>in ?\\item<CR>o/\\item\\|\\end{itemize}<CR>b$

1 Disclaimer: I don't know LateX

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633