「立体インデント」を自動化する


2018/07/28

ソースと結果を直しました。

---

@set0gut1 さんの[寄り目を利用した「目diff」および「立体インデント」の考案](https://qiita.com/set0gut1/items/be9247910eb7c155d592)からインスパイアされた記事です。
Python3 で立体インデント化したソースコードを出力できるようにしました。

# ソース
いろいろツッコミどころのあるコードですが、大目に見てやってください…
ソースコードは[ここ](https://github.com/nao0x2c6/3d-indent/)にもあげています。

```python3:make3Dindent.py
#!/usr/bin/env python3
import sys
import os

start = '{' # ネストの起点
end = '}' # ネストの終点
width = 20 # 改行幅

def make3Dindent(stream, reverse=False):
  depth = 0 # いまの浮かび具合（右側のテキストに適用）
  depthList = [0] # 行ごとの浮かび具合の管理
  leftLines = []
  rightLines = []
  for line in stream: 
    charcount = 0
    left = ''
    right = ''
    for c in line:
      charcount += 1
      if(c == start):
        # 浮かび具合を上げる
        depth += 1
        left += c
        right += ' ' + c
      elif(c == end):
        # 浮かび具合を下げる
        depth -= 1
        left += c + ' '
        right += c
      elif c == "\n": # 改行処理
        pad = (width - charcount) * ' '
        leftLines.append(left + pad)
        rightLines.append(right + pad)
        depthList.append(depth)
        left = ''
        right = ''
        charcount = 0
        continue
      else:
        left += c
        right += c

      if charcount >= width:
        leftLines.append(left)
        rightLines.append(right)
        depthList.append(depth)
        left = ''
        right = ''
        charcount = 0

  if c != "\n" and charcount > 0:
    pad = (width - charcount) * ' '
    leftLines.append(left + pad)
    rightLines.append(right + pad)
    depthList.append(depth)

  depthList.pop()

  maxDepth = max(depthList)
  maxLength = max([len(line) for line in leftLines])

  retRight = []
  retLeft = []

  baseWidth = maxDepth + maxLength
  for (l, r, d) in zip(leftLines, rightLines, depthList):
    retLeft.append((' ' * (maxDepth - d)) + l + (' ' * (baseWidth - (len(l) + (maxDepth - d)))))
    retRight.append((' ' * d) + r + (' ' * (baseWidth - (len(r) + d))))

  # 焦点補助のドット
  pad = ' ' * int(baseWidth / 2)
  dotLine = pad + '●' + pad
  retLeft.append(dotLine)
  retRight.append(dotLine)

  if reverse:
    # 沈む
    return (retRight, retLeft)
  else:
    # 浮かぶ
    return (retLeft, retRight)

# 出力用
def printLeftRight(left, right):
  for (l, r) in zip(left, right):
    print (l + ' ' + r)


if __name__ == '__main__':
  if len(sys.argv) == 1:
    (left, right) = make3Dindent(sys.stdin)
    printLeftRight(left, right)
  else:
    input_file = sys.argv[1]
    if not os.path.isfile(input_file):
      print(input_file + ': not found')
      sys.exit(1)

    f = open(input_file)
    (left, right) = make3Dindent(f)
    f.close()
    printLeftRight(left, right)
```

# 実行
```bash
$ ./make3Dindent.py input.js

# 標準入力からも可能
$ ./make3Dindent.py < input.js
```

# 結果
```javascript
   for(var i=1;i<=100;i   for(var i=1;i<=100;i     
   ++){if(i%3===0){if(i   ++) {if(i%3===0) {if(i   
 %5===0){console.log(       %5===0) {console.log(  
"FizzBuzz")} else{con        "FizzBuzz")}else {con 
sole.log("Fizz")} } el       sole.log("Fizz")}}el  
  se if(i%5===0){conso     se if(i%5===0) {conso   
 le.log("Buzz")} else{      le.log("Buzz")}else {  
 console.log(i)} }          console.log(i)}}       
            ●                         ●            
```

# 問題
改行位置を`width`で指定しているので、単語の途中でも改行されてしまう。

# 所感
動作確認で目がいたい…
`printLeftRight` ってなんだよ