branch: externals/org-gnosis
commit 3648b8d67e2a2ff1a38c8bae3f41b2334952d461
Author: Thanos Apollo <[email protected]>
Commit: Thanos Apollo <[email protected]>

    [Refactor] Simplify parsing and title processing.
    
    * Refactor parsing to use recursive approach
    * Simplify title processing
      * Remove duplicate title renaming logic
---
 org-gnosis.el | 200 +++++++++++++++++++++-------------------------------------
 1 file changed, 73 insertions(+), 127 deletions(-)

diff --git a/org-gnosis.el b/org-gnosis.el
index 76c6c1b5fb..c26217f7b7 100644
--- a/org-gnosis.el
+++ b/org-gnosis.el
@@ -167,17 +167,6 @@ If nil, journal entries are created as separate files in
 
 
 
-(defun org-gnosis--find-master-id (id-stack level topic-id)
-  "Find the appropriate master ID for a headline at LEVEL.
-ID-STACK contains parent IDs, LEVEL is current headline level,
-TOPIC-ID is fallback."
-  (if (= level 1)
-      topic-id
-    (or (cl-loop for i from (- level 2) downto 0
-                 for parent-id = (nth i id-stack)
-                 when parent-id return parent-id)
-        topic-id)))
-
 (defun org-gnosis--combine-tags (inherited-tags headline-tags)
   "Combine INHERITED-TAGS and HEADLINE-TAGS, removing duplicates."
   (delete-dups (append (or inherited-tags '()) (or headline-tags '()))))
@@ -206,55 +195,13 @@ Optional argument FLATTEN, when non-nil, flattens the 
result."
   "Drop TABLE from `gnosis-db'."
   (emacsql (org-gnosis-db-get) `[:drop-table ,table]))
 
-(defun org-gnosis-adjust-title (input &optional node-id)
-  "Adjust the INPUT string to replace id link structures with plain text.
-
-If node title contains an id link, it's inserted as link for NODE-ID
-in the database."
-  (cl-assert (and (stringp input) (not (string-empty-p input))) nil
-            "Input must be a non-empty string, got: %S")
-  (let* ((id-links '())
-        (new-input (replace-regexp-in-string
-                     "\\[\\[id:[^]]+\\]\\[\\(.*?\\)\\]\\]"
-                     (lambda (match)
-                       (let ((link-text (match-string 1 match)))
-                         (when (and link-text (not (string-empty-p link-text)))
-                           (push link-text id-links))
-                         link-text))
-                     input)))
-    ;; Only insert links if we have a valid node-id and found links
-    (when (and node-id id-links (not (string-empty-p node-id)))
-      (condition-case err
-          (emacsql-with-transaction (org-gnosis-db-get)
-            (cl-loop for link in (reverse id-links)
-                     do (org-gnosis--insert-into 'links `([,node-id ,link]))))
-        (error "Warning: Failed to insert title links for %s: %S" node-id 
err)))
-    new-input))
-
-(defun org-gnosis-parse-headline (headline inherited-tags master-id 
title-stack)
-  "Parse a HEADLINE and return a plist with its info.
-
-INHERITED-TAGS: Upper level headline tags.
-MASTER-ID: ID of the parent headline or topic.
-TITLE-STACK: List of parent titles for building hierarchical title.
-
-Note: This function assumes the headline has already been validated
-to have an ID."
-  (let* ((title (org-element-property :raw-value headline))
-         (id (org-element-property :ID headline))
-         (level (org-element-property :level headline))
-         (headline-tags (org-element-property :tags headline))
-         (all-tags (org-gnosis--combine-tags inherited-tags headline-tags))
-         (full-title (if title-stack
-                         (concat (mapconcat #'identity title-stack ":")
-                                 ":"
-                                 (string-trim title))
-                       (string-trim title))))
-    (list :title full-title
-          :id id
-          :tags all-tags
-          :master master-id
-          :level level)))
+(defun org-gnosis-adjust-title (input)
+  "Strip org link markup from INPUT, keeping only link descriptions.
+Converts [[id:xxx][Description]] to Description."
+  (replace-regexp-in-string
+   "\\[\\[id:[^]]+\\]\\[\\(.*?\\)\\]\\]"
+   "\\1"
+   input))
 
 (defun org-gnosis-get-id ()
   "Return id for heading at point."
@@ -309,7 +256,7 @@ Returns (title tags id). ID will be nil if no file-level ID 
exists."
                         (when (string= (org-element-property :key kw) "TITLE")
                           (org-element-property :value kw)))
                       nil t))
-        (title (when title-raw (org-gnosis-adjust-title title-raw id)))
+        (title (when title-raw (org-gnosis-adjust-title title-raw)))
         (tags (org-gnosis-get-filetags parsed-data)))
     ;; Only validate title, ID is optional
     (unless (and title (not (string-empty-p title)))
@@ -327,65 +274,73 @@ Returns (title tags id). ID will be nil if no file-level 
ID exists."
     (when (and filetags (not (string-empty-p (string-trim filetags))))
       (remove "" (split-string filetags ":")))))
 
-(defun org-gnosis-parse-topic (parsed-data)
-  "Parse topic information from the PARSED-DATA."
-  (let* ((topic-info (org-gnosis-get-data--topic parsed-data))
-         (topic-title (nth 0 topic-info))
-         (topic-tags (nth 1 topic-info))
-         (topic-id (nth 2 topic-info)))
-    (when topic-id
-      (list :title topic-title
-           :id topic-id :tags topic-tags :master 0 :level 0))))
+(defun org-gnosis--parse-headlines-recursive (element parent-id parent-title 
parent-tags)
+  "Recursively parse headlines from ELEMENT.
+ELEMENT can be the parsed-data (org-data) or a headline element.
+PARENT-ID is the ID of nearest ancestor with ID (or 0).
+PARENT-TITLE is the hierarchical title path (only from ancestors with IDs).
+PARENT-TAGS are the inherited tags from ancestors."
+  (let (results)
+    (org-element-map (org-element-contents element) 'headline
+      (lambda (headline)
+        (let* ((current-id (org-element-property :ID headline))
+               (title (org-element-property :raw-value headline))
+               (level (org-element-property :level headline))
+               (headline-tags (org-element-property :tags headline))
+               (combined-tags (org-gnosis--combine-tags parent-tags 
headline-tags)))
+
+          (if current-id
+              ;; This headline has an ID - process it
+              (let* ((clean-title (org-gnosis-adjust-title (string-trim 
title)))
+                     (full-title (if parent-title
+                                    (concat parent-title ":" clean-title)
+                                  clean-title))
+                     (entry (list :id current-id
+                                 :title full-title
+                                 :tags combined-tags
+                                 :master (or parent-id 0)
+                                 :level level))
+                     ;; Recursively process children with THIS as parent
+                     (children (org-gnosis--parse-headlines-recursive
+                               headline
+                               current-id
+                               full-title
+                               combined-tags)))
+                (setq results (append results (cons entry children))))
+
+            ;; No ID - skip this headline but process children
+            ;; Children inherit from the same parent context
+            (let ((children (org-gnosis--parse-headlines-recursive
+                           headline
+                           parent-id
+                           parent-title
+                           combined-tags)))
+              (setq results (append results children))))))
+      nil nil 'headline)
+    results))
 
 (defun org-gnosis-buffer-data (&optional data)
   "Parse DATA in current buffer for topics & headlines with their ID, tags, 
links."
   (let* ((parsed-data (or data (org-element-parse-buffer)))
-         (topic (org-gnosis-parse-topic parsed-data))
-         (topic-id (plist-get topic :id))
-         (topic-title (plist-get topic :title)))
-    ;; Topic is optional - only include if it has an ID
-    (let ((headlines '())
-          (tag-stack (list (plist-get topic :tags)))
-          (id-stack (list topic-id))
-          (title-stack '()))
-      (org-element-map parsed-data 'headline
-        (lambda (headline)
-          (let* ((level (org-element-property :level headline))
-                 (headline-tags (org-element-property :tags headline))
-                 (current-id (org-element-property :ID headline))
-                 (current-title (org-element-property :raw-value headline)))
-            ;; Adjust stacks to proper level
-            (while (>= (length id-stack) level)
-              (pop id-stack))
-            (while (>= (length tag-stack) level)
-              (pop tag-stack))
-            (while (>= (length title-stack) level)
-              (pop title-stack))
-            ;; Calculate combined tags
-            (let* ((inherited-tags (or (car tag-stack) '()))
-                   (combined-tags (org-gnosis--combine-tags inherited-tags 
headline-tags))
-                   ;; Build parent titles: only include topic title if it has 
an ID
-                   (parent-titles (if topic-id
-                                      (cons topic-title (reverse title-stack))
-                                    (reverse title-stack))))
-              ;; Calculate master ID from current stack state
-              (let ((master-id (when current-id
-                                (org-gnosis--find-master-id id-stack level 
topic-id))))
-                ;; Push current values to stacks for children
-                (push current-id id-stack)
-                (push combined-tags tag-stack)
-                ;; Only push title to stack if headline has an ID
-                (when current-id
-                  (push (string-trim current-title) title-stack))
-                ;; Only parse headlines with IDs
-                (when current-id
-                  (when-let* ((parsed (org-gnosis-parse-headline headline 
combined-tags master-id
-                                                                  
parent-titles)))
-                    (push parsed headlines))))))))
-      ;; Only include topic if it has an ID
-      (nreverse (if topic-id
-                    (cons topic headlines)
-                  headlines)))))
+         (topic-info (org-gnosis-get-data--topic parsed-data))
+         (topic-title (nth 0 topic-info))
+         (topic-tags (nth 1 topic-info))
+         (topic-id (nth 2 topic-info))
+         ;; Recursively parse all headlines
+         (headlines (org-gnosis--parse-headlines-recursive
+                    parsed-data
+                    topic-id
+                    (when topic-id topic-title)
+                    topic-tags)))
+    ;; Only include topic if it has an ID
+    (if topic-id
+        (cons (list :title topic-title
+                   :id topic-id
+                   :tags topic-tags
+                   :master 0
+                   :level 0)
+              headlines)
+      headlines)))
 
 (defun org-gnosis-get-file-info (filename)
   "Get data for FILENAME.
@@ -409,26 +364,17 @@ If JOURNAL is non-nil, update file as a journal entry."
             (data (butlast info))
             (table (if journal 'journal 'nodes))
             (filename (file-name-nondirectory file))
-            (links (and (> (length info) 1) (apply #'append (last info))))
-            (titles (org-gnosis-select 'title table nil t)))
+            (links (and (> (length info) 1) (apply #'append (last info)))))
        ;; Add gnosis topic and nodes
        (message "Parsing: %s" filename)
        (emacsql-with-transaction (org-gnosis-db-get)
          (cl-loop for item in data
                   when (plist-get item :id)  ;; Only process items with IDs
-                  do (let ((title (org-gnosis-adjust-title
-                                   (plist-get item :title)))
+                  do (let ((title (plist-get item :title))
                            (id (plist-get item :id))
                            (master (plist-get item :master))
                            (tags (plist-get item :tags))
                            (level (plist-get item :level)))
-                       ;; Handle duplicate titles gracefully
-                       (when (member title titles)
-                         (let ((counter 1))
-                           (while (member (format "%s (%d)" title counter) 
titles)
-                             (setq counter (1+ counter)))
-                           (setq title (format "%s (%d)" title counter))
-                           (message "Duplicate title found, renamed to: %s" 
title)))
                        (org-gnosis--insert-into table `([,id ,filename ,title 
,level
                                                              ,(prin1-to-string 
tags)]))
                        ;; Insert tags

Reply via email to