Allow specifying field values from org-mode properties (#22)
* Allow specifying field values from org-mode properties * Update documentation and fine-tune the diff * Refactor, fix indentation, and other minor fixes Renamed all-fields to named-fields since the heading and content before first subheading can also be a field. --------- Co-authored-by: Renat Galimov <renat@Renats-MacBook-Pro.local> Co-authored-by: orgtre <orgtre@posteo.net>
This commit is contained in:
parent
87394670a2
commit
ab7b33b48c
2 changed files with 113 additions and 92 deletions
13
README.org
13
README.org
|
@ -84,6 +84,19 @@ If you're using [[https://github.com/radian-software/straight.el][straight.el]]
|
||||||
This works for all note types, just make the first 2 fields absent and
|
This works for all note types, just make the first 2 fields absent and
|
||||||
~anki-editor~ will use note heading as first field and the text below the heading as second field.
|
~anki-editor~ will use note heading as first field and the text below the heading as second field.
|
||||||
|
|
||||||
|
,* You can extract a field value from an org-property
|
||||||
|
:PROPERTIES:
|
||||||
|
:ANKI_NOTE_TYPE: Basic
|
||||||
|
:ANKI_FIELD_FRONT: Can one define an anki-field inside an org-mode property?
|
||||||
|
:ANKI_PREPEND_HEADING: nil
|
||||||
|
:END:
|
||||||
|
|
||||||
|
Yes. In this example, =anki-editor= will use the =ANKI_FIELD_FRONT= property value as
|
||||||
|
a front side of the Anki card and the body of the card as its back.
|
||||||
|
|
||||||
|
,** Front
|
||||||
|
Notice that property fields will override subheading fields.
|
||||||
|
This block will be skipped
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
- Anki deck is provided by ~ANKI_DECK~ property. This property is
|
- Anki deck is provided by ~ANKI_DECK~ property. This property is
|
||||||
|
|
188
anki-editor.el
188
anki-editor.el
|
@ -442,6 +442,8 @@ The implementation is borrowed and simplified from ox-html."
|
||||||
(defconst anki-editor-prop-deck "ANKI_DECK")
|
(defconst anki-editor-prop-deck "ANKI_DECK")
|
||||||
(defconst anki-editor-prop-format "ANKI_FORMAT")
|
(defconst anki-editor-prop-format "ANKI_FORMAT")
|
||||||
(defconst anki-editor-prop-prepend-heading "ANKI_PREPEND_HEADING")
|
(defconst anki-editor-prop-prepend-heading "ANKI_PREPEND_HEADING")
|
||||||
|
(defconst anki-editor-prop-field-prefix "ANKI_FIELD_"
|
||||||
|
"Anki fields with names got from an org-node property.")
|
||||||
(defconst anki-editor-prop-tags "ANKI_TAGS")
|
(defconst anki-editor-prop-tags "ANKI_TAGS")
|
||||||
(defconst anki-editor-prop-tags-plus (concat anki-editor-prop-tags "+"))
|
(defconst anki-editor-prop-tags-plus (concat anki-editor-prop-tags "+"))
|
||||||
(defconst anki-editor-prop-failure-reason "ANKI_FAILURE_REASON")
|
(defconst anki-editor-prop-failure-reason "ANKI_FAILURE_REASON")
|
||||||
|
@ -806,6 +808,14 @@ Return a list of cons of (FIELD-NAME . FIELD-CONTENT)."
|
||||||
do (org-forward-heading-same-level nil t)
|
do (org-forward-heading-same-level nil t)
|
||||||
until (= last-pt (point)))))
|
until (= last-pt (point)))))
|
||||||
|
|
||||||
|
(defun anki-editor--property-fields (fields)
|
||||||
|
"Extract Anki FIELDS from entry properties."
|
||||||
|
(cl-loop for field in fields
|
||||||
|
for property = (concat anki-editor-prop-field-prefix (upcase field))
|
||||||
|
for property-value = (org-entry-get-with-inheritance property)
|
||||||
|
when property-value
|
||||||
|
collect (cons field property-value)))
|
||||||
|
|
||||||
(defun anki-editor--note-contents-before-subheading ()
|
(defun anki-editor--note-contents-before-subheading ()
|
||||||
"Get content between heading at point and next sub/heading.
|
"Get content between heading at point and next sub/heading.
|
||||||
|
|
||||||
|
@ -857,97 +867,95 @@ 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-matching (cl-intersection
|
(let* ((model-fields (alist-get
|
||||||
(alist-get note-type
|
note-type anki-editor--model-fields
|
||||||
anki-editor--model-fields
|
nil nil #'string=))
|
||||||
nil nil #'string=)
|
(property-fields (anki-editor--property-fields model-fields))
|
||||||
(mapcar #'car subheading-fields)
|
(named-fields (seq-uniq (append subheading-fields property-fields)
|
||||||
:test #'string=))
|
(lambda (left right)
|
||||||
(fields-missing (cl-set-difference
|
(string= (car left) (car right)))))
|
||||||
(alist-get note-type
|
(fields-matching (cl-intersection
|
||||||
anki-editor--model-fields
|
model-fields (mapcar #'car named-fields)
|
||||||
nil nil #'string=)
|
:test #'string=))
|
||||||
(mapcar #'car subheading-fields)
|
(fields-missing (cl-set-difference
|
||||||
:test #'string=))
|
model-fields (mapcar #'car named-fields)
|
||||||
(fields-extra (cl-set-difference
|
:test #'string=))
|
||||||
(mapcar #'car subheading-fields)
|
(fields-extra (cl-set-difference
|
||||||
(alist-get note-type
|
(mapcar #'car named-fields) model-fields
|
||||||
anki-editor--model-fields
|
:test #'string=))
|
||||||
nil nil #'string=)
|
(fields (cl-loop for f in fields-matching
|
||||||
:test #'string=))
|
collect (cons f (alist-get
|
||||||
(fields (cl-loop for f in fields-matching
|
f named-fields
|
||||||
collect (cons f (alist-get
|
nil nil #'string=))))
|
||||||
f subheading-fields
|
(heading-format anki-editor-prepend-heading-format))
|
||||||
nil nil #'string=))))
|
(cond ((equal 0 (length fields-missing))
|
||||||
(heading-format anki-editor-prepend-heading-format))
|
(when (< 0 (length fields-extra))
|
||||||
(cond ((equal 0 (length fields-missing))
|
(user-error "Failed to map all named fields")))
|
||||||
(when (< 0 (length fields-extra))
|
((equal 1 (length fields-missing))
|
||||||
(user-error "Failed to map all subheadings to a field")))
|
(if (equal 0 (length fields-extra))
|
||||||
((equal 1 (length fields-missing))
|
(if (equal "" (string-trim content-before-subheading))
|
||||||
(if (equal 0 (length fields-extra))
|
(push (cons (car fields-missing) heading)
|
||||||
(if (equal "" (string-trim content-before-subheading))
|
fields)
|
||||||
(push (cons (car fields-missing) heading)
|
(if prepend-heading
|
||||||
fields)
|
(push (cons (car fields-missing)
|
||||||
(if prepend-heading
|
(concat
|
||||||
(push (cons (car fields-missing)
|
(format heading-format heading)
|
||||||
(concat
|
content-before-subheading))
|
||||||
(format heading-format heading)
|
fields)
|
||||||
content-before-subheading))
|
(push (cons (car fields-missing)
|
||||||
fields)
|
content-before-subheading)
|
||||||
(push (cons (car fields-missing)
|
fields)))
|
||||||
content-before-subheading)
|
(if (equal "" (string-trim content-before-subheading))
|
||||||
fields)))
|
(push (cons (car fields-missing)
|
||||||
(if (equal "" (string-trim content-before-subheading))
|
(anki-editor--concat-fields
|
||||||
(push (cons (car fields-missing)
|
fields-extra subheading-fields level))
|
||||||
(anki-editor--concat-fields
|
fields)
|
||||||
fields-extra subheading-fields level))
|
(if prepend-heading
|
||||||
fields)
|
(push (cons (car fields-missing)
|
||||||
(if prepend-heading
|
(concat
|
||||||
(push (cons (car fields-missing)
|
(format heading-format heading)
|
||||||
(concat
|
content-before-subheading
|
||||||
(format heading-format heading)
|
|
||||||
content-before-subheading
|
|
||||||
(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
|
(anki-editor--concat-fields
|
||||||
fields-extra subheading-fields level))
|
fields-extra subheading-fields
|
||||||
fields)
|
level)))
|
||||||
(push (cons (car fields-missing)
|
fields)
|
||||||
heading)
|
(push (cons (car fields-missing)
|
||||||
fields))
|
(concat content-before-subheading
|
||||||
(progn
|
(anki-editor--concat-fields
|
||||||
(push (cons (nth 1 fields-missing)
|
fields-extra subheading-fields
|
||||||
(concat content-before-subheading
|
level)))
|
||||||
(anki-editor--concat-fields
|
fields)))))
|
||||||
fields-extra subheading-fields level)))
|
((equal 2 (length fields-missing))
|
||||||
fields)
|
(if (equal 0 (length fields-extra))
|
||||||
(push (cons (car fields-missing)
|
(progn
|
||||||
heading)
|
(push (cons (nth 1 fields-missing)
|
||||||
fields)))))
|
content-before-subheading)
|
||||||
((< 2 (length fields-missing))
|
fields)
|
||||||
(user-error (concat "Cannot map note fields: "
|
(push (cons (car fields-missing)
|
||||||
"more than two fields missing"))))
|
heading)
|
||||||
fields)))
|
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))
|
||||||
|
(user-error (concat "Cannot map note fields: "
|
||||||
|
"more than two fields missing"))))
|
||||||
|
fields)))
|
||||||
|
|
||||||
(defun anki-editor--concat-fields (field-names field-alist level)
|
(defun anki-editor--concat-fields (field-names field-alist level)
|
||||||
"Concat field names and content of fields in list `field-names'."
|
"Concat field names and content of fields in list `field-names'."
|
||||||
|
|
Loading…
Reference in a new issue