Sublime Textにペーストするとファイル名になる。
iTermのように、フルパスで貼り付けたい
iTermなら、ペースト、フルパスになるじゃん?Sublime Text でも同じことをしたかったんですね。
クリップボードにあるデータのフルパスを取得する必要がある。
OSX JavaScriptでサービスを作るアプローチ
OSX JavaScript から クリップボードを取得する方法
#!/usr/bin/env osjscript var app = Application("Finder") app.includeStandardAdditions=true ret = app.theClipboard() console.log(ret); a = app.clipboardInfo() console.log(JSON.stringify(a));
しかし、これでは、フルパスは取れない。Pathオブジェクトにかけると、カレントパスが出てくる
console.log(ret);⇛ console.log(Path(ret)); takuya@rena:/Applications$ osjscript ~/Desktop/paste.js /Applications/true [[null,4],[null,281],[null,258],[null,349],[null,10],[null,144],["ctxt",4],[null,22],[null,4],[null,8],[null,144],[null,22]]
このざま!
Sublime Text 3のAPIを叩いてみるアプローチ
Sublime Text 3で、ctrl + ` ( shift + @ ) を押して、コンソールを起動して、APIを叩く
>sublime.get_clipboard()
これで取れそう?⇛だめ。
じゃぁ、Sublime Text 3 のAPIをハックしてみよう
/Applications/Sublime Text 3.app/Contents/MacOS/sublime.py
get_clipboard のメソッドをハックして、上書きすればいいんじゃね?
だめ。出来ない。 sublime_api を呼んでいて、 デリゲートしてる。sublime_api の get_clipboard を上書きするのも面倒だし、 sublime_api.get_clipboard をコールしたら、すでにプレーンテキスト、ペーストボードがテキスト化された状態で出てくるので手が出せない。
じゃぁAutomatorで対応すればいいんじゃない?
だったら、Automatorで、フルパスでコピーして貼り付ければいいんじゃない?
一応出来る。
でもやりたいのは、ファイルパスをクリップボードにコピーした状態で、貼り付けたら、フルパスに変換して取り出すやつ。
そもそも、OSX の ペーストボードってどうなってるの?
#!/usr/bin/python from AppKit import * pb = NSPasteboard.generalPasteboard() pbstring = pb.stringForType_(NSStringPboardType) print u"Pastboard string: %s".encode("utf-8") % repr(pbstring)
以上のような感じで取得することが出来る。
takuya@rena:~/Desktop$ echo こんにちは | pbcopy takuya@rena:~/Desktop$ python paste_sample.py Pastboard string: u'\u3053\u3093\u306b\u3061\u306f\n'
ただ、pythonでやると、import Appkit がやたら遅い。ペーストコマンドの上書きには現実的なアプローチじゃない。
MacRubyなら!速いんじゃない?
#!/usr/bin/env macruby # encoding: UTF-8 framework 'Cocoa' puts NSPasteboard.generalPasteboard.pasteboardItems .map { |pbi| pbi.stringForType('public.file-url') }.compact .map { |url| NSURL.URLWithString(url).path }
これは、バンドルだけあって、速い!!!!!
コレ最強。
public.file-url ってなに?⇛UTI
UTI : uniformed type identifier
というもので、OSXでは古くから、クリップボードの種類を識別して、アプリケーション側で適切なデータを取り出すようになっている。
つまり、WebkitでHTMLをコピーした時、クリップボードにはHTMLとプレーンテキストが入っていて、どちらでも好きな方をプログラム側で取り出してくださいってことね。
クリップボード(ペーストボード)を扱う、コマンドライン・アプリケーションを作る。
UTI をしらべるために、ちょっとペーストボードを扱うCMDアプリケーションを作ってみた
AppkitをLinkに追加して、っと
インポートで、参照を追加
#import<Foundation/Foundation.h> #import<AppKit/Appkit.h>
ペーストボードを取得して
NSPasteboard*pasteboard = [NSPasteboardgeneralPasteboard]; NSPasteboardItem*a = [pasteboardpasteboardItems].firstObject;
複数入ってるから、先頭を取得して、
NSArray*t = a.types;
クリップボードのデータのタイプを取得する
中身を見れば、OK
フォルダ・ファイルをクリップボードに入れてる時は
( "public.file-url", "com.apple.icns", "public.utf16-external-plain-text", "public.utf8-plain-text" )
画像をクリップボードに入れてる時(スクショをクリップボードに入れた時)
( "public.png" )
HTML(ブラウザのHTML Body内でコピー)をクリップボードに入れてた時
( "public.utf8-plain-text", "public.html" )
ブラウザのアドレスバーをクリップボードに入れてた時
( "dyn.ah62d4rv4gu8zs3pcnzme2641rf4guzdmsv0gn64uqm10c6xenv61a3k", "dyn.ah62d4rv4gu8yc6durvwwaznwmuuha2pxsvw0e55bsmwca7d3sbwu", "public.utf8-plain-text", "public.url", "public.url-name" )
今回欲しいのは、ファイルのパスですね。
ファイル(フォルダ)をコピーすると4種類のデータがペーストボードに入ってる。
( "public.file-url", "com.apple.icns", "public.utf16-external-plain-text", "public.utf8-plain-text" )
プログラム側で、この中から、選んで、取得する必要があるらしい。
フォルダをコピーしたには、テキストやアイコンまで入ってる模様。
たとえば、アイコンを取り出してみる。
NSPasteboard*pasteboard = [NSPasteboardgeneralPasteboard];NSPasteboardItem*a = [pasteboardpasteboardItems].firstObject; NSData*d = [adataForType:@"com.apple.icns"]; NSLog(@"%@", d );
やばいほど、出力が長い。。。ま、しっかりアイコンが取り出せる事がわかる。
おなじクリップボードから、テキストとして取り出してみる。
NSPasteboard*pasteboard = [NSPasteboardgeneralPasteboard];NSPasteboardItem*a = [pasteboardpasteboardItems].firstObject; NSData*d = [adataForType:@"public.utf8-plain-text"]; NSLog(@"%@", d ); NSString*str = [[NSStringalloc]initWithData:dencoding:NSUTF8StringEncoding]; NSLog(@"%@",str );
直接NSStringで取り出せるが、とりあえずNSDataにしてある。
ファイル名を取り出してみる。
NSPasteboard*pasteboard = [NSPasteboardgeneralPasteboard];NSPasteboardItem*a = [pasteboardpasteboardItems].firstObject; NSData*d = [adataForType:@"public.file-url"]; NSString*str = [[NSStringalloc]initWithData:dencoding:NSUTF8StringEncoding]; NSLog(@"%@",str );
取り出せるけど、ちょっと期待と違うかも。
ファイル名が file:// から始まっていて、URLエンコードされているのをなんとかする。
NSPasteboard*pasteboard = [NSPasteboardgeneralPasteboard];NSPasteboardItem*a = [pasteboardpasteboardItems].firstObject; NSData*d = [adataForType:@"public.file-url"]; NSString*str = [[NSStringalloc]initWithData:dencoding:NSUTF8StringEncoding]; NSLog(@"%@",str ); NSURL*u = [NSURLURLWithString:str]; NSLog(@"%@",u.path);
無事取り出せた。これで、ファイルをクリップボードに入れた時に、テキストではなく、フルパスで取り出す方法がわかる。
これで、ペーストボードの仕組みが少しわかった。 ま、Windowsとかも同じようなものだし。クリップボードには複数の選択肢が入っていてアプリ側が自分にあった物を取り出す。
これで、さっきの macruby に戻って
#!/usr/bin/env macruby # encoding: UTF-8 framework 'Cocoa' puts NSPasteboard.generalPasteboard.pasteboardItems .map { |pbi| pbi.stringForType('public.file-url') }.compact .map { |url| NSURL.URLWithString(url).path }
この仕組みがよく理解できたので、そのまま使っても困らなさそうだ。
クリップボードに複数のファイルが入った状態で、実行すると。。。
/Users/takuya/Desktop/pasteboard.rb takuya@rena:~/Desktop$ macruby pasteboard.rb 2> /dev/null /Users/takuya/Desktop/paste.js /Users/takuya/Desktop/クリップボード /Users/takuya/Desktop/スクリーンショット 2014-12-25 15.55.17.png /Users/takuya/Desktop/pasteboard.rb
ちゃんと複数ファイルのフルパスを取り出すことが出来た。
ここまで出来たものを、ワンライナー化して。
macruby -W0 -e "framework 'Cocoa';puts NSPasteboard.generalPasteboard.pasteboardItems.map {|pbi|pbi.stringForType('public.file-url')}.compact.map {|url|NSURL.URLWithString(url).path}"
これを、Pythonから実行する
#!/usr/bin/env python import subprocess cmd = "/usr/local/bin/macruby -W0 "+ \ " -e \"framework 'Cocoa';"+ \ "puts NSPasteboard.generalPasteboard.pasteboardItems.map{|pbi|"+\ " pbi.stringForType('public.file-url')}.compact.map{|url|"+\ " NSURL.URLWithString(url).path"+\ "}\" " ret = subprocess.check_output( cmd )
Pythonから実行できたら、SublimeTextの拡張パッケージとして作りなおす
新規プラグインとして、作って。
run_commandで実行
import sublime, sublime_plugin import subprocess class PasteAsPathCommand(sublime_plugin.TextCommand): def run(self, edit): cmd = ["/usr/local/bin/macruby", "-W0", "-e", "framework'Cocoa';"+\ "a=NSPasteboard.generalPasteboard.pasteboardItems;"+\ "a.map!{|pbi|pbi.stringForType('public.file-url')};"+\ "a.compact!;"+\ "a.map!{|url|NSURL.URLWithString(url).path};"+\ "puts(a)" ] try : ret = subprocess.check_output( cmd ) if not ( len(ret) > 0 ) : ret = sublime.get_clipboard() else: ret = ret.decode("utf-8") # self.view.insert(edit, self.view.sel()[0].begin(), "Hello, World!") # self.view.insert(edit, self.view.sel()[0], ret ) self.view.replace(edit, self.view.sel()[0], ret ) #sublime.message_dialog(ret.decode("utf-8")) except subprocess.CalledProcessError as e: raise e #sublime.message_dialog(e.cmd) #sublime.message_dialog(e.output) #sublime.message_dialog(e)
登録する
コマンドに登録する。
[ { "Caption": "Edit", "id" : "edit", "children" : [ { "command": "paste_as_path", "mnemonic" : "" , "caption": "Paste(Extend)" } ] } ]
最後にショートカットを登録
// ペースト { "keys": ["ctrl+v"], "command": "paste_as_path" }, //dummy {} ]
コレで出来上がり。
まぁ、これででも、通常ペーストより遅いんだ。 やっぱりサブプロセスの起動は遅いよねぇ かといって、コレ以上高速化する理由もないし。
SublimeTextにフルパスで貼り付けできるだけ良しとしよう。
正式なのは、SublimeTextの対応待ちにしよう。こだわって作ってもそのうちメインストリートで対応されるだろうし