2

Say I have an indented block like so

int main(int argc, char **argv){
    printf ("odd numbers between 1 and 10:\n");
    // level 1
    for (int i = 1; i <= 10 ; i ++) { 
        // level 2
        if (i % 2 == 1){
            printf (" %d",i);
            // level 3
            ....
        }
        ...
    }
}

i{ would work here because the indents are contained inside braces but it would not for languages like python or indented lists in markdown, etc.

Would it be possible to define a text object, say iI ("in Indentation") or something similar to that effect to operate only on the indentation level the cursor is on ? So suppose we are in the if block (level 3) then diI will only delete in that indentation level and similarly for level 1 it will delete everything in the block.

1
3

There are plenty of implementations for indent text objects -- I guess due to different way "indent" object is defined.

I have my own: check https://gist.github.com/habamax/4662821a1dad716f5c18205489203a67

With following snippet in your vimrc you will be able to vii or vai to select indent:

"" Indent text object
"" Useful for python-like indentation based programming languages
func! s:indent_textobj(inner)
    if getline('.') =~ '^\s*$'
        let ln_start = s:detect_nearest_line()
        let ln_end = ln_start
    else
        let ln_start = line('.')
        let ln_end = ln_start
    endif

    let indent = indent(ln_start)
    if indent > 0
        while indent(ln_start) >= indent && ln_start > 0
            let ln_start = prevnonblank(ln_start-1)
        endwhile

        while indent(ln_end) >= indent && ln_end <= line('$')
            let ln_end = s:nextnonblank(ln_end+1)
        endwhile
    else
        while indent(ln_start) == 0 && ln_start > 0 && getline(ln_start) !~ '^\s*$'
            let ln_start -= 1
        endwhile
        while indent(ln_start) > 0 && ln_start > 0
            let ln_start = prevnonblank(ln_start-1)
        endwhile
        while indent(ln_start) == 0 && ln_start > 0 && getline(ln_start) !~ '^\s*$'
            let ln_start -= 1
        endwhile

        while indent(ln_end) == 0 && ln_end <= line('$') && getline(ln_end) !~ '^\s*$'
            let ln_end += 1
        endwhile
        while indent(ln_end) > 0 && ln_end <= line('$')
            let ln_end = s:nextnonblank(ln_end+1)
        endwhile
    endif

    if a:inner || indent == 0
        let ln_start = s:nextnonblank(ln_start+1)
    endif

    if a:inner
        let ln_end = prevnonblank(ln_end-1)
    else
        let ln_end = ln_end-1
    endif

    if ln_end < ln_start
        let ln_end = ln_start
    endif

    exe ln_end
    normal! V
    exe ln_start
endfunc


func! s:nextnonblank(lnum) abort
    let res = nextnonblank(a:lnum)
    if res == 0
        let res = line('$')+1
    endif
    return res
endfunc


func! s:detect_nearest_line() abort
    let lnum = line('.')
    let nline = s:nextnonblank(lnum)
    let pline = prevnonblank(lnum)
    if abs(nline - lnum) > abs(pline - lnum) || getline(nline) =~ '^\s*$'
        return pline
    else
        return nline
    endif
endfunc


onoremap <silent>ii :<C-u>call <sid>indent_textobj(v:true)<CR>
onoremap <silent>ai :<C-u>call <sid>indent_textobj(v:false)<CR>
xnoremap <silent>ii :<C-u>call <sid>indent_textobj(v:true)<CR>
xnoremap <silent>ai :<C-u>call <sid>indent_textobj(v:false)<CR>

1
  • 1
    works perfectly thanks!
    – First User
    Dec 2 '20 at 13:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.