[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master d493232 077/135: Added support for interactive editing of
From: |
Ian Dunn |
Subject: |
[elpa] master d493232 077/135: Added support for interactive editing of blockers and triggers |
Date: |
Mon, 17 Feb 2020 10:52:56 -0500 (EST) |
branch: master
commit d493232c706aeefc0b4af11b6f53d6fa4eedf86f
Author: Ian Dunn <address@hidden>
Commit: Ian Dunn <address@hidden>
Added support for interactive editing of blockers and triggers
---
org-edna.el | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 205 insertions(+)
diff --git a/org-edna.el b/org-edna.el
index aa319d2..bda6ec3 100644
--- a/org-edna.el
+++ b/org-edna.el
@@ -762,6 +762,211 @@ one is specified, the last will be used.
+;;; Popout editing
+
+(defvar org-edna-edit-original-marker nil)
+(defvar org-edna-blocker-section-marker nil)
+(defvar org-edna-trigger-section-marker nil)
+
+(defcustom org-edna-edit-buffer-name "*Org Edna Edit Blocker/Trigger*"
+ "Name of the popout buffer for editing blockers/triggers."
+ :type 'string
+ :group 'org-edna)
+
+(defun org-edna-in-edit-buffer-p ()
+ (string-equal (buffer-name) org-edna-edit-buffer-name))
+
+(defun org-edna-replace-newlines (string)
+ "Replace newlines with spaces in STRING."
+ (string-join (split-string string "\n" t) " "))
+
+(defun org-edna-edit-text-between-markers (first-marker second-marker)
+ "Collect the text between FIRST-MARKER and SECOND-MARKER."
+ (buffer-substring (marker-position first-marker)
+ (marker-position second-marker)))
+
+(defun org-edna-edit-blocker-section-text ()
+ (when (org-edna-in-edit-buffer-p)
+ (let ((original-text (org-edna-edit-text-between-markers
+ org-edna-blocker-section-marker
+ org-edna-trigger-section-marker)))
+ ;; Strip the BLOCKER key
+ (when (string-match "^BLOCKER\n\\(\\(?:.*\n\\)+\\)" original-text)
+ (org-edna-replace-newlines (match-string 1 original-text))))))
+
+(defun org-edna-edit-trigger-section-text ()
+ (when (org-edna-in-edit-buffer-p)
+ (let ((original-text (org-edna-edit-text-between-markers
+ org-edna-trigger-section-marker
+ (point-max-marker))))
+ ;; Strip the TRIGGER key
+ (when (string-match "^TRIGGER\n\\(\\(?:.*\n\\)+\\)" original-text)
+ (org-edna-replace-newlines (match-string 1 original-text))))))
+
+(defvar org-edna-edit-map
+ (let ((map (make-sparse-keymap)))
+ (org-defkey map "\C-x\C-s" 'org-edna-edit-finish)
+ (org-defkey map "\C-c\C-s" 'org-edna-edit-finish)
+ (org-defkey map "\C-c\C-c" 'org-edna-edit-finish)
+ (org-defkey map "\C-c'" 'org-edna-edit-finish)
+ (org-defkey map "\C-c\C-q" 'org-edna-edit-abort)
+ (org-defkey map "\C-c\C-k" 'org-edna-edit-abort)
+ map))
+
+(defun org-edna-edit ()
+ "Edit the blockers and triggers for current headline in a separate buffer."
+ (interactive)
+ ;; Move to the start of the current headline
+ (let* ((heading-point (save-excursion
+ (org-back-to-heading)
+ (point-marker)))
+ (blocker (or (org-entry-get heading-point "BLOCKER") ""))
+ (trigger (or (org-entry-get heading-point "TRIGGER") ""))
+ (wc (current-window-configuration))
+ (sel-win (selected-window)))
+ (org-switch-to-buffer-other-window org-edna-edit-buffer-name)
+ (erase-buffer)
+ ;; Keep global-font-lock-mode from turning on font-lock-mode
+ (let ((font-lock-global-modes '(not fundamental-mode)))
+ (fundamental-mode))
+ (use-local-map org-edna-edit-map)
+ (setq-local font-lock-global-modes (list 'not major-mode))
+ (setq-local org-edna-edit-original-marker heading-point)
+ (setq-local org-window-configuration wc)
+ (setq-local org-selected-window sel-win)
+ (setq-local org-finish-function 'org-edna-edit-finish)
+ (insert (substitute-command-keys "\\<org-mode-map>\
+Edit blockers and triggers in this buffer under their respective sections
below.
+All lines under a given section will be merged into one when saving back to
+the source buffer. Finish with `\\[org-ctrl-c-ctrl-c]' or
`\\[org-edit-special]'.\n\n"))
+ (setq-local org-edna-blocker-section-marker (point-marker))
+ (insert (format "BLOCKER\n%s\n\n" blocker))
+ (setq-local org-edna-trigger-section-marker (point-marker))
+ (insert (format "TRIGGER\n%s\n\n" trigger))
+
+ ;; Change syntax table to make ! and ? symbol constituents
+ (modify-syntax-entry ?! "_")
+ (modify-syntax-entry ?? "_")
+
+ ;; Set up completion
+ (add-hook 'completion-at-point-functions 'org-edna-completion-at-point nil
t)))
+
+(defun org-edna-edit-finish ()
+ (interactive)
+ (let ((blocker (org-edna-edit-blocker-section-text))
+ (trigger (org-edna-edit-trigger-section-text))
+ (pos-marker org-edna-edit-original-marker)
+ (wc org-window-configuration)
+ (sel-win org-selected-window))
+ (set-window-configuration wc)
+ (select-window sel-win)
+ (goto-char pos-marker)
+ (unless (string-empty-p blocker)
+ (org-entry-put nil "BLOCKER" blocker))
+ (unless (string-empty-p trigger)
+ (org-entry-put nil "TRIGGER" trigger))
+ (kill-buffer org-edna-edit-buffer-name)))
+
+(defun org-edna-edit-abort ()
+ (interactive)
+ (let ((pos-marker org-edna-edit-original-marker)
+ (wc org-window-configuration)
+ (sel-win org-selected-window))
+ (set-window-configuration wc)
+ (select-window sel-win)
+ (goto-char pos-marker)
+ (kill-buffer org-edna-edit-buffer-name)))
+
+;;; Completion
+
+(defun org-edna-between-markers-p (point first-marker second-marker)
+ "Return non-nil if POINT is between FIRST-MARKER and SECOND-MARKER in the
current buffer."
+ (and (markerp first-marker)
+ (markerp second-marker)
+ (eq (marker-buffer first-marker)
+ (marker-buffer second-marker))
+ (eq (current-buffer) (marker-buffer first-marker))
+ (<= (marker-position first-marker) point)
+ (>= (marker-position second-marker) point)))
+
+(defun org-edna-edit-in-blocker-section-p ()
+ "Return non-nil if `point' is in an edna blocker edit section."
+ (org-edna-between-markers-p (point)
+ org-edna-blocker-section-marker
+ org-edna-trigger-section-marker))
+
+(defun org-edna-edit-in-trigger-section-p ()
+ "Return non-nil if `point' is in an edna trigger edit section."
+ (org-edna-between-markers-p (point)
+ org-edna-trigger-section-marker
+ (point-max-marker)))
+
+(defun org-edna--collect-keywords (keyword-type &optional suffix)
+ (let ((suffix (or suffix ""))
+ (edna-sym-list)
+ (edna-rx (rx-to-string `(and
+ string-start
+ "org-edna-"
+ ,keyword-type
+ "/"
+ (submatch (one-or-more ascii))
+ ,suffix
+ string-end))))
+ (mapatoms
+ (lambda (s)
+ (when (string-match edna-rx (symbol-name s))
+ (cl-pushnew (concat (match-string-no-properties 1 (symbol-name s))
suffix)
+ edna-sym-list))))
+ edna-sym-list))
+
+(defun org-edna--collect-finders ()
+ (org-edna--collect-keywords "finder"))
+
+(defun org-edna--collect-actions ()
+ (org-edna--collect-keywords "action" "!"))
+
+(defun org-edna--collect-conditions ()
+ (org-edna--collect-keywords "condition" "?"))
+
+(defun org-edna-completions-for-blocker ()
+ "Return a list of all allowed Edna keywords for a blocker."
+ `(,@(org-edna--collect-finders)
+ ,@(org-edna--collect-conditions)
+ "consideration"))
+
+(defun org-edna-completions-for-trigger ()
+ "Return a list of all allowed Edna keywords for a trigger."
+ `(,@(org-edna--collect-finders)
+ ,@(org-edna--collect-actions)))
+
+(defun org-edna-completion-table-function (string pred action)
+ (let ((completions (cond
+ ;; Don't offer completion inside of arguments
+ ((> (syntax-ppss-depth (syntax-ppss)) 0) nil)
+ ((org-edna-edit-in-blocker-section-p)
+ (org-edna-completions-for-blocker))
+ ((org-edna-edit-in-trigger-section-p)
+ (org-edna-completions-for-trigger)))))
+ (pcase action
+ (`nil
+ (try-completion string completions pred))
+ (`t
+ (all-completions string completions pred))
+ (`lambda
+ (test-completion string completions pred))
+ (`(boundaries . _) nil)
+ (`metadata
+ `(metadata . ((category . org-edna)
+ (annotation-function . nil)
+ (display-sort-function . identity)
+ (cycle-sort-function . identity)))))))
+
+(defun org-edna-completion-at-point ()
+ (when-let ((bounds (bounds-of-thing-at-point 'symbol)))
+ (list (car bounds) (cdr bounds) 'org-edna-completion-table-function)))
+
+
+
(declare-function lm-report-bug "lisp-mnt" (topic))
(defun org-edna-submit-bug-report (topic)
- [elpa] master 827d7c1 060/135: Fixed up chain test, (continued)
- [elpa] master 827d7c1 060/135: Fixed up chain test, Ian Dunn, 2020/02/17
- [elpa] master 89c2ecf 057/135: Added tests for conditions, Ian Dunn, 2020/02/17
- [elpa] master 03bafb0 061/135: Documented delete-property! action, Ian Dunn, 2020/02/17
- [elpa] master 4c26cc8 041/135: Added copyright and licensing information to all files, Ian Dunn, 2020/02/17
- [elpa] master 390ee68 067/135: Fixed ancestors example, Ian Dunn, 2020/02/17
- [elpa] master 3b3625f 064/135: Cleaned up package header and added various docstrings, Ian Dunn, 2020/02/17
- [elpa] master a71c519 068/135: Added docstrings to some finders, Ian Dunn, 2020/02/17
- [elpa] master 3ff308b 055/135: Updated to new syntax, Ian Dunn, 2020/02/17
- [elpa] master 7618794 066/135: Added in-buffer settings and new sections to Documentation, Ian Dunn, 2020/02/17
- [elpa] master 9a6ba17 071/135: Fixed up documentation, Ian Dunn, 2020/02/17
- [elpa] master d493232 077/135: Added support for interactive editing of blockers and triggers,
Ian Dunn <=
- [elpa] master 0aa0bf8 082/135: Don't present variables for keyword completion, Ian Dunn, 2020/02/17
- [elpa] master f265a6b 102/135: Fixed error reporting, Ian Dunn, 2020/02/17
- [elpa] master 9a38d60 050/135: Fixed chain-find and planning actions, Ian Dunn, 2020/02/17
- [elpa] master 836feb4 062/135: Fixed actions and documentation, Ian Dunn, 2020/02/17
- [elpa] master 24a0228 059/135: Added tests for actions, Ian Dunn, 2020/02/17
- [elpa] master 0b0cb67 063/135: Use org source blocks instead of example blocks in documentation, Ian Dunn, 2020/02/17
- [elpa] master e6f88e7 069/135: Silenced byte-compiler, Ian Dunn, 2020/02/17
- [elpa] master ef302af 070/135: Added more docstrings, Ian Dunn, 2020/02/17
- [elpa] master 00abd93 073/135: Fixed tests copyright string, Ian Dunn, 2020/02/17
- [elpa] master c442e77 076/135: Only require subr-x during compilation, Ian Dunn, 2020/02/17