NOTE: swiftformatについてのメモ¶
インストール方法や設定方法は公式サイトを見るなりググれば大量に出てくるのでそちらを見てください。 私はhomebrewでインストールしました。
車輪の再発明かもしれないけどswiftformatを使いやすくするための小さなスクリプの紹介です。
目的はXCodeから手軽にformatをするために以下の機能を持っています。
XCodeからメニューで選べる
ショートカットキーを設定出来る
ファイルに保存前の編集中のバッファに適応できる
Undo/Redoできる
プロジェクト毎のconfigファイルを自動で読み込んで適応できる
ファイル単位で適応できる
以下の機能は不要と思われるので作らなかった
選択範囲だけの適応
プロジェクト全体に適応
スクリプト¶
以下はそのスクリプト
#!/usr/bin/osascript
use framework "Foundation"
use framework "AppKit"
use scripting additions
-- 定数定義したい
property NSString : a reference to current application's NSString
property NSPasteboard : a reference to current application's NSPasteboard
property NSPasteboardTypeString : a reference to current application's NSPasteboardTypeString
tell application "Xcode"
activate
-- エラー処理 1個もドキュメントがない
if (count of documents) is 0 then
display notification "ファイルが指定されてないです"
beep
return
end if
-- ドキュメントファイルへのパスを取得したい
-- 方法は現在XCodeで最も前面に出ているWindowのタイトルを取得し、そのタイトル名から
-- ドキュメントを特定し、特定したドキュメントからファイルパスを取得する
-- AppleScriptの文字列とNSStringを行ったり来たりしてるのは単純に私のAppleScriptの技術不足
-- 最前面のwidnowを取得
set mainWindow to window 1
-- 最前面のWindowのタイトルを取得
set mainWindowTitle to mainWindow's name
-- Windowのタイトル文字列を区切り文字列で分解する。NSStringに変換して便利なメソッドを使う
set theWindowTitle to NSString's stringWithString:mainWindowTitle
set theWordList to theWindowTitle's componentsSeparatedByString:" — "
-- 末尾がEditedの場合は編集中なので後ろから2番目がドキュメント名と仮定する
-- それ以外は末尾がドキュメント名と仮定する
if ((item -1 of theWordList) as string is "Edited") then
set theDocumentName to (item -2 of theWordList) as string
else
set theDocumentName to (item -1 of theWordList) as string
end if
-- 名前が一致するdocumentを探し出す
set theCurrentDocument to document 1 whose name ends with theDocumentName
-- エラー処理
if theCurrentDocument is -1 then
display notification "そんなドキュメントはないです"
beep
return
end if
-- ファイルパスを取り出す
set theCurrentDocumentPath to path of theCurrentDocument
-- エラー処理
if theCurrentDocumentPath is "" then
display notification "ファイルに結び付けられていないDocumentです"
beep
return
end if
-- 拡張子とファイルがあるディレクトリへのパスを取り出す
set theFilePathString to NSString's stringWithString:theCurrentDocumentPath
set theExtension to theFilePathString's pathExtension as string
set theDirectoryPath to theFilePathString's stringByDeletingLastPathComponent as string
set theFileNameString to theFilePathString's lastPathComponent as string
-- エラー処理
if theExtension is not "swift" then
display notification "拡張子がswiftではないです"
beep
return
end if
-- 既存のペーストボードデータを破壊しないようにデータの保存
set savedClipboard to the clipboard
set thePasteboard to NSPasteboard's generalPasteboard()
thePasteboard's clearContents()
thePasteboard's setString:(theCurrentDocument's text) forType:NSPasteboardTypeString
-- unix shell スクリプトを使うのはcurrent directoryの設定ファイルを使わせるため
-- 標準入出力を使うだけだと設定ファイルを無視するのでstdinpathでファイル名を渡してます
set theOutput to do shell script "cd " & theDirectoryPath & "; pbpaste -Prefer txt | /opt/homebrew/bin/swiftformat stdin --stdinpath " & theFileNameString & " | pbcopy"
-- キーコマンドは送信しているが動作が安定しない
tell application "System Events"
tell process "Xcode"
keystroke "a" using command down -- select all
keystroke "v" using {command down, option down, shift down} -- cmd opt shift paste スタイルを維持してペースト
delay 0.5 -- clipbardの内容を同期させるに必要らしい
end tell
end tell
-- 既存のペーストボードデータを破壊しないようにデータの復帰
set the clipboard to savedClipboard
end tell
上記のスクリプトファイルを適当な場所に置おく。XCodeからアクセスできる場所ならばどこでも良い。 2つの設定ファイル.swiftformatと.swift-versionの設定はググってください。
XCodeから呼び出すための設定¶
以下はこのスクリプトをXCodeに設定してメニューから呼び出したりショートカットをつけられるようにする方法です。
対象のファイルを編集中にXCodeのBehaviorからスクリプトを呼び出すことでswiftformatを呼び出せます。
でBehaviorsの編集Windowを呼び出して以下の画面を出す

左下の+ボタンを押して custom behavior を新規に作る

右側のゴチャゴチャした部分を下にスクロールして最後の Run Script のチェックを入れる。ファイルを選ぶボタンを押して上記のスクリプトファイルを選ぶ。
ここでAppleScriptのファイルが選べないことがあった。どうやって選択したかは覚えていないので各自工夫してください。

最後に項目名の右側をクリックしてキーボードショートカットを決める。

あとはAppleScript経由でXcodeを操作するためにXcodeのアクセサビリティを許可する必要があったはず。が、どこで設定したかは忘れた。
使い方は簡単で以下の2ステップ
formatするファイルを開いてEditorにフォーカスを合わせる
behaviors メニューから選ぶかショートカットキーを押す
通常はテキスト編集中に呼び出すのでショートカットを押す1ステップで機能するはず。
感想¶
私はソースコードの整形ツールはコードを編集しながら使うものだと思っています。 しかしググった結果はそのような使い方をしている人は居ないようでビルド時やコミット時に整形ツールを呼び出すのが標準的な使い方のようでした。
また受託開発のお仕事などでは formatting rule はプロジェクト毎に変わるのが当たり前だと思うのですが、Xcode extension を使う場合は プロジェクト毎に formatting rule を変更するのではなくて最後に読み込んだ設定が有効になるようでした。
上記の2つの事は単純に実装上の問題なのか開発スタイルの問題なのかはわかりませんが私のスタイルには合わないのでこのスクリプトを書きましたが私が何か見落としているのかもしれない。
Comments
comments powered by Disqus