【注意】最后更新于 May 31, 2023,文中内容可能已过时,请谨慎使用。
需求:在 github 的 README 中显示日常项目状态,显示本周主要的任务,列出项目清单和任务安排。设计:使用 org-ql 查询任务状态,列出项目和代办实现,导出 json 格式。
基于 org-ql-search 实现获取查询,使用 org-ql-view 模板格式化结果数据
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
| (defun build-readme-view (name)
"Choose and display the `org-ql-views' view NAME.
Interactively, prompt for NAME."
(let* ((result)
(view (alist-get name org-ql-views nil nil #'string=))
)
(-let* (((&plist :buffers-files :query :sort :narrow :super-groups :title) view)
(super-groups (cl-typecase super-groups
(symbol (symbol-value super-groups))
(list super-groups))))
(message "step 1:+++++++ %s" query)
(build-readme-search buffers-files query
:super-groups super-groups :narrow narrow :sort sort :title title
:buffer org-ql-view-buffer)
))
)
(cl-defun build-readme-search (buffers-files query &key narrow super-groups sort title
(buffer org-ql-view-buffer))
(declare (indent defun))
(interactive (list (org-ql-view--complete-buffers-files)
(read-string "Query: " (when org-ql-view-query
(format "%S" org-ql-view-query)))
:narrow (or org-ql-view-narrow (eq current-prefix-arg '(4)))
:super-groups (org-ql-view--complete-super-groups)
:sort (org-ql-view--complete-sort)))
;; NOTE: Using `with-temp-buffer' is a hack to work around the fact that `make-local-variable'
;; does not work reliably from inside a `let' form when the target buffer is current on entry
;; to or exit from the `let', even though `make-local-variable' is actually done in
;; `org-ql-view--display'. So we do all this within a temp buffer, which works around it.
(with-temp-buffer
(let* ((query (cl-etypecase query
(string (if (or (string-prefix-p "(" query)
(string-prefix-p "\"" query))
;; Read sexp query.
(read query)
;; Parse non-sexp query into sexp query.
(org-ql--query-string-to-sexp query)))
(list query)))
(results (org-ql-select buffers-files query
:action 'element-with-markers
:narrow narrow
:sort sort))
(strings (-map #'org-ql-view--format-element results))
(buffer (or buffer (format "%s %s*" org-ql-view-buffer-name-prefix (or title query))))
(header (org-ql-view--header-line-format
:buffers-files buffers-files :query query :title title))
;; Bind variables for `org-ql-view--display' to set.
(org-ql-view-buffers-files buffers-files)
(org-ql-view-query query)
(org-ql-view-sort sort)
(org-ql-view-narrow narrow)
(org-ql-view-super-groups super-groups)
(org-ql-view-title title))
(when super-groups
(let ((org-super-agenda-groups (cl-etypecase super-groups
(symbol (symbol-value super-groups))
(list super-groups))))
(setf strings (org-super-agenda--group-items strings))))
;;TODO no show view
;; insert buffer to coverto build readme
(message "build readme file:\n %s " (s-join "\n" strings))
;; (org-ql-view--display :buffer buffer :header header :string (s-join "\n" strings))
)))
(build-readme-view "readme")
|
1
2
3
4
5
6
7
8
9
10
11
| build readme file:
原则
TODO 诚信原则
TODO 成长原则
TODO 自我管理原则
基于 org-super-agenda 定义 agenda 模板
TODO 快速导出 agenda 清单模板 9d ago :@学习笔记:
TODO 每日安排 :@学习笔记:
TODO 每周安排 :@学习笔记:
|
基于 org-ql-select 获取查询结果,自定义 list-to-org 格式化结果内容
借助 org-ql-search 和 org-ql-view 能够实现
自定义保存 org-ql-views 模板
保存不同查询的模板,保存之后,可以通过 org-ql-view 命令,快速查看模板列表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| (defun org-build-readme-save ()
"create new view"
(interactive)
(let* ((name (read-string "Save view as: " org-ql-view-title))
;; Bind `org-ql-view-title' to the name that was read, in case
;; it's different, which `org-ql-view--plist' will pick up.
(org-ql-view-title name)
(plist (list :buffers-files (org-agenda-files)
:query '(todo "TODO")
:sort '(date priority)
:title "This week"
:super-groups '((:auto-parent t))
)))
(when (or (not (map-elt org-ql-views name nil))
(yes-or-no-p (format "Overwrite view \"%s\"?" name)))
(setf (map-elt org-ql-views name nil #'equal) plist)
(customize-set-variable 'org-ql-views org-ql-views)
(customize-mark-to-save 'org-ql-views))))
(org-build-readme-save)
|
实现 org-ql-select 支持 org-ql-views 模板中的 query 查询
org-readme-search
参考 org-ql-view 和 org-ql-search 简化搜索逻辑,实现基于 org-ql-select 方法实现自定义查询的效果。通过传入 org-ql-views 模板的名称,利用模板的 query
属性的查询条件。结果还是 list 格式。想要输出格式的内容,需要自定义方法 list-to-org
打印。list-to-org
将 org-ql-select 查询到的 elisp list 列表格式,转为 org 格式。
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
|
(defun list-to-org (list)
"Convert LIST to org-mode string."
(mapconcat
(lambda (elem)
(cond ((stringp elem) elem)
((keywordp elem) (concat ":" (symbol-name elem)))
((listp elem) (list-to-org elem))
(t (format "%S" elem))))
list
"\n"))
(defun org-readme-search (name)
"Choose and display the `org-ql-views' view NAME.
Interactively, prompt for NAME."
(let* ((result)
(view (alist-get name org-ql-views nil nil #'string=))
)
(-let* (((&plist :buffers-files :query :sort :narrow :super-groups :title) view)
(super-groups (cl-typecase super-groups
(symbol (symbol-value super-groups))
(list super-groups))))
(message "step 1:+++++++ %s" query)
(let* ((query (cl-etypecase query
(string (if (or (string-prefix-p "(" query)
(string-prefix-p "\"" query))
;; Read sexp query.
(read query)
;; Parse non-sexp query into sexp query.
(org-ql--query-string-to-sexp query)))
(list query)))
)
(message "step 2:_++++++_ %s" query)
(setq result (org-ql-select buffers-files query
:action '(list
;; (org-get-heading)
(buffer-substring-no-properties (point-at-bol) (point-at-eol))
)
:narrow narrow
:sort sort))
))
(message "reuslts : %s" (list-to-org result))
))
(org-readme-search "readme")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| reuslts : **** TODO 诚信原则
**** TODO 成长原则
**** TODO 自我管理原则
*** TODO [#A] 二象限模式[0/3] :habit:
*** TODO 健身模式 :habit:
*** TODO [#B] 工作模式 :habit:
*** TODO [#B] 阅读模式 :habit:
*** TODO [#B] 饭桌礼仪 :habit:
*** TODO [#B] 社交模式 :habit:
* TODO 思考最重要的事情撰写使命宣言 :blue:
* TODO [#B] 拓宽思路的过程 :anki:
** TODO 开发 auto-gpt emacs 插件
** TODO 捷径通过 emacsclient 命令,或 ifff 集成 Auto-GPT
**** TODO 待完善的模板
*** TODO ob-babel 导入第三库遇到的问题
**** TODO Macro: =org-ql-defpred=
**** TODO 每日安排
**** TODO 每周安排
**** TODO 快速导出 agenda 清单模板
**** TODO cowuoshi
**** TODO 开始计划
**** TODO 明天到家
|
替换 REAME.md 模块数据
在 README.md
中有固定模式,使用 elisp 处理围绕文本的内容替换为自己的内容。
1
2
3
| <!-- dairy starts -->
<!-- dairy ends -->
|
python 使用正则实现的替换方式 replace_chunk
:
1
2
3
4
5
6
7
8
9
| def replace_chunk(content, marker, chunk, inline=False):
r = re.compile(
r"<!\-\- {} starts \-\->.*<!\-\- {} ends \-\->".format(marker, marker),
re.DOTALL,
)
if not inline:
chunk = "\n{}\n".format(chunk)
chunk = "<!-- {} starts -->{}<!-- {} ends -->".format(marker, chunk, marker)
return r.sub(chunk, content)
|
将上述方法翻译成 elisp 语法实现
使用 temp-buffer 实现文本替换
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
| (defun build-readme-replace-chunk (content marker chunk &optional inline)
"Replace the chunk between <--- MARKER starts --> and
<!--- MARKER ends --> in CONTENT with CHUNK.
If INLINE is non-nil, do not add newlines around CHUNK."
(let* ((start (concat "<!-- " marker " starts -->"))
(end (concat "<!-- " marker " ends -->"))
(pattern (concat start "[^^]*?" end)))
(with-temp-buffer
(insert content)
(goto-char (point-min))
(if (re-search-forward pattern nil t)
(progn
(setq chunk (format "%s\n%s\n%s" start chunk end))
(replace-match chunk t t)
(buffer-string))
content))
))
(setq readme-content "
Hello
<!-- chunk starts -->
world
<!-- chunk ends -->
!")
(build-readme-replace-chunk readme-content "chunk" "there")
|
1
2
3
4
5
6
|
Hello
<!-- chunk starts -->
there
<!-- chunk ends -->
!
|
它的工作原理如下:
- 它定义了
start
和 end
字符串,即起始和结束标记。 - 它定义了一个正则表达式模式,能匹配从起始标记到结束标记的内容。
- 它创建一个临时缓冲区并插入
content
内容。 - 它向前搜索第一个模式匹配。
- 如果找到匹配,它用
chunk
替换匹配内容,并可选地添加换行符。 - 然后它返回临时缓冲区的内容,替换了匹配内容。
- 否则它只返回原始的
content
不变。
使用 repace-regexp-in-string 方法替换
这是将 Python 方法 replace_chunk
翻译成 Emacs Lisp 语法的示例:
1
2
3
4
5
6
7
8
9
| (defun replace-chunk-regex (content marker chunk &optional inline)
"Replace content between markers denoted by MARKER with CHUNK.
If INLINE is non-nil, do not add extra newline characters around CHUNK."
(let ((regex (concat "<!-- " marker " starts -->[^^]*<!-- " marker " ends -->")))
(setq chunk (if inline chunk (concat "\n" chunk "\n")))
(setq chunk (concat "<!-- " marker " starts -->" chunk "<!-- " marker " ends -->"))
(replace-regexp-in-string regex chunk content t t)))
(replace-chunk-regex readme-content "chunk" "there")
|
1
2
3
4
5
6
|
Hello
<!-- chunk starts -->
there
<!-- chunk ends -->
!
|
这个函数首先定义了一个正则表达式来匹配和找到要替换的文本。然后,根据 inline
参数,添加或去掉块的前后额外换行符。最后,使用 Emacs Lisp 内置函数 =replace-regexp-in-string=,将找到的文本块替换为新的文本块。
替换文本之后保存到 README.md 文件中
使用 elisp 脚本替换 README.md 文件中{{内容}} 格式的内容为变量A的值,然后保存文件。
定义一个入参 readmeFile
在 temp-buffer 中操作该文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| (defun build-readme-replace (readmeFile marker chunk &optional inline)
"Replace the chunk between <--- MARKER starts --> and
<!--- MARKER ends --> in CONTENT with CHUNK.
If INLINE is non-nil, do not add newlines around CHUNK."
(let* ((start (concat "<!-- " marker " starts -->"))
(end (concat "<!-- " marker " ends -->"))
(pattern (concat start "[^^]*?" end)))
(with-temp-file readmeFile
(insert-file-contents readmeFile)
;;(insert-buffer-substring (current-buffer))
(goto-char (point-min))
(if (re-search-forward pattern nil t)
(progn
(setq chunk (format "%s\n%s\n%s" start chunk end))
(replace-match chunk t t)
(write-file readmeFile))
))
))
(build-readme-replace "~/hsg/iNotes/README.md" "dairy" " test text")
|
这个函数的工作方式如下:
- 它提示用户输入要保存的文件名,并将其存储在
readmeFile
变量中 - 它获取当前缓冲区,并将其存储在
buffer
变量中 - 它使用
with-temp-buffer
宏创建一个新的临时缓冲区 - 它使用
insert-file-contents
将 readmeFile
的内容插入临时缓冲区 - 它使用
write-file
将临时缓冲区内容写入 readmeFile
文件
这里希望直接保存内容到文件中,不需要提示,在使用 save-buffer
方式保存时,会显示一条确认消息,确认已将缓冲区保存到指定的文件名