使用 url.el 快速插入 URL 與網頁標題

Emacs 22 以後有內建 url.el ,可以拿 Emacs 處理 URL,像是拿來抓天氣預報這類對編輯器來說很不務正業有趣又實用的事情。

所以這篇在幹麻

用 markdown, org-mode 寫東西很方便,但插入網頁連結就有點麻煩(例如[Title](Link)),因為貼上 URL 後還要自己輸入標題,這裡就可以用 url.el 來自動抓網頁的標題,這樣就只需要貼 URL,就能自動吐[Title](URL)給你。很方便的:D

Org: [[http://www.gnu.org/][The GNU Operating System]]
Markdown: [The GNU Operating System](http://www.gnu.org/)
HTML: <a href="http://www.gnu.org/">The GNU Operating System</a>
Twittering-mode: "The GNU Operating System"( http://www.gnu.org/ ) //

完整程式碼

這個功能需要用到html-entities-convert.el,請先安裝好再使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
(require 'url)
(require 'html-entities-convert)
(require 'org)
(require 'sgml-mode)
(require 'markdown-mode)
(require 'recentf)

(add-to-list 'recentf-exclude "/tmp/url-retrieve-.+")

(defun url-get-page-title (url)
(let* ((temp-file (format "%s%s" "/tmp/url-retrieve-" (random))))
(url-copy-file url temp-file)
(find-file temp-file)
(goto-char (point-min))
(re-search-forward "\<title\>\\(\\(:?.\\|\n\\)*?\\)\</title\>" nil :no-error)
(setq url-gotten-page-title (html-entities-convert (match-string 1)))
;convert html entities
(setq url-gotten-page-title (replace-regexp-in-string "\n" "" url-gotten-page-title))
; remove \n in title
(kill-buffer)
(delete-file temp-file)
(format "%s" url-gotten-page-title)
(message (format "%s" url-gotten-page-title))))

(defun insert-link-with-title ()
"Insert link along with title (grabbed via url).
Format is determined by what major mode is being used currently.
Currently support: Markdown/Org/HTML/Twittering
If in beginning of a line, insert a - prefix as a list. For example:

Markdown:
- [The GNU Operating System](http://www.gnu.org/)
Org:
- [[http://www.gnu.org/][The GNU Operating System]]
"
(interactive)
(let* ((LINK (if (boundp 'LINK)
LINK
(read-from-minibuffer "Page's URL: ")))
(TITLE (if (boundp 'TITLE)
TITLE
(url-get-page-title LINK))))
(cond ((eq major-mode 'markdown-mode)
(if (not (equal 0 (current-column))) ;if not in the beginning of line,
(insert (format "[%s](%s)" TITLE LINK)) ;insert "[title](link)"
(progn (insert (format "- [%s](%s)" TITLE LINK))
(newline)))) ;or insert "- [title](link)" and new line.
((eq major-mode 'org-mode)
(if (not (equal 0 (current-column)))
(insert (format "[[%s][%s]]" LINK TITLE))
(progn (insert (format "- [[%s][%s]]" LINK TITLE))
(newline))))
((eq major-mode 'html-mode)
(insert (format "<a href=\"%s\">%s</a>" LINK TITLE)))
((eq major-mode 'twittering-edit-mode)
(insert (format "\"%s\"( %s ) // " TITLE LINK)))
(t
(let (major-mode)
(setq major-mode (intern (concat
(completing-read "Select a format: "
'(("org")
("markdown" )
("html")
("twittering-edit"))
nil t "" )
"-mode")))
(insert-link-with-title)))
)))

(define-key markdown-mode-map (kbd "C-c i l") 'insert-link-with-title)
(define-key org-mode-map (kbd "C-c i l") 'insert-link-with-title)
(define-key html-mode-map (kbd "C-c i l") 'insert-link-with-title)

;; If twittering-mode has been installed and loaded.
(when (require 'twittering-mode nil 'no-error)
(defun twittering-share-link ()
"Share link with twittering-edit-mode buffer,
and insert page's title automatically.

If major-mode is already twittering-edit-mode, insterting
directly without opening a new buffer."
(interactive)
(if (not (equal major-mode 'twittering-edit-mode))
(twittering-update-status-interactive) nil)
(insert-link-with-title))
(define-key twittering-edit-mode-map (kbd "C-c i l") 'insert-link-with-title)
(define-key twittering-mode-map (kbd "C-c i l") 'twittering-share-link))

使用方式

org-mode, markdown-mode, html-modetwittering-mode任一 mode 中輸入 C-c i l,並貼上 URL,就會自動吐出符合該 mode 的網頁標題連結。

以下是實作細節。

斯斯有兩種

url retrieve 也有分 url-retrieveurl-retrieve-synchronously 兩種,分別就是 asynchronously 和 synchronously,請 C-h f 參考 Emacs 內建文件並看下面的範例。

url-retrieve

(url-retrieve URL CALLBACK &optional CBARGS SILENT INHIBIT-COOKIES)

這個是 asynchronously retrieve,需要加上 CALLBACK 告訴他抓完了要作什麼事情。這裡可以定義一個函數並用 quote 表示,或直接用 lambda 匿名函數。

1
2
3
4
5
6
(let ((url-request-method "GET")
(url-request-extra-headers '(("Content-Type" . "application/x-www-form-urlencoded"))))
(url-retrieve "http://slashdot.org/"
(lambda (status) ;status 不知是啥,暫時先不管反正去掉了就不能跑(喂)
(switch-to-buffer (current-buffer)) ;我不懂為何這樣就可以切過去...
(set-buffer-multibyte t)))) ;開啟 multibyte 字元支援(中文顯示必須)

url-retrieve-synchronously

(url-retrieve-synchronously URL)

1
2
3
4
(let ((url-request-method "GET")
(url-request-extra-headers '(("Content-Type" . "application/x-www-form-urlencoded"))))
(switch-to-buffer (url-retrieve-synchronously "http://slashdot.org/"))
(set-buffer-multibyte t))

一開始都不知道到底該如何切換到含有 url-retrieve-synchronously 抓下的內容的 buffer,搞了很久我才發現 url-retrieve-synchronously 的回傳值就是 buffer 的名稱…直接用 (switch-to-buffer) 切過去就可以了。

HTML Entities

我是沒找到有人提供類似工具,不過有了這個 HTML/XML Entities 列表,要做出來就變得很容易。

安裝 html-entities-convert.el 即可。使用方法:(require 'html-entities-convert) 後, (html-enetities-convert "&gt\;"),就會吐出 > 給你。

regexp 如何表示符合「包含 \n 的所有字元」

有時 <title>...</title> 之間會被插入換行,請參考這篇

cursor 目前行數和列數

函數名差太多很難記:

1
2
(line-number-at-pos)   ;目前 cursor 的 line number
(current-column) ;目前 cursor 的 column number

有了這個,就可以在插入 markdown link 時,檢查是否在行首,如果在行首就多插入一個 - 。(比較符合我的使用習慣,如果你不喜歡,可以把那個 if 判斷式拿掉。)

UTF-8 以外的編碼

這次是編碼問題。去抓 http://d.hatena.ne.jp/ 上的內容時,會發現日文無法正確顯示

原因似乎是 http://d.hatena.ne.jp/ 的所有內容都是 EUC-JP 編碼,而之前可以成功抓下來的都是 UTF-8,這個問題似乎無解(至少我沒找到)…。嘗試過了幾種方法:

  • 把抓到的字串用 (encode-coding-string STRING 'euc-jp) 處理。
  • 把抓到的字串用 (decode-coding-string STRING 'euc-jp) 處理。
  • 直接整個 buffer (set-buffer-file-coding-system 'euc-jp)

不過恭禧,三個都無效 XD

我對編碼沒有概念,所以之前遇到這種問題也只會revert-buffer-with-coding-system,只會使用但並不了解原理。

Emacs 資源大多還是以處理英文為主,遇到這種 i18n 方面的問題,日文資源應該算最多的(不知為何日本好像很愛用 Emacs),但我還是沒找到此問題解決方法…

然而偶然發現一個奇怪的現象:如果把 url-retrieve 的 buffer 直接 C-x C-s 存檔、kill-buffer 後,再去開那個檔案,Emacs 竟然可以自動辨識編碼且正確顯示內容,問題是我真的不知道 Emacs 到底是用甚麼方法去辨識的,所以…目前看起來只能這樣了。

使用 url-copy-file直接把整個 html 檔下載到/tmp等等,啊前面搞那麼久都在幹麻…orz 啊,這就是人蔘啊。 ,然後直接打開那個檔案做你需要的操作,結束後再用 delete-file 刪掉。

Emacs 與編碼相關資源

筆記

我好像一直在寫一些很沒有水準的東西(掩面)。

參考資料

現在寫 Markdown 連結輕鬆多了,不過還有一個很想要但好像也沒人做的功能,就是插入 flickr 的 photo raw link。不過目前我完全不懂 API 那些東西…有空再看看能怎麼作吧?・ω・


[2014-01-05 月 16:34] 開始寫。
[2014-01-07 月 00:14] 寫完總共 8 小時 49 分鐘。deploy.
[2014-01-07 火 00:35] 修正 #installation 連結
[2014-01-07 火 16:14] 加上 twittering-edit-mode 判斷、建議的 key-binding、twittering 的格式。30 分鐘。
[2014-01-08 水 13:27] 加上 markdown 判斷是否在行首的功能。
[2014-01-16 木 12:21] 忘記解決 recentf 問題惹。
[2014-05-10 土 04:50] 修改 html entities 的 replace 方式,可能效率會比原本的差,但不用再裝其他 dependancies。
[2014-06-06 金 23:03] 以前 Code 實在寫很爛,總算給它整個重寫,而且現在支援 Org/Markdown/HTML/Twittering 四種連結格式。