Make insertion commands more smart
This commit is contained in:
parent
763c0368a6
commit
9ef7759fa7
2 changed files with 115 additions and 29 deletions
|
@ -52,9 +52,9 @@ anki-editor -- an Emacs package that helps you create Anki cards in Org-mode
|
|||
** Command Cheatsheet
|
||||
|
||||
| Command | Brief Description |
|
||||
|-----------------------------------------------+---------------------------------------------------------------|
|
||||
|-----------------------------------------------+------------------------------------------------------|
|
||||
| =anki-editor-submit= | Send notes in current buffer to Anki. |
|
||||
| =anki-editor-insert-deck= | Insert a deck heading with the same level as current heading. |
|
||||
| =anki-editor-insert-deck= | Insert a deck heading. |
|
||||
| =anki-editor-insert-note= | Insert the skeleton of a note. |
|
||||
| =anki-editor-insert-tags= | Insert a tag at point with autocompletion. |
|
||||
| =anki-editor-export-heading-contents-to-html= | Export the contents of the heading at point to HTML. |
|
||||
|
|
124
anki-editor.el
124
anki-editor.el
|
@ -170,23 +170,46 @@ of that heading."
|
|||
|
||||
;;;###autoload
|
||||
(defun anki-editor-insert-deck (&optional prefix)
|
||||
"Insert a deck heading with the same level as current heading.
|
||||
With PREFIX, only insert the deck name."
|
||||
"Insert a deck heading.
|
||||
With PREFIX, only insert the deck name at point."
|
||||
(interactive "P")
|
||||
(message "Fetching decks...")
|
||||
(let ((decks (sort (anki-editor--anki-connect-invoke-result "deckNames" 5) #'string-lessp))
|
||||
deckname)
|
||||
(setq deckname (completing-read "Choose a deck: " decks))
|
||||
(unless prefix (org-insert-heading-respect-content))
|
||||
(if prefix
|
||||
(insert deckname)
|
||||
(unless prefix (anki-editor--set-tags-fix anki-editor-deck-tag))))
|
||||
(let (inserted)
|
||||
(anki-editor--visit-superior-headings
|
||||
(lambda ()
|
||||
(when (member anki-editor-deck-tag (org-get-tags))
|
||||
(anki-editor--insert-deck-heading deckname)
|
||||
(setq inserted t))))
|
||||
|
||||
(unless inserted
|
||||
(anki-editor--insert-deck-heading deckname))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun anki-editor-insert-note ()
|
||||
"Insert the skeleton of a note.
|
||||
|
||||
The contents to be insrted are structured with a note heading
|
||||
that's one level lower to the current one as well as subheadings
|
||||
that correspond to fields."
|
||||
along with subheadings that correspond to fields.
|
||||
|
||||
Where the note is inserted depends on where the point is.
|
||||
|
||||
When the point is somewhere inside a note heading, the new note
|
||||
is inserted below this note with same heading level.
|
||||
|
||||
Or when the point is outside any note heading but inside a
|
||||
heading that isn't tagged with 'deck' but under a deck heading,
|
||||
the new note is one level lower to and is inserted at the bottom
|
||||
of this heading.
|
||||
|
||||
Or when the point is inside a deck heading, the behavior is the
|
||||
same as above.
|
||||
|
||||
Otherwise, it's inserted at point."
|
||||
(interactive)
|
||||
(message "Fetching note types...")
|
||||
(let ((note-types (sort (anki-editor--anki-connect-invoke-result "modelNames" 5) #'string-lessp))
|
||||
|
@ -195,19 +218,39 @@ that correspond to fields."
|
|||
(message "Fetching note fields...")
|
||||
(setq fields (anki-editor--anki-connect-invoke-result "modelFieldNames" 5 `((modelName . ,note-type)))
|
||||
note-heading (read-from-minibuffer "Enter the heading: " "Item"))
|
||||
(org-insert-heading-respect-content)
|
||||
(org-do-demote)
|
||||
(insert note-heading)
|
||||
(anki-editor--set-tags-fix anki-editor-note-tag)
|
||||
(org-set-property (substring (symbol-name anki-editor-prop-note-type) 1) note-type)
|
||||
(dolist (field fields)
|
||||
(save-excursion
|
||||
(org-insert-heading-respect-content)
|
||||
(org-do-demote)
|
||||
(insert field)))
|
||||
(org-next-visible-heading 1)
|
||||
(end-of-line)
|
||||
(newline-and-indent)))
|
||||
|
||||
;; find and go to the best position, then insert the note
|
||||
(let ((cur-point (point))
|
||||
pt-of-grp
|
||||
inserted)
|
||||
(anki-editor--visit-superior-headings
|
||||
(lambda ()
|
||||
(let ((tags (org-get-tags)))
|
||||
(cond
|
||||
;; if runs into a note heading, inserts the note heading with
|
||||
;; the same level
|
||||
((member anki-editor-note-tag tags)
|
||||
(progn
|
||||
(anki-editor--insert-note-skeleton note-heading note-type fields)
|
||||
(setq inserted t)
|
||||
t))
|
||||
;; if runs into a deck heading, inserts the note heading one
|
||||
;; level lower to current deck heading or to the group
|
||||
;; heading that was visited before
|
||||
((member anki-editor-deck-tag tags)
|
||||
(progn
|
||||
(when pt-of-grp (goto-char pt-of-grp))
|
||||
(anki-editor--insert-note-skeleton note-heading note-type fields t)
|
||||
(setq inserted t)
|
||||
t))
|
||||
;; otherwise, consider it as a group heading and save its
|
||||
;; point for further consideration, then continue
|
||||
(t (progn
|
||||
(unless pt-of-grp (setq pt-of-grp (point)))
|
||||
nil))))))
|
||||
(unless inserted
|
||||
(goto-char cur-point)
|
||||
(anki-editor--insert-note-skeleton note-heading note-type fields)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun anki-editor-insert-tags ()
|
||||
|
@ -259,6 +302,12 @@ bugfixes or new features of anki-connect."
|
|||
|
||||
;;; Core Functions
|
||||
|
||||
(defun anki-editor--insert-deck-heading (deckname)
|
||||
"Insert a deck heading with DECKNAME."
|
||||
(org-insert-heading-respect-content)
|
||||
(insert deckname)
|
||||
(anki-editor--set-tags-fix anki-editor-deck-tag))
|
||||
|
||||
(defun anki-editor--process-note-heading (deck)
|
||||
"Process note heading at point.
|
||||
DECK is used when the action is note creation."
|
||||
|
@ -276,6 +325,28 @@ DECK is used when the action is note creation."
|
|||
(push `(deck . ,deck) note)
|
||||
(anki-editor--save-note note)))
|
||||
|
||||
(defun anki-editor--insert-note-skeleton (heading note-type fields &optional demote)
|
||||
"Insert a note skeleton with HEADING, NOTE-TYPE and FIELDS.
|
||||
If DEMOTE is t, demote the inserted note heading."
|
||||
(org-insert-heading-respect-content)
|
||||
(when demote (org-do-demote))
|
||||
(insert heading)
|
||||
(anki-editor--set-tags-fix anki-editor-note-tag)
|
||||
(org-set-property (substring (symbol-name anki-editor-prop-note-type) 1) note-type)
|
||||
(dolist (field fields)
|
||||
(save-excursion
|
||||
(org-insert-heading-respect-content)
|
||||
(org-do-demote)
|
||||
(insert field)))
|
||||
|
||||
;; TODO: Is it a good idea to automatically move to the first field
|
||||
;; heading and open a new line ?
|
||||
|
||||
;; (org-next-visible-heading 1)
|
||||
;; (end-of-line)
|
||||
;; (newline-and-indent)
|
||||
)
|
||||
|
||||
(defun anki-editor--save-note (note)
|
||||
"Request anki-connect for updating or creating NOTE."
|
||||
(if (= (alist-get 'note-id note) -1)
|
||||
|
@ -446,6 +517,21 @@ DECK is used when the action is note creation."
|
|||
(insert replacement)
|
||||
(cons original replacement)))
|
||||
|
||||
(defun anki-editor--visit-superior-headings (visitor &optional level)
|
||||
"Move point to and call VISITOR at each superior heading from point.
|
||||
Don't pass LEVEL, it's only used in recursion.
|
||||
Stops when VISITOR returns t or point reaches the beginning of buffer."
|
||||
(let (stop)
|
||||
(when (org-at-heading-p)
|
||||
(let ((cur-level (car (org-heading-components))))
|
||||
(when (or (null level) (< cur-level level))
|
||||
(setq level cur-level
|
||||
stop (funcall visitor)))))
|
||||
(when (and (not stop) (/= (point) (point-min)))
|
||||
(org-previous-visible-heading 1)
|
||||
(anki-editor--visit-superior-headings visitor level))))
|
||||
|
||||
|
||||
(provide 'anki-editor)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
Loading…
Reference in a new issue