结合ivy使用书签

使用浏览器书签时,有时候太多了,找起来很麻烦。最近我开始使用emacs-w3m 来存书签。默认保存到 ~/.w3m/bookmark.html 文件中。

然后我尝试了 helm-bookmarks ,它可以使用helm来帮助选择书签。

但我是 ivy 用户,平时少有使用helm。还有一个问题是我的书签中有很多中文标题,如果需要选择中文标题的书签时,还需要切换输入法。

针对中文标题这个问题,由于之前我 hack 过一下ivy,使它可以支持拼音首字母搜索,可以参考一下 我的配置 。所以我只需要使用ivy帮助我选择书签就可以了。

首先需要解析我的书签文件,使用 libxml-parse-html-region 可以解析文件, 返回一个 DOM object 。还需要 dom.el 来解析 DOM object 。不过据说 Emacs 25 已经内置了 dom.el 了。如果使用 Emacs 24 的同学可能需要自行下载一下。

思路很简单,把所有的 a 标签全部解析出来,做出来一个 ((name1 url1) (name2 url2) ...) 这样格式的列表。使用 ivy-read 来选择一个 name ,最后根据 name 找到对应的 url

代码如下:

;;; use `ivy' to use w3m-bookmarks
(require 'dom)
;;; where your bookmark file located
(setq peng-bookmark-file "~/.w3m/bookmark.html")

(defun peng-ivy-get-bookmarks ()
  "use `ivy-read' to select bookmark and return its url
"
  (let* ((dom (with-temp-buffer
                (insert-file-contents peng-bookmark-file)
                (libxml-parse-html-region (point-min)
                                          (point-max))))
         (a (dom-by-tag dom 'a))
         (mylist (mapcar 'peng-parse-dom-tag-to-personal-format a))
         (name (ivy-read "Jump to bookmarks:" (mapcar 'car mylist)))
         ;; todo: I need to find `name' in mylist, if find, break the
         ;; loop. But now, I just use `mapcar' to deal with every
         ;; element in mylist and get `final-result' by removing all
         ;; nil element in the list `result'. It's very ugly code!
         (result (mapcar #'(lambda (data)
                             (if (string= (car data)
                                          name)
                                 (cdr data)
                               nil))
                         mylist))
         (final-result (caar (remove nil result))))
    final-result))

(defun peng-parse-dom-tag-to-personal-format (dom)
  "parse a tag to `(name url) format'"
  (let ((url (dom-attr dom 'href))
        (name (dom-text dom)))
    (list name url)))

最后,写几个 interactive 的函数来选择用不同的浏览器打开:

(defun peng-bookmarks-firefox ()
  (interactive)
  (let ((url (peng-ivy-get-bookmarks)))
    (browse-url-firefox url)))

(defun peng-bookmarks-chrome ()
  (interactive)
  (let ((url (peng-ivy-get-bookmarks)))
    (browse-url-chromium url)))

(defun peng-bookmarks-w3m ()
  (interactive)
  (let ((url (peng-ivy-get-bookmarks)))
    (w3m-goto-url-new-session url)))

在函数 peng-ivy-get-bookmarks 中,得到标题名 title 后,从 ((name1 url1) (name2 url2) ...) 列表中找对应的url时,我写得很丑:用map返回一个 (nil nil (URL) nil ...) 这样的列表,然后又用 remove 把所有的 nil 去掉。最后剩下的就是需要的 url 。其实应该是循环 mylist ,找到对应的 name 后就退出的。但我不知道怎么写。至少现在是可以工作了。

最后,我尝试了把我 firefox 的书签导出到html中,使用上面的函数也可以工作。只需要把 peng-bookmark-file 改成对应的html文件就好。

2 个赞

印象中 Firefox 的书签有存在一个 Bookmark HTML 文件里,所有可以避免自己手动导出。

另外 Google Chrome 甚至直接存在一个 JSON 文件里,如我的在 ~/Library/Application\ Support/Google/Chrome/Profile 1/Bookmarks,也很容易解析。

最近从 helm 转到 ivy,只剩下 helm-firefox 没有 ivy 的替代品,自已写了一个,总算彻底去掉 helm 了:

https://github.com/tangxinfa/counsel-firefox-bookmarks

提了个 Merge Request 到 ivy

https://github.com/abo-abo/swiper/pull/1593
1 个赞