Banner of improving-tex-editing-in-vim-with-a-smart-'select-inside-any-pair'-function-.jpg

Improving TeX Editing in Vim with a Smart 'Select Inside Any Pair' Function


Category: vim

📅 November 23, 2025   |   👁️ Views: 1

Author:   mosaid

Improving Your TeX Workflow in Vim with a Smart “Select Inside Any Pair” Function

One of the reasons I love working in Vim is how easily I can extend it to match the way I write and edit. When dealing with LaTeX files—especially long derivations, nested parentheses, or math environments—I often need to quickly select the text inside {…}, (…), or even inside $…$ blocks. Doing this manually wastes time, interrupts my flow, and becomes frustrating when there are many levels of nesting.

To fix this, I wrote a custom function that intelligently selects whatever “pair” I am currently inside: curly braces, brackets, parentheses, math mode, quotes—anything. This small function has massively improved how I work inside TeX files.

Why This Function Matters

Instant selection in TeX math: When working between $…$, a single keystroke selects the content cleanly—even in long expressions.

Nested structures handled automatically: For { { ( … ) } }, the function detects the correct level around the cursor instead of selecting the wrong one.

Works across all common pairs: Parentheses, brackets, braces, math mode, quotes, and even symmetric delimiters are supported.

Better than built-in text objects: Vim’s default vi( and friends work only when your cursor is right next to the delimiter. This custom function works even if you're deep inside the content.

The Function That Does the Magic

    
function! SelectInsideAnyPair()
    let l:savepos = getpos('.')

    let l:pairs = [
    \ ['{', '}'],
    \ ['[', ']'],
    \ ['(', ')'],
    \ ['$', '$'],
    \ ['"', '"'],
    \ ["'", "'"]
    \ ]

    for l:pair in l:pairs
        let l:open  = l:pair[0]
        let l:close = l:pair[1]

        if l:open ==# l:close
            " symmetric delimiters
            let l:start = searchpos('\V' . l:open, 'bnW')
            let l:end   = searchpos('\V' . l:close, 'nW')
        else
            " asymmetric pairs (handles nesting)
            let l:start = searchpairpos('\V' . l:open, '', '\V' . l:close, 'bnW')
            let l:end   = searchpairpos('\V' . l:open, '', '\V' . l:close, 'nW')
        endif

        if l:start[0] > 0 && l:end[0] > 0
            let l:cur = getpos('.')
            let l:start_before_cursor = (l:start[0] < l:cur[1]) || (l:start[0] == l:cur[1] && l:start[1] <= l:cur[2])
            let l:end_after_cursor   = (l:end[0]   > l:cur[1]) || (l:end[0]   == l:cur[1] && l:end[1]   >= l:cur[2])

            if l:start_before_cursor && l:end_after_cursor
                " start just after opening delimiter
                call setpos('.', [0, l:start[0], l:start[1] + 1, 0])
                normal! v
                " end just before closing delimiter
                call setpos('.', [0, l:end[0], l:end[1], 0])
                normal! h
                return
            endif
        endif
    endfor

    " --- fallback: select whole line (without trailing $) ---
    let l:lnum = line('.')
    let l:line = getline(l:lnum)

    " compute start and end columns
    let l:start_col = 1
    let l:end_col   = len(l:line)

    " if line ends with $, exclude it
    if l:end_col > 0 && l:line[-1:] ==# '$'
        let l:end_col -= 1
    endif

    if l:end_col >= l:start_col
        call setpos('.', [0, l:lnum, l:start_col, 0])
        normal! v
        call setpos('.', [0, l:lnum, l:end_col, 0])
    else
        call setpos('.', l:savepos)
        echo "Nothing to select"
    endif
endfunction

nnoremap <silent> <leader>v :call SelectInsideAnyPair()<CR>

    

This tiny Vim function eliminates the repetitive hassle of manually selecting content inside brackets, quotes, or math delimiters. For LaTeX-heavy workflows, it’s a real productivity booster.


← How I Use Vim to Do Quick Calculations While Writing LaTeX Automating Code Transformations in Vim with RunScriptsOnSelect →