379 lines
13 KiB
EmacsLisp
379 lines
13 KiB
EmacsLisp
;;; init-org.el --- org-mode config -*- lexical-binding: t -*-
|
||
|
||
;;; Commentary:
|
||
|
||
;;; Code:
|
||
|
||
(use-package org)
|
||
|
||
;; org-contrib 和 org-checklist
|
||
(use-package org-contrib
|
||
:straight (org-contrib :type git :host github :repo "emacsmirror/org-contrib"
|
||
:depth 1))
|
||
|
||
(add-hook 'org-mode-hook
|
||
(lambda ()
|
||
(require 'org-checklist nil t)))
|
||
|
||
;; org-download
|
||
(use-package org-download
|
||
:config
|
||
(when *is-mac*
|
||
(setq org-download-image-dir "./images")
|
||
(unless (file-exists-p "./images")
|
||
(make-directory "./images" t))
|
||
(setq org-download-link-format "[[file:%s]]\n")
|
||
(setq org-download-timestamp "%Y%m%d_%H%M%S_")
|
||
(setq org-download-dragndrop t)
|
||
(setq org-image-actual-width nil)
|
||
(setq org-download-image-html-width 600)
|
||
(add-hook 'org-mode-hook 'org-display-inline-images)))
|
||
|
||
;; org-journal
|
||
(use-package org-journal
|
||
:config
|
||
(setq org-journal-file-format "%Y-%m-%d.org"
|
||
org-journal-file-header "#+category: journal\n#+STARTUP: content\n\n"
|
||
org-journal-time-prefix "\n*** TODO "
|
||
org-journal-time-format "%m-%d \n<%Y-%m-%d %a %H:%M:%S>\n"
|
||
org-journal-hide-entries-p t
|
||
org-journal-date-format (lambda (time)
|
||
(format "* %s %s\n\n[%s]\n"
|
||
(format-time-string "%Y-%m-%d" time)
|
||
(format-time-string "%a" time)
|
||
(format-time-string "%Y-%m-%d %a" time)))
|
||
org-journal-carryover-items "TODO=\"TODO\"|TODO=\"STARTED\"|TODO=\"WAITING\"|TODO=\"SOMEDAY\"")
|
||
|
||
(when *is-windows*
|
||
(setq org-journal-dir "h:/emacs/my-org-daily/"))
|
||
|
||
(when *is-mac*
|
||
(setq org-journal-dir "~/org/my-org-daily/"))
|
||
|
||
(add-hook 'org-journal-mode-hook 'font-lock-update)
|
||
|
||
(defun my/journal-setup-new-day ()
|
||
(save-excursion
|
||
(org-back-to-heading)
|
||
(let ((day-end (save-excursion (org-end-of-subtree) (point))))
|
||
(unless (search-forward "** daily" day-end t)
|
||
(goto-char day-end)
|
||
(insert "\n\n** daily\n\n\n")
|
||
(insert "** tasks\n")))))
|
||
|
||
(add-hook 'org-journal-after-header-create-hook #'my/journal-setup-new-day)
|
||
|
||
(defun my/journal-position-cursor-after-time ()
|
||
(when (derived-mode-p 'org-journal-mode)
|
||
(org-back-to-heading)
|
||
(end-of-line)))
|
||
|
||
(add-hook 'org-journal-after-entry-create-hook #'my/journal-position-cursor-after-time)
|
||
|
||
(defun org-journal-open-entry-for-editing (&optional event)
|
||
"Open journal entry for the date under cursor in calendar for editing."
|
||
(interactive
|
||
(list last-nonmenu-event))
|
||
(let* ((date (calendar-cursor-to-date t event))
|
||
(time (org-journal--calendar-date->time date))
|
||
(org-journal-file (org-journal--get-entry-path time)))
|
||
(if (file-exists-p org-journal-file)
|
||
(progn
|
||
(funcall org-journal-find-file org-journal-file)
|
||
(unless (org-journal--daily-p)
|
||
(org-journal--goto-entry date)))
|
||
(message "No journal entry for this date."))))
|
||
|
||
(defun my/org-journal-mark-old-todos-as-comment (entries)
|
||
"Change TODO entry's TODO keyword to COMMENT after carryover."
|
||
(save-excursion
|
||
(let* ((todo-keywords (cl-remove-if
|
||
(lambda (kw) (member kw org-done-keywords))
|
||
org-todo-keywords-1))
|
||
(todo-pattern (when todo-keywords
|
||
(regexp-opt todo-keywords 'words))))
|
||
(when todo-pattern
|
||
(mapc (lambda (entry)
|
||
(let ((start (car entry))
|
||
(end (set-marker (make-marker) (cadr entry))))
|
||
(goto-char start)
|
||
(while (re-search-forward (concat "^\\(\\*+ \\)" todo-pattern) end t)
|
||
(replace-match "\\1COMMENT" t nil))
|
||
(set-marker end nil)))
|
||
(reverse entries))))
|
||
(save-buffer)))
|
||
|
||
(setq org-journal-handle-old-carryover-fn #'my/org-journal-mark-old-todos-as-comment))
|
||
|
||
;; 在org-journal加载cns-mode
|
||
(add-hook 'org-journal-mode-hook
|
||
(lambda ()
|
||
(global-cns-mode-enable-in-buffer)))
|
||
|
||
|
||
;; 一些随org-mode加载的基础配置
|
||
(add-hook 'org-mode-hook
|
||
(lambda ()
|
||
(require 'org-tempo)
|
||
(org-indent-mode)
|
||
(setq-local electric-pair-inhibit-predicate
|
||
(lambda (char)
|
||
(or (eq char ?<)
|
||
(electric-pair-default-inhibit char))))))
|
||
|
||
(with-eval-after-load 'org
|
||
(require 'org-download)
|
||
(setq org-return-follows-link t))
|
||
|
||
;; 预加载,防止windows的org-mode加载卡顿
|
||
(if *is-windows*
|
||
(progn (run-with-idle-timer
|
||
0.5 nil
|
||
(lambda ()
|
||
(with-temp-buffer
|
||
(org-mode)
|
||
(org-mode))
|
||
(message "Org preloaded")))))
|
||
|
||
;; 设置标题字体大小
|
||
;; (dolist (face org-level-faces)
|
||
;; (set-face-attribute face nil :height 1))
|
||
|
||
;; log默认放入drawer中
|
||
(setq org-log-done t)
|
||
(setq org-log-into-drawer t)
|
||
|
||
;; 将系统时间(星期)设为英文,防止中文乱码
|
||
(setq system-time-locale "C")
|
||
|
||
;; 设置todo关键字
|
||
(setq org-todo-keywords
|
||
(quote ((sequence "TODO(t)" "STARTED(s)" "|" "DONE(d!/!)")
|
||
(sequence "WAITING(w@/!)" "SOMEDAY(S)" "|" "CANCELLED(c@/!)" "MEETING(m)" "PHONE(p)"))))
|
||
|
||
;; agenda相关
|
||
(global-set-key (kbd "C-c a") 'org-agenda)
|
||
|
||
(when *is-windows*
|
||
(setq org-agenda-files '("h:/emacs/hugo/this-is-my-blog/all-blog.org"
|
||
"h:/emacs/my-org-daily/"
|
||
"h:/emacs/my-org-note/")))
|
||
(when *is-mac*
|
||
(setq org-agenda-files '("~/org/my-org-daily"
|
||
"~/org/my-org-note/")))
|
||
|
||
(setq org-agenda-span 'day)
|
||
|
||
(setq org-default-priority ?D)
|
||
|
||
(setq org-agenda-skip-function-global
|
||
'(org-agenda-skip-entry-if 'todo '("COMMENT")))
|
||
|
||
;; org-ql
|
||
(use-package org-ql)
|
||
|
||
(setq org-ql-views
|
||
'(("tags: emacs category:note"
|
||
:buffers-files org-agenda-files
|
||
:query (and (tags "emacs") (category "note"))
|
||
:sort date)
|
||
|
||
("tags: org"
|
||
:buffers-files org-agenda-files
|
||
:query (tags "org"))
|
||
|
||
("all important todos"
|
||
:buffers-files org-agenda-files
|
||
:query (and (not (done)) (todo))
|
||
:super-groups
|
||
((:name "priority = A" :priority "A")
|
||
(:name "priority = B" :priority "B")
|
||
(:name "started" :todo "STARTED")
|
||
(:name "someday" :todo "SOMEDAY")))))
|
||
|
||
;; org-super-agenda 配置
|
||
(use-package org-super-agenda
|
||
:config
|
||
(org-super-agenda-mode))
|
||
|
||
;; 自定义已完成任务视图
|
||
(setq org-agenda-custom-commands
|
||
'(("pa" "Priority >= A"
|
||
((tags-todo "+PRIORITY=\"A\"")))
|
||
("pb" "Priority >= B"
|
||
((tags-todo "+PRIORITY<=\"B\"")))
|
||
("pc" "Priority >= C"
|
||
((tags-todo "+PRIORITY<=\"C\"")))
|
||
("qd" "TODO entries sort by closed time"
|
||
((org-ql-block '(closed)
|
||
((org-ql-block-header "Closed TODOs")
|
||
(org-ql-sort '(closed))
|
||
(org-super-agenda-groups
|
||
'((:auto-ts t)))))))
|
||
("qS" "SOMEDAYs"
|
||
((org-ql-block '(todo "SOMEDAY")
|
||
((org-ql-block-header "SOMEDAYs")
|
||
(org-ql-sort '(start-time))
|
||
(org-super-agenda-groups
|
||
'((:auto-ts t)))))))
|
||
("qs" "STARTEDs"
|
||
((org-ql-block '(todo "STARTED")
|
||
((org-ql-block-header "STARTEDs")
|
||
(org-ql-sort '(start-time))
|
||
(org-super-agenda-groups
|
||
'((:auto-ts t)))))))
|
||
("qt" "TODOs sort by start time"
|
||
((org-ql-block '(todo)
|
||
((org-ql-block-header "TODOs")
|
||
(org-ql-sort '(start-time))
|
||
(org-super-agenda-groups
|
||
'((:auto-ts t)))))))))
|
||
|
||
(setq org-tag-alist '(
|
||
("scheme" . ?s)
|
||
("emacs" . ?e)
|
||
("org" . ?o)
|
||
("linux" . ?l)
|
||
("computer-stuff" . ?c)
|
||
("philosophy" . ?p)
|
||
("game" . ?g)
|
||
("gamestudies" . ?G)
|
||
))
|
||
|
||
;; (setq org-tags-exclude-from-inheritance '())
|
||
|
||
;; capture模板
|
||
(defvar my-org-capture-temp-title "" "Temporarily store the org-capture filename.")
|
||
(setq org-capture-templates
|
||
'(("n" "New Note" plain
|
||
(file (lambda ()
|
||
(setq my-org-capture-temp-title (read-string "Filename (without .org): "))
|
||
(expand-file-name (if *is-windows*
|
||
(format "%s/%s-%s.org"
|
||
"h:/emacs/my-org-note/"
|
||
(format-time-string "%Y%m%d")
|
||
my-org-capture-temp-title)
|
||
(format "%s/%s-%s.org"
|
||
"~/org/my-org-note/"
|
||
(format-time-string "%Y%m%d")
|
||
my-org-capture-temp-title)))))
|
||
"#+title: %(symbol-value 'my-org-capture-temp-title)
|
||
#+date: %U
|
||
#+category: %^{Category|none|project|book|article|note|insight|fun}
|
||
|
||
* %(symbol-value 'my-org-capture-temp-title) %^g
|
||
:PROPERTIES:
|
||
:END:
|
||
%U
|
||
|
||
%?
|
||
|
||
|
||
# Local Variables:
|
||
# org-use-tag-inheritance: nil
|
||
# End:
|
||
"
|
||
:jump-to-captured t)))
|
||
|
||
(global-set-key (kbd "C-c o c") 'org-capture)
|
||
|
||
;; 批量调整表格
|
||
(defun org-table-align-all ()
|
||
"Ajust all table in current buffer."
|
||
(interactive)
|
||
(save-excursion
|
||
(goto-char (point-min))
|
||
(org-table-map-tables 'org-table-align t)))
|
||
|
||
;; hugo
|
||
|
||
;; 安装ox-hugo
|
||
(use-package ox-hugo)
|
||
|
||
|
||
(with-eval-after-load 'org-capture
|
||
(defun org-hugo-new-subtree-post-capture-template ()
|
||
"Returns `org-capture' template string for new Hugo post.
|
||
See `org-capture-templates' for more information."
|
||
(let* ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
|
||
(fname (org-hugo-slug title)))
|
||
(mapconcat #'identity
|
||
`(
|
||
,(concat "* TODO " title)
|
||
":PROPERTIES:"
|
||
,(concat ":EXPORT_FILE_NAME: " fname)
|
||
":END:"
|
||
"\n\n") ;Place the cursor here finally
|
||
"\n")))
|
||
(if *is-windows*
|
||
(add-to-list 'org-capture-templates
|
||
'("h" ;`org-capture' binding + h
|
||
"Hugo post"
|
||
entry
|
||
;; It is assumed that below file is present in `org-directory'
|
||
;; and that it has a "Blog Ideas" heading. It can even be a
|
||
;; symlink pointing to the actual location of all-posts.org!
|
||
(file+headline "h:/emacs/hugo/this-is-my-blog/all-blog.org" "Blog Ideas")
|
||
(function org-hugo-new-subtree-post-capture-template)))
|
||
(add-to-list 'org-capture-templates
|
||
'("h" ;`org-capture' binding + h
|
||
"Hugo post"
|
||
entry
|
||
;; It is assumed that below file is present in `org-directory'
|
||
;; and that it has a "Blog Ideas" heading. It can even be a
|
||
;; symlink pointing to the actual location of all-posts.org!
|
||
(file+headline "~/org/hugo/this-is-my-blog/all-blog.org" "Blog Ideas")
|
||
(function org-hugo-new-subtree-post-capture-template)))))
|
||
|
||
;; 快速部署
|
||
(defun my-blog-publish ()
|
||
"Publish blog , git add/commit/push , generate commit message."
|
||
(interactive)
|
||
(let* ((blog-dir (if *is-windows*
|
||
"h:/emacs/hugo/this-is-my-blog/"
|
||
"~/org/hugo/this-is-my-blog/"))
|
||
(timestamp (format-time-string "%Y-%m-%d %H:%M:%S"))
|
||
(default-directory blog-dir))
|
||
|
||
;; save buffer
|
||
(save-buffer)
|
||
|
||
;; git add
|
||
(message "Adding files...")
|
||
(eshell-command "git add .")
|
||
|
||
;; git commit with timestamp
|
||
(message "Committing: %s" timestamp)
|
||
(eshell-command (format "git commit -m \"Update: %s\"" timestamp))
|
||
|
||
;; git push
|
||
(message "Pushing to remote...")
|
||
(let ((exit-code (eshell-command "git push")))
|
||
(if (eq exit-code t)
|
||
(message "Push may have failed or nothing to push, check *Messages* buffer for details")
|
||
(message "Push successfully %s" timestamp)))))
|
||
|
||
(global-set-key (kbd "C-c o p") 'my-blog-publish)
|
||
|
||
;; 预览博客
|
||
(defun my-blog-preview ()
|
||
"Save and preview blog."
|
||
(interactive)
|
||
(save-buffer)
|
||
(let ((default-directory (if *is-windows*
|
||
"h:/emacs/hugo/this-is-my-blog/"
|
||
"~/org/hugo/this-is-my-blog/")))
|
||
;; 启动 Hugo server
|
||
(start-process "hugo-server" "*hugo-server*"
|
||
"hugo" "server" "-D" "--bind" "0.0.0.0" "--port" "1313")
|
||
;; 等待服务器启动
|
||
(sleep-for 2)
|
||
;; 自动打开浏览器
|
||
(browse-url "http://localhost:1313")))
|
||
|
||
(global-set-key (kbd "C-c o v") 'my-blog-preview)
|
||
|
||
(provide 'init-org)
|
||
|
||
;;; init-org.el ends here
|