diff --git a/README.org b/README.org index d7ade25..ef4493b 100644 --- a/README.org +++ b/README.org @@ -51,16 +51,15 @@ anki-editor -- an Emacs package that helps you create Anki cards in Org-mode ** Command Cheatsheet - | Command | Keybinding | Brief Description | - |-----------------------------------------------+------------+---------------------------------------------------------------| - | =anki-editor-setup-default-keybindings= | N/A | Set up default keybindings. | - | =anki-editor-submit= | C-c a s | Send notes in current buffer to Anki. | - | =anki-editor-insert-deck= | C-c a i d | Insert a deck heading with the same level as current heading. | - | =anki-editor-insert-note= | C-c a i n | Insert the skeleton of a note. | - | =anki-editor-insert-tags= | C-c a i t | Insert a tag at point with autocompletion. | - | =anki-editor-export-heading-contents-to-html= | C-c a e | Export the contents of the heading at point to HTML. | - | =anki-editor-convert-region-to-html= | N/A | Convert and replace region to HTML. | - | =anki-editor-anki-connect-upgrade= | N/A | Upgrade anki-connect to the latest version. | + | Command | Keybinding | Brief Description | + |-----------------------------------------------+------------+--------------------------------------------------------------------------------------------------------------------------| + | =anki-editor-mode= | N/A | The minor mode. Your don't have to turn it on in order to use this package, but it sets the default keybindings for you. | + | =anki-editor-submit= | C-c a s | Send notes in current buffer to Anki. | + | =anki-editor-insert-deck= | C-c a i d | Insert a deck heading with the same level as current heading. | + | =anki-editor-insert-note= | C-c a i n | Insert the skeleton of a note. | + | =anki-editor-insert-tags= | C-c a i t | Insert a tag at point with autocompletion. | + | =anki-editor-export-heading-contents-to-html= | C-c a e | Export the contents of the heading at point to HTML. | + | =anki-editor-convert-region-to-html= | N/A | Convert and replace region to HTML. | *Since I'm not a native English speaker, let me know if there's any ambiguity or grammatical mistakes.* diff --git a/anki-editor.el b/anki-editor.el index b00304a..990411f 100644 --- a/anki-editor.el +++ b/anki-editor.el @@ -49,25 +49,49 @@ (defconst anki-editor-prop-failure-reason :ANKI_FAILURE_REASON) (defconst anki-editor-buffer-html-output "*anki-editor HTML Output*") +(defgroup anki-editor nil + "Customizations for anki-editor." + :group 'org) + (defcustom anki-editor-note-tag "note" - "Headings with this tag will be considered as notes." - :group 'anki-editor) + "Headings with this tag will be considered as notes.") + (defcustom anki-editor-deck-tag "deck" - "Headings with this tag will be considered as decks." - :group 'anki-editor) + "Headings with this tag will be considered as decks.") + +(defcustom anki-editor-keymap-prefix + (kbd "C-c a") + "Anki-editor keymap prefix.") (defcustom anki-editor-anki-connect-listening-address "127.0.0.1" - "The network address anki-connect is listening." - :group 'anki-editor) + "The network address anki-connect is listening.") (defcustom anki-editor-anki-connect-listening-port "8765" - "The port number anki-connect is listening." - :group 'anki-editor) + "The port number anki-connect is listening.") +;;; Minor Mode + +(defvar anki-editor--key-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "s") #'anki-editor-submit) + (define-key map (kbd "i d") #'anki-editor-insert-deck) + (define-key map (kbd "i n") #'anki-editor-insert-note) + (define-key map (kbd "i t") #'anki-editor-insert-tags) + (define-key map (kbd "e") #'anki-editor-export-heading-contents-to-html) + map)) + +(defvar anki-editor-mode-map + (let ((map (make-sparse-keymap))) + (define-key map anki-editor-keymap-prefix anki-editor--key-map) + map)) + +(define-minor-mode anki-editor-mode "Minor mode that helps you create Anki cards in an Org buffer." + :init-value nil + :keymap anki-editor-mode-map) ;;; anki-connect @@ -77,7 +101,7 @@ ("version" . ,version))) (request-body (json-encode (if params - (add-to-list 'data `("params" . ,params)) + (push `("params" . ,params) data) data))) (request-tempfile (make-temp-file "emacs-anki-editor"))) @@ -88,9 +112,9 @@ (let* ((raw-resp (shell-command-to-string (format "curl %s:%s --silent -X POST --data-binary @%s" - anki-editor-anki-connect-listening-address - anki-editor-anki-connect-listening-port - request-tempfile))) + (shell-quote-argument anki-editor-anki-connect-listening-address) + (shell-quote-argument anki-editor-anki-connect-listening-port) + (shell-quote-argument request-tempfile)))) resp error) (when (file-exists-p request-tempfile) (delete-file request-tempfile)) @@ -115,20 +139,22 @@ (defun anki-editor--anki-connect-map-note (note) "Convert NOTE to the form that anki-connect accepts." - `(("id" . ,(alist-get 'note-id note)) - ("deckName" . ,(alist-get 'deck note)) - ("modelName" . ,(alist-get 'note-type note)) - ("fields" . ,(alist-get 'fields note)) - ;; Convert tags to a vector since empty list is identical to nil - ;; which will become None in Python, but anki-connect requires it - ;; to be type of list. - ("tags" . ,(vconcat (alist-get 'tags note))))) + (let-alist note + (list (cons "id" .note-id) + (cons "deckName" .deck) + (cons "modelName" .note-type) + (cons "fields" .fields) + ;; Convert tags to a vector since empty list is identical to nil + ;; which will become None in Python, but anki-connect requires it + ;; to be type of list. + (cons "tags" (vconcat .tags))))) (defun anki-editor--anki-connect-heading-to-note (heading) "Convert HEADING to a note in the form that anki-connect accepts." (anki-editor--anki-connect-map-note (anki-editor--heading-to-note heading))) +;;; Commands ;;;###autoload (defun anki-editor-submit () @@ -197,12 +223,11 @@ that correspond to fields." (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) - (seq-each (lambda (field) - (save-excursion - (org-insert-heading-respect-content) - (org-do-demote) - (insert field))) - fields) + (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))) @@ -239,34 +264,21 @@ that correspond to fields." (insert (anki-editor--generate-html (delete-and-extract-region (region-beginning) (region-end))))) -(setq anki-editor--key-map `((,(kbd "C-c a s") . ,#'anki-editor-submit) - (,(kbd "C-c a i d") . ,#'anki-editor-insert-deck) - (,(kbd "C-c a i n") . ,#'anki-editor-insert-note) - (,(kbd "C-c a i t") . ,#'anki-editor-insert-tags) - (,(kbd "C-c a e") . ,#'anki-editor-export-heading-contents-to-html))) - -;;;###autoload -(defun anki-editor-setup-default-keybindings () - "Set up the default keybindings." - (interactive) - (dolist (map anki-editor--key-map) - (local-set-key (car map) (cdr map))) - (message "anki-editor default keybindings have been set")) - ;;;###autoload (defun anki-editor-anki-connect-upgrade () "Upgrade anki-connect to the latest version. This will display a confirmation dialog box in Anki asking if you -want to continue. The upgrading is done by downloading the latest +want to continue. The upgrading is done by downloading the latest code in the master branch of its Github repo. This is useful when new version of this package depends on the bugfixes or new features of anki-connect." (interactive) - (let ((result (anki-editor--anki-connect-invoke-result "upgrade" 5))) - (when (and (booleanp result) result) - (message "anki-connect has upgraded, you may have to restart Anki to make it in effect.")))) + (when (yes-or-no-p "NOTE: This will download the latest codebase of anki-connect to your system, which is not guaranteed to be safe or stable. Generally, you don't need this command, this is useful only when new version of this package requires the updates of anki-connect that are not released yet. Do you still want to continue?") + (let ((result (anki-editor--anki-connect-invoke-result "upgrade" 5))) + (when (and (booleanp result) result) + (message "anki-connect has been upgraded, you might have to restart Anki to make it in effect."))))) ;;; Core Functions @@ -284,7 +296,7 @@ DECK is used when the action is note creation." (insert content) (car (org-element-contents (org-element-parse-buffer))))) note (anki-editor--heading-to-note note-elem)) - (add-to-list 'note `(deck . ,deck)) + (push `(deck . ,deck) note) (anki-editor--save-note note))) (defun anki-editor--save-note (note) @@ -395,19 +407,19 @@ DECK is used when the action is note creation." (lambda (original) (anki-editor--hash type original)))) - (add-to-list 'anki-editor--replacement-records - `(,(cdr memo) . ((type . ,type) - (original . ,(car memo)))))))) + (push `(,(cdr memo) . ((type . ,type) + (original . ,(car memo)))) + anki-editor--replacement-records)))) -(setq anki-editor--anki-latex-syntax-map - `((,(format "^%s" (regexp-quote "$$")) . "[$$]") - (,(format "%s$" (regexp-quote "$$")) . "[/$$]") - (,(format "^%s" (regexp-quote "$")) . "[$]") - (,(format "%s$" (regexp-quote "$")) . "[/$]") - (,(format "^%s" (regexp-quote "\\(")) . "[$]") - (,(format "%s$" (regexp-quote "\\)")) . "[/$]") - (,(format "^%s" (regexp-quote "\\[")) . "[$$]") - (,(format "%s$" (regexp-quote "\\]")) . "[/$$]"))) +(defvar anki-editor--anki-latex-syntax-map + `((,(format "^%s" (regexp-quote "$$")) . "[$$]") + (,(format "%s$" (regexp-quote "$$")) . "[/$$]") + (,(format "^%s" (regexp-quote "$")) . "[$]") + (,(format "%s$" (regexp-quote "$")) . "[/$]") + (,(format "^%s" (regexp-quote "\\(")) . "[$]") + (,(format "%s$" (regexp-quote "\\)")) . "[/$]") + (,(format "^%s" (regexp-quote "\\[")) . "[$$]") + (,(format "%s$" (regexp-quote "\\]")) . "[/$$]"))) (defun anki-editor--wrap-latex (content) "Wrap CONTENT with Anki-style latex markers." @@ -432,7 +444,7 @@ DECK is used when the action is note creation." (pcase (alist-get 'type ele-data) ('latex-fragment (replace-match (anki-editor--convert-latex-fragment (alist-get 'original ele-data)) t t)) ('latex-environment (replace-match (anki-editor--wrap-latex (alist-get 'original ele-data)) t t))) - (add-to-list 'translated record))) + (push record translated))) (setq anki-editor--replacement-records (cl-set-difference anki-editor--replacement-records translated)))) ;;; Utilities