function! s:get_comment_pair() abort let pair = split(&commentstring, '%s') return [pair[0], get(pair, 1, '')] endfunction function! s:is_comment(from, to) abort let lnum = a:from while lnum <= a:to if synIDattr(synID(lnum, indent(lnum) + 1, 1), 'name') !~ 'comment' return v:false endif let lnum += 1 endwhile return v:true endfunction function! s:comment_on(from, to) abort let lines = getline(a:from, a:to) let min_indent = lines[0] for line in lines if line =~ '^\s*$' continue endif let current_indent = matchstr(line, '\s*') if len(current_indent) >= len(min_indent) continue endif let min_indent = current_indent if min_indent == '' break endif endfor "let indent = indent(a:from) "let idx = 1 "while indent > 0 && idx < len(lines) " if line =~ '^\s*$' " continue " endif " if indent >= indent(lines[idx]) " continue " endif " let indent = indent(lines[idx]) " let idx += 1 "endwhile let [open, close] = s:get_comment_pair() if open =~ '\S$' let open ..= ' ' endif if close =~ '^\S' let close = ' ' .. close endif let indent_len = len(min_indent) let lnum = a:from for line in lines call setline(lnum, min_indent .. trim(open .. line[indent_len:] .. close)) let lnum += 1 endfor endfunction function! s:comment_off(from, to) abort let [open, close] = s:get_comment_pair() let lines = getline(a:from, a:to) let lnum = a:from for line in lines let line = substitute(line, open .. '\s\?', '', '') let line = substitute(line, '\s*' .. close .. '\s*$', '', '') call setline(lnum, line) let lnum += 1 endfor endfunction function! mi#comment#operator_toggle(context = {}, type = '') abort if a:type == '' let context = { 'dot_command': v:false } let &operatorfunc = function('mi#comment#operator_toggle', [context]) return 'g@' endif let from = line("'[") let to = line("']") if from > to let [from, to] = [to, from] endif if s:is_comment(from, to) call s:comment_off(from, to) else call s:comment_on(from, to) endif let a:context.dot_command = v:true endfunction