Map subheadings not matching note field to fields if applicable

Subheadings not matching a note field are considered part of the
content before the first (sub)heading for field matching purposes.
This commit is contained in:
orgtre 2022-11-01 18:18:11 +01:00
parent bd08644cd4
commit 6f5df94102

View file

@ -621,27 +621,28 @@ Where the subtree is created depends on PREFIX."
(tags (cl-set-difference (anki-editor--get-tags) (tags (cl-set-difference (anki-editor--get-tags)
anki-editor-ignored-org-tags anki-editor-ignored-org-tags
:test #'string=)) :test #'string=))
(heading (anki-editor--export-string (heading (substring-no-properties (org-get-heading t t t)))
(substring-no-properties (org-get-heading t t t)) (level (org-current-level))
format))
(subheading-fields (anki-editor--build-fields))
(content-before-subheading-raw
(anki-editor--note-contents-before-subheading))
(content-before-subheading (content-before-subheading
(anki-editor--export-string content-before-subheading-raw format)) (anki-editor--note-contents-before-subheading))
fields) (subheading-fields (anki-editor--build-fields))
(fields (anki-editor--map-fields heading
content-before-subheading
subheading-fields
note-type
level))
(exported-fields (mapcar (lambda (x)
(cons
(car x)
(anki-editor--export-string (cdr x) format)))
fields)))
(unless deck (error "Missing deck")) (unless deck (error "Missing deck"))
(unless note-type (error "Missing note type")) (unless note-type (error "Missing note type"))
(setq fields (anki-editor--map-fields heading
content-before-subheading
content-before-subheading-raw
subheading-fields
note-type))
(make-anki-editor-note :id note-id (make-anki-editor-note :id note-id
:model note-type :model note-type
:deck deck :deck deck
:tags tags :tags tags
:fields fields))) :fields exported-fields)))
(defun anki-editor--get-tags () (defun anki-editor--get-tags ()
(let ((tags (anki-editor--entry-get-multivalued-property-with-inheritance (let ((tags (anki-editor--entry-get-multivalued-property-with-inheritance
@ -669,19 +670,19 @@ Return a list of cons of (FIELD-NAME . FIELD-CONTENT)."
for element = (org-element-at-point) for element = (org-element-at-point)
for heading = (substring-no-properties for heading = (substring-no-properties
(org-element-property :raw-value element)) (org-element-property :raw-value element))
for format = (anki-editor-entry-format)
;; contents-begin includes drawers and scheduling data, ;; contents-begin includes drawers and scheduling data,
;; which we'd like to ignore, here we skip these ;; which we'd like to ignore, here we skip these
;; elements and reset contents-begin. ;; elements and reset contents-begin.
for begin = (cl-loop for eoh = (org-element-property :contents-begin element) for begin = (save-excursion
then (org-element-property :end subelem) (cl-loop for eoh = (org-element-property :contents-begin element)
while eoh then (org-element-property :end subelem)
for subelem = (progn while eoh
(goto-char eoh) for subelem = (progn
(org-element-context)) (goto-char eoh)
while (memq (org-element-type subelem) (org-element-context))
'(drawer planning property-drawer)) while (memq (org-element-type subelem)
finally return (and eoh (org-element-property :begin subelem))) '(drawer planning property-drawer))
finally return (and eoh (org-element-property :begin subelem))))
for end = (org-element-property :contents-end element) for end = (org-element-property :contents-end element)
for raw = (or (and begin for raw = (or (and begin
end end
@ -692,8 +693,9 @@ Return a list of cons of (FIELD-NAME . FIELD-CONTENT)."
;; scope is `tree' ;; scope is `tree'
(min (point-max) end))) (min (point-max) end)))
"") "")
for content = (anki-editor--export-string raw format) ;; for content = (anki-editor--export-string raw format)
collect (cons heading content) ;; collect (cons heading content)
collect (cons heading raw)
;; proceed to next field entry and check last-pt to ;; proceed to next field entry and check last-pt to
;; see if it's already the last entry ;; see if it's already the last entry
do (org-forward-heading-same-level nil t) do (org-forward-heading-same-level nil t)
@ -735,40 +737,96 @@ Leading whitespace, drawers, and planning content is skipped."
(defun anki-editor--map-fields (heading (defun anki-editor--map-fields (heading
content-before-subheading content-before-subheading
content-before-subheading-raw
subheading-fields subheading-fields
note-type) note-type
level)
"Map `heading', pre-subheading content, and subheadings to fields. "Map `heading', pre-subheading content, and subheadings to fields.
When the `subheading-fields' don't match the `note-type's fields, When the `subheading-fields' don't match the `note-type's fields,
map missing fields to the `heading' and/or `content-before-subheading'. map missing fields to the `heading' and/or `content-before-subheading'.
Return a list of cons of (FIELD-NAME . FIELD-CONTENT)." Return a list of cons of (FIELD-NAME . FIELD-CONTENT)."
(anki-editor--with-collection-data-updated (anki-editor--with-collection-data-updated
(let ((fields subheading-fields)) (let* ((fields-matching (cl-intersection
(when-let ((missing (cl-set-difference (alist-get note-type
(alist-get note-type anki-editor--model-fields
anki-editor--model-fields nil nil #'string=)
nil nil #'string=) (mapcar #'car subheading-fields)
(mapcar #'car fields) :test #'string=))
:test #'string=))) (fields-missing (cl-set-difference
(if (and (equal 1 (length missing)) (alist-get note-type
(equal "" (string-trim content-before-subheading-raw))) anki-editor--model-fields
(push (cons (car missing) nil nil #'string=)
heading) (mapcar #'car subheading-fields)
fields) :test #'string=))
(if (equal 1 (length missing)) (fields-extra (cl-set-difference
(push (cons (car missing) (mapcar #'car subheading-fields)
content-before-subheading) (alist-get note-type
fields) anki-editor--model-fields
(progn nil nil #'string=)
(push (cons (nth 1 missing) :test #'string=))
content-before-subheading) (fields (cl-loop for f in fields-matching
fields) collect (cons f (alist-get
(push (cons (car missing) f subheading-fields
heading) nil nil #'string=)))))
fields))))) (cond ((equal 0 (length fields-missing))
(when (< 0 (length fields-extra))
(error "Failed to map all subheadings to a field.")))
((equal 1 (length fields-missing))
(if (equal 0 (length fields-extra))
(if (equal "" (string-trim content-before-subheading))
(push (cons (car fields-missing) heading)
fields)
(push (cons (car fields-missing) content-before-subheading)
fields))
(if (equal "" (string-trim content-before-subheading))
(push (cons (car fields-missing)
(anki-editor--concat-fields
fields-extra subheading-fields level))
fields)
(push (cons (car fields-missing)
(concat content-before-subheading
(anki-editor--concat-fields
fields-extra subheading-fields level)))
fields))))
((equal 2 (length fields-missing))
(if (equal 0 (length fields-extra))
(progn
(push (cons (nth 1 fields-missing)
content-before-subheading)
fields)
(push (cons (car fields-missing)
heading)
fields))
(if (equal "" (string-trim content-before-subheading))
(progn
(push (cons (nth 1 fields-missing)
(anki-editor--concat-fields
fields-extra subheading-fields level))
fields)
(push (cons (car fields-missing)
heading)
fields))
(progn
(push (cons (nth 1 fields-missing)
(concat content-before-subheading
(anki-editor--concat-fields
fields-extra subheading-fields level)))
fields)
(push (cons (car fields-missing)
heading)
fields)))))
((< 2 (length fields-missing))
(error "Cannot map note fields: More than two fields missing.")))
fields))) fields)))
(defun anki-editor--concat-fields (field-names field-alist level)
"Concat field names and content of fields in list `field-names'."
(let ((format (anki-editor-entry-format)))
(cl-loop for f in field-names
concat (concat (make-string (+ 1 level) ?*) " " f "\n\n"
(string-trim (alist-get f field-alist nil nil #'string=))
"\n\n"))))
;;; Minor mode ;;; Minor mode