时间:2023-07-26 07:12:02 | 来源:网站运营
时间:2023-07-26 07:12:02 来源:网站运营
20.使用org-mode管理浏览器书签:#東方Project あたいってばさいきょ〜ね!!! - なにょち的插画 - pixiv^G
正则得到的结果。 query-replace
有点像,也许可以考虑使用相似方法在 emacs 中实现。 pip install buku
后得到了这样的结果… M-x info
再找 Org,为了写这一部分我花了两天时间把整个文档读了一遍)。当然我更建议读 Org Mode Compact Guide,这个更加简单。 NAMEDESCRIPTION
或是 URL
等固定名字了。这里有篇文章可作参考:bookmarking with org-modeC-c C-x C-w
(org-cut-special)来删除一整个标题的内容。 * 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......
不过我不打算使用这种分层分类的方式,如上文所见,我不是太喜欢它,分类层级太多的话查找起来并不方便。我准备直接使用类似表格的格式,也就是只使用同一级标题表示书签,这样实现起来也更加方便。 :ARCHIVE:
的 tag 即可。使用 org-agenda 还可以对归档文件进行方便地搜索。 C-c C-o
即可在默认浏览器中打开链接,但这还是比不上从浏览器中直接点方便。要是能够同步 org 文件和书签栏的话效果应该挺不错的,这就需要我去学习怎么写插件了。使用数据库来与浏览器交互应该会更好,所以等 29 吧(笑)。 [ ]
如果使用 sqlite 就可对数据进行加密(免费版好像不行…,需要使用 SEE 加密工具),这样应该会更加安全一点,也许可以做一个写入和读出数据库的功能,实现 org 文本和数据库的无缝转换[ ]
如果功能足够丰富的话,可以考虑写个 minor-mode[X]
添加下载网页的功能,把值得收藏的网页下载并存储下来url
和 description
可使用 org 的默认格式表示: [[link][description]]
。当在标题上按下 C-c C-o
或单击鼠标时就可在浏览器打开该网页tag
tag 直接放在标题后面,使用 :
分隔,举例来说是这样: :a:b:c:
PROPERTIES
里面存放链接添加时间等日常无需了解的数据text
放在标题的正文部分,对书签的内容做进一步说明* [[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:
就是创建时间, :END:
后面的文本就是详细描述部分,这部分的内容就随意了。 org-map-entries
遍历文件中的所有标题org-heading-components
获取标题的一些状态,具体内容可 C-h f
org-entry-get
org-entry-put
获取和设置属性值org-map-tree
遍历所有嵌套的标题yyorg-bookmark
,链接放在本节的最后。首先从设计思路上来说吧。 t-
而不是 yyorg-bookmark-
,emacs 在读取时会自动转换。yyorg-bookmark
里。 #+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 的管理函数,它们可以是 yyorg-bookmark
的函数的包装,或是自己定义的管理函数。 yyorg-bookmark-enchant
的命令,使用该命令即可将模板文件附加到当前 buffer 末尾,这样完成了一个 yyorg
书签数据库的建立,完成了对 buffer 的“附魔”(笑)。 symbol-value
来获取。那要如何获取书签文件的 buffer 呢?好在 org-mode 提供了一个模板名与文件对应的关联表 org-capture-templates
,在进行内容捕获时,org-mode 根据它来选择对应的模板,并写入对应的文件。 (yyorg-bookmark--template-filename key)
,根据 org-capture-templates
和模板名获取捕获的目标文件(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))))
当然这也带来一个问题,代码变长了不少(毕竟是打洞做法……) org-capture-templates
添加/删除的处理。它是我这个包里最重要的全局资源,用来关联模板和书签文件。为了避免出现一些低级错误,比如类型错误,模板错误等,需要对添加过程做一些检查。同时考虑到它的全局性,在添加同名模板时也要检查是否冲突,由用户来决定是否覆盖已存在的同名模板。 yyorg-bookmark-add-template
函数来添加模板,一个简单的例子如下,这是附魔文件里的例子模板: (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 提供了一些函数: org-entry-get
,获取某一点所在 HEADLINE 的属性值org-entry-put
,设置某一点所在 HEADLINE 的属性值org-find-entry
,寻找第一个匹配的属性值,返回位置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 中属性值都是以字符串保存的,如果要进行数学运算并不方便。我添加了一些计数器操作,可以较方便的对某个属性值进行自增和自减,最终的可用函数如下: (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 的属性值,这个函数可配合 emacs 的 narrow
功能实现区域枚举。 (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)))
上面这些函数基本上就是 yyorg-bookmark.el
文件中实现的功能了,接下来我们来到附魔模板的代码编写,来实现一些更加贴近用户的操作。 yyorg-bookmark
,这里不使用 yyorg-bookmark-add-template
函数: (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
,你可以看到剪切板中的内容被放到了添加项的标题中, TIME
属性值成为了当前时间。接着按下 C-c C-c
完成捕获。动图如下所示: org-capture
后,"Hello world" 出现在了标题位置,这是 %c
的作用,其他的特殊符号可参考官方文档。 org-capture
,更要命的是我从来不用苹果的笔记本电脑。 org-protocol
。 emacsclient file
命令在已启动的 emacs 中打开文件,这样就不会有多个 emacs 实例了,再也不用担心 emacs 启动太慢了(笑)。 (require 'server)(unless (eq (server-running-p) t) (server-start))
org-protocol 默认支持三种协议,我们要使用的那一种是 capture
,传递给 emacsclient 的字符串是这样的一个格式: emacsclient "org-protocol://capture?template=X&url=URL&title=TITLE&body=BODY"
调用 emacsclient 后, org-capture
会使用模板 X
来处理捕获内容,并完成捕获。可以看到上面的内容包括三个部分,分别是 url,标题和内容,使用文档中的对应的特殊符号即可在 org-capture
模板中获取这些字符串。通过设置一些选项, org-capture
可以不需要 C-c C-c
确认而直接完成捕获过程,这样就可以一键捕获了。 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/""
具体原理可以参考 “有个网站想打开此应用”原理是什么?,这里直接摘过来了: 作者:Hawaii在添加相应的注册表项后,当你在浏览器地址栏中输入类似
链接:https://www.zhihu.com/question/410173377/answer/1366638756
来源:本站
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1.浏览器解析URL,得到协议部分thunder://
2.浏览器尝试在已知的协议列表中匹配thunder协议
3.thunder不是已知协议,浏览器转而在注册表中查找thunder协议的注册信息,也即HKEY_CLASSES_ROOT/thunder这个键
4.浏览器使用这个键下的Shell/Open/command子键的值作为运行此协议的程序路径,并将URL的路径部分作为程序的参数
5.浏览器弹出提示框“有个网站想打开此应用”,询问用户是否要执行此协议关联的程序。
org-protocol://capture?template=l&url=baidu.com&title=百度一下你就知道&body=hello
的 url 时,浏览器就会提示你是否运行 emacsclient,点击运行即可执行捕获动作。 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());
在一些浏览器中你可以将那一长条代码放到书签地址中,然后点击书签即可实现捕获。我在 firefox 和 edge 上进行了尝试,firefox 可行但 edge 不可行。edge 不允许从书签处执行 JS 代码。使用书签不能适用于所有浏览器。下图是 firefox 的编辑书签对话框: org-capture
的插件,这里是源代码及文档。按照它的说明配置好 emacs 后,点击浏览器插件的那个马头(还是独角兽?)就可以一键保存了。很可惜这个插件在 edge 上并没有。 // ==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)))
这一部分我参考的资料有很多: org-cut-subtree
( C-c C-x C-w
)来删除整一个书签的内容。 C-c k
来删除书签,使用 C-c i
来添加书签(信息)。与普通的文本操作不同,这两个操作会修改一些管理信息。 :repeat:
tag。 C-c k
上。与之相反, C-c i
的作用是将项的 信息 添加到哈希表中,若信息已存在则不进行操作。 C-c k
删除了某书签,但是我们又想让它恢复到没有删除之前的状态,那可以 C-/
(undo)然后使用 C-c i
将书签信息重新添加到哈希表中。 C-c r
快捷键,它根据当前的书签项来刷新哈希表,你可以多次删除多个书签后直接使用它,而不需要使用多次 C-c k
。 %^g
),添加 tag 可将光标移至 headline 处并按下 C-c C-c
,然后 emacs 会提提供一些已存在的 tag 供你选择。这些没什么好说的,下面参考官方文档简单提一下嵌套 tag 的写法。 # 注意空格,所有的空格都是必要的#+TAGS: [ GTD : Control Persp ]#+TAGS: [ Control : Context Task ]#+TAGS: [ Persp : Vision Goal AOF Project ]
C-c C-a a
添加文件后 org-mode 会给标题分配一个唯一的 ID,以及和 ID 关联的文件夹,附加的文件默认会复制到该文件夹内。使用 C-c C-a o
可打开文件夹内某一文件,使用 C-c C-a f
可在 emacs 中打开该文件目录。 (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-path
为 wget
的绝对路径,不过由于我现在懒得弄 linux,我也没有写使用 bash 的 linux 版本,我将下载键绑定在了 C-c u
上。org-attach 提供的默认下载功能太弱,不建议使用。 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 ]
了。 t
列出所有的 TODO
项, T
列出带有特殊 TODO
标志的项m
搜索 tag/prop/todo, M
只对 TODO
项进行搜索s
关键词搜索, S
只对 TODO
项进行搜索/
使用多 buffer occur
进行搜索?
找到带有 :FLAGGED:
tag 的项#
列出所有阻塞的项目yyorg-bookmark
的全部功能,感兴趣的话可以自己动手,在附魔模板的基础上添加自己想要的功能。 关键词:书签,浏览,管理,使用