PlantUML のコードをインデント略記から生成する Vim プラグイン（雑）


多用するのでプラグイン作ってみた。
`class`って書いたり`package ... {`って括弧書いたり面倒だよね。

超適当配布なので VimScript １ファイル製。

## 仕様
変換ルール

記法     | 変換          | 備考                                   
:--      | :--           | :--                                    
. foo    | package foo   | html の class 属性のイメージで . にした
# foo    | namespace foo | html の id 属性のイメージで # にした   
+ Foo    | class Foo     |                                        
- Foo    | enum Foo      |                                        
* Foo    | interface Foo |                                        
空行     | 捨てられる    | 実装辛くてどーでも良くなってしまった   
それ以外 | そのまま      | 線とかコメントとか                     

HTML のリスト記法のイメージなので、

```
. foo
  + FooId
  # FooType
```

が

```
package foo {
  class FooId
  enum FooType
}
```
になるってこと。


## ソース
```vim
command! PlantVim call PlantVim()

function! PlantVim()
    call s:init(getline(1, '$'))

    let node = s:subs(0)

    execute 'normal ggdG'

    call s:put('@startuml')
    call s:put('')
    call s:indentingSetLine(node, '')
    call s:put('')
    call s:put('@enduml')

    execute 'normal Gddgg0'
endfunction

function! s:init(input)
    let g:n = 0
    let g:input = filter(a:input, 'v:val != ""')
    let g:max = len(g:input) - 1
endfunction

function! s:subs(depth)
    let xs = []
    while g:n <= g:max && s:depth(g:input[g:n]) >= a:depth
        let g:n = g:n + 1

        if s:depth(g:input[g:n - 1]) == a:depth
            if s:needParse(g:input[g:n - 1]) == 'true'
                let parsed = s:parse(g:input[g:n - 1])
                let parsed['subs'] = s:subs(a:depth + 1)
                let xs = xs + [parsed]
            else
                let xs = xs + [{'line': g:input[g:n - 1], 'output': 'raw', 'subs': []}]
            endif
        endif
    endwhile
    return xs
endfunction

function! s:depth(line)
    let x = 0
    for c in split(a:line, '\zs')
        if c == ' '
            let x = x + 1
        else
            break
        endif
    endfor
    return x / 2
endfunction

function! s:parse(line)
    let type = {'.': 'package', '#': 'namespace', '+': 'class', '-': 'enum', '*': 'interface'}[split(a:line, '^ ')[0][0]]
    let containable = get({'package': 'containable', 'namespace': 'containable'}, type, 'no_contains')
    let content = split(a:line, ' ')[1]

    return {'line': type . ' ' . content, 'output': containable}
endfunction

function! s:needParse(line)
    return (a:line =~ '\.\|#\|+\|- \|\*') == 1 ? 'true' : 'false'
endfunction

function! s:indentingSetLine(node, pad)
    for elm in a:node
        if elm['output'] == 'containable'
            call s:put(a:pad . elm['line'] . ' {')
            call s:indentingSetLine(elm['subs'], a:pad . '  ')
            call s:put(a:pad . '}')
        elseif elm['output'] == 'no_contains'
            call s:put(a:pad . elm['line'])
        else
            call s:put(elm['line'])
        endif
    endfor
endfunction

function! s:put(line)
    call append(line('$') - 1, a:line)
endfunction
```

## 導入
上のファイルを`plant.vim`という名前で保存して、vi を起動して`:so ~/Downloads/plant.vim`をする。（パスは読み替えて）

インデント略記が書き終わったら`:PlantVim`とすると変換される。

## デモ
<img src="https://qiita-image-store.s3.amazonaws.com/0/113398/e9bb83b0-2834-9012-b727-7bec42f1bc29.gif" alt="アニメ" width="400">

こうなる
![model.png](https://qiita-image-store.s3.amazonaws.com/0/113398/ef6b7d84-6667-4293-83e9-20152aa53258.png)

## おしまい
`while`とかグローバル変数と添字アクセスとか数年ぶりに書いた。ちょー辛い、もう二度とやりたくない。

本当は改行を保ちたいとか不可逆だしファイルを書き換えちゃうのでやり直しが効かないとか対応したかったけど、これ以上を VimScript １ファイルでやるのはもう無理。

気に入って、かつ機能追加したくなったらメインロジックは全部 haskell か何かに移して VimScript はすっかすかにする。

ﾉｼ