Refactor api functions

This commit is contained in:
louie 2019-11-03 00:22:27 +08:00 committed by louie
parent 3c52675134
commit 375d6d66d2

View file

@ -77,7 +77,7 @@
(defconst anki-editor-org-tag-regexp "^\\([[:alnum:]_@#%]+\\)+$") (defconst anki-editor-org-tag-regexp "^\\([[:alnum:]_@#%]+\\)+$")
(defconst anki-editor-exporter-raw "raw") (defconst anki-editor-exporter-raw "raw")
(defconst anki-editor-exporter-default "default") (defconst anki-editor-exporter-default "default")
(defconst anki-editor-ankiconnect-version 5) (defconst anki-editor-api-version 5)
(defgroup anki-editor nil (defgroup anki-editor nil
"Customizations for anki-editor." "Customizations for anki-editor."
@ -109,11 +109,11 @@ they're absent in Org entries, such as special tags `marked',
form entries." form entries."
:type '(repeat string)) :type '(repeat string))
(defcustom anki-editor-anki-connect-listening-address (defcustom anki-editor-api-host
"127.0.0.1" "127.0.0.1"
"The network address AnkiConnect is listening.") "The network address AnkiConnect is listening.")
(defcustom anki-editor-anki-connect-listening-port (defcustom anki-editor-api-port
"8765" "8765"
"The port number AnkiConnect is listening.") "The port number AnkiConnect is listening.")
@ -122,14 +122,6 @@ form entries."
;;; AnkiConnect ;;; AnkiConnect
(defun anki-editor--anki-connect-action (action &optional params version)
(let (a)
(when version
(push `(version . ,version) a))
(when params
(push `(params . ,params) a))
(push `(action . ,action) a)))
(defun anki-editor--anki-connect-invoke-queue () (defun anki-editor--anki-connect-invoke-queue ()
(let (action-queue) (let (action-queue)
(lambda (&optional action params handler) (lambda (&optional action params handler)
@ -139,19 +131,22 @@ form entries."
(apply #'anki-editor--anki-connect-invoke-multi (nreverse action-queue)) (apply #'anki-editor--anki-connect-invoke-multi (nreverse action-queue))
(setq action-queue nil)))))) (setq action-queue nil))))))
(defun anki-editor--anki-connect-invoke (action &optional params) (defun anki-editor-api-call (action &rest params)
"Invoke AnkiConnect with ACTION and PARAMS." "Invoke AnkiConnect with ACTION and PARAMS."
(let ((request-body (json-encode (let ((payload (list :action action :version anki-editor-api-version))
(anki-editor--anki-connect-action action params anki-editor-ankiconnect-version)))
(request-backend 'curl) (request-backend 'curl)
(json-array-type 'list) (json-array-type 'list)
reply err) reply err)
(when params
(plist-put payload :params params))
(request (format "http://%s:%s" (request (format "http://%s:%s"
anki-editor-anki-connect-listening-address anki-editor-api-host
anki-editor-anki-connect-listening-port) anki-editor-api-port)
:type "POST" :type "POST"
:parser 'json-read :parser 'json-read
:data request-body :data (json-encode payload)
:success (cl-function (lambda (&key data &allow-other-keys) :success (cl-function (lambda (&key data &allow-other-keys)
(setq reply data))) (setq reply data)))
:error (cl-function (lambda (&key _ &key error-thrown &allow-other-keys) :error (cl-function (lambda (&key _ &key error-thrown &allow-other-keys)
@ -160,12 +155,12 @@ form entries."
(when err (error "Error communicating with AnkiConnect using cURL: %s" err)) (when err (error "Error communicating with AnkiConnect using cURL: %s" err))
(or reply (error "Got empty reply from AnkiConnect")))) (or reply (error "Got empty reply from AnkiConnect"))))
(defmacro anki-editor--anki-connect-invoke-result (&rest args) (defun anki-editor-api-call-result (&rest args)
"Invoke AnkiConnect with ARGS, return the result from response "Invoke AnkiConnect with ARGS, return the result from response
or raise an error." or raise an error."
`(let-alist (anki-editor--anki-connect-invoke ,@args) (let-alist (apply #'anki-editor-api-call args)
(when .error (error .error)) (when .error (error .error))
.result)) .result))
(defun anki-editor--anki-connect-invoke-multi (&rest actions) (defun anki-editor--anki-connect-invoke-multi (&rest actions)
"Invoke AnkiConnect with ACTIONS, a list of (action . result-handler) pairs." "Invoke AnkiConnect with ACTIONS, a list of (action . result-handler) pairs."
@ -190,7 +185,7 @@ or raise an error."
;; to be type of list. ;; to be type of list.
(cons "tags" (vconcat .tags))))) (cons "tags" (vconcat .tags)))))
(defun anki-editor--anki-connect-store-media-file (path) (defun anki-editor-api--store-media-file (path)
"Store media file for PATH, which is an absolute file name. "Store media file for PATH, which is an absolute file name.
The result is the path to the newly stored media file." The result is the path to the newly stored media file."
(let* ((bytes (with-temp-buffer (let* ((bytes (with-temp-buffer
@ -200,17 +195,14 @@ The result is the path to the newly stored media file."
(media-file-name (format "%s-%s%s" (media-file-name (format "%s-%s%s"
(file-name-base path) (file-name-base path)
hash hash
(file-name-extension path t))) (file-name-extension path t))))
content) (when (eq :json-false
(when (equal :json-false (anki-editor--anki-connect-invoke-result (anki-editor-api-call-result 'retrieveMediaFile
"retrieveMediaFile" :filename media-file-name))
`((filename . ,media-file-name))))
(message "Storing media file %s to Anki, this might take a while" path) (message "Storing media file %s to Anki, this might take a while" path)
(setq content (base64-encode-string bytes)) (anki-editor-api-call-result 'storeMediaFile
(anki-editor--anki-connect-invoke-result :filename media-file-name
"storeMediaFile" :data (base64-encode-string bytes)))
`((filename . ,media-file-name)
(data . ,content))))
media-file-name)) media-file-name))
@ -331,7 +323,7 @@ The implementation is borrowed and simplified from ox-html."
(file-name-absolute-p raw-path)) (file-name-absolute-p raw-path))
(setq raw-path (concat (file-name-as-directory home) raw-path))) (setq raw-path (concat (file-name-as-directory home) raw-path)))
;; storing file to Anki and return the modified path ;; storing file to Anki and return the modified path
(anki-editor--anki-connect-store-media-file (expand-file-name (url-unhex-string raw-path))))) (anki-editor-api--store-media-file (expand-file-name (url-unhex-string raw-path)))))
(t (throw 'giveup nil)))) (t (throw 'giveup nil))))
(attributes-plist (attributes-plist
(let* ((parent (org-export-get-parent-element link)) (let* ((parent (org-export-get-parent-element link))
@ -354,7 +346,7 @@ The implementation is borrowed and simplified from ox-html."
;; Audio file. ;; Audio file.
((string-suffix-p ".mp3" path t) ((string-suffix-p ".mp3" path t)
(format "[sound:%s]" path)) (format "[sound:%s]" path))
;; External link with a description part. ;; External link with a description part.
((and path desc) (format "<a href=\"%s\"%s>%s</a>" ((and path desc) (format "<a href=\"%s\"%s>%s</a>"
@ -479,11 +471,11 @@ Where the subtree is created depends on PREFIX."
(defun anki-editor-all-tags () (defun anki-editor-all-tags ()
"Get all tags from Anki." "Get all tags from Anki."
(anki-editor--anki-connect-invoke-result "getTags")) (anki-editor-api-call-result 'getTags))
(defun anki-editor-deck-names () (defun anki-editor-deck-names ()
"Get all decks names from Anki." "Get all decks names from Anki."
(anki-editor--anki-connect-invoke-result "deckNames")) (anki-editor-api-call-result 'deckNames))
(defun anki-editor--enable-tag-completion () (defun anki-editor--enable-tag-completion ()
(and anki-editor-mode anki-editor-org-tags-as-anki-tags)) (and anki-editor-mode anki-editor-org-tags-as-anki-tags))
@ -504,7 +496,7 @@ Where the subtree is created depends on PREFIX."
(defun anki-editor-note-types () (defun anki-editor-note-types ()
"Get note types from Anki." "Get note types from Anki."
(anki-editor--anki-connect-invoke-result "modelNames")) (anki-editor-api-call-result 'modelNames))
(defun anki-editor-note-at-point () (defun anki-editor-note-at-point ()
"Construct an alist representing a note from current entry." "Construct an alist representing a note from current entry."
@ -632,7 +624,7 @@ name and the cdr of which is field content."
(defun anki-editor-setup-minor-mode () (defun anki-editor-setup-minor-mode ()
"Set up this minor mode." "Set up this minor mode."
(anki-editor-anki-connect-check) (anki-editor-api-check)
(add-hook 'org-property-allowed-value-functions #'anki-editor--get-allowed-values-for-property nil t) (add-hook 'org-property-allowed-value-functions #'anki-editor--get-allowed-values-for-property nil t)
(advice-add 'org-set-tags :before #'anki-editor--before-set-tags) (advice-add 'org-set-tags :before #'anki-editor--before-set-tags)
(advice-add 'org-get-buffer-tags :around #'anki-editor--get-buffer-tags) (advice-add 'org-get-buffer-tags :around #'anki-editor--get-buffer-tags)
@ -715,9 +707,8 @@ matching non-empty `ANKI_FAILURE_REASON' properties."
(interactive (list (list (org-entry-get nil anki-editor-prop-note-id)))) (interactive (list (list (org-entry-get nil anki-editor-prop-note-id))))
(when (or (not (called-interactively-p 'interactive)) (when (or (not (called-interactively-p 'interactive))
(yes-or-no-p (format "Do you really want to delete note %s? The deletion can't be undone. " (nth 0 noteids)))) (yes-or-no-p (format "Do you really want to delete note %s? The deletion can't be undone. " (nth 0 noteids))))
(anki-editor--anki-connect-invoke-result (anki-editor-api-call-result 'deleteNotes
"deleteNotes" :notes noteids)
`((notes . ,noteids)))
(org-entry-delete nil anki-editor-prop-note-id) (org-entry-delete nil anki-editor-prop-note-id)
(when (called-interactively-p 'interactive) (when (called-interactively-p 'interactive)
(message "Deleted note %s" (nth 0 noteids))))) (message "Deleted note %s" (nth 0 noteids)))))
@ -738,7 +729,8 @@ same as how it is used by `M-RET'(org-insert-heading)."
(sort (anki-editor-note-types) #'string-lessp))) (sort (anki-editor-note-types) #'string-lessp)))
(fields (progn (fields (progn
(message "Fetching note fields...") (message "Fetching note fields...")
(anki-editor--anki-connect-invoke-result "modelFieldNames" `((modelName . ,note-type))))) (anki-editor-api-call-result 'modelFieldNames
:modelName note-type)))
(note-heading (read-from-minibuffer "Enter the note heading (optional): "))) (note-heading (read-from-minibuffer "Enter the note heading (optional): ")))
(anki-editor--insert-note-skeleton prefix (anki-editor--insert-note-skeleton prefix
@ -789,17 +781,17 @@ same as how it is used by `M-RET'(org-insert-heading)."
;;; More utilities ;;; More utilities
(defun anki-editor-anki-connect-check () (defun anki-editor-api-check ()
"Check if correct version of AnkiConnect is serving." "Check if correct version of AnkiConnect is serving."
(interactive) (interactive)
(let ((ver (anki-editor--anki-connect-invoke-result "version"))) (let ((ver (anki-editor-api-call-result 'version)))
(if (<= anki-editor-ankiconnect-version ver) (if (<= anki-editor-api-version ver)
(when (called-interactively-p 'interactive) (when (called-interactively-p 'interactive)
(message "AnkiConnect v.%d is running" ver)) (message "AnkiConnect v.%d is running" ver))
(error "anki-editor requires minimal version %d of AnkiConnect installed" (error "anki-editor requires minimal version %d of AnkiConnect installed"
anki-editor-ankiconnect-version)))) anki-editor-api-version))))
(defun anki-editor-anki-connect-upgrade () (defun anki-editor-api-upgrade ()
"Upgrade AnkiConnect to the latest version. "Upgrade AnkiConnect to the latest version.
This will display a confirmation dialog box in Anki asking if you This will display a confirmation dialog box in Anki asking if you
@ -810,14 +802,14 @@ This is useful when new version of this package depends on the
bugfixes or new features of AnkiConnect." bugfixes or new features of AnkiConnect."
(interactive) (interactive)
(when (yes-or-no-p "This is going to download the latest AnkiConnect from the Internet to your computer, do you want to continue? ") (when (yes-or-no-p "This is going to download the latest AnkiConnect from the Internet to your computer, do you want to continue? ")
(let ((result (anki-editor--anki-connect-invoke-result "upgrade"))) (let ((result (anki-editor-api-call-result 'upgrade)))
(when (and (booleanp result) result) (when (and (booleanp result) result)
(message "AnkiConnect has been upgraded, you might have to restart Anki for the changes to take effect."))))) (message "AnkiConnect has been upgraded, you might have to restart Anki for the changes to take effect.")))))
(defun anki-editor-sync-collections () (defun anki-editor-sync-collections ()
"Synchronizes the local anki collections with ankiweb." "Synchronizes the local anki collections with ankiweb."
(interactive) (interactive)
(anki-editor--anki-connect-invoke "sync")) (anki-editor-api-call-result 'sync))
(defun anki-editor-gui-browse (&optional query) (defun anki-editor-gui-browse (&optional query)
"Open Anki Browser with QUERY. "Open Anki Browser with QUERY.
@ -828,22 +820,22 @@ note or deck."
(_ (format "deck:%s" (_ (format "deck:%s"
(or (org-entry-get-with-inheritance anki-editor-prop-deck) (or (org-entry-get-with-inheritance anki-editor-prop-deck)
"current")))))) "current"))))))
(anki-editor--anki-connect-invoke "guiBrowse" `((query . ,(or query ""))))) (anki-editor-api-call 'guiBrowse :query (or query "")))
(defun anki-editor-gui-add-cards () (defun anki-editor-gui-add-cards ()
"Open Anki Add Cards dialog with presets from current note "Open Anki Add Cards dialog with presets from current note
entry." entry."
(interactive) (interactive)
(anki-editor--anki-connect-invoke-result (anki-editor-api-call-result 'guiAddCards
"guiAddCards" :note `((:options (:closeAfterAdding t))
`((note . ,(cons '(options . ((closeAfterAdding . t))) ,(anki-editor--api-note
(anki-editor--anki-connect-map-note (anki-editor-note-at-point)))))
(anki-editor-note-at-point)))))))
(defun anki-editor-find-notes (&optional query) (defun anki-editor-find-notes (&optional query)
"Find notes with QUERY." "Find notes with QUERY."
(interactive "sQuery: ") (interactive "sQuery: ")
(let ((nids (anki-editor--anki-connect-invoke-result "findNotes" `((query . ,query))))) (let ((nids (anki-editor-api-call-result 'findNotes
:query query)))
(if (called-interactively-p 'interactive) (if (called-interactively-p 'interactive)
(message "%S" nids) (message "%S" nids)
nids))) nids)))