出于整理瀏覽器收藏夾的需要,我稍微折騰了一下,最后還是選擇了使用 org-mode 來管理書簽。本文記錄了我的一點折騰過" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > 20.使用org-mode管理瀏覽器書簽

20.使用org-mode管理瀏覽器書簽

時間:2023-07-26 07:12:02 | 來源:網(wǎng)站運營

時間:2023-07-26 07:12:02 來源:網(wǎng)站運營

20.使用org-mode管理瀏覽器書簽:#東方Project あたいってばさいきょ?ね?。?! - なにょち的插畫 - pixiv

出于整理瀏覽器收藏夾的需要,我稍微折騰了一下,最后還是選擇了使用 org-mode 來管理書簽。本文記錄了我的一點折騰過程和最后使用 org-mode 管理記錄的方法。

除最后一節(jié)需要對 org-mode 有較為全面的了解之外,其他部分不需要任何的 emacs 知識,希望我翻到的一些瀏覽器插件和命令行工具對你有所幫助。已經(jīng)有不少人在 org-mode 上折騰過了,所以遇到問題時也不用太擔心,善用搜索引擎。

本文包括的內(nèi)容:

本文使用的環(huán)境如下:

1. 寫在前面

至少在三年前我沒有想過這個問題,只有一臺電腦是不需要考慮同步問題的,手機一般也只用來查看一下網(wǎng)頁,沒有保留鏈接的必要。但是兩年前情況變了,我因為玩系統(tǒng)的需要組了一臺垃圾電腦,這樣一來我就有兩份收藏夾需要管理。之后除 edge 外我還使用過 firefox,chrome 等瀏覽器,我面對的就是多臺電腦加多個瀏覽器的情況。這樣看上去挺麻煩的,不過我也將就用到現(xiàn)在,這也許說明收藏夾不是什么太重要的東西(笑),畢竟搜索引擎足夠強力。

這一時期我對收藏的管理主要在歸檔和同步兩方面,歸檔主要是通過將書簽存入文本文件并放入移動硬盤或網(wǎng)盤來完成,而同步是通過瀏覽器提供的導入/導出功能,以文件傳輸?shù)男问酵瓿?。不論? firefox 還是 edge 都提供了 import/export 功能以導入/導出 HTML 形式的收藏夾,非常方便,但我歸檔的時候似乎沒想到直接使用導出文件,還非要手動一條條輸鏈接到 txt 里…

當然不用我說你也知道,一般的瀏覽器都提供了同步功能。通過在不同機器上登入瀏覽器賬號就可以同步收藏夾。但是我發(fā)現(xiàn) edge 的登入似乎有些困難,時而登的上時而登不上,而 firefox 我也一直懶得注冊,也就沒怎么用過。

除了同步和歸檔,書簽的數(shù)量也是一個問題,書簽越來越多,光靠手翻來查找效率也不太行。這么一大堆鏈接肯定有不少已經(jīng)用不了了。使用瀏覽器的一些插件可以解決或緩解這些問題,這就是下一節(jié)的主題。

2. 使用瀏覽器插件管理書簽

我們可以使用一些插件來增強瀏覽器的書簽管理功能。我平時用 Chrome 用的少,這里就用 firefox 上的插件舉例了。我基本上沒用過什么瀏覽器插件,所以這里只是嘗鮮,沒有長期用戶體驗陳述。

在開始之前,我們先列出瀏覽器收藏管理的一些基礎功能,這樣方便看看增強性插件強在哪里:

以下插件推薦來自 7 Firefox Extensions for Managing Your Bookmarks

2.1. 清除冗余收藏 Boorkmark Dupes

在 firefox 中已收藏網(wǎng)頁的收藏按鈕會被標藍(默認主題下),這個時候點擊該按鈕只能對書簽內(nèi)容進行編輯而無法再次保存,這樣就避免了重復的書簽。不過我們可以通過在管理書簽(Ctrl+Shift+O)窗口中復制書簽,這是一種創(chuàng)建重復書簽的方法。日常使用中可能時不時會出現(xiàn)重復的書簽,這就造成了冗余。

這個插件的作用就是檢測并刪除冗余書簽。它的界面如下圖所示:

它提供的功能還算豐富,可選擇除最新/最舊的重復書簽并一件刪除。它還提供了使用正則匹配的高級模式,不過一般用不著。

2.2. 清除 404 書簽 404 Bookmarks

使用這個插件可以發(fā)現(xiàn)并移除已經(jīng)無法訪問的書簽,就像這樣:

具體的原理我不太清楚,不知道能不能在 emacs 中實現(xiàn)…

2.3. 搜索功能強化 Bookmark search plus 2

firefox 默認提供的搜索功能可以對 title 和 url 進行搜索,一般來說也是夠用的,這個插件對搜索進行了強化,可以使用正則進行搜索,可以搜索特定收藏文件夾。下右圖是使用 ^G 正則得到的結(jié)果。

2.4. 交互式書簽管理 Keep or Delete Bookmarks

打開該插件后,它會顯示某個書簽以及一些操作,你可以選擇刪除,保留和跳過。這樣就不需要你一個個翻書簽了,它會自動遍歷。如果選擇保留,書簽就不會再被它拿出來,選擇刪除即刪除書簽,選擇跳過,下次使用該插件時書簽還會被顯示。

這和 emacs 里的 query-replace 有點像,也許可以考慮使用相似方法在 emacs 中實現(xiàn)。

2.5. 安全 Private Bookmarks

Private Bookmarks 可以創(chuàng)建一個有密碼的書簽文件夾,鎖定后該文件夾不可見。需要在隱私訪問(private browsing)標簽頁中才能打開它。首次使用時需要設置密碼。

2.6. 更好的界面 BookmarksHome

就像我從來都是把 org 文件轉(zhuǎn)成帶 css 的 HTML 來讀一樣,這個插件可以將收藏夾中的鏈接以更易讀的 HTML 頁面展示出來,出于隱私考慮,這里就截一小個文件夾:

3. 瀏覽器書簽管理的一些問題

我列出來的問題可能部分被上面的插件解決掉了,不過我還是列一下吧,這里針對的是瀏覽器最基礎的書簽功能。這是“我”認為存在的問題。

老實說我有點吹毛求疵了,畢竟書簽一般只是放在瀏覽器里面的快捷方式而已。

3.1. 分類與 tag

就像文件系統(tǒng)一樣,收藏夾不僅允許創(chuàng)建收藏書簽,還允許創(chuàng)建收藏文件夾,可在文件夾內(nèi)存放收藏書簽。通過給文件夾命名,我們就可以對收藏書簽做一個簡單的分類,比如編程,視頻,博客,百科等等。如果還要繼續(xù)細分還可以創(chuàng)建三級甚至二級收藏夾。不過一般最多兩級就夠了。

這樣的層級管理有個問題,那就是書簽可能同時具有多種屬性,假設我現(xiàn)在發(fā)現(xiàn)了一篇講解在 emacs 中使用數(shù)據(jù)庫的文章,如果我有“數(shù)據(jù)庫”文件夾的話我應該放入其中,但是“emacs”文件夾也應該放一個(一般來說這類文章 emacs 內(nèi)容更多一些,但是與數(shù)據(jù)庫也是相關的)。這會導致存書簽的時候還要糾結(jié)一下,幾年下來我發(fā)現(xiàn)我的“other”分類里面存放了最多的書簽,而其他文件夾里面更新間隔很長。

憑現(xiàn)在的我來看,使用文件夾來分類是一種非常樸素的思路,相當于給每個書簽打上了一個 tag,這樣就方便歸類查找。更好的方式也許是在一個大的文件夾內(nèi)(也許是文件內(nèi))存儲書簽,在其他地方存放 tag 信息,單個書簽可擁有一個或多個 tag,這樣方便對書簽進行基于 tag 的查找。

當然這兩種方式并不沖突,層級分類也可以使用 tag。

在 Edge 中我似乎沒有看到 tag 功能,不過 firefox 中有個叫做標簽的功能,可以根據(jù)標簽來尋找書簽。

3.2. 搜索

就像我在上面提到的,一般瀏覽器會提供對標簽名字和 url 的搜索功能,這個功能一般來說也是夠用的(誰還每次搜索的時候想想怎么寫正則)。但假設我想要搜索一段時間內(nèi)保存的書簽,或是根據(jù)其他的一些信息(比如訪問頻次)來搜索的話,基礎的功能是不夠用的,瀏覽器里面也不一定存儲了這些時間和訪問次數(shù)的信息。

不過話又說回來,除非說閑到蛋疼,我也懶得加上根據(jù)時間檢索的功能。

3.3. 同步

這里與其說是敘述不足之處,不如說是我在吐槽些無關痛癢的東西罷了(笑)。

貌似十幾年前 firefox 就開始提供同步功能了,大致原理是將書簽上傳至 firefox 服務器上并加密,除用戶外的任何人都無法訪問。Edge 上也可以同步,不過需要 Microsoft 賬號,然而在我的電腦上登入 Microsoft 一直是個問題。

瀏覽器提供的同步功能我基本上沒有用過,所以也沒法做什么評價。在多個瀏覽器間的收藏夾同步插件也有,比如 BookmarkHub - 書簽同步(使用 gist 來做同步,作者也是心大,不過私有 gist 也許是足夠安全的)。不管是瀏覽器同步還是插件同步,書簽總是要上傳到第三方的,也許會存在一定的安全問題。

不過這個安全問題就見仁見智了,這主要取決于用戶是否信任服務提供者。我覺得一切上網(wǎng)的東西都是不安全的(笑),不過我對云盤之類的比較滿意。

4. 非瀏覽器收藏管理

書簽一般都是通過瀏覽器管理的,但是肯定會有人因為各種原因?qū)g覽器書簽管理不滿(比如現(xiàn)在的我),進而自己造一些小工具。我比較關注一些 CLI 小玩意,所以這里就不介紹 GUI 式的管理軟件了,我想在 emacs 里弄個簡單的界面。

4.1. buku

buku is a powerful bookmark manager and a personal textual min-web. —— readme

buku 支持以下特性:

這里面有很多好用的特性值得我借鑒(如果我要自己寫一個類似的話),比如使用了 sqlite3(話說 emacs 29 里面已經(jīng)集成了)。但是很可惜我使用了 windows 系統(tǒng),通過 pip install buku 后得到了這樣的結(jié)果…

在我通過一遍遍運行來找到缺失的包并成功添加第一條書簽后,我發(fā)現(xiàn)中文有部分亂碼……也許是 Pypi 上的包有些小 bug,或是 windows 上的編碼問題。如果能夠開箱即用(指一條 pip 搞定)的話我還挺想用一下的,畢竟 emacs 界面已經(jīng)有人寫好了:ebuku

4.2. nb

與 buku 不同,這是一個用于筆記管理的軟件,如果把書簽當作筆記的話就可以用了(笑)。它使用 git 來進行同步和版本管理,支持多種編輯器。

可惜我實在太懶了,看到文檔里要求 WSL 環(huán)境我就不太想弄了(也許 MSYS 可以,誰知道呢)。

4.3. 其他

除了 buku 和 nb 外,我找到的其他大多都是些小玩具,我感覺也沒有必要列這么多了,下面簡單拉個列表結(jié)束掉這一節(jié)。

用 go 寫的簡單管理軟件

相比 buku 就沒有那么多功能了,算是個比較小巧的書簽管理工具。具體內(nèi)容可以去 github 上看看。

這大概算是個玩具項目,通過命令 boom 可以創(chuàng)建列表并保存,之后可以通過命令方便地將書簽放入剪貼板中。不過它并不是專門為書簽管理設計的,it's just a toy。

5. 使用 org-mode 管理書簽

說了這么多總算是來到了本文的核心部分。這一節(jié)中我會就幾個方面來展開介紹,并在最后給出可用的代碼。閱讀這一節(jié)需要你對 org-mode 有較為全面的了解(至少要用過基本功能),沒用過的話可以閱讀 The Org Manual(或者是 M-x info 再找 Org,為了寫這一部分我花了兩天時間把整個文檔讀了一遍)。當然我更建議讀 Org Mode Compact Guide,這個更加簡單。

5.1. 為什么選擇 org-mode

這一部分可看作我給 org-mode 和 emacs 做的廣告。(笑)

emacs 是一款活化石級別的編輯器,適配的平臺極其廣泛,且至今仍在活躍開發(fā)中,目前最新的版本(29)已經(jīng)加入了 sqlite 支持。換句話說,使用 emacs 不用擔心出現(xiàn)一個平臺可用而另一平臺不可用的問題。自 1976 年開始,有無數(shù)的人為 emacs 寫過代碼,這也就意味著 emacs 具有非常豐富的生態(tài),在遇到問題時可以在他人的代碼中尋求解決方案。

org-mode 可以用來記筆記,做項目規(guī)劃,記錄時間日期,進行文學編程等。換句話的話它就是個通用的文本管理系統(tǒng),提供了豐富的功能??紤]到文本文件是一種“放之四海而皆準”的文件格式(換句話說就是只有基本文本),我準備使用文本來保存書簽。利用 org-mode 的功能可以方便地實現(xiàn)增刪改查等功能,這些會在后面的內(nèi)容中講到。

不過話又說回來,不管是 emacs 還是 org-mode 都有一定的學習成本,我屬于是沉沒成本太高了,如果沒有了解到 emacs 的話我也許就直接使用瀏覽器自帶書簽管理功能了。

5.2. 計劃實現(xiàn)的功能

參考瀏覽器書簽,buku 等等管理軟件,這里列出我想要實現(xiàn)的功能,考慮各方面因素,我應該不會全部實現(xiàn)。

5.2.1. 基本的增刪改查

1.增加

最直接的添加方法是從瀏覽器中復制 url 再放到 org 文件中。但這樣顯然稱不上快捷,畢竟瀏覽器只要點一下星標就可以設置書簽的標題文本和存放位置,這就像個模板一樣,只需要往里面填需要的信息就行了。

這個功能可以使用 org-capture 來實現(xiàn),通過編寫 capture 模板來添加 url 和附加信息,這樣就不用手動敲入 NAMEDESCRIPTION 或是 URL 等固定名字了。這里有篇文章可作參考:bookmarking with org-mode

另外,也許可以考慮使用 org-mode 的 protocol 功能和一些瀏覽器插件來實現(xiàn)一鍵保存,就像這個帖子提到的那樣:從瀏覽器一鍵保存書簽到 Org Mode

2.刪除或修改

修改自然不用說,emacs 的本工作就是編輯文本,刪除的話可以使用 C-c C-x C-w (org-cut-special)來刪除一整個標題的內(nèi)容。

3.查找

一般瀏覽器支持 name 和 url 查找,這些功能 emacs 自帶的搜索自然不在話下,在 emacs 中也可以很方便地使用正則進行查找。

除了文本查找外自然還需要基于標簽(tag)的查找,這個可以考慮使用 org-agenda 的功能來實現(xiàn)。我也許會添加基于時間的查找功能,這樣就可以根據(jù)時間段進行查找(算了算了)。

org-agenda 應該是基于文本的查找,既然 emacs29 都內(nèi)置數(shù)據(jù)庫了,有時間的話我會考慮用數(shù)據(jù)庫來實現(xiàn)更加快速的查找。不過要體現(xiàn)出數(shù)據(jù)庫的優(yōu)勢也許得上萬甚至上十萬書簽了。等到 29 出正式版再說吧。

5.2.2. 分類與 tag

瀏覽器提供了文件夾式的分類功能,將一個個書簽放入書簽文件夾就相當于給書簽貼上了標簽,方便根據(jù)分類進行查找。在 org-mode 中要實現(xiàn)分層分類功能,可以考慮使用屬性(PROPERTIES)來表明該 HEADLINE 是書簽實體還是分類。具體來說的話就像這樣:

* Search Engine:PROPERTY::Is-Folder: t:END:** Baidu:PROPERTIES::Is-Folder : nil:END:www.baidu.com** google:PROPERTIES::Is-Folder : nil:END:www.google.com...... 不過我不打算使用這種分層分類的方式,如上文所見,我不是太喜歡它,分類層級太多的話查找起來并不方便。我準備直接使用類似表格的格式,也就是只使用同一級標題表示書簽,這樣實現(xiàn)起來也更加方便。

存儲格式只有一層的話如何分類呢?使用 org-mode 的 tag 功能可以實現(xiàn)。而且 org-mode 的 tag 還支持嵌套,需要的話可以實現(xiàn)基于 tag 的分層。

5.2.3. 歸檔與同步

歸檔非常簡單,直接使用 org-mode 的 archive 功能即可,將當前文件內(nèi)的全部數(shù)據(jù)歸入另一文件中。org-mode 還支持所謂的內(nèi)部歸檔,加上 :ARCHIVE: 的 tag 即可。使用 org-agenda 還可以對歸檔文件進行方便地搜索。

至于同步問題,無論是自建服務器提供同步功能,還是使用同步盤服務都可以(我已經(jīng)把瀏覽器同步排除了)。我使用了兩年的 Onedrive,速度還行,同步效果也不錯,就是有點小貴。最近發(fā)現(xiàn)金山云盤有 1 個 G 的免費空間,就算開會員也比 Ondrive 要便宜不少,暫時先用著。

這里還是要說一句,不管是自建還是使用云盤同步,數(shù)據(jù)都不可能是絕對安全的,一定要做好本地備份。

5.2.4. 與瀏覽器的互操作

在 org-mode 中,通過點擊鏈接或 C-c C-o 即可在默認瀏覽器中打開鏈接,但這還是比不上從瀏覽器中直接點方便。要是能夠同步 org 文件和書簽欄的話效果應該挺不錯的,這就需要我去學習怎么寫插件了。使用數(shù)據(jù)庫來與瀏覽器交互應該會更好,所以等 29 吧(笑)。

除了上面的愿景,還可以考慮實現(xiàn)從 Edge 或 firefox 的書簽導出文件中導入書簽,以及從 org 中導出 Edge 或 firefox 可識別的書簽文件。這部分應該可以參考 buku 的代碼。

5.2.5. 其他

暫時就這么多吧。

5.2.6. 數(shù)據(jù)格式設計

在這一節(jié)的最后我們說下書簽模板的設計。

如前所述,我不會采用嵌套標題來表示分層分類格式,那樣不便于一些遍歷操作。我會使用簡單的同一級標題來保存。標題中的內(nèi)容就是帶描述的鏈接,這樣鼠標一點就可以打開鏈接。tag 就是它的分類。一個標題可有多個 tag。

那么一個項需要具有哪些元素呢?

把整個網(wǎng)站下載下來使用 orig-attach 保存也算是一種備份手段,我會在后文說明實現(xiàn)方法以及演示使用方法。它為每個書簽分配一個唯一的 ID 值和唯一的文件夾位置,需要保存的文件可放在文件夾內(nèi)。(功能描述在這里)

以下為一個示例:

* [[https://baidu.com][百度一下,你就知道]] :search:ATTACH::PROPERTIES::ID: 114514-191981:YYOB-CREATE-TIME: [2022-07-27 Wed 19:36]:YYOB-ID: 1:YYOB-MD5: c4ca4238a0b923820dcc509a6f75849b:END:百度,一個搜索引擎...... 在上面的例子中, :search: 就是 tag, :PROPERTIES: 中的 :ID: 就是 attach 的 ID 值,用來索引保存的文件位置。 :YYOB-CREATE-TIME: 就是創(chuàng)建時間, :END: 后面的文本就是詳細描述部分,這部分的內(nèi)容就隨意了。

在正式開始編寫之前,我先介紹 org 的幾個 handy function,它們可以用來進行一些常用操作。

how to recursively search org-mode headers non-interactively?

5.3. 實現(xiàn)與演示

接下來就開始介紹我的具體實現(xiàn)思路和部分代碼,我將它實現(xiàn)為一個 elisp 包,名字叫做 yyorg-bookmark ,鏈接放在本節(jié)的最后。首先從設計思路上來說吧。

多虧了 emacs 28 的新特性 shorthands,寫代碼的時候可以少寫很多包前綴。下面的代碼的包前綴都使用 t- 而不是 yyorg-bookmark- ,emacs 在讀取時會自動轉(zhuǎn)換。

5.3.1. 設計思路

首先我們需要認識到,每個書簽文件其實就是一個數(shù)據(jù)庫,某種意義上來說我寫的這個包就是個非常簡陋的 DBMS。除數(shù)據(jù)外 DB 還要保存一些管理信息,如果我把這些變量放在包里面就會增加發(fā)生沖突的可能性(比如名字沖突等)。所以我將它們放在了書簽文件中,將一些通用管理函數(shù)放在了 yyorg-bookmark 里。

多虧了 org-mode 提供的文學編程功能,我可以在 org 文件中創(chuàng)建 elisp 代碼塊,并在文件載入 emacs 時運行以進行一些初始化操作。具體來說就像這樣:

How can I evaluate elisp in an orgmode file when it is opened?

#+NAME: startup#+BEGIN_SRC emacs-lisp(your-code-here)#+END_SRC...# Local Variables:# org-confirm-babel-evaluate: nil# eval: (progn (org-babel-goto-named-src-block "startup") (org-babel-execute-src-block) (outline-hide-sublevels 1))# End: 通過將變量使用 setq-local 設置就可設置 buffer 局部變量,這樣就不容易引起 buffer 間沖突。同時代碼塊里也可以包含一些專用于 buffer 的管理函數(shù),它們可以是 yyorg-bookmark 的函數(shù)的包裝,或是自己定義的管理函數(shù)。

我將上面這樣的模塊放入了一個模板文件中,并添加了一個叫做 yyorg-bookmark-enchant 的命令,使用該命令即可將模板文件附加到當前 buffer 末尾,這樣完成了一個 yyorg 書簽數(shù)據(jù)庫的建立,完成了對 buffer 的“附魔”(笑)。

分離帶來好處的同時也帶來一個問題,我要怎樣才能訪問 buffer 中的局部數(shù)據(jù)呢?可以通過將當前 buffer 切換至書簽文件并使用 symbol-value 來獲取。那要如何獲取書簽文件的 buffer 呢?好在 org-mode 提供了一個模板名與文件對應的關聯(lián)表 org-capture-templates ,在進行內(nèi)容捕獲時,org-mode 根據(jù)它來選擇對應的模板,并寫入對應的文件。

在書簽文件載入時,書簽文件需要將自己的模板添加到這個表中,這樣就可以令 org-mode 在捕獲內(nèi)容時根據(jù)選擇將內(nèi)容輸入到對應書簽文件中。我們可以查詢模板名來獲取對應書簽文件名。我編寫了一些輔助函數(shù),下面是一個比較好用的內(nèi)部函數(shù):

通過下面的代碼即可獲取和修改書簽文件中的局部變量:

(defun t-get-local-value (key symbol) "get buffer-local value in target file" (let* ((filename (t--template-filename key)) (buf (get-file-buffer filename))) (save-current-buffer (set-buffer buf) (symbol-value symbol))))(defun t-set-local-value (key symbol value) "set buffer-local value in target file" (let* ((filename (t--template-filename key)) (buf (get-file-buffer filename))) (save-current-buffer (set-buffer buf) (set symbol value))))當然這也帶來一個問題,代碼變長了不少(畢竟是打洞做法……)

5.3.2. 一些輔助函數(shù)

在完成了最基本的分隔工作后就可以寫一些包裝函數(shù)了,這一部分是對 org-mode 部分函數(shù)的封裝,讓這些函數(shù)好用一些。

首先是對 org-capture-templates 添加/刪除的處理。它是我這個包里最重要的全局資源,用來關聯(lián)模板和書簽文件。為了避免出現(xiàn)一些低級錯誤,比如類型錯誤,模板錯誤等,需要對添加過程做一些檢查。同時考慮到它的全局性,在添加同名模板時也要檢查是否沖突,由用戶來決定是否覆蓋已存在的同名模板。

我編寫了 yyorg-bookmark-add-template 函數(shù)來添加模板,一個簡單的例子如下,這是附魔文件里的例子模板:

(yyorg-bookmark-add-template :key "l" :desc "Add browser bookmark" :type 'entry :target `(file+headline ,(buffer-file-name) "Bookmarks") :temp "* %c %^g/n:PROPERTIES:/n:YYOB-CREATE-TIME: %T/n:YYOB-ID: %(yyorg-bookmark-control-key-counter /"l/")/n:END:" :props '(:prepend t)))除了添加外也要考慮刪除,我還編寫了 yyorg-bookmark-remove-template 用于從 minibuffer 中選擇并刪除模板。

(defun t-remove-template (key) "remove a template from `org-capture-templates'use minibuffer to select a key" (interactive (list (completing-read "key: " (t--template-keys) nil t))) (setq org-capture-templates (cl-delete-if (lambda (x) (string= key (car x))) org-capture-templates))) 再然后就是對屬性值的操作,org-mode 提供了一些函數(shù):

使用上面的三個函數(shù),我編寫了兩個輔助函數(shù),它們接受一個可選參數(shù)來判斷自己是否在 HEADLINE 上,若是則直接使用當前位置,否則使用 org-find-entry 查找屬性位置:

(defun t--get-property (pname &optional on-headline) "return string if found, or nil if not" (let ((place (if on-headline (point) (org-find-property pname)))) (if place (org-entry-get place pname) nil)))(defun t--set-property (pname strval &optional on-headline) "set property `pname' if found and return t, or nil if notif on-headline is set and point is on headlinethis function will always success" (let ((place (if on-headline (point) (org-find-property pname)))) (if place (prog1 t (org-entry-put place pname strval)) nil)))另外,由于 org-mode 中屬性值都是以字符串保存的,如果要進行數(shù)學運算并不方便。我添加了一些計數(shù)器操作,可以較方便的對某個屬性值進行自增和自減,最終的可用函數(shù)如下:

(defun t-control-counter (pname op &optional on-headline) "control counter's value'+ is add1, '- is sub1, 'r is reset to 0, 'z is unchangereturn the origin value" (cl-case op ((+) (t-increase-counter pname on-headline)) ((-) (t-decrease-counter pname on-headline)) ((r) (t-reset-counter pname on-headline)) ((z) (t--get-property pname on-headline)) (t (error "unrecognized op %s" op))))最后是對標題屬性值的枚舉,可以獲取所有 HEADLINE 的屬性值,這個函數(shù)可配合 emacs 的 narrow 功能實現(xiàn)區(qū)域枚舉。

(defun t-get-all-entries-properties (pnames) "get all entries specific propertyreturn form is ( ((p1 . v1) (p2 . v2) ...) ... )in other words, return value is a nested alistyou can use it with narrow" (let ((pro-list)) (org-map-tree (lambda () (let ((a (org-entry-properties)) (b)) (mapc (lambda (x) (let ((c (assoc x a))) (when c (push c b)))) pnames) (when b (push b pro-list))))) (reverse pro-list)))上面這些函數(shù)基本上就是 yyorg-bookmark.el 文件中實現(xiàn)的功能了,接下來我們來到附魔模板的代碼編寫,來實現(xiàn)一些更加貼近用戶的操作。

5.3.3. 獲取書簽

首先從獲取書簽的功能開始。最直接也最費勁的方法是手動添加,這里就不提了,我們來寫個 capture template 吧,文檔在這:Capture templates。拿我在上面寫的模板做使用例,考慮到現(xiàn)在還沒有使用 yyorg-bookmark ,這里不使用 yyorg-bookmark-add-template 函數(shù):

(add-to-list 'org-capture-templates `("l" "Add browser bookmark" entry (file+headline ,(buffer-file-name) "Bookmarks") "* %c/n:PROPERTIES:/n:TIME: %T/n:END:" :prepend t)) 上面這段代碼的作用是將模板 "l" 添加到 org-capture-templates 中。這里目標選擇當前 buffer 對應文件,HEADLINE 選擇 Bookmark,屬性選擇 :prepent t ,這表示將新的項添加到最前。完整版的例子在代碼倉庫的附魔模板文件中。

我們可以體驗一下這段代碼的效果,按下 M-x org-capture ,然后選中 l ,你可以看到剪切板中的內(nèi)容被放到了添加項的標題中, TIME 屬性值成為了當前時間。接著按下 C-c C-c 完成捕獲。動圖如下所示:

https://www.zhihu.com/video/1537990288115425280 可見,在我復制 "Hello world" 并調(diào)用 org-capture 后,"Hello world" 出現(xiàn)在了標題位置,這是 %c 的作用,其他的特殊符號可參考官方文檔。

使用這個簡單的模板已經(jīng)可以實現(xiàn)收集書簽了,工作流大概是:在瀏覽器中復制鏈接,在 emacs 中完成捕獲動作,在瀏覽器復制標題,在 emacs 中添加標題,完成。但這顯然是不夠快捷的,這樣得往返瀏覽器兩次,最好是點一下就能存上。

這里有篇使用 applescript 拷貝瀏覽器鏈接到 org-mode 的文章,這樣做就不用跑兩趟了。不過他這還是要手動 org-capture ,更要命的是我從來不用蘋果的筆記本電腦。

好在有更好的解決方案,它就是 org-protocol

5.3.4. 一鍵獲取

就像我在上面說的,存書簽最好是點一下就好,而 emacs 正巧也有相應的解決方案。熟悉 emacs 的你應該知道 emacs 有個 emacsclient,如果 emacs 啟動了 server,那么可用 emacsclient file 命令在已啟動的 emacs 中打開文件,這樣就不會有多個 emacs 實例了,再也不用擔心 emacs 啟動太慢了(笑)。

org-protocol hack 了 emacsclient,除了傳遞文件名給 emacsclient 外,還可以傳字符串過去,org-protocol 會檢查字符串中有沒有已經(jīng)注冊的協(xié)議,如果有的話它會將字符串的內(nèi)容交給對應程序處理。換句話說,只消使用適當?shù)淖址?emacs 外面調(diào)用 emacsclient,emacs 就能做出相應的動作。

你可以使用以下代碼啟動 emacs server:

(require 'server)(unless (eq (server-running-p) t) (server-start))org-protocol 默認支持三種協(xié)議,我們要使用的那一種是 capture ,傳遞給 emacsclient 的字符串是這樣的一個格式:

emacsclient "org-protocol://capture?template=X&url=URL&title=TITLE&body=BODY" 調(diào)用 emacsclient 后, org-capture 會使用模板 X 來處理捕獲內(nèi)容,并完成捕獲??梢钥吹缴厦娴膬?nèi)容包括三個部分,分別是 url,標題和內(nèi)容,使用文檔中的對應的特殊符號即可在 org-capture 模板中獲取這些字符串。通過設置一些選項, org-capture 可以不需要 C-c C-c 確認而直接完成捕獲過程,這樣就可以一鍵捕獲了。

那么,我們要如何在瀏覽器中調(diào)用 emacsclient 呢?可以參考 Intercept calls from emacsclient to trigger custom actions 。如果你和我一樣使用 Windows,那只需運行下面的 reg 腳本即可(或者自己在 regedit 中添加):

REGEDIT4; see https://orgmode.org/worg/org-contrib/org-protocol.html; and https://github.com/sprig/org-capture-extension[HKEY_CLASSES_ROOT/org-protocol]@="URL:Org Protocol""URL Protocol"=""[HKEY_CLASSES_ROOT/org-protocol/shell][HKEY_CLASSES_ROOT/org-protocol/shell/open][HKEY_CLASSES_ROOT/org-protocol/shell/open/command]; use you own path to emacsclientw.exe@="/"path//to//your//emacs//bin//emacsclientw.exe" /"%1/""具體原理可以參考 “有個網(wǎng)站想打開此應用”原理是什么?,這里直接摘過來了:

作者:Hawaii
鏈接:https://www.zhihu.com/question/410173377/answer/1366638756
來源:本站
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

1.瀏覽器解析URL,得到協(xié)議部分thunder://
2.瀏覽器嘗試在已知的協(xié)議列表中匹配thunder協(xié)議
3.thunder不是已知協(xié)議,瀏覽器轉(zhuǎn)而在注冊表中查找thunder協(xié)議的注冊信息,也即HKEY_CLASSES_ROOT/thunder這個鍵
4.瀏覽器使用這個鍵下的Shell/Open/command子鍵的值作為運行此協(xié)議的程序路徑,并將URL的路徑部分作為程序的參數(shù)
5.瀏覽器彈出提示框“有個網(wǎng)站想打開此應用”,詢問用戶是否要執(zhí)行此協(xié)議關聯(lián)的程序。
在添加相應的注冊表項后,當你在瀏覽器地址欄中輸入類似 org-protocol://capture?template=l&url=baidu.com&title=百度一下你就知道&body=hello 的 url 時,瀏覽器就會提示你是否運行 emacsclient,點擊運行即可執(zhí)行捕獲動作。

現(xiàn)在還剩最后一步,那就是一個能夠獲取網(wǎng)頁信息和發(fā)送數(shù)據(jù)的按鈕,使用簡單的 JS 代碼即可獲取協(xié)議所需的信息并發(fā)送:

location.href = 'org-protocol://capture?template=' + key + '&url=' + encodeURIComponent(location.href) + '&title=' + encodeURIComponent(document.title) + '&body=' + encodeURIComponent(window.getSelection());// use this for bookmarkjavascript:location.href='org-protocol://capture?template='+'yyobp'+'&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)+'&body='+encodeURIComponent(window.getSelection());在一些瀏覽器中你可以將那一長條代碼放到書簽地址中,然后點擊書簽即可實現(xiàn)捕獲。我在 firefox 和 edge 上進行了嘗試,firefox 可行但 edge 不可行。edge 不允許從書簽處執(zhí)行 JS 代碼。使用書簽不能適用于所有瀏覽器。下圖是 firefox 的編輯書簽對話框:

既然書簽不行那可以使用瀏覽器插件,在 firefox 上有個叫做 org-capture 的插件,這里是源代碼及文檔。按照它的說明配置好 emacs 后,點擊瀏覽器插件的那個馬頭(還是獨角獸?)就可以一鍵保存了。很可惜這個插件在 edge 上并沒有。

最后我想到了使用油猴腳本,油猴在許多瀏覽器上都可用(我就用 edge 和 firefox),腳本編寫也比較容易。通過它實現(xiàn)一鍵保存對我來說是最可行的方案了。下面給出我的油猴腳本和效果截圖:

// ==UserScript==// @name yyob-add-bookmark// @namespace http://tampermonkey.net/// @version 0.1// @description use org-protocol and tm-script to add bookmark// @author include-yy// @match *://*/*// @grant unsafeWindow// @grant GM_registerMenuCommand// ==/UserScript==(function() { 'use strict'; // Your code here... // all templates // [key, description, accesskey] // comment or uncomment to add/remove item let all = [ ['yyobp', 'Add Bookmark', 'a'], ['L', 'add bk 2', 'p'] ]; let i = 0; // https://stackoverflow.com/questions/25750183/how-to-create-a-toolbar-button-for-a-chrome-tampermonkey-user-script // how to add MenuCommand for (i = 0; i < all.length; i++) { let name = all[i][0]; let desc = all[i][1]; let hotkey = all[i][2]; GM_registerMenuCommand(desc, function() { main(name); }, hotkey); } // https://github.com/toure00/org-capture-tag-bookmark // how to capture link and description function main (key) { location.href = 'org-protocol://capture?template=' + key + '&url=' + encodeURIComponent(location.href) + '&title=' + encodeURIComponent(document.title) + '&body=' + encodeURIComponent(window.getSelection()); } // my original thought was to use radio/checkbox dialog to add or remove template to use // but I found it easier to just add/remove a list in a list variable :p // if you want to do like this, you can refer to // https://stackoverflow.com/questions/11668111/how-do-i-pop-up-a-custom-form-dialog-in-a-greasemonkey-script // and https://github.com/toure00/org-capture-tag-bookmark // if you want to use jQuery, just paste blow line to the ==userscript== block // @require https://code.jquery.com/jquery-2.1.4.min.js})();https://www.zhihu.com/video/1537991207703166976下面是上面演示中使用的捕獲模板, template 部分看著非常別扭,下面會解釋原因:

(yyorg-bookmark-add-template :key "yyobp" :desc "Add browser bookmark" :type 'entry :target `(file+headline ,(buffer-file-name) "Bookmarks") :temp "* [[%:link][%:description]] %(yyorg-bookmark-add-repeat-tag (md5 /"%:link/") (yyorg-bookmark-get-local-value /"yyobp/" 'yyob-hashtable) 'gethash)/n:PROPERTIES:/n:YYOB-ID: %(if (string= (yyorg-bookmark-add-repeat-tag (md5 /"%:link/") (yyorg-bookmark-get-local-value /"yyobp/" 'yyob-hashtable) 'gethash) /"/") (progn (puthash (md5 /"%:link/") (yyorg-bookmark-control-key-counter /"yyobp/" 'z) (yyorg-bookmark-get-local-value /"yyobp/" 'yyob-hashtable)) (yyorg-bookmark-control-key-counter /"yyobp/")) (gethash (md5 /"%:link/") (yyorg-bookmark-get-local-value /"yyobp/" 'yyob-hashtable)))/n:YYOB-CREATE-TIME: %T/n:YYOB-MD5: %(md5 /"%:link/")/n:END:%(if (string= /"/" /"%i/") /"/" /"/n%i/")" :props '(:prepend t :immediate-finish t :jump-to-captured t))) 這一部分我參考的資料有很多:

5.3.5. 添加和刪除

通過上面的一些操作我們實現(xiàn)了從瀏覽器直接發(fā)送書簽到 emacs,接下來要考慮的是添加和刪除的問題。如果僅僅是文本的添加和刪除的話這一節(jié)是不必要的,添加可通過手動添加或 org-protocol 獲取,而刪除可直接調(diào)用 org-cut-subtreeC-c C-x C-w )來刪除整一個書簽的內(nèi)容。

對添加和刪除的管理是為了更好的去重 。在我的實現(xiàn)中,可以使用局部快捷鍵 C-c k 來刪除書簽,使用 C-c i 來添加書簽(信息)。與普通的文本操作不同,這兩個操作會修改一些管理信息。

在我的捕獲模板中,創(chuàng)建 entry 時除了創(chuàng)建時間外,每個書簽的屬性還包括書簽的 ID 和 url 的 MD5 值。ID 是由全局計數(shù)器分配的,每添加一個 新的 url 就會使計數(shù)器自增 1,使得每個 url 的 ID 值都是唯一的。url ID 唯一但書簽并不唯一。上面那一團亂麻似的模板就是為了實現(xiàn)獲取書簽時檢查是否已存在 url。在捕獲新項時,若 url 已存在則使用已存在書簽的 ID,不自增計數(shù)器,若不存在則使用計數(shù)器值并自增計數(shù)器。

我使用了一個局部哈希表來存儲當前書簽的 MD5 和 ID,它用來檢查某個 url 的 MD5 是否已存在。在獲取書簽時模板代碼會根據(jù)書簽是否已存在判斷是否更新哈希表。若書簽 url 已存在,書簽還會在標題打上 :repeat: tag。

在刪除書簽時,我們同樣需要對哈希表進行維護。如果刪除的項是唯一的項,那就需要在哈希表中刪除 MD5-ID 鍵值對。如果不唯一則無需修改哈希表。我將完成這個工作的函數(shù)綁定到了 C-c k 上。與之相反, C-c i 的作用是將項的 信息 添加到哈希表中,若信息已存在則不進行操作。

假設我們使用 C-c k 刪除了某書簽,但是我們又想讓它恢復到?jīng)]有刪除之前的狀態(tài),那可以 C-/ (undo)然后使用 C-c i 將書簽信息重新添加到哈希表中。

方便起見,我還添加了 C-c r 快捷鍵,它根據(jù)當前的書簽項來刷新哈希表,你可以多次刪除多個書簽后直接使用它,而不需要使用多次 C-c k

這一部分的代碼實現(xiàn)主要在附魔模板中,因為使用了局部變量所以訪問起來有點麻煩,代碼形狀有些奇怪。在之后的版本中我可能會使用宏來簡化一部分操作。

5.3.6. tag

我沒有在模板中使用添加 tag 的功能( %^g ),添加 tag 可將光標移至 headline 處并按下 C-c C-c ,然后 emacs 會提提供一些已存在的 tag 供你選擇。這些沒什么好說的,下面參考官方文檔簡單提一下嵌套 tag 的寫法。

在文件開頭添加下面的 tag 頭,可以得到下面的嵌套格式:

# 注意空格,所有的空格都是必要的#+TAGS: [ GTD : Control Persp ]#+TAGS: [ Control : Context Task ]#+TAGS: [ Persp : Vision Goal AOF Project ]有人使用油猴實現(xiàn)了在瀏覽器內(nèi)添加 tag 的功能,不過感覺有點麻煩,可能只能用于一些常用 tag(不過這也夠了),所以我就沒有實現(xiàn)。

5.3.7. 下載網(wǎng)頁

org-mode 提供了一個叫做 attach 的功能,可以將一些文件與標題關聯(lián)起來。使用 C-c C-a a 添加文件后 org-mode 會給標題分配一個唯一的 ID,以及和 ID 關聯(lián)的文件夾,附加的文件默認會復制到該文件夾內(nèi)。使用 C-c C-a o 可打開文件夾內(nèi)某一文件,使用 C-c C-a f 可在 emacs 中打開該文件目錄。

兩年前我首次讀 manual 時對這個功能不以為意,現(xiàn)在一看,這功能太適合存儲下載下來的網(wǎng)頁了。使用 wget 等命令行工具很容易實現(xiàn)一鍵下載網(wǎng)頁的功能。它的實現(xiàn)非常簡單:

(defun t-get-url-from-link (str) "get link from [[link][description]]" (cl-assert (string= (substring str 0 2) "[[")) (let ((i 0)) (while (and (not (= (aref str i) ?/])) (< i (length str))) (cl-incf i)) (if (= i (length str)) (error "link not found") (substring str 2 i))));; https://stackoverflow.com/questions/13505113/how-to-open-the-native-cmd-exe-window-in-emacs;; https://www.tecmint.com/wget-download-file-to-specific-directory/;; https://www.anycodings.com/1questions/2463613/is-it-possible-for-wget-to-flatten-the-result-directories(defun t-attach-use-wget (link) "-E -H -k -K -p -nd -e robots=off -P target-directory used only on windows just to modify cmd to bash and something else to adapt to linux or use advice" (let* ((dir-path (org-attach-dir-get-create)) (wget-exe (or t-wget-path "wget"))) (let ((proc (start-process "yyob-wget" nil "cmd.exe" "/C" "start" "cmd.exe" "/K" wget-exe "-E" "-k" "-K" "-p" "-nd" "-e" "robots=off" link "-P" dir-path))) (set-process-query-on-exit-flag proc nil)))) 在 windows 上使用需要配置 yyorg-wget-pathwget 的絕對路徑,不過由于我現(xiàn)在懶得弄 linux,我也沒有寫使用 bash 的 linux 版本,我將下載鍵綁定在了 C-c u 上。org-attach 提供的默認下載功能太弱,不建議使用。

這里我使用 cmd 創(chuàng)建了可見的 cmd 窗口,而不是在 emacs 中使用 buffer 顯示輸出,我發(fā)現(xiàn)亂碼問題似乎很難解決。下面的一些文章或帖子可能對你在亂碼問題上有所幫助:

5.3.8. 查找

org-agenda 提供了非常好的聚合搜索功能,它可以一次性搜索多個文件,不過你需要通過 C-c [ 把 buffer 加入到它的搜索列表中,如果想要移除某 buffer 就在該 buffer 中按下 C-c ] 。使用 M-x org-agenda 即可進入搜索選擇界面,它提供了非常多的選擇,org-mode 建議將該命令綁定到 C-c a 上:

(global-set-key (bkd "C-c a") 'org-agenda) 我在書簽文件中添加了局部快捷鍵 C-c m ,它可以直接清空 org-agenda 使用的文件表,這樣就不用一個一個 C-c ] 了。

下面是 agenda 搜索選擇界面:

org-agenda 提供的搜索功能包括但不限于:

和時間管理相關的功能我不是很熟悉,這里就不進一步展開了。

可以參考 Advanced searching 學習 org-agenda 搜索技巧。

5.3.9. 源碼與演示視頻

源代碼我放在 github 上了,方便查看和下載:

由于長時間的 gif 圖一般都比較大(怎么感覺 gif 比 mp4 還要大呢…),而且不方便查看,所以我為上面的某些操作錄制了一個視頻,放在了 bilibili 上:

以上就是 yyorg-bookmark 的全部功能,感興趣的話可以自己動手,在附魔模板的基礎上添加自己想要的功能。

6. 后記

本來我是準備在暑假開始(我好像沒有暑假了)的六月初對自己的數(shù)據(jù)做一些整理。一些現(xiàn)在已經(jīng)沒用的歷史文件直接命好名丟硬盤里就行,反正再看也是幾年或十幾年之后了。但是一些活數(shù)據(jù),像是書簽,收藏和圖片是需要不斷更新的,我之前也沒想過要怎么管理。七月初整理完死數(shù)據(jù)后又忙了一些雜事,這才拖到現(xiàn)在。

剛開始寫這個小擴展時我的目的只是添加書簽到文本中,后來不斷查閱資料發(fā)現(xiàn)可以添加許多有用的功能,比如重復檢測,網(wǎng)頁下載等等。當我完成這個擴展時,我發(fā)現(xiàn)這是一個非常通用的擴展,除了書簽外還可以用來管理圖片,漫畫和影視作品。有時間的話我會繼續(xù)添加一些方便的功能。

Enjoy and have fun~

#東方 不思議の幻想郷 - fuepo的插畫 - pixiv

#東方project 幽霊楽団 - MEOWRON的插畫 - pixiv

#東方 プリズムリバー三姉妹 - かたのん的插畫 - pixiv

#レイラ?プリズムリバー 「お姉ちゃん」 - 春雨的插畫 - pixiv

關鍵詞:書簽,瀏覽,管理,使用

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關閉