aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/org-config.el66
-rw-r--r--modules/org-faces-config.el129
2 files changed, 195 insertions, 0 deletions
diff --git a/modules/org-config.el b/modules/org-config.el
index 26b5f0aa5..783109349 100644
--- a/modules/org-config.el
+++ b/modules/org-config.el
@@ -132,6 +132,72 @@ edge, less the tag width.")
(add-hook 'org-mode-hook #'cj/org--manage-tag-display-prop)
(font-lock-add-keywords 'org-mode cj/org-right-align-tags-keyword t)
+;; ------------------------ Org Table Header Highlighting --------------------
+;; Org faces the whole table -- header rows included -- with `org-table'; it has
+;; no in-buffer header-row face. `org-table-header' is used only by the sticky
+;; header line of `org-table-header-line-mode'. This font-lock keyword prepends
+;; `org-table-header' onto a table's header rows (the non-hline rows above its
+;; first hline), so the themed header style lands in place in the buffer.
+
+(declare-function org-at-table-p "org")
+(declare-function org-at-table-hline-p "org")
+(declare-function org-table-begin "org-table")
+(declare-function org-table-end "org-table")
+
+(defcustom cj/org-fontify-table-headers t
+ "When non-nil, highlight org table header rows with the `org-table-header' face.
+A header row is a non-hline table row above its table's first hline. Org has no
+in-buffer header-row face of its own, so this supplies one, deferring its whole
+appearance to the themed `org-table-header' face."
+ :type 'boolean
+ :group 'org)
+
+(defun cj/--org-table-first-hline-position ()
+ "Return the start position of the first hline in the table at point, or nil.
+Point must be inside an org table."
+ (save-excursion
+ (let ((end (org-table-end))
+ (found nil))
+ (goto-char (org-table-begin))
+ (while (and (not found) (< (point) end))
+ (when (org-at-table-hline-p)
+ (setq found (line-beginning-position)))
+ (forward-line 1))
+ found)))
+
+(defun cj/--org-table-header-row-p ()
+ "Return non-nil if the line at point is a header row of its org table.
+A header row is a non-hline table row positioned above the table's first hline.
+A table with no hline has no header rows."
+ (and (org-at-table-p)
+ (not (org-at-table-hline-p))
+ (let ((hline (cj/--org-table-first-hline-position)))
+ (and hline (< (line-beginning-position) hline)))))
+
+(defun cj/--org-fontify-table-header-matcher (limit)
+ "Font-lock matcher for the next org table header row before LIMIT.
+Returns non-nil when a header row is found, with match group 0 spanning the
+whole row line."
+ (let (beg end found)
+ (while (and (not found)
+ (re-search-forward "^[ \t]*|.*$" limit t))
+ (setq beg (match-beginning 0)
+ end (match-end 0))
+ (save-excursion
+ (goto-char beg)
+ (when (cj/--org-table-header-row-p)
+ (setq found t))))
+ (when found
+ (set-match-data (list beg end))
+ t)))
+
+(defconst cj/org-table-header-keyword
+ '((cj/--org-fontify-table-header-matcher (0 'org-table-header prepend)))
+ "Font-lock keyword prepending `org-table-header' onto org table header rows.")
+
+(when cj/org-fontify-table-headers
+ (font-lock-add-keywords 'org-mode cj/org-table-header-keyword t))
+
;; ----------------------------- Org TODO Settings ---------------------------
(defun cj/org-todo-settings ()
diff --git a/modules/org-faces-config.el b/modules/org-faces-config.el
new file mode 100644
index 000000000..e0dfa83fd
--- /dev/null
+++ b/modules/org-faces-config.el
@@ -0,0 +1,129 @@
+;;; org-faces-config.el --- Custom faces for the org agenda header row -*- lexical-binding: t; coding: utf-8; -*-
+;; author Craig Jennings <c@cjennings.net>
+
+;;; Commentary:
+;;
+;; Layer: 2 (Core UX).
+;; Category: C/S.
+;; Load shape: eager.
+;; Eager reason: the faces must exist before org renders the agenda.
+;; Top-level side effects: defines the org-faces-* faces; sets
+;; org-todo-keyword-faces and org-priority-faces once org loads.
+;; Runtime requires: none (org wiring is deferred via with-eval-after-load).
+;;
+;; Custom faces for the agenda "header row" -- the TODO keyword and the
+;; priority cookie -- so each keyword and each priority is its own themeable
+;; element rather than sharing org's built-in org-todo / org-done / org-priority.
+;; They are named org-faces-* (not org-*) so it's obvious they are this config's
+;; layer, not built-in org. Each carries a real default color so the agenda is
+;; legible on any theme; a theme (e.g. one generated by theme-studio's
+;; "org-faces" app) overrides them. The -dim variants are the dimmed colors
+;; auto-dim-config.el remaps these to in non-selected windows, so keywords stay
+;; recognizable when a window recedes.
+;;
+;; Note: this file is org-faces-CONFIG, not org-faces -- org ships its own
+;; `org-faces' feature (lisp/org/org-faces.el), so reusing that name would
+;; shadow org's face definitions on the load path.
+
+;;; Code:
+
+(eval-when-compile (require 'org))
+
+(defgroup org-faces-config nil
+ "Custom faces for the org agenda header row (keywords and priorities)."
+ :group 'org)
+
+;; --------------------------- Keyword faces (focused) -------------------------
+
+(defface org-faces-todo '((t (:foreground "#8fbf73" :weight bold)))
+ "Face for the TODO keyword." :group 'org-faces-config)
+(defface org-faces-project '((t (:foreground "#7a9abe" :weight bold)))
+ "Face for the PROJECT keyword." :group 'org-faces-config)
+(defface org-faces-doing '((t (:foreground "#e8c668" :weight bold)))
+ "Face for the DOING keyword." :group 'org-faces-config)
+(defface org-faces-waiting '((t (:foreground "#c9b08a" :weight bold)))
+ "Face for the WAITING keyword." :group 'org-faces-config)
+(defface org-faces-verify '((t (:foreground "#d98a5a" :weight bold)))
+ "Face for the VERIFY keyword." :group 'org-faces-config)
+(defface org-faces-stalled '((t (:foreground "#9a8fb0" :weight bold)))
+ "Face for the STALLED keyword." :group 'org-faces-config)
+(defface org-faces-delegated '((t (:foreground "#7fc0a8" :weight bold)))
+ "Face for the DELEGATED keyword." :group 'org-faces-config)
+(defface org-faces-failed '((t (:foreground "#d05a5a" :weight bold)))
+ "Face for the FAILED keyword." :group 'org-faces-config)
+(defface org-faces-done '((t (:foreground "#6f7a82" :weight bold)))
+ "Face for the DONE keyword." :group 'org-faces-config)
+(defface org-faces-cancelled '((t (:foreground "#6f7a82" :weight bold :strike-through t)))
+ "Face for the CANCELLED keyword." :group 'org-faces-config)
+
+;; -------------------------- Priority faces (focused) -------------------------
+
+(defface org-faces-priority-a '((t (:foreground "#7aa0d0" :weight bold)))
+ "Face for the [#A] priority cookie." :group 'org-faces-config)
+(defface org-faces-priority-b '((t (:foreground "#e8c668")))
+ "Face for the [#B] priority cookie." :group 'org-faces-config)
+(defface org-faces-priority-c '((t (:foreground "#8fbf73")))
+ "Face for the [#C] priority cookie." :group 'org-faces-config)
+(defface org-faces-priority-d '((t (:foreground "#8a8a8a")))
+ "Face for the [#D] priority cookie." :group 'org-faces-config)
+
+;; ----------------------------- Keyword faces (dim) ---------------------------
+;; auto-dim-config.el remaps the focused faces above to these in non-selected
+;; windows; a darker shade of the same hue keeps the keyword recognizable.
+
+(defface org-faces-todo-dim '((t (:foreground "#5f7a4d" :weight bold)))
+ "Dimmed TODO keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-project-dim '((t (:foreground "#4f6680" :weight bold)))
+ "Dimmed PROJECT keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-doing-dim '((t (:foreground "#9a8544" :weight bold)))
+ "Dimmed DOING keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-waiting-dim '((t (:foreground "#87745c" :weight bold)))
+ "Dimmed WAITING keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-verify-dim '((t (:foreground "#8f5a3c" :weight bold)))
+ "Dimmed VERIFY keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-stalled-dim '((t (:foreground "#665e75" :weight bold)))
+ "Dimmed STALLED keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-delegated-dim '((t (:foreground "#547d6c" :weight bold)))
+ "Dimmed DELEGATED keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-failed-dim '((t (:foreground "#8a3c3c" :weight bold)))
+ "Dimmed FAILED keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-done-dim '((t (:foreground "#4a5158" :weight bold)))
+ "Dimmed DONE keyword for non-selected windows." :group 'org-faces-config)
+(defface org-faces-cancelled-dim '((t (:foreground "#4a5158" :weight bold :strike-through t)))
+ "Dimmed CANCELLED keyword for non-selected windows." :group 'org-faces-config)
+
+;; ---------------------------- Priority faces (dim) ---------------------------
+
+(defface org-faces-priority-a-dim '((t (:foreground "#4f6a8a" :weight bold)))
+ "Dimmed [#A] priority cookie for non-selected windows." :group 'org-faces-config)
+(defface org-faces-priority-b-dim '((t (:foreground "#9a8544")))
+ "Dimmed [#B] priority cookie for non-selected windows." :group 'org-faces-config)
+(defface org-faces-priority-c-dim '((t (:foreground "#5f7a4d")))
+ "Dimmed [#C] priority cookie for non-selected windows." :group 'org-faces-config)
+(defface org-faces-priority-d-dim '((t (:foreground "#5a5a5a")))
+ "Dimmed [#D] priority cookie for non-selected windows." :group 'org-faces-config)
+
+;; ---------------------------------- Wiring -----------------------------------
+;; Map each keyword string and priority char to its face once org is loaded, so
+;; the values stick regardless of when org initializes.
+
+(with-eval-after-load 'org
+ (setq org-todo-keyword-faces
+ '(("TODO" . org-faces-todo)
+ ("PROJECT" . org-faces-project)
+ ("DOING" . org-faces-doing)
+ ("WAITING" . org-faces-waiting)
+ ("VERIFY" . org-faces-verify)
+ ("STALLED" . org-faces-stalled)
+ ("DELEGATED" . org-faces-delegated)
+ ("FAILED" . org-faces-failed)
+ ("DONE" . org-faces-done)
+ ("CANCELLED" . org-faces-cancelled)))
+ (setq org-priority-faces
+ '((?A . org-faces-priority-a)
+ (?B . org-faces-priority-b)
+ (?C . org-faces-priority-c)
+ (?D . org-faces-priority-d))))
+
+(provide 'org-faces-config)
+;;; org-faces-config.el ends here