<commit_before>let s:save_cpo = &cpo
set cpo&vim

scriptencoding utf-8

function! s:log(_) " {{{
  if get(g:, 'operator#furround#debug', 0)
    silent! call vimconsole#log(a:_)
  endif
endfunction " }}}
" default block {{{
" vim 7.3 では '[^\n]' ではだめらしい.
let s:CR = "\n"
let s:wsc_tex = '\s*\%(%[^' . s:CR . ']*\)\=\n\='  " white space with comment 
let s:ws_tex = '\s*\n\='  " white spece
let s:prm_tex = '\%(\[[^' .s:CR. '\]]\+\]\|{[^' .s:CR. '}]\+}\)*' " parameters [] or {}
let s:default_config = {
\ '-' : {
\   'merge_default_config' : 0,
\   'block' : [
\     {'start': '(', 'end': ')'},
\     {'start': '{', 'end': '}'},
\     {'start': '[', 'end': ']'},
\     {'start': '<', 'end': '>'},
\     {'start': '"', 'end': '"'},
\     {'start': "'", 'end': "'"},
\     {'start': "`", 'end': "`"},
\   ]},
\ 'tex' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '\\begin\s*{\(\k\+\*\=\)}' . s:prm_tex . s:ws_tex,
\      'end_expr': '\\end\s*{\1}',
\      'end': '\end{\1}', 'regexp': 1},
\     {'start': '\\begin\s*{\(\k\+\*\=\)}' . s:prm_tex . s:wsc_tex,
\      'end_expr': '\\end\s*{\1}' . s:wsc_tex,
\      'end': '\end{\1}', 'regexp': 1},
\     {'start': '{\\\k\+\s\+', 'end': '}',
\      'regexp': 1, 'comment' : '{\bf xxx}'},
\     {'start': '\\\k\+' . s:prm_tex . '{', 'end': '}',
\      'regexp': 1, 'comment' : '\hoge[xxx]{yyy}'},
\     {'start': '\\verb\*\=\(.\)', 'end': '\1', 'regexp': 1},
\     {'start': '\(\$\$\=\)', 'end': '\1', 'regexp': 1},
\     {'start': '\\[', 'end': '\]', 'regexp': 1},
\     {'start': '\\(', 'end': '\)', 'regexp': 1},
\   ]},
\ 'c' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '\k\+(', 'end': ')', 'regexp': 1},
\     {'start': '\k\+\[', 'end': ']', 'regexp': 1},
\   ]},
\ 'vim' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '\([vgslabwt]:\)\?[A-Za-z_][0-9A-Za-z_#.]*(',
\      'end': ')', 'regexp': 1},
\     {'start': '\([vgslabwt]:\)\?[A-Za-z_][0-9A-Za-z_#.]*\[',
\      'end': ']', 'regexp': 1},
\   ]},
\ 'help' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '*', 'end': '*'},
\     {'start': '|', 'end': '|'},
\   ]},
\ 'html' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '<\(\k\+\)\%(\s\+[^>]\+\)*>\(\s\|\n\)*',
\      'end': '</\1>', 'regexp': 1},
\   ]},
\ }
" }}}

function! s:escape(pattern) " {{{
    return escape(a:pattern, '\~ .*^[''$')
endfunction " }}}

function! s:escape_n(str, mlist, conv) " {{{
  let s = a:str
  if s =~ '\\[1-9]'
    for i in range(1, min([len(a:mlist)-1, 9]))
      if a:conv
        let s = substitute(s, '\\' . i, '\=s:escape(a:mlist[' . i . '])', 'g')
      else
        let s = substitute(s, '\\' . i, '\=a:mlist[' . i . ']', 'g')
      endif
    endfor
  endif

  return s
endfunction " }}}

function! s:is_valid_config() " {{{
  return  exists("g:operator#furround#config") &&
  \ type(g:operator#furround#config) == type({})
endfunction " }}}

function! s:get_val(key, val) " {{{
  " g:operator#furround#config から
  " default 値付きの値取得.
  if !s:is_valid_config()
    return a:val
  endif

  let dic = g:operator#furround#config
  for ft in [&filetype, '-']
    if has_key(dic, ft) && has_key(dic[ft], a:key)
      return dic[ft][a:key]
    endif
  endfor

  return a:val
endfunction " }}}

function! s:get(key, val) " {{{
  return get(g:, 'operator#furround#' . a:key, a:val)
endfunction " }}}

function! s:get_pair_lhs(str, blocks, idx, slen) " {{{
  let min = a:slen
  let bmin = {}
  for b in a:blocks
    let i = match(a:str, b.start, a:idx)
    if i < 0
      continue
    endif

    if i < min
      let min = i
      let bmin = b

      if min == a:idx
        break
      endif
    endif
  endfor

  if min == a:slen
    " not found
    return {}
  endif

  let mlist = matchlist(a:str, bmin.start, min)
"  let regexp = get(bmin, 'regexp', 0)

  let pe = s:escape_n(bmin.end, mlist, 0)
  if mlist[0] =~ '\n$'
    let pe = "\n" . pe
  endif

  if has_key(bmin, 'end_expr')
    let pee = s:escape_n(bmin.end_expr, mlist, 1)
  else
    let pee = s:escape(pe)
  endif

  return {
  \ 'index' : min,
  \ 'start_str' : mlist[0],
  \ 'start_len' : len(mlist[0]),
  \ 'end_str' : pe,
  \ 'end_expr' : pee,
  \ }
endfunction " }}}

function! s:get_pair_rhs(str, stack, l, pair_idx) " {{{
  " stack に対する閉じ括弧チェック
  let stack = a:stack
  let l = a:l

  while len(stack) > 0
    " 閉じ括弧チェック
    let mrhs = match(a:str, stack[-1].end_expr, l)
    if mrhs < 0 || mrhs > a:pair_idx
      break
    endif

    let mstr = matchstr(a:str, stack[-1].end_expr, mrhs)
    call remove(stack, -1)
    let l = len(mstr) + mrhs
  endwhile

  return [l, stack]
endfunction " }}}

function! s:get_pair(str) " {{{
  let blocks = s:get_conf()
  let stack = []
  let slen = len(a:str)
  let l = 0
  while l < slen
    let pair = s:get_pair_lhs(a:str, blocks, l, slen)
    if len(pair) == 0
      break
    endif
    call s:log(pair)

    let [l, stack] = s:get_pair_rhs(a:str, stack, l, pair.index)
    if l > pair.index
      " 他の閉括弧で開括弧がつぶされた
      continue
    endif

    call add(stack, pair)
    let l = pair.index + pair.start_len
  endwhile

  let [l, stack] = s:get_pair_rhs(a:str, stack, l, slen)

  if len(stack) == 0
    return []
  endif

  let r = ''
  for i in range(len(stack)-1, 0, -1)
    let r .= stack[i].end_str
  endfor
  return ['', r]
endfunction " }}}

function! s:get_pair_from_key(str) " {{{
  " #conig[&filetype][key] に a:str があればそれを block とする
  if !s:is_valid_config()
    return []
  endif

  let blocks = g:operator#furround#config
  for ft in [&filetype, '-']
    if has_key(blocks, ft) 
      if has_key(blocks[ft], 'key') && has_key(blocks[ft]['key'], a:str)
        return blocks[ft]['key'][a:str]
      endif

      if !get(blocks[ft], 'merge_default_config_user', 0)
        break
      endif
    endif
  endfor

  return []
endfunction " }}}

function! s:get_block_append(str) " {{{

  " 開括弧がなかった場合には key が登録されているか確認する
  let pair = s:get_pair_from_key(a:str)
  if len(pair) > 0
    return pair
  endif

  let pair = s:get_pair(a:str)
  if len(pair) == 0
    " 何もなかったらデフォルトの括弧を追加する
    let pair = s:get_val('default_append_block', ['(', ')'])
  endif

  return [a:str . pair[0], pair[1]]
endfunction " }}}

function! s:get_reg_rmcr(r) " {{{
  let str = getreg(a:r)
  let len = len(str)
  while len > 1 && str[len - 1] == '\n'
    let str = str[0 : len - 2]
    let len -= 1
  endwhile
  return str
endfunction " }}}

function! operator#furround#complete_reg(...) " {{{
  let regs = '"0123456789-*+.:%#/'
  let list = map(split(regs, '.\zs'), 's:get_reg_rmcr(v:val)')
  let list = filter(list, 'v:val !~ ''^\s*$'' && v:val !~ ''\n''')
  return  join(list, "\n")
endfunction " }}}

:highlight furround_hl_group ctermfg=Blue ctermbg=LightRed
function! s:input(motion, reg) " {{{

  if s:get('highlight', 1)
    call s:knormal(printf('`[%s`]"%sy', s:funcs_motion[a:motion].v, a:reg))
    let hlgroup = s:get('hlgroup', 'furround_hl_group')
    let mids = s:funcs_motion[a:motion].highlight(getpos("'["), getpos("']"), hlgroup)
    :redraw
  else
    let mids = []
  endif

  try
    if s:get('debug', 0)<commit_msg>add config.complete<commit_after>let s:save_cpo = &cpo
set cpo&vim

scriptencoding utf-8

function! s:log(_) " {{{
  if get(g:, 'operator#furround#debug', 0)
    silent! call vimconsole#log(a:_)
  endif
endfunction " }}}

" default block {{{
" vim 7.3 では '[^\n]' ではだめらしい.
let s:CR = "\n"
let s:wsc_tex = '\s*\%(%[^' . s:CR . ']*\)\=\n\='  " white space with comment 
let s:ws_tex = '\s*\n\='  " white spece
let s:prm_tex = '\%(\[[^' .s:CR. '\]]\+\]\|{[^' .s:CR. '}]\+}\)*' " parameters [] or {}
let s:default_config = {
\ '-' : {
\   'merge_default_config' : 0,
\   'block' : [
\     {'start': '(', 'end': ')'},
\     {'start': '{', 'end': '}'},
\     {'start': '[', 'end': ']'},
\     {'start': '<', 'end': '>'},
\     {'start': '"', 'end': '"'},
\     {'start': "'", 'end': "'"},
\     {'start': "`", 'end': "`"},
\   ]},
\ 'tex' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '\\begin\s*{\(\k\+\*\=\)}' . s:prm_tex . s:ws_tex,
\      'end_expr': '\\end\s*{\1}',
\      'end': '\end{\1}', 'regexp': 1},
\     {'start': '\\begin\s*{\(\k\+\*\=\)}' . s:prm_tex . s:wsc_tex,
\      'end_expr': '\\end\s*{\1}' . s:wsc_tex,
\      'end': '\end{\1}', 'regexp': 1},
\     {'start': '{\\\k\+\s\+', 'end': '}',
\      'regexp': 1, 'comment' : '{\bf xxx}'},
\     {'start': '\\\k\+' . s:prm_tex . '{', 'end': '}',
\      'regexp': 1, 'comment' : '\hoge[xxx]{yyy}'},
\     {'start': '\\verb\*\=\(.\)', 'end': '\1', 'regexp': 1},
\     {'start': '\(\$\$\=\)', 'end': '\1', 'regexp': 1},
\     {'start': '\\[', 'end': '\]', 'regexp': 1},
\     {'start': '\\(', 'end': '\)', 'regexp': 1},
\   ]},
\ 'c' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '\k\+(', 'end': ')', 'regexp': 1},
\     {'start': '\k\+\[', 'end': ']', 'regexp': 1},
\   ]},
\ 'vim' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '\([vgslabwt]:\)\?[A-Za-z_][0-9A-Za-z_#.]*(',
\      'end': ')', 'regexp': 1},
\     {'start': '\([vgslabwt]:\)\?[A-Za-z_][0-9A-Za-z_#.]*\[',
\      'end': ']', 'regexp': 1},
\   ]},
\ 'help' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '*', 'end': '*'},
\     {'start': '|', 'end': '|'},
\   ]},
\ 'html' : {
\   'merge_default_config' : 1,
\   'block' : [
\     {'start': '<\(\k\+\)\%(\s\+[^>]\+\)*>\(\s\|\n\)*',
\      'end': '</\1>', 'regexp': 1},
\   ]},
\ }
" }}}

function! s:escape(pattern) " {{{
    return escape(a:pattern, '\~ .*^[''$')
endfunction " }}}

function! s:escape_n(str, mlist, conv) " {{{
  let s = a:str
  if s =~ '\\[1-9]'
    for i in range(1, min([len(a:mlist)-1, 9]))
      if a:conv
        let s = substitute(s, '\\' . i, '\=s:escape(a:mlist[' . i . '])', 'g')
      else
        let s = substitute(s, '\\' . i, '\=a:mlist[' . i . ']', 'g')
      endif
    endfor
  endif

  return s
endfunction " }}}

function! s:is_valid_config() " {{{
  return  exists("g:operator#furround#config") &&
  \ type(g:operator#furround#config) == type({})
endfunction " }}}

function! s:get_val(key, val) " {{{
  " g:operator#furround#config から
  " default 値付きの値取得.
  if !s:is_valid_config()
    return a:val
  endif

  let dic = g:operator#furround#config
  for ft in [&filetype, '-']
    if has_key(dic, ft) && has_key(dic[ft], a:key)
      return dic[ft][a:key]
    endif
  endfor

  return a:val
endfunction " }}}

function! s:get(key, val) " {{{
  return get(g:, 'operator#furround#' . a:key, a:val)
endfunction " }}}

function! s:get_pair_lhs(str, blocks, idx, slen) " {{{
  let min = a:slen
  let bmin = {}
  for b in a:blocks
    let i = match(a:str, b.start, a:idx)
    if i < 0
      continue
    endif

    if i < min
      let min = i
      let bmin = b

      if min == a:idx
        break
      endif
    endif
  endfor

  if min == a:slen
    " not found
    return {}
  endif

  let mlist = matchlist(a:str, bmin.start, min)
"  let regexp = get(bmin, 'regexp', 0)

  let pe = s:escape_n(bmin.end, mlist, 0)
  if mlist[0] =~ '\n$'
    let pe = "\n" . pe
  endif

  if has_key(bmin, 'end_expr')
    let pee = s:escape_n(bmin.end_expr, mlist, 1)
  else
    let pee = s:escape(pe)
  endif

  return {
  \ 'index' : min,
  \ 'start_str' : mlist[0],
  \ 'start_len' : len(mlist[0]),
  \ 'end_str' : pe,
  \ 'end_expr' : pee,
  \ }
endfunction " }}}

function! s:get_pair_rhs(str, stack, l, pair_idx) " {{{
  " stack に対する閉じ括弧チェック
  let stack = a:stack
  let l = a:l

  while len(stack) > 0
    " 閉じ括弧チェック
    let mrhs = match(a:str, stack[-1].end_expr, l)
    if mrhs < 0 || mrhs > a:pair_idx
      break
    endif

    let mstr = matchstr(a:str, stack[-1].end_expr, mrhs)
    call remove(stack, -1)
    let l = len(mstr) + mrhs
  endwhile

  return [l, stack]
endfunction " }}}

function! s:get_pair(str) " {{{
  let blocks = s:get_conf()
  let stack = []
  let slen = len(a:str)
  let l = 0
  while l < slen
    let pair = s:get_pair_lhs(a:str, blocks, l, slen)
    if len(pair) == 0
      break
    endif
    call s:log(pair)

    let [l, stack] = s:get_pair_rhs(a:str, stack, l, pair.index)
    if l > pair.index
      " 他の閉括弧で開括弧がつぶされた
      continue
    endif

    call add(stack, pair)
    let l = pair.index + pair.start_len
  endwhile

  let [l, stack] = s:get_pair_rhs(a:str, stack, l, slen)

  if len(stack) == 0
    return []
  endif

  let r = ''
  for i in range(len(stack)-1, 0, -1)
    let r .= stack[i].end_str
  endfor
  return ['', r]
endfunction " }}}

function! s:get_pair_from_key(str) " {{{
  " #conig[&filetype][key] に a:str があればそれを block とする
  if !s:is_valid_config()
    return []
  endif

  let blocks = g:operator#furround#config
  for ft in [&filetype, '-']
    if has_key(blocks, ft) 
      if has_key(blocks[ft], 'key') && has_key(blocks[ft]['key'], a:str)
        return blocks[ft]['key'][a:str]
      endif

      if !get(blocks[ft], 'merge_default_config_user', 0)
        break
      endif
    endif
  endfor

  return []
endfunction " }}}

function! s:get_block_append(str) " {{{

  " 開括弧がなかった場合には key が登録されているか確認する
  let pair = s:get_pair_from_key(a:str)
  if len(pair) > 0
    return pair
  endif

  let pair = s:get_pair(a:str)
  if len(pair) == 0
    " 何もなかったらデフォルトの括弧を追加する
    let pair = s:get_val('default_append_block', ['(', ')'])
  endif

  return [a:str . pair[0], pair[1]]
endfunction " }}}

function! s:get_reg_rmcr(r) " {{{
  let str = getreg(a:r)
  let len = len(str)
  while len > 1 && str[len - 1] == '\n'
    let str = str[0 : len - 2]
    let len -= 1
  endwhile
  return str
endfunction " }}}

function! operator#furround#complete_reg(A, L, P) " {{{
  let fn = s:get_val('complete', 0)
  if type(fn) == type(function('tr'))
    return fn(a:A, a:L, a:P)
  endif
  if type(fn) == type([])
    let l = fn
  else
    let l = []
  endif

  let regs = '"0123456789-*+.:%#/'
  let list = map(split(regs, '.\zs'), 's:get_reg_rmcr(v:val)')
  let list = filter(list, 'v:val !~ ''^\s*$'' && v:val !~ ''\n''')
  return  join(l + list, "\n")
endfunction " }}}

:highlight furround_hl_group ctermfg=Blue ctermbg=LightRed
function! s:input(motion, reg) " {{{

  if s:get('highlight', 1)
    call s:knormal(printf('`[%s`]"%sy', s:funcs_motion[a:motion].v, a:reg))
    let hlgroup = s:get('hlgroup', 'furround_hl_group')
    let mids = s:funcs_motion[a:motion].highlight(getpos("'["), getpos("']"), hlgroup)
    :redraw
  else
    let mids = []
  endif

  try
    if s:get('debug', 0)
{'fraction_non_alphanumeric': 0.2464320354718027, 'fraction_numerical': 0.011015657475405293, 'mean_word_length': 2.6470439615967662, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1530119', 'n_tokens_mistral': 6590, 'n_tokens_neox': 6346, 'n_words': 1287}