如题。今天第一次试用这种 AI 进行辅助编程。不然的话,以我的代码水平,写一个这种函数,可能得磨蹭一天时间了。
我试了 aider.el, 但一来没有高亮,二来似乎没法折行,体验比终端使用难免会差一些,所以体感上还是有 vterm 配合 aider 终端会好一些。
说回代码。功能其实很简单,就是写了一个 TODO 项之后,运行这个函数,就会自动将其中的时间解析成一个 schedule,从而可以在 agenda 中查看。
支持的句式举例:
- TODO 周一10点到11点
- TODO 星期二九点到20点
- TODO Wednesday 6点32分 to 七点26
;;; org-time-parser.el --- Automatic time parsing for org-mode headings -*- lexical-binding: t; -*-
;;; package --- Summary
;;; Commentary:
;;; This is a function used to automatically parse and set SCHEDULED dates/times from TODO headings.
;;; Code:
(require 'org)
(defun my/org-parse-time-from-title ()
"Automatically parse and set SCHEDULED dates/times from TODO headings.
Supports:
- Relative dates: today/今天, tomorrow/明天, day after tomorrow/后天
- Weekdays: Monday-Sunday, 周一-周日, 星期一-星期日
- Time ranges: 14h30-16h, 11点15分-13点, 9:00-10:30
- Automatic date fallback to today when no date specified"
(interactive)
(when (org-get-todo-state)
(let ((title (org-get-heading))
(today (format-time-string "<%Y-%m-%d %a>" (current-time)))
time-range)
;; Check for date keywords
(let ((date (cond
((string-match-p "\\<today\\>\\|\\<今天\\>" title)
(current-time))
((string-match-p "\\<tomorrow\\>\\|\\<明天\\>" title)
(time-add (current-time) (* 60 60 24 1)))
((string-match-p "\\<day after tomorrow\\>\\|\\<后天\\>" title)
(time-add (current-time) (* 60 60 24 2)))
((string-match "\\<\\(周[一二三四五六日]\\|星期[一二三四五六日]\\|Monday\\|Tuesday\\|Wednesday\\|Thursday\\|Friday\\|Saturday\\|Sunday\\)\\>" title)
(let* ((chn-weekday (match-string 1 title))
(translations '(("周一" . "Monday") ("周二" . "Tuesday") ("周三" . "Wednesday")
("周四" . "Thursday") ("周五" . "Friday") ("周六" . "Saturday")
("周日" . "Sunday") ("星期一" . "Monday") ("星期二" . "Tuesday")
("星期三" . "Wednesday") ("星期四" . "Thursday") ("星期五" . "Friday")
("星期六" . "Saturday") ("星期日" . "Sunday")))
(eng-weekday (or (cdr (assoc chn-weekday translations)) chn-weekday)))
(date-to-time (org-read-date nil nil (concat "next " eng-weekday)))))
(t (current-time))))) ; Default to today if no date specified
(when date
(setq today (format-time-string "<%Y-%m-%d %a>" date))
;; Check for time range
;; TODO conditions like 明天早上五点到明天晚上8点,明天早上8点到后天下午20点,明天打游戏到晚上8点,两天后睡觉
(when (string-match "\\([0-9]+\\|[一二三四五六七八九十]+\\)\\(?:点\\([0-9]+\\|[一二三四五六七八九十]+\\)?分?\\|h\\([0-9]+\\|[一二三四五六七八九十]+\\)?\\)\\(?:\\s-*\\(?:到\\|to\\|-\\)\\s-*\\([0-9]+\\|[一二三四五六七八九十]+\\)\\(?:点\\([0-9]+\\|[一二三四五六七八九十]+\\)?分?\\|h\\([0-9]+\\|[一二三四五六七八九十]+\\)?\\)\\)?" title)
(let* ((chn-number-map '(("一" . "1") ("二" . "2") ("三" . "3") ("四" . "4")
("五" . "5") ("六" . "6") ("七" . "7") ("八" . "8")
("九" . "9") ("十" . "10") ("两" . "2")))
(to-arabic (lambda (s)
(mapconcat (lambda (c) (or (cdr (assoc (string c) chn-number-map)) (string c)))
(string-to-list s) "")))
(start-hr (funcall to-arabic (match-string 1 title)))
(start-min (funcall to-arabic (or (match-string 2 title) (match-string 3 title) "00")))
(end-hr (and (match-string 4 title) (funcall to-arabic (match-string 4 title))))
(end-min (and end-hr (funcall to-arabic (or (match-string 5 title) (match-string 6 title) "00")))))
(setq time-range (if (and end-hr end-min)
(format " %s:%s-%s:%s"
start-hr (if (string-empty-p start-min) "00" start-min)
end-hr (if (string-empty-p end-min) "00" end-min))
(format " %s:%s"
start-hr (if (string-empty-p start-min) "00" start-min))))))
;; (evil-open-below 1)
(org-entry-put (point) "SCHEDULED"
(concat today (or time-range ""))))))))
;; Automatically run parser after heading creation
;; (add-hook 'org-capture-after-finalize-hook #'my/org-parse-time-from-title)
;; (add-hook 'org-roam-post-node-insertion-hook #'my/org-parse-time-from-title)
;; FIXME These hooks will be executed in the buffer which calls the capture, not the buffer where the node is inserted.
(provide 'org-time-parser)
;;; org-time-parser.el ends here