前回 AppleScript を使って Safari から情報を得るにて 現在 Safari で開いているページの情報を得る方法を調べた。 これができるなら、Emacs でバッファに Safari の情報差し込むことができそう。 やってみる。
Scratch バッファで試していく。
まずはこのコード(シェルスクリプト)を elisp で動くようにしてみる。
osascript \
-e 'tell application "Safari"' \
-e 'set theName to name of document 1' \
-e 'end tell' \
-e 'return theName'
AppleScript コマンドを組み立てる関数 my/to-get-title-cmd を書く:
(defun my/to-get-title-cmd ()
(concat
"osascript"
" -e 'tell application \"Safari\"'"
" -e 'set theName to name of document 1'"
" -e 'end tell'"
" -e 'return theName'"))
この関数の末尾で C-x C-e しておく。(以後は説明を省略)
そして この関数を使ってみる:
(my/to-get-title-cmd)
結果:

できた。AppleScript コマンドを組み立てた文字列を得ただけだが・・・。
次に、これを実行して結果を得る関数 my/get-title を書く:
(defun my/get-title ()
(shell-command-to-string
(my/to-get-title-cmd)))
(my/get-title)
shell-command-to-string というコマンドを実行して結果を文字列として返してくれる関数があるらしい。
実行:

もちろん、これは Safari でこのページ https://blog.mindboardapps.com/posts/get-something-from-safari/ を開いていた場合の例です。
うまくページタイトルが取得できていますが、行末の改行までふくめて取得しているので、これを削除する。
(defun my/get-title ()
(string-trim
(shell-command-to-string
(my/to-get-title-cmd))))
(my/get-title)

あと interactive にして、関数を実行したら、カーソル位置に結果の文字列(ここではタイトル文字列)を挿入する関数を書くだけ:
(defun my/insert-title ()
(interactive)
(insert (my/get-title)))
M-x: my/insert-title する:

この関数を実行:

カーソル位置にタイトルが挿入された。
ただ、これカーソル位置が挿入された文字列の末尾に移動しているのが気に入らない。 挿入前のカーソル位置に居てほしい・・・ save-excursion を使えばいいらしい。
(defun my/insert-title ()
(interactive)
(save-excursion (insert (my/get-title))))
これで挿入開始位置にカーソルが戻る(維持される)ので、間違えた場合などはすぐに C-k できる。 (いやどうなの? C-x u して undo すればいいわけだから、C-k で間違いを取り消すとか意味ないのかな・・・)
最後に C-c i で挿入できるようにキーバインドを定義:
(global-set-key (kbd "C-c i") #'my/insert-title)

ここまで書いてきたコードをまとめると:
(defun my/to-get-title-cmd ()
(concat
"osascript"
" -e 'tell application \"Safari\"'"
" -e 'set theName to name of document 1'"
" -e 'end tell'"
" -e 'return theName'"))
(defun my/get-title ()
(string-trim
(shell-command-to-string
(my/to-get-title-cmd))))
(defun my/insert-title ()
(interactive)
(save-excursion (insert (my/get-title))))
さらに my/to-get-title-cmd の AppleScript は次のように短く書くことができる:
osascript -e 'tell application "Safari" to return name of document 1'
したがって、my/get-title を次のように書き直す:
(defun my/get-title ()
(string-trim
(shell-command-to-string
"osascript -e 'tell application \"Safari\" to return name of document 1'")))
URL を取得する関数 my/get-url も書こう:
(defun my/get-url ()
(string-trim
(shell-command-to-string
"osascript -e 'tell application \"Safari\" to return URL of document 1'")))
マークダウン形式のリンク文字列を返す関数 my/get-link :
(defun my/get-link ()
(format "[%s](%s)" (my/get-title) (my/get-url)))
そして、interactive な my/insert-link 関数を書く:
(defun my/insert-link ()
(interactive)
(save-excursion (insert (my/get-link))))
M-x: my/insert-link すれば、次のようにマークダウン形式のリンク文字列を挿入できる:
[my notebook - Qwen Image Edit 2511 による着色 改善版](https://blog.mindboardapps.com/posts/image-to-image-qwen-image-edit-16gb-again/)
(defun my/get-title ()
(string-trim
(shell-command-to-string
"osascript -e 'tell application \"Safari\" to return name of document 1'")))
(defun my/get-url ()
(string-trim
(shell-command-to-string
"osascript -e 'tell application \"Safari\" to return URL of document 1'")))
(defun my/get-link ()
(format "[%s](%s)" (my/get-title) (my/get-url)))
(defun my/insert-link ()
(interactive)
(save-excursion (insert (my/get-link))))
(global-set-key (kbd "C-c i") #'my/insert-link)
あとは、これを init.el などに定義すればよい。
もう少し整理するとこんな感じ:
(defun my/do-osascript (x)
(string-trim
(shell-command-to-string
(format "osascript -e '%s'" x))))
(defun my/get-link ()
(let
(
(title (my/do-osascript "tell application \"Safari\" to return name of document 1"))
(url (my/do-osascript "tell application \"Safari\" to return URL of document 1"))
)
(format "[%s](%s)" title url)
)
)
(defun my/insert-link ()
(interactive)
(save-excursion (insert (my/get-link))))
(global-set-key (kbd "C-c i") #'my/insert-link)
カッコとの戦いになる。