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:
parent
bd08644cd4
commit
6f5df94102
1 changed files with 109 additions and 51 deletions
160
anki-editor.el
160
anki-editor.el
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue