Make insertion commands more smart

This commit is contained in:
louie 2018-01-22 21:57:35 +08:00
parent 763c0368a6
commit 9ef7759fa7
2 changed files with 115 additions and 29 deletions

View file

@ -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. |

View file

@ -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)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;