Closed26

vimでtexのアウトラインを作ってみる

OmochiceOmochice

:vimgrepとかをするとロケーションリストで文字列と行番号が紐ついてるやつがでてくる。
texを書いてるときに\sectionとかをツリー表示にして見れるようにしたい

OmochiceOmochice

vim-lspのLspDocumentSymbolやvista.vimだとツリー表示になってないので自分で作る

OmochiceOmochice

まず、バッファ内のsectionを取得する。

let l:lines = getline(1, '$')
let l:sections = []
for l:idx in range(len(l:lines))
  if l:lines[l:idx] =~ '\(=?sub\)*section'
    let l:sections += [l:lines[l:idx] . ':' . string(l:idx+1)]
  endif
endfor
call map(l:qlist, {_, v -> substitute(v, '^\s*', '', '')[1:})

これで先頭の空白文字と\を取り除いた(sub)*sectionを取得できる

OmochiceOmochice

これをツリーっぽい表現に変える。
それぞれの要素の深さを持つl:depthも取っておく。
subの数で云々するので今はいらないけど後でsection以外も出したくなるとあったほうが便利そうなのでつけておく)

function! s:get_sections()
  let l:lines = getline(1, '$')
  let l:sections = []
  let l:depths = []
  for l:idx in range(len(l:lines))
    if l:lines[l:idx] =~ '\(=?sub\)*section'
      let l:sections += [l:lines[l:idx][1:] . ':' . string(l:idx+1)]
      let l:depths += [count(l:lines[l:idx], 'sub')]
    endif
  endfor
  call map(l:qlist, {_, v -> substitute(v, '^\s*', '', '')})
  return make_tree(l:sections, l:depths)
endfunction

function! s:is_last_element(i, d) abort
  return a:i == len(a:d) - 1
        \ || a:d[a:i] > a:d[a:i + 1]
endfunction

function! s:make_tree(contents, depths) abort
  let l:results = map(
        \ a:contents,
        \ {i, val -> repeat('| ', a:depths[i]) . (s:is_last_element(i, a:depths) ? '└ ' : '├ ') . a:contents[i]}
        \ )
  return l:results
endfunction
OmochiceOmochice

出来上がる配列

\section{1}
\subsection{1-1}
\subsection{1-2}
\subsubsection{1-2-1}
\subsubsection{1-2-2}
\subsection{1-3}

\section{2}
\seccction{this should not be shown}

で実行すると

[
\ '├ section{1}:1',
\ '| ├ subsection{1-1}:2',
\ '| ├ subsection{1-2}:3',
\ '| | ├ subsubsection{1-2-1}:4',
\ '| | └ subsubsection{1-2-2}:5',
\ '| └ subsection{1-3}:6',
\ '└ section{2}:8'
\ ]

となる

OmochiceOmochice

できた配列をlocation-listに流し込めればよさそう
でも単純にcall setloclist(0, ['foo', 'bar'])してもうまくいかないみたい

OmochiceOmochice

errorfotmatの形式があっていないのでうまくいかない。
正規表現的に表すと(│ )*[└├] (sub)*section{\.}:\d
最後の\dが行番号になる

OmochiceOmochice

let &errorformat = 'section{}:%l'にするとつぎのようになる

|1| 
|| subsection{}:2
|| subsection{}:3
|| subsubsection{}:4
|| subsubsection{}:5
|| subsection{}:6
|8| 
OmochiceOmochice

自分でバッファ作ってやるとするとジャンプ処理を書かないといけないなあ
開いてるアウトラインのバッファがどのバッファに紐付いてるかを取得しないといけない

OmochiceOmochice

ロケーションリストにしたいなら最低限ファイル名と行番号がいるみたい

function! Hoge() abort
  let l:foo = [
        \ '[' .. expand('%') .. ']taro:1',
        \ '[' .. expand('%') .. ']jiro:2'
        \ ]

  let l:eft = &errorformat
  let &errorformat = '[%f]%m:%l'
  lgetexpr l:foo | vertical botright lopen
  let &errorfile = l:eft
endfunction

これを実行すると次のようになる

https://gyazo.com/3f8ac2b8a1cbf34a7a59d3b7f22804db

OmochiceOmochice

各要素にバッファ名を入れて

['[foo.tex] ├ section{hoge}']

みたいな文字列の配列にした上で let &errorformat = '[%f]%m:%l'にするとうまく行く

ただし、バッファ名と行番号が表示されて幅を取るのでconcealするかなにかして隠さないといけない

OmochiceOmochice

カーソルを合わせるとconcealが解除されるのはset concealcursor=で制御できる。
ロケーションリスト(nomodifiable)に対してやるのでsetlocal concealcursor=ncで良さそう

このスクラップは2022/03/11にクローズされました