Fixed multi blocker end time. WIP on broken elgantt
This commit is contained in:
@@ -571,227 +571,219 @@ fc --> UC3
|
||||
[[/tmp/babel-pg1Nry/plantuml-SHtP1g.png][/tmp/babel-pg1Nry/plantuml-SHtP1g.png]]
|
||||
|
||||
* Elgantt
|
||||
#+begin_src emacs-lisp
|
||||
;; (require 'cl-lib)
|
||||
;; (require 'dash)
|
||||
;; (unless (fboundp 'first) (defalias 'first #'car))
|
||||
Broken for now..
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(require 'cl-lib)
|
||||
(require 'dash)
|
||||
(require 'elgantt)
|
||||
(unless (fboundp 'first) (defalias 'first #'car))
|
||||
|
||||
;; ;; Clear rules to ensure the new global color logic takes effect immediately
|
||||
;; (setq elgantt--display-rules nil)
|
||||
;; Clear rules to ensure the new global color logic takes effect immediately
|
||||
(setq elgantt--display-rules nil)
|
||||
|
||||
;; (defface gortium/elgantt-weekend-face
|
||||
;; '((t (:background "#32302f" :extend nil)))
|
||||
;; "Gruvbox Dark0_Hard/Soft mix for subtle weekend stripes.")
|
||||
(defface gortium/elgantt-weekend-face
|
||||
'((t (:background "#32302f" :extend nil)))
|
||||
"Gruvbox Dark0_Hard/Soft mix for subtle weekend stripes.")
|
||||
|
||||
;; (defun gortium/internal--month-to-num (name)
|
||||
;; "Convert month string to number safely."
|
||||
;; (let ((case-fold-search t))
|
||||
;; (cond ((string-match-p "Jan" name) 1) ((string-match-p "Feb" name) 2)
|
||||
;; ((string-match-p "Mar" name) 3) ((string-match-p "Apr" name) 4)
|
||||
;; ((string-match-p "May" name) 5) ((string-match-p "Jun" name) 6)
|
||||
;; ((string-match-p "Jul" name) 7) ((string-match-p "Aug" name) 8)
|
||||
;; ((string-match-p "Sep" name) 9) ((string-match-p "Oct" name) 10)
|
||||
;; ((string-match-p "Nov" name) 11) ((string-match-p "Dec" name) 12) (t 1))))
|
||||
(defun gortium/internal--month-to-num (name)
|
||||
"Convert month string to number safely."
|
||||
(let ((case-fold-search t))
|
||||
(cond ((string-match-p "Jan" name) 1) ((string-match-p "Feb" name) 2)
|
||||
((string-match-p "Mar" name) 3) ((string-match-p "Apr" name) 4)
|
||||
((string-match-p "May" name) 5) ((string-match-p "Jun" name) 6)
|
||||
((string-match-p "Jul" name) 7) ((string-match-p "Aug" name) 8)
|
||||
((string-match-p "Sep" name) 9) ((string-match-p "Oct" name) 10)
|
||||
((string-match-p "Nov" name) 11) ((string-match-p "Dec" name) 12) (t 1))))
|
||||
|
||||
;; (defun gortium/elgantt-draw-weekend-guides ()
|
||||
;; "Draw weekend guides for the ENTIRE buffer once to prevent scroll lag."
|
||||
;; (interactive)
|
||||
;; (when (derived-mode-p 'elgantt-mode)
|
||||
;; (let* ((inhibit-modification-hooks t)
|
||||
;; (header-line-1 (save-excursion
|
||||
;; (goto-char (point-min))
|
||||
;; (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
|
||||
;; (col-indices '())
|
||||
;; (search-pos 0))
|
||||
(defun gortium/elgantt-draw-weekend-guides ()
|
||||
"Draw weekend guides for the ENTIRE buffer once to prevent scroll lag."
|
||||
(interactive)
|
||||
(when (derived-mode-p 'elgantt-mode)
|
||||
(let* ((inhibit-modification-hooks t)
|
||||
(header-line-1 (save-excursion
|
||||
(goto-char (point-min))
|
||||
(buffer-substring-no-properties (line-beginning-position) (line-end-position))))
|
||||
(col-indices '())
|
||||
(search-pos 0))
|
||||
|
||||
;; (save-excursion
|
||||
;; (save-restriction
|
||||
;; (widen)
|
||||
;; ;; 1. Clear ALL weekend overlays in the entire buffer
|
||||
;; (remove-overlays (point-min) (point-max) 'gortium-weekend t)
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
;; 1. Clear ALL weekend overlays in the entire buffer
|
||||
(remove-overlays (point-min) (point-max) 'gortium-weekend t)
|
||||
|
||||
;; ;; 2. Parse header once to find column indexes (Fast)
|
||||
;; (while (string-match "|[[:space:]]*\\([[:alpha:]]+\\)[[:space:]]+\\([0-9]\\{4\\}\\)" header-line-1 search-pos)
|
||||
;; (let* ((month-start-col (match-beginning 0))
|
||||
;; (month-name (match-string 1 header-line-1))
|
||||
;; (year (string-to-number (match-string 2 header-line-1)))
|
||||
;; (month-num (gortium/internal--month-to-num month-name))
|
||||
;; (next-pipe (string-match "|" header-line-1 (1+ month-start-col)))
|
||||
;; (month-width (if next-pipe (- next-pipe month-start-col 1) 31)))
|
||||
;; (dotimes (d month-width)
|
||||
;; (let* ((day (1+ d))
|
||||
;; (time (condition-case nil (encode-time 0 0 12 day month-num year) (error nil))))
|
||||
;; (when time
|
||||
;; (let ((dow (nth 6 (decode-time time)))
|
||||
;; (actual-col (+ month-start-col 1 d)))
|
||||
;; (when (member dow '(0 6))
|
||||
;; (push actual-col col-indices))))))
|
||||
;; (setq search-pos (or next-pipe (length header-line-1)))))
|
||||
;; 2. Parse header once to find column indexes (Fast)
|
||||
(while (string-match "|[[:space:]]*\\([[:alpha:]]+\\)[[:space:]]+\\([0-9]\\{4\\}\\)" header-line-1 search-pos)
|
||||
(let* ((month-start-col (match-beginning 0))
|
||||
(month-name (match-string 1 header-line-1))
|
||||
(year (string-to-number (match-string 2 header-line-1)))
|
||||
(month-num (gortium/internal--month-to-num month-name))
|
||||
(next-pipe (string-match "|" header-line-1 (1+ month-start-col)))
|
||||
(month-width (if next-pipe (- next-pipe month-start-col 1) 31)))
|
||||
(dotimes (d month-width)
|
||||
(let* ((day (1+ d))
|
||||
(time (condition-case nil (encode-time 0 0 12 day month-num year) (error nil))))
|
||||
(when time
|
||||
(let ((dow (nth 6 (decode-time time)))
|
||||
(actual-col (+ month-start-col 1 d)))
|
||||
(when (member dow '(0 6))
|
||||
(push actual-col col-indices))))))
|
||||
(setq search-pos (or next-pipe (length header-line-1)))))
|
||||
|
||||
;; ;; 3. Apply to the WHOLE buffer line by line
|
||||
;; (unless (null col-indices)
|
||||
;; (goto-char (point-min))
|
||||
;; (forward-line 2) ;; Skip headers
|
||||
;; (while (not (eobp))
|
||||
;; (let ((line-end (line-end-position)))
|
||||
;; (dolist (col col-indices)
|
||||
;; (move-to-column col)
|
||||
;; (let ((p (point)))
|
||||
;; ;; Ensure we are still on the same line and at the correct column
|
||||
;; (when (and (< p line-end) (= (current-column) col))
|
||||
;; (let ((ov (make-overlay p (1+ p))))
|
||||
;; (overlay-put ov 'face 'gortium/elgantt-weekend-face)
|
||||
;; (overlay-put ov 'gortium-weekend t)
|
||||
;; (overlay-put ov 'priority 100)
|
||||
;; (overlay-put ov 'evaporate t))))))
|
||||
;; (forward-line 1)))))
|
||||
;; (message "Weekend guides rendered for the whole buffer."))))
|
||||
;; 3. Apply to the WHOLE buffer line by line
|
||||
(unless (null col-indices)
|
||||
(goto-char (point-min))
|
||||
(forward-line 2) ;; Skip headers
|
||||
(while (not (eobp))
|
||||
(let ((line-end (line-end-position)))
|
||||
(dolist (col col-indices)
|
||||
(move-to-column col)
|
||||
(let ((p (point)))
|
||||
;; Ensure we are still on the same line and at the correct column
|
||||
(when (and (< p line-end) (= (current-column) col))
|
||||
(let ((ov (make-overlay p (1+ p))))
|
||||
(overlay-put ov 'face 'gortium/elgantt-weekend-face)
|
||||
(overlay-put ov 'gortium-weekend t)
|
||||
(overlay-put ov 'priority 100)
|
||||
(overlay-put ov 'evaporate t))))))
|
||||
(forward-line 1)))))
|
||||
(message "Weekend guides rendered for the whole buffer."))))
|
||||
|
||||
;; ;; Run it only once when the buffer is loaded
|
||||
;; (add-hook 'elgantt-mode-hook #'gortium/elgantt-draw-weekend-guides)
|
||||
;; Run it only once when the buffer is loaded
|
||||
(add-hook 'elgantt-mode-hook #'gortium/elgantt-draw-weekend-guides)
|
||||
|
||||
;; (use-package! elgantt
|
||||
;; :commands (elgantt-open elgantt-open-current-org-file)
|
||||
;; :config
|
||||
;; ;; --- 1. Environment & UI ---
|
||||
;; (add-hook 'elgantt-mode-hook
|
||||
;; (lambda ()
|
||||
;; (setq-local org-phscroll-mode nil)
|
||||
;; (setq-local image-roll-mode nil)
|
||||
;; (setq truncate-lines t)))
|
||||
(use-package! elgantt
|
||||
:commands (elgantt-open elgantt-open-current-org-file)
|
||||
:config
|
||||
;; --- 1. Environment & UI ---
|
||||
(add-hook 'elgantt-mode-hook
|
||||
(lambda ()
|
||||
(setq-local org-phscroll-mode nil)
|
||||
(setq-local image-roll-mode nil)
|
||||
(setq truncate-lines t)))
|
||||
|
||||
;; (setq elgantt-start-date "2026-01-01")
|
||||
(setq elgantt-start-date "2026-01-01")
|
||||
|
||||
;; (setq elgantt-header-column-offset 40
|
||||
;; elgantt-header-type 'root
|
||||
;; elgantt-show-header-depth t
|
||||
;; elgantt-insert-blank-line-between-top-level-header t
|
||||
;; elgantt-startup-folded nil
|
||||
;; elgantt-draw-overarching-headers nil
|
||||
;; elgantt-scroll-to-current-month-at-startup nil)
|
||||
(setq elgantt-header-column-offset 40
|
||||
elgantt-header-type 'root
|
||||
elgantt-show-header-depth t
|
||||
elgantt-insert-blank-line-between-top-level-header t
|
||||
elgantt-startup-folded nil
|
||||
elgantt-draw-overarching-headers nil
|
||||
elgantt-scroll-to-current-month-at-startup nil)
|
||||
|
||||
;; (setq elgantt-user-set-color-priority-counter 0)
|
||||
(setq elgantt-user-set-color-priority-counter 0)
|
||||
|
||||
;; (elgantt-create-display-rule draw-active-timestamp-range
|
||||
;; :parser ((override-color . ((when-let ((colors (org-entry-get (point) "ELGANTT-COLOR")))
|
||||
;; (split-string colors " "))))
|
||||
;; (range-dates . ((save-excursion
|
||||
;; (org-back-to-heading t)
|
||||
;; (let ((limit (save-excursion (outline-next-heading) (point))))
|
||||
;; (when (re-search-forward "<\\([^>]+\\)>--<\\([^>]+\\)>" limit t)
|
||||
;; (list (match-string 1) (match-string 2))))))))
|
||||
;; :args (elgantt-org-id)
|
||||
;; :body ((when (and elgantt-org-id range-dates)
|
||||
;; (let* ((colors (or override-color '("#fabd2f" "#fe8019")))
|
||||
;; (s-str (substring (car range-dates) 0 10))
|
||||
;; (e-str (substring (cadr range-dates) 0 10))
|
||||
;; (p1 (save-excursion (when (elgantt--goto-date s-str) (point))))
|
||||
;; (p2 (save-excursion (when (elgantt--goto-date e-str) (point)))))
|
||||
;; (when (and (numberp p1) (numberp p2))
|
||||
;; (elgantt--draw-gradient
|
||||
;; (car colors) (cadr colors)
|
||||
;; (truncate p1) (truncate p2) nil ;; <-- FIX: Removed (1+ ...) to stop overshoot
|
||||
;; `(priority ,(setq elgantt-user-set-color-priority-counter
|
||||
;; (1- elgantt-user-set-color-priority-counter))
|
||||
;; :elgantt-user-overlay ,elgantt-org-id))))))))
|
||||
;; --- Rule 1: Active Timestamp Range ---
|
||||
(elgantt-create-display-rule draw-active-timestamp-range
|
||||
:parser ((override-color . ((when-let ((colors (org-entry-get (point) "ELGANTT-COLOR")))
|
||||
(split-string colors " "))))
|
||||
(range-dates . ((save-excursion
|
||||
(org-back-to-heading t)
|
||||
(let ((limit (save-excursion (outline-next-heading) (point))))
|
||||
(when (re-search-forward "<\\([^>]+\\)>--<\\([^>]+\\)>" limit t)
|
||||
(list (match-string 1) (match-string 2))))))))
|
||||
:args (elgantt-org-id)
|
||||
:body ((when (and elgantt-org-id range-dates)
|
||||
(let* ((colors (or override-color '("#fabd2f" "#fe8019")))
|
||||
(s-str (substring (car range-dates) 0 10))
|
||||
(e-str (substring (cadr range-dates) 0 10))
|
||||
(p1 (save-excursion (when (elgantt--goto-date s-str) (point))))
|
||||
(p2 (save-excursion (when (elgantt--goto-date e-str) (point)))))
|
||||
(when (and (numberp p1) (numberp p2))
|
||||
(elgantt--draw-gradient
|
||||
(car colors) (cadr colors)
|
||||
(truncate p1) (truncate p2) nil
|
||||
`(priority ,(setq elgantt-user-set-color-priority-counter
|
||||
(1- elgantt-user-set-color-priority-counter))
|
||||
:elgantt-user-overlay ,elgantt-org-id)))))))
|
||||
|
||||
;; ;; --- 2. Effort Rule (With Weekend Extension) ---
|
||||
;; ;; (elgantt-create-display-rule draw-scheduled-to-effort-end
|
||||
;; ;; :parser ((override-color . ((when-let ((colors (org-entry-get (point) "ELGANTT-COLOR")))
|
||||
;; ;; (split-string colors " "))))
|
||||
;; ;; (elgantt-effort . ((org-entry-get (point) "EFFORT")))
|
||||
;; ;; (wknd-days . ((when-let ((val (org-entry-get (point) "WEEKEND_DAYS")))
|
||||
;; ;; (string-to-number val)))))
|
||||
;; ;; :args (elgantt-scheduled elgantt-effort elgantt-org-id)
|
||||
;; ;; :body ((when (and elgantt-scheduled elgantt-effort)
|
||||
;; ;; (let* ((start-ts (ts-parse elgantt-scheduled))
|
||||
;; ;; (raw-mins (org-duration-to-minutes elgantt-effort))
|
||||
;; ;; ;; Add the weekend jump days to the visual length
|
||||
;; ;; (total-days (+ (ceiling (/ (float raw-mins) 1440.0)) (or wknd-days 0)))
|
||||
;; ;; (p1 (save-excursion
|
||||
;; ;; (elgantt--goto-date (ts-format "%Y-%m-%d" start-ts))
|
||||
;; ;; (point)))
|
||||
;; ;; (colors (or override-color '("#8ec07c" "#458588"))))
|
||||
;; ;; (when (numberp p1)
|
||||
;; ;; (if (<= total-days 1)
|
||||
;; ;; (elgantt--create-overlay (truncate p1) (1+ (truncate p1))
|
||||
;; ;; `(face (:background ,(car colors))
|
||||
;; ;; priority ,(setq elgantt-user-set-color-priority-counter
|
||||
;; ;; (1- elgantt-user-set-color-priority-counter))
|
||||
;; ;; :elgantt-user-overlay ,elgantt-org-id))
|
||||
;; ;; ;; FIX 1: compute p2 by date (handles "|" separators)
|
||||
;; ;; ;; FIX 2: keep original "Rule of 2" behavior to avoid +1 day overshoot
|
||||
;; ;; (let* ((end-ts (ts-adjust 'day (- total-days 2) start-ts))
|
||||
;; ;; (p2 (save-excursion
|
||||
;; ;; (elgantt--goto-date (ts-format "%Y-%m-%d" end-ts))
|
||||
;; ;; (point))))
|
||||
;; ;; (when (numberp p2)
|
||||
;; ;; (elgantt--draw-gradient
|
||||
;; ;; (car colors) (cadr colors)
|
||||
;; ;; (truncate p1) (1+ (truncate p2)) nil
|
||||
;; ;; `(priority ,(setq elgantt-user-set-color-priority-counter
|
||||
;; ;; (1- elgantt-user-set-color-priority-counter))
|
||||
;; ;; :elgantt-user-overlay ,elgantt-org-id))))))))))
|
||||
;; --- Rule 2: Effort Rule ---
|
||||
(elgantt-create-display-rule draw-scheduled-to-effort-end
|
||||
:parser ((override-color . ((when-let ((colors (org-entry-get (point) "ELGANTT-COLOR")))
|
||||
(split-string colors " "))))
|
||||
(elgantt-effort . ((org-entry-get (point) "EFFORT")))
|
||||
(wknd-days . ((when-let ((val (org-entry-get (point) "WEEKEND_DAYS")))
|
||||
(string-to-number val)))))
|
||||
:args (elgantt-scheduled elgantt-effort elgantt-org-id)
|
||||
:body ((when (and elgantt-scheduled elgantt-effort)
|
||||
(let* ((start-ts (ts-parse elgantt-scheduled))
|
||||
(raw-mins (org-duration-to-minutes elgantt-effort))
|
||||
(total-days (+ (ceiling (/ (float raw-mins) 1440.0)) (or wknd-days 0)))
|
||||
(p1 (save-excursion
|
||||
(elgantt--goto-date (ts-format "%Y-%m-%d" start-ts))
|
||||
(point)))
|
||||
(colors (or override-color '("#8ec07c" "#458588"))))
|
||||
(when (numberp p1)
|
||||
(if (<= total-days 1)
|
||||
(elgantt--create-overlay (truncate p1) (1+ (truncate p1))
|
||||
`(face (:background ,(car colors))
|
||||
priority ,(setq elgantt-user-set-color-priority-counter
|
||||
(1- elgantt-user-set-color-priority-counter))
|
||||
:elgantt-user-overlay ,elgantt-org-id))
|
||||
(let* ((end-ts (ts-adjust 'day (- total-days 2) start-ts))
|
||||
(p2 (save-excursion
|
||||
(elgantt--goto-date (ts-format "%Y-%m-%d" end-ts))
|
||||
(point))))
|
||||
(when (numberp p2)
|
||||
(elgantt--draw-gradient
|
||||
(car colors) (cadr colors)
|
||||
(truncate p1) (1+ (truncate p2)) nil
|
||||
`(priority ,(setq elgantt-user-set-color-priority-counter
|
||||
(1- elgantt-user-set-color-priority-counter))
|
||||
:elgantt-user-overlay ,elgantt-org-id)))))))))))
|
||||
|
||||
;; (elgantt-create-display-rule draw-blocker-lines
|
||||
;; :parser ((blocker-raw . ((org-entry-get (point) "BLOCKER"))))
|
||||
;; :args (elgantt-org-id elgantt-scheduled)
|
||||
;; :body ((when (and elgantt-org-id blocker-raw (not (string-empty-p blocker-raw)))
|
||||
;; ;; 1. GET DESTINATION (Start of current task)
|
||||
;; ;; We use the built-in elgantt-scheduled arg if available, it's faster and safer.
|
||||
;; (let* ((p-dest (save-excursion
|
||||
;; (let ((d-start (or (when (stringp elgantt-scheduled) (substring elgantt-scheduled 0 10))
|
||||
;; (elgantt-with-point-at-orig-entry nil
|
||||
;; (save-excursion
|
||||
;; (org-back-to-heading t)
|
||||
;; (when (re-search-forward "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" (line-end-position) t)
|
||||
;; (match-string 1)))))))
|
||||
;; (when (and d-start (elgantt--goto-date d-start)) (point))))))
|
||||
;; --- Rule 3: Blocker Lines ---
|
||||
(elgantt-create-display-rule draw-blocker-lines
|
||||
:parser ((blocker-raw . ((org-entry-get (point) "BLOCKER"))))
|
||||
:args (elgantt-org-id elgantt-scheduled)
|
||||
:body ((when (and elgantt-org-id blocker-raw (not (string-empty-p blocker-raw)))
|
||||
(let* ((p-dest (save-excursion
|
||||
(let ((d-start (or (when (stringp elgantt-scheduled) (substring elgantt-scheduled 0 10))
|
||||
(elgantt-with-point-at-orig-entry nil
|
||||
(save-excursion
|
||||
(org-back-to-heading t)
|
||||
(when (re-search-forward "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" (line-end-position) t)
|
||||
(match-string 1)))))))
|
||||
(when (and d-start (elgantt--goto-date d-start)) (point))))))
|
||||
(when (numberp p-dest)
|
||||
(let ((id-list (split-string (if (string-match "ids(\\(.*?\\))" blocker-raw) (match-string 1 blocker-raw) blocker-raw) "[ ,]+" t)))
|
||||
(dolist (blocker-id id-list)
|
||||
(save-excursion
|
||||
(when (elgantt--goto-id blocker-id)
|
||||
(let ((d-end-str nil)
|
||||
(row-start (line-beginning-position))
|
||||
(row-end (line-end-position)))
|
||||
(elgantt-with-point-at-orig-entry nil
|
||||
(save-excursion
|
||||
(org-back-to-heading t)
|
||||
(let ((limit (save-excursion (outline-next-heading) (point))))
|
||||
(if (re-search-forward "<[^>]+>--<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" limit t)
|
||||
(setq d-end-str (match-string 1))
|
||||
(let ((s (org-entry-get (point) "SCHEDULED"))
|
||||
(e (org-entry-get (point) "EFFORT"))
|
||||
(w (string-to-number (or (org-entry-get (point) "WEEKEND_DAYS") "0"))))
|
||||
(when (and s e)
|
||||
(setq d-end-str (ts-format "%Y-%m-%d" (ts-adjust 'day (1- (+ (ceiling (/ (float (org-duration-to-minutes e)) 1440.0)) w)) (ts-parse s))))))))))
|
||||
(when d-end-str
|
||||
(save-excursion
|
||||
(elgantt--goto-date d-end-str)
|
||||
(let ((p-source (point)))
|
||||
(if (and (>= p-source row-start) (<= p-source row-end))
|
||||
(elgantt--draw-line (truncate p-source) (truncate p-dest) "#b8bb26")
|
||||
(let ((col-offset (- p-source (save-excursion (goto-char p-source) (line-beginning-position)))))
|
||||
(goto-char row-start)
|
||||
(forward-char col-offset)
|
||||
(elgantt--draw-line (point) (truncate p-dest) "#b8bb26")))))))))))))))))
|
||||
|
||||
;; (when (numberp p-dest)
|
||||
;; (let ((ids-string (if (string-match "ids(\\(.*?\\))" blocker-raw) (match-string 1 blocker-raw) blocker-raw))
|
||||
;; (id-list (split-string (if (string-match "ids(\\(.*?\\))" blocker-raw) (match-string 1 blocker-raw) blocker-raw) "[ ,]+" t)))
|
||||
;; (dolist (blocker-id id-list)
|
||||
;; (save-excursion
|
||||
;; (when (elgantt--goto-id blocker-id)
|
||||
;; (let ((d-end-str nil)
|
||||
;; (row-start (line-beginning-position))
|
||||
;; (row-end (line-end-position)))
|
||||
;; ;; 2. GET BLOCKER END DATE
|
||||
;; (elgantt-with-point-at-orig-entry nil
|
||||
;; (save-excursion
|
||||
;; (org-back-to-heading t)
|
||||
;; (let ((limit (save-excursion (outline-next-heading) (point))))
|
||||
;; (if (re-search-forward "<[^>]+>--<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" limit t)
|
||||
;; (setq d-end-str (match-string 1))
|
||||
;; (let ((s (org-entry-get (point) "SCHEDULED"))
|
||||
;; (e (org-entry-get (point) "EFFORT"))
|
||||
;; (w (string-to-number (or (org-entry-get (point) "WEEKEND_DAYS") "0"))))
|
||||
;; (when (and s e)
|
||||
;; (setq d-end-str (ts-format "%Y-%m-%d" (ts-adjust 'day (1- (+ (ceiling (/ (float (org-duration-to-minutes e)) 1440.0)) w)) (ts-parse s))))))))))
|
||||
|
||||
;; ;; 3. DRAW
|
||||
;; (when d-end-str
|
||||
;; (save-excursion
|
||||
;; (elgantt--goto-date d-end-str)
|
||||
;; (let ((p-source (point)))
|
||||
;; (if (and (>= p-source row-start) (<= p-source row-end))
|
||||
;; (elgantt--draw-line (truncate p-source) (truncate p-dest) "#b8bb26")
|
||||
;; ;; Force to row if it jumped
|
||||
;; (let ((col-offset (- p-source (save-excursion (goto-char p-source) (line-beginning-position)))))
|
||||
;; (goto-char row-start)
|
||||
;; (forward-char col-offset)
|
||||
;; (elgantt--draw-line (point) (truncate p-dest) "#b8bb26")))))))))))))))
|
||||
;; )
|
||||
|
||||
;; (defun elgantt-open-current-org-file ()
|
||||
;; (interactive)
|
||||
;; (if-let ((file (buffer-file-name)))
|
||||
;; (progn
|
||||
;; (setq elgantt-agenda-files (list file))
|
||||
;; (elgantt--reset-org-ql-cache)
|
||||
;; (elgantt-open))
|
||||
;; (message "No file!")))
|
||||
(defun elgantt-open-current-org-file ()
|
||||
(interactive)
|
||||
(if-let ((file (buffer-file-name)))
|
||||
(progn
|
||||
(setq elgantt-agenda-files (list file))
|
||||
(elgantt--reset-org-ql-cache)
|
||||
(elgantt-open))
|
||||
(message "No file!")))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
@@ -2351,36 +2343,38 @@ Returns nil if no range found (safe, non-blocking)."
|
||||
(when (and (string-match "ids(\\(.*?\\))" clean)
|
||||
(not (string-empty-p (s-trim (match-string 1 clean)))))
|
||||
(dolist (tid (split-string (match-string 1 clean) "[ ,]+" t))
|
||||
(let* ((clean-id (replace-regexp-in-string "[\"']\\|id:" "" tid))
|
||||
(pos (org-id-find clean-id t)) ;; Returns marker OR (file . pos)
|
||||
(let* ((clean-id (replace-regexp-in-string "[\"']\\|id: " "" tid))
|
||||
(pos (org-id-find clean-id t))
|
||||
(computed-end (gethash clean-id task-end-map))
|
||||
(blocker-end
|
||||
(cond
|
||||
(computed-end computed-end)
|
||||
(blocker-end nil))
|
||||
|
||||
;; Use a temporary buffer context to check properties if pos is a list
|
||||
((and pos (let ((m (if (markerp pos) pos (set-marker (make-marker) (cdr pos) (find-file-noselect (car pos))))))
|
||||
;; 1. Determine this specific blocker's end time
|
||||
(setq blocker-end
|
||||
(cond
|
||||
(computed-end computed-end) ;; Use what we just calculated in this session
|
||||
(pos (let ((m (if (markerp pos) pos
|
||||
(set-marker (make-marker) (cdr pos)
|
||||
(find-file-noselect (car pos))))))
|
||||
(with-current-buffer (marker-buffer m)
|
||||
(org-with-point-at m
|
||||
(cond
|
||||
;; Priority 1: Use actual CLOSED timestamp if DONE
|
||||
((org-entry-get nil "CLOSED")
|
||||
(org-time-string-to-time (org-entry-get nil "CLOSED")))
|
||||
;; Priority 2: Use range end if FIXED
|
||||
((string-equal "t" (org-entry-get nil "FIXED"))
|
||||
(gortium/org--get-range-end m))
|
||||
(t nil))))))
|
||||
;; This captures the result of the let/with-current-buffer block
|
||||
(let ((m (if (markerp pos) pos (set-marker (make-marker) (cdr pos) (find-file-noselect (car pos))))))
|
||||
(with-current-buffer (marker-buffer m)
|
||||
(org-with-point-at m
|
||||
(if (org-entry-get nil "CLOSED")
|
||||
(org-time-string-to-time (org-entry-get nil "CLOSED"))
|
||||
(gortium/org--get-range-end m))))))
|
||||
(t nil))))
|
||||
(t nil)))
|
||||
|
||||
;; 2. Update the "Latest" tracker
|
||||
(if blocker-end
|
||||
(setq latest-time (if (or (null latest-time) (time-less-p latest-time blocker-end))
|
||||
blocker-end latest-time))
|
||||
(when (or (null latest-time) (time-less-p latest-time blocker-end))
|
||||
(setq latest-time blocker-end))
|
||||
;; If ANY blocker is not resolved/found, the whole task is not ready
|
||||
(setq all-resolved nil)))))
|
||||
|
||||
;; Only return a time if EVERY ID in the list was successfully resolved
|
||||
(when all-resolved latest-time)))
|
||||
|
||||
(defun gortium/internal--update-properties (pos start wknd id end task-end-map)
|
||||
@@ -2559,10 +2553,14 @@ Returns list of task IDs involved in cycles, or nil if no cycles found."
|
||||
(with-current-buffer buf
|
||||
(org-element-with-disabled-cache
|
||||
(let* ((off-days (if (stringp offset) (string-to-number offset) 0))
|
||||
;; CRITICAL: For fixed tasks, base-start is THEIR start.
|
||||
;; For dependent tasks, base-start is the blocker's END.
|
||||
(base-start (cond (is-fixed (or (gortium/org--get-range-start pos) sched (current-time)))
|
||||
(t (or blocker-end (or sched (current-time))))))
|
||||
(base-start (cond
|
||||
;; 1. If FIXED, use its own defined start
|
||||
(is-fixed (or (gortium/org--get-range-start pos) sched (current-time)))
|
||||
;; 2. If it HAS a blocker, it MUST use blocker-end.
|
||||
;; If blocker-end is nil, this task isn't 'ready' yet.
|
||||
(has-blocker blocker-end)
|
||||
;; 3. If no blocker and not fixed, use current schedule or now
|
||||
(t (or sched (current-time)))))
|
||||
|
||||
(final-start (if is-fixed base-start
|
||||
(gortium/internal--snap-to-working-hours
|
||||
|
||||
@@ -400,227 +400,6 @@
|
||||
;; Enable plantuml-mode for PlantUML files
|
||||
(add-to-list 'auto-mode-alist '("\\.plantuml\\'" . plantuml-mode))
|
||||
|
||||
;; (require 'cl-lib)
|
||||
;; (require 'dash)
|
||||
;; (unless (fboundp 'first) (defalias 'first #'car))
|
||||
|
||||
;; ;; Clear rules to ensure the new global color logic takes effect immediately
|
||||
;; (setq elgantt--display-rules nil)
|
||||
|
||||
;; (defface gortium/elgantt-weekend-face
|
||||
;; '((t (:background "#32302f" :extend nil)))
|
||||
;; "Gruvbox Dark0_Hard/Soft mix for subtle weekend stripes.")
|
||||
|
||||
;; (defun gortium/internal--month-to-num (name)
|
||||
;; "Convert month string to number safely."
|
||||
;; (let ((case-fold-search t))
|
||||
;; (cond ((string-match-p "Jan" name) 1) ((string-match-p "Feb" name) 2)
|
||||
;; ((string-match-p "Mar" name) 3) ((string-match-p "Apr" name) 4)
|
||||
;; ((string-match-p "May" name) 5) ((string-match-p "Jun" name) 6)
|
||||
;; ((string-match-p "Jul" name) 7) ((string-match-p "Aug" name) 8)
|
||||
;; ((string-match-p "Sep" name) 9) ((string-match-p "Oct" name) 10)
|
||||
;; ((string-match-p "Nov" name) 11) ((string-match-p "Dec" name) 12) (t 1))))
|
||||
|
||||
;; (defun gortium/elgantt-draw-weekend-guides ()
|
||||
;; "Draw weekend guides for the ENTIRE buffer once to prevent scroll lag."
|
||||
;; (interactive)
|
||||
;; (when (derived-mode-p 'elgantt-mode)
|
||||
;; (let* ((inhibit-modification-hooks t)
|
||||
;; (header-line-1 (save-excursion
|
||||
;; (goto-char (point-min))
|
||||
;; (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
|
||||
;; (col-indices '())
|
||||
;; (search-pos 0))
|
||||
|
||||
;; (save-excursion
|
||||
;; (save-restriction
|
||||
;; (widen)
|
||||
;; ;; 1. Clear ALL weekend overlays in the entire buffer
|
||||
;; (remove-overlays (point-min) (point-max) 'gortium-weekend t)
|
||||
|
||||
;; ;; 2. Parse header once to find column indexes (Fast)
|
||||
;; (while (string-match "|[[:space:]]*\\([[:alpha:]]+\\)[[:space:]]+\\([0-9]\\{4\\}\\)" header-line-1 search-pos)
|
||||
;; (let* ((month-start-col (match-beginning 0))
|
||||
;; (month-name (match-string 1 header-line-1))
|
||||
;; (year (string-to-number (match-string 2 header-line-1)))
|
||||
;; (month-num (gortium/internal--month-to-num month-name))
|
||||
;; (next-pipe (string-match "|" header-line-1 (1+ month-start-col)))
|
||||
;; (month-width (if next-pipe (- next-pipe month-start-col 1) 31)))
|
||||
;; (dotimes (d month-width)
|
||||
;; (let* ((day (1+ d))
|
||||
;; (time (condition-case nil (encode-time 0 0 12 day month-num year) (error nil))))
|
||||
;; (when time
|
||||
;; (let ((dow (nth 6 (decode-time time)))
|
||||
;; (actual-col (+ month-start-col 1 d)))
|
||||
;; (when (member dow '(0 6))
|
||||
;; (push actual-col col-indices))))))
|
||||
;; (setq search-pos (or next-pipe (length header-line-1)))))
|
||||
|
||||
;; ;; 3. Apply to the WHOLE buffer line by line
|
||||
;; (unless (null col-indices)
|
||||
;; (goto-char (point-min))
|
||||
;; (forward-line 2) ;; Skip headers
|
||||
;; (while (not (eobp))
|
||||
;; (let ((line-end (line-end-position)))
|
||||
;; (dolist (col col-indices)
|
||||
;; (move-to-column col)
|
||||
;; (let ((p (point)))
|
||||
;; ;; Ensure we are still on the same line and at the correct column
|
||||
;; (when (and (< p line-end) (= (current-column) col))
|
||||
;; (let ((ov (make-overlay p (1+ p))))
|
||||
;; (overlay-put ov 'face 'gortium/elgantt-weekend-face)
|
||||
;; (overlay-put ov 'gortium-weekend t)
|
||||
;; (overlay-put ov 'priority 100)
|
||||
;; (overlay-put ov 'evaporate t))))))
|
||||
;; (forward-line 1)))))
|
||||
;; (message "Weekend guides rendered for the whole buffer."))))
|
||||
|
||||
;; ;; Run it only once when the buffer is loaded
|
||||
;; (add-hook 'elgantt-mode-hook #'gortium/elgantt-draw-weekend-guides)
|
||||
|
||||
;; (use-package! elgantt
|
||||
;; :commands (elgantt-open elgantt-open-current-org-file)
|
||||
;; :config
|
||||
;; ;; --- 1. Environment & UI ---
|
||||
;; (add-hook 'elgantt-mode-hook
|
||||
;; (lambda ()
|
||||
;; (setq-local org-phscroll-mode nil)
|
||||
;; (setq-local image-roll-mode nil)
|
||||
;; (setq truncate-lines t)))
|
||||
|
||||
;; (setq elgantt-start-date "2026-01-01")
|
||||
|
||||
;; (setq elgantt-header-column-offset 40
|
||||
;; elgantt-header-type 'root
|
||||
;; elgantt-show-header-depth t
|
||||
;; elgantt-insert-blank-line-between-top-level-header t
|
||||
;; elgantt-startup-folded nil
|
||||
;; elgantt-draw-overarching-headers nil
|
||||
;; elgantt-scroll-to-current-month-at-startup nil)
|
||||
|
||||
;; (setq elgantt-user-set-color-priority-counter 0)
|
||||
|
||||
;; (elgantt-create-display-rule draw-active-timestamp-range
|
||||
;; :parser ((override-color . ((when-let ((colors (org-entry-get (point) "ELGANTT-COLOR")))
|
||||
;; (split-string colors " "))))
|
||||
;; (range-dates . ((save-excursion
|
||||
;; (org-back-to-heading t)
|
||||
;; (let ((limit (save-excursion (outline-next-heading) (point))))
|
||||
;; (when (re-search-forward "<\\([^>]+\\)>--<\\([^>]+\\)>" limit t)
|
||||
;; (list (match-string 1) (match-string 2))))))))
|
||||
;; :args (elgantt-org-id)
|
||||
;; :body ((when (and elgantt-org-id range-dates)
|
||||
;; (let* ((colors (or override-color '("#fabd2f" "#fe8019")))
|
||||
;; (s-str (substring (car range-dates) 0 10))
|
||||
;; (e-str (substring (cadr range-dates) 0 10))
|
||||
;; (p1 (save-excursion (when (elgantt--goto-date s-str) (point))))
|
||||
;; (p2 (save-excursion (when (elgantt--goto-date e-str) (point)))))
|
||||
;; (when (and (numberp p1) (numberp p2))
|
||||
;; (elgantt--draw-gradient
|
||||
;; (car colors) (cadr colors)
|
||||
;; (truncate p1) (truncate p2) nil ;; <-- FIX: Removed (1+ ...) to stop overshoot
|
||||
;; `(priority ,(setq elgantt-user-set-color-priority-counter
|
||||
;; (1- elgantt-user-set-color-priority-counter))
|
||||
;; :elgantt-user-overlay ,elgantt-org-id))))))))
|
||||
|
||||
;; ;; --- 2. Effort Rule (With Weekend Extension) ---
|
||||
;; ;; (elgantt-create-display-rule draw-scheduled-to-effort-end
|
||||
;; ;; :parser ((override-color . ((when-let ((colors (org-entry-get (point) "ELGANTT-COLOR")))
|
||||
;; ;; (split-string colors " "))))
|
||||
;; ;; (elgantt-effort . ((org-entry-get (point) "EFFORT")))
|
||||
;; ;; (wknd-days . ((when-let ((val (org-entry-get (point) "WEEKEND_DAYS")))
|
||||
;; ;; (string-to-number val)))))
|
||||
;; ;; :args (elgantt-scheduled elgantt-effort elgantt-org-id)
|
||||
;; ;; :body ((when (and elgantt-scheduled elgantt-effort)
|
||||
;; ;; (let* ((start-ts (ts-parse elgantt-scheduled))
|
||||
;; ;; (raw-mins (org-duration-to-minutes elgantt-effort))
|
||||
;; ;; ;; Add the weekend jump days to the visual length
|
||||
;; ;; (total-days (+ (ceiling (/ (float raw-mins) 1440.0)) (or wknd-days 0)))
|
||||
;; ;; (p1 (save-excursion
|
||||
;; ;; (elgantt--goto-date (ts-format "%Y-%m-%d" start-ts))
|
||||
;; ;; (point)))
|
||||
;; ;; (colors (or override-color '("#8ec07c" "#458588"))))
|
||||
;; ;; (when (numberp p1)
|
||||
;; ;; (if (<= total-days 1)
|
||||
;; ;; (elgantt--create-overlay (truncate p1) (1+ (truncate p1))
|
||||
;; ;; `(face (:background ,(car colors))
|
||||
;; ;; priority ,(setq elgantt-user-set-color-priority-counter
|
||||
;; ;; (1- elgantt-user-set-color-priority-counter))
|
||||
;; ;; :elgantt-user-overlay ,elgantt-org-id))
|
||||
;; ;; ;; FIX 1: compute p2 by date (handles "|" separators)
|
||||
;; ;; ;; FIX 2: keep original "Rule of 2" behavior to avoid +1 day overshoot
|
||||
;; ;; (let* ((end-ts (ts-adjust 'day (- total-days 2) start-ts))
|
||||
;; ;; (p2 (save-excursion
|
||||
;; ;; (elgantt--goto-date (ts-format "%Y-%m-%d" end-ts))
|
||||
;; ;; (point))))
|
||||
;; ;; (when (numberp p2)
|
||||
;; ;; (elgantt--draw-gradient
|
||||
;; ;; (car colors) (cadr colors)
|
||||
;; ;; (truncate p1) (1+ (truncate p2)) nil
|
||||
;; ;; `(priority ,(setq elgantt-user-set-color-priority-counter
|
||||
;; ;; (1- elgantt-user-set-color-priority-counter))
|
||||
;; ;; :elgantt-user-overlay ,elgantt-org-id))))))))))
|
||||
|
||||
;; (elgantt-create-display-rule draw-blocker-lines
|
||||
;; :parser ((blocker-raw . ((org-entry-get (point) "BLOCKER"))))
|
||||
;; :args (elgantt-org-id elgantt-scheduled)
|
||||
;; :body ((when (and elgantt-org-id blocker-raw (not (string-empty-p blocker-raw)))
|
||||
;; ;; 1. GET DESTINATION (Start of current task)
|
||||
;; ;; We use the built-in elgantt-scheduled arg if available, it's faster and safer.
|
||||
;; (let* ((p-dest (save-excursion
|
||||
;; (let ((d-start (or (when (stringp elgantt-scheduled) (substring elgantt-scheduled 0 10))
|
||||
;; (elgantt-with-point-at-orig-entry nil
|
||||
;; (save-excursion
|
||||
;; (org-back-to-heading t)
|
||||
;; (when (re-search-forward "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" (line-end-position) t)
|
||||
;; (match-string 1)))))))
|
||||
;; (when (and d-start (elgantt--goto-date d-start)) (point))))))
|
||||
|
||||
;; (when (numberp p-dest)
|
||||
;; (let ((ids-string (if (string-match "ids(\\(.*?\\))" blocker-raw) (match-string 1 blocker-raw) blocker-raw))
|
||||
;; (id-list (split-string (if (string-match "ids(\\(.*?\\))" blocker-raw) (match-string 1 blocker-raw) blocker-raw) "[ ,]+" t)))
|
||||
;; (dolist (blocker-id id-list)
|
||||
;; (save-excursion
|
||||
;; (when (elgantt--goto-id blocker-id)
|
||||
;; (let ((d-end-str nil)
|
||||
;; (row-start (line-beginning-position))
|
||||
;; (row-end (line-end-position)))
|
||||
;; ;; 2. GET BLOCKER END DATE
|
||||
;; (elgantt-with-point-at-orig-entry nil
|
||||
;; (save-excursion
|
||||
;; (org-back-to-heading t)
|
||||
;; (let ((limit (save-excursion (outline-next-heading) (point))))
|
||||
;; (if (re-search-forward "<[^>]+>--<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" limit t)
|
||||
;; (setq d-end-str (match-string 1))
|
||||
;; (let ((s (org-entry-get (point) "SCHEDULED"))
|
||||
;; (e (org-entry-get (point) "EFFORT"))
|
||||
;; (w (string-to-number (or (org-entry-get (point) "WEEKEND_DAYS") "0"))))
|
||||
;; (when (and s e)
|
||||
;; (setq d-end-str (ts-format "%Y-%m-%d" (ts-adjust 'day (1- (+ (ceiling (/ (float (org-duration-to-minutes e)) 1440.0)) w)) (ts-parse s))))))))))
|
||||
|
||||
;; ;; 3. DRAW
|
||||
;; (when d-end-str
|
||||
;; (save-excursion
|
||||
;; (elgantt--goto-date d-end-str)
|
||||
;; (let ((p-source (point)))
|
||||
;; (if (and (>= p-source row-start) (<= p-source row-end))
|
||||
;; (elgantt--draw-line (truncate p-source) (truncate p-dest) "#b8bb26")
|
||||
;; ;; Force to row if it jumped
|
||||
;; (let ((col-offset (- p-source (save-excursion (goto-char p-source) (line-beginning-position)))))
|
||||
;; (goto-char row-start)
|
||||
;; (forward-char col-offset)
|
||||
;; (elgantt--draw-line (point) (truncate p-dest) "#b8bb26")))))))))))))))
|
||||
;; )
|
||||
|
||||
;; (defun elgantt-open-current-org-file ()
|
||||
;; (interactive)
|
||||
;; (if-let ((file (buffer-file-name)))
|
||||
;; (progn
|
||||
;; (setq elgantt-agenda-files (list file))
|
||||
;; (elgantt--reset-org-ql-cache)
|
||||
;; (elgantt-open))
|
||||
;; (message "No file!")))
|
||||
|
||||
(setq org-roam-directory (file-truename "~/ExoKortex/")
|
||||
org-roam-db-location (file-truename "~/ExoKortex/2-Areas/IT/Roam/org-roam.db")
|
||||
org-attach-id-dir "assets/"
|
||||
@@ -1697,36 +1476,38 @@ Returns nil if no range found (safe, non-blocking)."
|
||||
(when (and (string-match "ids(\\(.*?\\))" clean)
|
||||
(not (string-empty-p (s-trim (match-string 1 clean)))))
|
||||
(dolist (tid (split-string (match-string 1 clean) "[ ,]+" t))
|
||||
(let* ((clean-id (replace-regexp-in-string "[\"']\\|id:" "" tid))
|
||||
(pos (org-id-find clean-id t)) ;; Returns marker OR (file . pos)
|
||||
(let* ((clean-id (replace-regexp-in-string "[\"']\\|id: " "" tid))
|
||||
(pos (org-id-find clean-id t))
|
||||
(computed-end (gethash clean-id task-end-map))
|
||||
(blocker-end
|
||||
(cond
|
||||
(computed-end computed-end)
|
||||
(blocker-end nil))
|
||||
|
||||
;; Use a temporary buffer context to check properties if pos is a list
|
||||
((and pos (let ((m (if (markerp pos) pos (set-marker (make-marker) (cdr pos) (find-file-noselect (car pos))))))
|
||||
;; 1. Determine this specific blocker's end time
|
||||
(setq blocker-end
|
||||
(cond
|
||||
(computed-end computed-end) ;; Use what we just calculated in this session
|
||||
(pos (let ((m (if (markerp pos) pos
|
||||
(set-marker (make-marker) (cdr pos)
|
||||
(find-file-noselect (car pos))))))
|
||||
(with-current-buffer (marker-buffer m)
|
||||
(org-with-point-at m
|
||||
(cond
|
||||
;; Priority 1: Use actual CLOSED timestamp if DONE
|
||||
((org-entry-get nil "CLOSED")
|
||||
(org-time-string-to-time (org-entry-get nil "CLOSED")))
|
||||
;; Priority 2: Use range end if FIXED
|
||||
((string-equal "t" (org-entry-get nil "FIXED"))
|
||||
(gortium/org--get-range-end m))
|
||||
(t nil))))))
|
||||
;; This captures the result of the let/with-current-buffer block
|
||||
(let ((m (if (markerp pos) pos (set-marker (make-marker) (cdr pos) (find-file-noselect (car pos))))))
|
||||
(with-current-buffer (marker-buffer m)
|
||||
(org-with-point-at m
|
||||
(if (org-entry-get nil "CLOSED")
|
||||
(org-time-string-to-time (org-entry-get nil "CLOSED"))
|
||||
(gortium/org--get-range-end m))))))
|
||||
(t nil))))
|
||||
(t nil)))
|
||||
|
||||
;; 2. Update the "Latest" tracker
|
||||
(if blocker-end
|
||||
(setq latest-time (if (or (null latest-time) (time-less-p latest-time blocker-end))
|
||||
blocker-end latest-time))
|
||||
(when (or (null latest-time) (time-less-p latest-time blocker-end))
|
||||
(setq latest-time blocker-end))
|
||||
;; If ANY blocker is not resolved/found, the whole task is not ready
|
||||
(setq all-resolved nil)))))
|
||||
|
||||
;; Only return a time if EVERY ID in the list was successfully resolved
|
||||
(when all-resolved latest-time)))
|
||||
|
||||
(defun gortium/internal--update-properties (pos start wknd id end task-end-map)
|
||||
@@ -1905,10 +1686,14 @@ Returns list of task IDs involved in cycles, or nil if no cycles found."
|
||||
(with-current-buffer buf
|
||||
(org-element-with-disabled-cache
|
||||
(let* ((off-days (if (stringp offset) (string-to-number offset) 0))
|
||||
;; CRITICAL: For fixed tasks, base-start is THEIR start.
|
||||
;; For dependent tasks, base-start is the blocker's END.
|
||||
(base-start (cond (is-fixed (or (gortium/org--get-range-start pos) sched (current-time)))
|
||||
(t (or blocker-end (or sched (current-time))))))
|
||||
(base-start (cond
|
||||
;; 1. If FIXED, use its own defined start
|
||||
(is-fixed (or (gortium/org--get-range-start pos) sched (current-time)))
|
||||
;; 2. If it HAS a blocker, it MUST use blocker-end.
|
||||
;; If blocker-end is nil, this task isn't 'ready' yet.
|
||||
(has-blocker blocker-end)
|
||||
;; 3. If no blocker and not fixed, use current schedule or now
|
||||
(t (or sched (current-time)))))
|
||||
|
||||
(final-start (if is-fixed base-start
|
||||
(gortium/internal--snap-to-working-hours
|
||||
|
||||
Reference in New Issue
Block a user