新しめのプログラミング言語にはREPLがあったり、スクリプトファイルから実行できたりします。書きながら勉強できますから、重宝しますよね。
が、しかし… エディターとターミナルを行ったり来たりするのはちょっと面倒です。
ウィンドウが2つあると、色々な操作が増えます。艦これのために動かしてスペースを開けるだけで数秒余計にかかります。
IDEを使えばいいんですが、こちらは基本的にサイズが大きいので「macbookでちょっと勉強」という用途には大げさだったりします。もっと単純に「右側に出力があって、左側に書いてキーボードショートカットで実行できる」というだけで充分なこともあります。
それくらいならNSTaskで簡単に作れそうです。
ということで実際に作りました。
セキュリティとかエラーとか考えなければ、左側に書いてcommand+Rで実行できるモノを40行で作ることができます。
作り方
基本的には、NSTaskを動かして出力をそのまんま書き込みます。
ViewControllerにrun()
を書いて、メニュー経由でこれを呼びましょう。
run()
は単純なメソッドで、(1)テキストを保存し、(2)保存したファイルをscalaに渡し、(3)タスクが終わるまで待ってから結果を読み取り、(4)データを変換して表示します。
import Cocoa
class ViewController: NSViewController {
@IBOutlet var editor: NSTextView! {
didSet {
editor.automaticTextReplacementEnabled = false
}
}
@IBOutlet var output: NSTextView!
@IBAction func run(sender: NSMenuItem) {
let task = NSTask()
let output = NSPipe()
let error = NSPipe()
let name = "scEditor" + NSDate().timeIntervalSince1970.description
if let path = NSTemporaryDirectory().stringByAppendingPathComponent(name).stringByAppendingPathExtension("scala") {
editor.string?.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil)
task.standardOutput = output
task.standardError = error
task.launchPath = "/usr/local/bin/scala"
task.arguments = [path]
task.launch()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
task.waitUntilExit()
let result = output.toString() + error.toString()
dispatch_async(dispatch_get_main_queue()) {
self.output.string = result
}
}
} else {
println("error")
}
}
}
extension NSPipe {
func toString() -> String {
let data = self.fileHandleForReading.availableData
return NSString(data: data, encoding: NSUTF8StringEncoding) ?? ""
}
}
キーボードショートカットは、メニューを弄ることで設定できます。
ファーストレスポンダー宛てにrun()
を呼ばせるだけです。
あとは適当にテキストビューを2つ置いて、アウトレットとして接続すれば動きます。