From 0aab3875c96167234ef00487119116ef69c9ab21 Mon Sep 17 00:00:00 2001 From: Gerard Vermeulen Date: Thu, 22 Jun 2023 17:34:55 +0200 Subject: [PATCH] Work on introspection for better `org-columns' support --- README.org | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/README.org b/README.org index 15ad2de..9d9ada9 100644 --- a/README.org +++ b/README.org @@ -2172,7 +2172,8 @@ list detailing and motivating each listing: ol-info org-id org-protocol - org-tempo))) + org-tempo) + org-use-property-inheritance t)) #+end_src #+caption[Setup =org-babel=]: @@ -2716,14 +2717,13 @@ Set ~org-html-style-default~ option to add =HTML+CSS+JS= for ~mhtml-mode~: *** Org introspection :PROPERTIES: :CUSTOM_ID: sec:org-introspection -:header-args:emacs-lisp: :exports code :tangle no :END: #+caption[Find valid entries for =org-babel-load-languages=]: #+caption: Find valid entries for =org-babel-load-languages=. #+name: lst:valid-org-babel-load-languages-entries #+header: :wrap "src emacs-lisp :results silent :tangle no" -#+begin_src emacs-lisp :exports both :results value pp +#+begin_src emacs-lisp :exports both :results value pp :tangle no (defun all-org-babel-execute-fns () "Find `ob-LANGUAGE' files in Org defining `org-babel-execute:LANGUAGE'. @@ -2803,7 +2803,7 @@ Return a list of items where the filename is the `car' of each item and the #+caption: Find active Org Babel languages. #+name: lst:org-babel-active-languages #+header: :wrap "src emacs-lisp :results silent :tangle no" -#+begin_src emacs-lisp :exports both :results value pp +#+begin_src emacs-lisp :exports both :results value pp :tangle no (defun org-babel-active-languages () (let ((result '("conf" "text" "toml"))) (mapatoms @@ -2851,6 +2851,83 @@ Return a list of items where the filename is the `car' of each item and the ("toml")) #+end_src +#+caption[Jump to an Org =headline=]: +#+caption: Jump to an Org =headline=. A ~mapcan~ filter works on the +#+caption: result of ~cl-loop~ and ~collect~ to return a list containing +#+caption: the position and text of all matching headlines. +#+name: lst:jump-org-headline +#+begin_src emacs-lisp :results silent +(with-eval-after-load 'org-element + (defun jump-org-headline (target) + "Jump to the first org-mode headline matching TARGET." + (interactive "sTarget: ") + (if (derived-mode-p 'org-mode) + (when-let + ((ok (mapcan + (lambda (x) (when (car x) (cdr x))) + (cl-loop + for (here . text) + in (org-element-map (org-element-parse-buffer) 'headline + (lambda (hl) + (cons (org-element-property :begin hl) + (org-element-property :raw-value hl)))) + collect (list (string= target text) here text)))) + (n (/ (length ok) 2))) + (goto-char (car ok)) + (when (> n 1) + (user-error "Found %s headlines `%s'" n target))) + (message "Found no `%s' headline (`%s')" target major-mode)))) +#+end_src + +#+caption[Sync the "#+COLUMNS:" keyword with a ":COLUMNS:" property]: +#+caption: Sync the "#+COLUMNS:" keyword with a ":COLUMNS:" property. +#+name: lst:sync-columns-keyword-with-property +#+begin_src emacs-lisp :results silent +(with-eval-after-load 'org + (defun get-org-columns-property (target level) + "Get headline TARGET \":COLUMNS:\" property at LEVEL." + (let (case-fold-search done (here (point))) + (goto-char (point-max)) + (while (re-search-backward org-complex-heading-regexp nil t) + (let ((beginning (match-beginning 1)) + (headline (match-string-no-properties 4))) + (when (and (string= headline target) + (= (org-current-level) level)) + (goto-char beginning))) + (setq done (plist-get (org-element--get-node-properties) ':COLUMNS))) + (goto-char here) + done)) + + (defun get-org-columns-keyword-match () + "Get \"#+COLUMNS:\" keyword match data for replacement." + (org-with-wide-buffer + (goto-char (point-min)) + (let ((case-fold-search t) done + (regexp "\\(^[ \t]*#\\+COLUMNS: \\)\\(.+\\)$")) + (while (and (not done) (re-search-forward regexp nil t 1)) + (when (eq (org-element-type (org-element-at-point)) 'keyword) + (setq done (list (regexp-quote (match-string 2)) + (match-beginning 2) (match-end 2))))) + done))) + + (defun sync-org-columns-keyword-property (target level) + "Sync \"#+COLUMNS:\" keyword with \":COLUMNS:\" property. +The property is from the drawer of headline matching TARGET and LEVEL." + (when-let ((property (get-org-columns-property target level)) + (match (get-org-columns-keyword-match))) + (org-with-wide-buffer + (goto-char (point-min)) + (let ((case-fold-search t) done + (regexp "\\(^[ \t]*#\\+COLUMNS: \\)\\(.+\\)$")) + (while (and (not done) (re-search-forward regexp nil t 1)) + (when (eq (org-element-type (org-element-at-point)) 'keyword) + (replace-string + (nth 0 match) property nil (nth 1 match) (nth 2 match)) + (setq done t))))) + (unless property (message "Can't find \":COLUMNS:\" property")) + (unless match (message "Can't find \"#+COLUMNS:\" keyword"))))) +#+end_src + *** [[https://github.com/bdarcus/citar][Citar: citing bibliography]] with [[https://orgmode.org/][Org Mode]] :PROPERTIES: :CUSTOM_ID: sec:citing-bibliography