6404abb473
It is retrieved with inheritance and if set takes precedence over the variable `anki-editor-prepend-heading`. Also add a function to toggle it and change the behavior of `anki-editor-toggle-format` to match it: Now every call to the toggling functions will actually toggle the value in effect and an entry property is only set if necessary.
180 lines
7.3 KiB
EmacsLisp
180 lines
7.3 KiB
EmacsLisp
;;; anki-editor-ui.el --- UI for anki-editor -*- lexical-binding: t; -*-
|
|
|
|
;; Author: orgtre
|
|
;; URL: https://github.com/orgtre/anki-editor
|
|
;; Package-Requires: ((emacs "25.1"))
|
|
|
|
;; This program is free software: you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation, either version 3 of the License, or (at
|
|
;; your option) any later version.
|
|
;;
|
|
;; This program is distributed in the hope that it will be useful, but
|
|
;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
;; General Public License for more details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
;;; Code:
|
|
|
|
(require 'transient)
|
|
(require 'anki-editor)
|
|
|
|
(defgroup anki-editor-ui nil
|
|
"Customizations for anki-editor-ui."
|
|
:group 'anki-editor)
|
|
|
|
(defcustom anki-editor-ui-match-preprompt
|
|
"Syntax: [+-&|][tag|{tagregex}|property[=|<>|<|>|<=|>=]value]\n"
|
|
"Extra syntax explanation shown before the match prompt."
|
|
:type 'string)
|
|
|
|
(defcustom anki-editor-ui-deck-preprompt
|
|
"Use 'TAB' to complete, ',' to select multiple, and 'RET' to finalize.\n"
|
|
"Extra syntax explanation shown before the deck prompt."
|
|
:type 'string)
|
|
|
|
(transient-define-prefix anki-editor-ui ()
|
|
"Transient for anki-editor."
|
|
[["Org"
|
|
("i" " insert default note" anki-editor-insert-default-note :level 1)
|
|
("t" " set default note type" anki-editor-set-default-note-type
|
|
:level 1)
|
|
("c" " cloze dwim" anki-editor-cloze-dwim :level 2)]
|
|
[""
|
|
("I" " insert note" anki-editor-insert-note :level 1)
|
|
("T" " set note type" anki-editor-set-note-type :level 1)
|
|
("d" " delete note" anki-editor-delete-note-at-point :level 1)]]
|
|
[["Push"
|
|
("." " note at point " anki-editor-push-note-at-point :level 2)
|
|
("n" " new notes " anki-editor-push-new-notes :level 2)
|
|
("p" " push with ui" anki-editor-ui-push :level 1)]
|
|
[""
|
|
("b" " notes in buffer" anki-editor-push-notes :level 2)
|
|
("f" " failed notes" anki-editor-retry-failed-notes :level 2)
|
|
""]]
|
|
[["Anki"
|
|
("g" " gui browse " anki-editor-gui-browse :level 2)
|
|
("s" " sync AnkiWeb " anki-editor-sync-collection :level 2)]
|
|
[""
|
|
("G" " gui add card" anki-editor-gui-add-cards :level 3)
|
|
("a" " api check" anki-editor-api-check 3)]]
|
|
[["Misc"
|
|
("h" " subtree to html " anki-editor-export-subtree-to-html
|
|
:level 4)
|
|
("C" " copy styles " anki-editor-copy-styles :level 4)
|
|
("F" " toggle format " anki-editor-toggle-format :level 4)]
|
|
[""
|
|
("H" " region to html" anki-editor-convert-region-to-html :level 4)
|
|
("R" " remove styles" anki-editor-remove-styles :level 4)
|
|
("P" " toggle prepend heading" anki-editor-toggle-prepend-heading
|
|
:level 4)]])
|
|
|
|
(transient-define-prefix anki-editor-ui-push ()
|
|
"Transient for pushing anki-editor notes."
|
|
:incompatible '(("new" "failed" "existing"))
|
|
["Push which notes?"
|
|
("n" " new" "new")
|
|
("f" " failed" "failed")
|
|
("e" " existing" "existing")
|
|
("t" " note-type" "note-type=" anki-editor-ui--read-note-type)
|
|
("d" " decks" "decks=" anki-editor-ui--read-decks :multi-value rest)
|
|
("m" " match" "match=" anki-editor-ui--read-match)]
|
|
["From where?"
|
|
("." " point" anki-editor-push-note-at-point)
|
|
("r" " active region" anki-editor-ui-push-region :if region-active-p)
|
|
("s" " subtree" anki-editor-ui-push-subtree)
|
|
("b" " narrowed buffer" anki-editor-ui-push-narrowed-buffer)
|
|
("B" " full buffer" anki-editor-ui-push-full-buffer)
|
|
("A" " agenda files" anki-editor-ui-push-agenda-files)])
|
|
|
|
(defun anki-editor-ui--read-note-type (prompt initial-input history)
|
|
(completing-read prompt (anki-editor-note-types)
|
|
nil nil initial-input history))
|
|
|
|
(defun anki-editor-ui--read-decks (prompt initial-input history)
|
|
(completing-read-multiple (concat anki-editor-ui-deck-preprompt prompt)
|
|
(anki-editor-deck-names)
|
|
nil nil initial-input history))
|
|
|
|
(defun anki-editor-ui--read-match (prompt initial-input history)
|
|
(read-string (concat anki-editor-ui-match-preprompt prompt)
|
|
initial-input history))
|
|
|
|
(defun anki-editor-ui-push-region (&optional args)
|
|
"Used by `anki-editor-ui-push' to push region with ARGS."
|
|
(interactive (list (transient-args transient-current-command)))
|
|
(anki-editor-ui-push--pass-args-and-push args 'region))
|
|
|
|
(defun anki-editor-ui-push-subtree (&optional args)
|
|
"Used by `anki-editor-ui-push' to push subtree with ARGS."
|
|
(interactive (list (transient-args transient-current-command)))
|
|
(anki-editor-ui-push--pass-args-and-push args 'tree))
|
|
|
|
(defun anki-editor-ui-push-narrowed-buffer (&optional args)
|
|
"Used by `anki-editor-ui-push' to push narrowed buffer with ARGS."
|
|
(interactive (list (transient-args transient-current-command)))
|
|
(anki-editor-ui-push--pass-args-and-push args nil))
|
|
|
|
(defun anki-editor-ui-push-full-buffer (&optional args)
|
|
"Used by `anki-editor-ui-push' to push full buffer with ARGS."
|
|
(interactive (list (transient-args transient-current-command)))
|
|
(anki-editor-ui-push--pass-args-and-push args 'file))
|
|
|
|
(defun anki-editor-ui-push-agenda-files (&optional args)
|
|
"Used by `anki-editor-ui-push' to push agenda files with ARGS."
|
|
(interactive (list (transient-args transient-current-command)))
|
|
(anki-editor-ui-push--pass-args-and-push args 'agenda))
|
|
|
|
(defun anki-editor-ui-push--pass-args-and-push (args scope)
|
|
"Pass the `transient-args` ARGS to `anki-editor-push-notes`.
|
|
Also pass SCOPE."
|
|
(let-alist (anki-editor-ui-push--parse-args args)
|
|
(anki-editor-push-notes
|
|
scope .fullmatch
|
|
(when .decks `(apply #'anki-editor-ui--skip-unless-decks
|
|
(quote ,.decks))))))
|
|
|
|
(defun anki-editor-ui-push--parse-args (args)
|
|
"Parse the `transient-args` ARGS from `anki-editor-push`.
|
|
Return an alist with the full match pattern and deck."
|
|
(let ((new (transient-arg-value "new" args))
|
|
(failed (transient-arg-value "failed" args))
|
|
(existing (transient-arg-value "existing" args))
|
|
(note-type (transient-arg-value "note-type=" args))
|
|
(decks (alist-get "decks=" args nil nil 'equal))
|
|
(match (transient-arg-value "match=" args)))
|
|
(let ((fullmatch
|
|
(concat
|
|
(when new
|
|
(concat "+" anki-editor-prop-note-id "=\"\""))
|
|
(when failed
|
|
(concat "+" anki-editor-prop-failure-reason "<>\"\""))
|
|
(when existing
|
|
(concat "+" anki-editor-prop-note-id "<>\"\""))
|
|
(when note-type
|
|
(concat "+" anki-editor-prop-note-type "=\"" note-type "\""))
|
|
;; note that the match syntax doesn't allows us to specify
|
|
;; several alternative note types here
|
|
(when match
|
|
(if (string-match "^[-+&|/]" match)
|
|
match
|
|
(concat "+" match))))))
|
|
(list (cons 'fullmatch fullmatch)(cons 'decks decks)))))
|
|
|
|
(defun anki-editor-ui--skip-unless-decks (&rest filter-decks)
|
|
"Skip function passed to `org-map-entries`.
|
|
A note (subtree) is skipped unless its deck is in FILTER-DECKS.
|
|
We can't just filter by deck using `org-map-entries` match argument,
|
|
since we need to turn off property inheritance when mapping notes."
|
|
(let ((deck (org-entry-get nil anki-editor-prop-deck t)))
|
|
(unless (member deck filter-decks)
|
|
(save-excursion
|
|
(org-end-of-subtree t)
|
|
(point)))))
|
|
|
|
(provide 'anki-editor-ui)
|
|
|
|
;;; anki-editor-ui.el ends here
|