Evernoteでノート間リンクを貼るApplescript
[2011.01.21追記] リンクを開こうとするとFinderがときどき[エラー-43]を吐く…何のエラーだろう?
ノート間リンクがないのはEvernoteの大きな欠点の1つだと思う。
ということでEvernoteのノート同士をリンクさせたように見せかける方法 - めざせ!へなちょこ脱出を参考に、擬似的にノート間リンクを実現するApplescriptを組んでみた。
まだあまり使っていないので不具合があるかもしれないけれど公開してみる。Butlerなどでショートカットキーで起動できるように設定しておくと良い。
注意点として、Evernoteのノートをダブルクリックで開いておく必要がある。メインウィンドウでもノートを選択して読むことができるが、その場合にはノートの選択状態がApplescriptで正しく認識されないみたい。
[追記]GUI scriptingを使っている関係で、テキストエリアをフォーカスしておく必要があるかも。つまり、ノート内のPDFとかにフォーカスが奪われていると正しく機能しない。
ランダム文字列の代わりにノートのlocal idを本文先頭に貼るようにした。多分このIDは不変だと思うけど、もし違ったらまともに機能しないことになるので教えて頂けると助かります。リンクを開くときは、デフォルトで~/Library/Caches/Metadata/com.evernote.Evernote/にあるp****.evernoteというファイルをFinderから開いている。リンクには方向性を付けて、開くときに選べるようにしてみた。
以下ソース
- リンクを貼るApplescript
- 2つのノートウィンドウを開いておく必要がある
-- Internote Link書式: -- ***Evernote Link Header***>>>{refTo list}***<<<{refFrom list}*** on run tell application "Evernote" activate if 3 > (count of windows) then display dialog "最低2つのノートウィンドウが必要です。" buttons {"OK"} default button 1 end if set w1 to name of window 1 set note1 to note 1 of window w1 set lid1 to local id of note1 set w2 to name of window 2 set note2 to note 1 of window w2 set lid2 to local id of note2 set askDir to display dialog w1 & linefeed & linefeed & w2 buttons {"/\\", "\\/"} default button 2 set ltor to "\\/" is button returned of askDir end tell -- idを抽出 set id1 to last item of split(lid1, "/p") set id2 to last item of split(lid2, "/p") set linkDeclaration to "***Evernote Link Header***" set str1 to getStr(w1, linkDeclaration) set notDeclared1 to "" is str1 if notDeclared1 then set refTo1 to {} set refFrom1 to {} else set refTo1 to getRefTo(str1) set refFrom1 to getRefFrom(str1) end if set str2 to getStr(w2, linkDeclaration) set notDeclared2 to "" is str2 if notDeclared2 then set refTo2 to {} set refFrom2 to {} else set refTo2 to getRefTo(str2) set refFrom2 to getRefFrom(str2) end if -- link headerを作成 if ltor then if refTo1 contains id2 then set id2 to {} if refFrom2 contains id1 then set id1 to {} setHeader(w1, refTo1 & id2, refFrom1, notDeclared1, linkDeclaration) setHeader(w2, refTo2, refFrom2 & id1, notDeclared1, linkDeclaration) else if refFrom1 contains id2 then set id2 to {} if refTo2 contains id1 then set id1 to {} setHeader(w1, refTo1, refFrom1 & id2, notDeclared2, linkDeclaration) setHeader(w2, refTo2 & id1, refFrom2, notDeclared2, linkDeclaration) end if end run on setHeader(wName, ref1, ref2, notDeclared, declaration) set the clipboard to "" set str1 to concatp(ref1) set str2 to concatp(ref2) delay 0.2 tell application "System Events" to tell process "Evernote" tell menu item wName of menu 1 of menu bar item "Window" of menu bar 1 click end tell delay 0.2 tell UI element 1 of scroll area 1 of window wName if notDeclared then set str to declaration & ">>>" & str1 & "***" & "<<<" & str2 & "***" & (ASCII character 13) & (ASCII character 13) set the clipboard to str delay 0.2 keystroke "v" using command down else set str to declaration & ">>>" & str1 & "***" & "<<<" & str2 & "***" set the clipboard to str delay 0.2 keystroke "v" using command down end if end tell delay 0.2 end tell end setHeader on getStr(wName, linkDeclaration) set the clipboard to "" delay 0.2 tell application "System Events" to tell process "Evernote" tell menu item wName of menu 1 of menu bar item "Window" of menu bar 1 click end tell delay 0.2 tell UI element 1 of scroll area 1 of window wName key code 126 using command down -- command-up: 先頭にカーソル移動 delay 0.2 key code 124 using {command down, shift down} -- 一行選択 delay 0.2 keystroke "c" using command down -- コビー delay 0.2 set theLine to the clipboard if theLine contains linkDeclaration then -- 先頭行がheaderなら(リンクが定義済みなら)さらに3行選択し、カット return theLine else -- 始めてリンクを設定する場合は、カーソルを先頭へ key code 126 using command down return "" end if end tell end tell end getStr on getRefTo(str) set flat to item 3 of split(str, "***") if ">>>" is flat then return {} else return rest of split(characters 4 thru end of flat as text, "p") end if end getRefTo on getRefFrom(str) set flat to item 4 of split(str, "***") if "<<<" is flat then return {} else return rest of split(characters 4 thru end of flat as text, "p") end if end getRefFrom on concatp(lst) set str to concat(lst, "p") if "" is str then return "" else return "p" & str end if end concatp on split(str, delim) -- try considering case set lastDelim to AppleScript's text item delimiters set AppleScript's text item delimiters to delim set spl to every text item of str set AppleScript's text item delimiters to lastDelim end considering return spl -- on error -- return "" -- end try end split on concat(lst, delim) set lastDelim to AppleScript's text item delimiters set AppleScript's text item delimiters to delim set conc to lst as string set AppleScript's text item delimiters to lastDelim return conc end concat on replace(str, repFrom, repTo) set spl to split(str, repFrom) if spl is "" then return str else return concat(spl, repTo) end if end replace
- リンクを開くApplescript
-- Internote Link書式: -- ***Evernote Link Header***>>>{refTo list}***<<<{refFrom list}*** on run tell application "Finder" set enDir to folder "com.evernote.Evernote" of folder "Metadata" of folder "Caches" of folder "Library" of (path to home folder) end tell tell application "Evernote" activate if 2 > (count of windows) then display dialog "最低1つのノートウィンドウが必要です。" buttons {"OK"} default button 1 return end if set wName to name of window 1 set askDir to display dialog "どの向きのリンクを開きますか?" buttons {"全部", ">>>", "<<<"} set dir to button returned of askDir end tell set linkDeclaration to "***Evernote Link Header***" set str to getLink(wName, linkDeclaration) if "" is str then display dialog "リンクはありません" buttons {"OK"} default button 1 giving up after 1 end if set refTo to getRefTo(str) set refFrom to getRefFrom(str) -- open link if dir is ">>>" then openLink(refTo, enDir) else if dir is "<<<" then openLink(refFrom, enDir) else if dir is "全部" then openLink(refTo, enDir) openLink(refFrom, enDir) end if end run on openLink(refList, enDir) repeat with id in refList tell application "Finder" open file ("p" & id & ".evernote") of enDir end tell end repeat end openLink on getLink(wName, linkDeclaration) set the clipboard to "" delay 0.2 tell application "System Events" to tell process "Evernote" tell menu item wName of menu 1 of menu bar item "Window" of menu bar 1 click end tell delay 0.2 tell UI element 1 of scroll area 1 of window wName key code 126 using command down -- command-up: 先頭にカーソル移動 delay 0.2 key code 124 using {command down, shift down} -- 一行選択 delay 0.2 keystroke "c" using command down -- コビー delay 0.2 set theLine to the clipboard if theLine contains linkDeclaration then -- 先頭行がheaderなら(リンクが定義済みなら)さらに3行選択し、カット return theLine else -- リンク未設定 key code 126 using command down return "" end if end tell end tell end getLink on getRefTo(str) set flat to item 3 of split(str, "***") if ">>>" is flat then return {} else return rest of split(characters 4 thru end of flat as text, "p") end if end getRefTo on getRefFrom(str) set flat to item 4 of split(str, "***") if "<<<" is flat then return {} else return rest of split(characters 4 thru end of flat as text, "p") end if end getRefFrom on split(str, delim) -- try considering case set lastDelim to AppleScript's text item delimiters set AppleScript's text item delimiters to delim set spl to every text item of str set AppleScript's text item delimiters to lastDelim end considering return spl -- on error -- return "" -- end try end split on concat(lst, delim) set lastDelim to AppleScript's text item delimiters set AppleScript's text item delimiters to delim set conc to lst as string set AppleScript's text item delimiters to lastDelim return conc end concat on replace(str, repFrom, repTo) set spl to split(str, repFrom) if spl is "" then return str else return concat(spl, repTo) end if end replace