branch: master commit 463d752d4703af93ea86df8f07b244e8fc851617 Author: Ian Dunn <du...@gnu.org> Commit: Ian Dunn <du...@gnu.org>
Cleaned up code and added documentation of new features * paced.el: Bumped version (paced-throw-error-on-no-registered): (paced-throw-error-on-no-current): New variables. (paced-operate-on-named-dictionary): Macro to replace ensured-registered. (paced-save-named-dictionary): (paced-reset-named-dictionary): (paced-sort-named-dictionary): (paced-repopulate-named-dictionary): (paced-edit-named-dictionary): (paced-print-named-dictionary): Use it. (paced-operate-on-current-dictionary): Macro to mimic named operations. (paced-save-current-dictionary): (paced-add-word-to-current-dictionary): (paced-populate-current-dictionary-from-buffer): (paced-populate-current-dictionary-from-region): (paced-reset-current-dictionary): (paced-sort-current-dictionary): (paced-repopulate-current-dictionary): (paced-edit-current-dictionary): (paced-print-current-dictionary): Use it. (paced-global-dictionary-enable-alist): Renamed from now obsolete variable paced-global-dict-enable-alist. (paced-local-dictionary-enable-alist): Likewise for local. (paced-dictionary-enable-list): Renamed from paced-dict-enable-list. (paced-test-dictionary-enable-condition): Likewise. (paced-populate-current-dictionary-from-buffer): Renamed from obsolete function paced-populate-buffer-dictionary. (paced-populate-current-dictionary-from-region): Renamed from obsolete function paced-populate-from-region. (paced-add-current-thing-to-dictionary): Renamed from obsolete function paced-add-current-thing-to-dict. * paced-async.el (paced-dictionary-repopulate-async): Renamed from paced-repopulate-dictionary-async. * paced-tests.el: Update names of variables and functions. * paced.org: Add documentation on new features. * paced.info: Regenerated from paced.org. --- paced-async.el | 10 +- paced-tests.el | 92 +++++++-------- paced.el | 367 +++++++++++++++++++++++++++++++++++++++------------------ paced.info | 278 ++++++++++++++++++++++++++++++++++++------- paced.org | 192 ++++++++++++++++++++++++++++-- test.mk | 2 +- 6 files changed, 718 insertions(+), 223 deletions(-) diff --git a/paced-async.el b/paced-async.el index 0f2777c..97c7de1 100644 --- a/paced-async.el +++ b/paced-async.el @@ -7,9 +7,9 @@ ;; Keywords: convenience, completion ;; Package-Requires: ((emacs "25.1") (async "1.9.1")) ;; URL: https://savannah.nongnu.org/projects/paced-el/ -;; Version: 1.0 +;; Version: 1.1 ;; Created: 22 Jan 2017 -;; Modified: 08 Dec 2017 +;; Modified: 04 Feb 2018 ;; This file is part of GNU Emacs. @@ -48,7 +48,7 @@ population commmands, or load additional files." :group 'paced :type 'file) -(cl-defmethod paced-repopulate-dictionary-async ((dictionary paced-dictionary)) +(cl-defmethod paced-dictionary-repopulate-async ((dictionary paced-dictionary)) "Repopulate DICTIONARY asynchronously. Note that DICTIONARY isn't modified directly by this process, but @@ -94,7 +94,7 @@ Note that this will empty the dictionary's contents." (list (paced-read-dictionary))) (paced-ensure-registered key) (let ((dict (paced-named-dictionary key))) - (paced-repopulate-dictionary-async dict))) + (paced-dictionary-repopulate-async dict))) ;;;###autoload (defun paced-repopulate-current-dictionary-async () @@ -105,7 +105,7 @@ Population commands are stored in the field of the same name. Note that this will empty the dictionary's contents." (interactive) (if-let* ((dict (paced-current-dictionary))) - (paced-repopulate-dictionary-async dict) + (paced-dictionary-repopulate-async dict) (error "No current dictionary found"))) (provide 'paced-async) diff --git a/paced-tests.el b/paced-tests.el index e7b5552..c7dc3bb 100644 --- a/paced-tests.el +++ b/paced-tests.el @@ -7,9 +7,9 @@ ;; Keywords: convenience, completion ;; Package-Requires: ((emacs "25.1") (async "1.9.1")) ;; URL: https://savannah.nongnu.org/projects/paced-el/ -;; Version: 1.0 +;; Version: 1.1 ;; Created: 22 Jan 2017 -;; Modified: 08 Dec 2017 +;; Modified: 04 Feb 2018 ;; This file is part of GNU Emacs. @@ -89,11 +89,11 @@ (ert-deftest paced-enable-list-symbol () "Test case for `paced-dictionary-enable-alist' being an arbitrary symbol." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((paced-test-enable-symbol . "test-dict-case"))) - (new-buffer (find-file-noselect paced-first-test-file)) - (new-dict (paced-make-dictionary "test-dict-case" - paced-test-dict-save-file - 'downcase))) + (paced-global-dictionary-enable-alist '((paced-test-enable-symbol . "test-dict-case"))) + (new-buffer (find-file-noselect paced-first-test-file))) + (paced-make-dictionary "test-dict-case" + paced-test-dict-save-file + 'downcase) (with-current-buffer new-buffer (setq paced-test-enable-symbol nil) (should-not (paced-current-dictionary)) @@ -105,12 +105,12 @@ (ert-deftest paced-enable-list-mode () "Test case for `paced-dictionary-enable-alist' being a mode symbol." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (buffer-one (find-file-noselect paced-first-test-file)) - (buffer-two (find-file-noselect paced-second-test-file)) - (new-dict (paced-make-dictionary "test-dict-case" - paced-test-dict-save-file - 'downcase))) + (buffer-two (find-file-noselect paced-second-test-file))) + (paced-make-dictionary "test-dict-case" + paced-test-dict-save-file + 'downcase) (with-current-buffer buffer-two (should-not (paced-current-dictionary))) (kill-buffer buffer-two) @@ -125,11 +125,11 @@ (ert-deftest paced-enable-list-function-symbol () "Test case for `paced-dictionary-enable-alist' being a function symbol." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((paced-test-function-symbol . "test-dict-case"))) - (buffer-one (find-file-noselect paced-first-test-file)) - (new-dict (paced-make-dictionary "test-dict-case" - paced-test-dict-save-file - 'downcase))) + (paced-global-dictionary-enable-alist '((paced-test-function-symbol . "test-dict-case"))) + (buffer-one (find-file-noselect paced-first-test-file))) + (paced-make-dictionary "test-dict-case" + paced-test-dict-save-file + 'downcase) (with-current-buffer buffer-one (setq-local paced-test-enable-symbol nil) (should-not (paced-current-dictionary)) @@ -141,11 +141,11 @@ (ert-deftest paced-enable-list-lambda-function () "Test case for `paced-dictionary-enable-alist' being a lambda form." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '(((lambda nil paced-test-enable-symbol) . "test-dict-case"))) - (buffer-one (find-file-noselect paced-first-test-file)) - (new-dict (paced-make-dictionary "test-dict-case" - paced-test-dict-save-file - 'downcase))) + (paced-global-dictionary-enable-alist '(((lambda nil paced-test-enable-symbol) . "test-dict-case"))) + (buffer-one (find-file-noselect paced-first-test-file))) + (paced-make-dictionary "test-dict-case" + paced-test-dict-save-file + 'downcase) (with-current-buffer buffer-one (setq-local paced-test-enable-symbol nil) (should-not (paced-current-dictionary)) @@ -157,12 +157,12 @@ (ert-deftest paced-enable-list-and-form () "Test case for `paced-dictionary-enable-alist' being an 'and' form." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '(((and text-mode paced-test-enable-symbol) . "test-dict-case"))) + (paced-global-dictionary-enable-alist '(((and text-mode paced-test-enable-symbol) . "test-dict-case"))) (buffer-one (find-file-noselect paced-first-test-file)) - (buffer-two (find-file-noselect paced-second-test-file)) - (new-dict (paced-make-dictionary "test-dict-case" - paced-test-dict-save-file - 'downcase))) + (buffer-two (find-file-noselect paced-second-test-file))) + (paced-make-dictionary "test-dict-case" + paced-test-dict-save-file + 'downcase) (with-current-buffer buffer-two (setq-local paced-test-enable-symbol nil) (should-not (paced-current-dictionary)) @@ -180,12 +180,12 @@ (ert-deftest paced-enable-list-or-form () "Test case for `paced-dictionary-enable-alist' being an 'or' form." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '(((or text-mode paced-test-enable-symbol) . "test-dict-case"))) + (paced-global-dictionary-enable-alist '(((or text-mode paced-test-enable-symbol) . "test-dict-case"))) (buffer-one (find-file-noselect paced-first-test-file)) - (buffer-two (find-file-noselect paced-second-test-file)) - (new-dict (paced-make-dictionary "test-dict-case" - paced-test-dict-save-file - 'downcase))) + (buffer-two (find-file-noselect paced-second-test-file))) + (paced-make-dictionary "test-dict-case" + paced-test-dict-save-file + 'downcase) (with-current-buffer buffer-two (setq-local paced-test-enable-symbol nil) (should-not (paced-current-dictionary)) @@ -205,7 +205,7 @@ (ert-deftest paced-populate-file () "Test case for single file populator." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd (paced-file-population-command :file paced-first-test-file)) (test-dict (paced-make-dictionary "test-dict-case" paced-test-dict-save-file @@ -224,7 +224,7 @@ (ert-deftest paced-populate-buffer () "Test case for single buffer populator." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (buffer "first.txt") (buffer-one (find-file-noselect paced-first-test-file)) (cmd (paced-buffer-population-command :buffer buffer)) @@ -246,7 +246,7 @@ (ert-deftest paced-populate-file-function () "Test case for file-function populator." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (pre-func (lambda () (insert (buffer-string)) t)) (cmd (paced-file-function-population-command :file paced-first-test-file :setup-func pre-func)) @@ -267,7 +267,7 @@ (ert-deftest paced-populate-directory-regexp () "Test case for directory-regexp populator." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd (paced-directory-regexp-population-command :directory paced-test-dir :regexp ".*\\.txt" :recursive t)) @@ -288,12 +288,12 @@ (ert-deftest paced-populate-file-list () "Test case for file-list populator." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (file-list (lambda () `(,paced-first-test-file))) (cmd (paced-file-list-population-command :generator file-list)) (test-dict (paced-make-dictionary "test-dict-case" - paced-test-dict-save-file - 'downcase))) + paced-test-dict-save-file + 'downcase))) (should (paced-dictionary-p test-dict)) (oset test-dict population-commands (list cmd)) (paced-dictionary-repopulate test-dict) @@ -307,7 +307,7 @@ (ert-deftest paced-multiple-population-commands () (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd1 (paced-file-population-command :file paced-first-test-file)) (cmd2 (paced-file-population-command :file paced-third-test-file)) (test-dict (paced-make-dictionary "test-dict-case" @@ -329,7 +329,7 @@ (ert-deftest paced-populator-settings () (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (exclude-command (lambda nil (nth 8 (syntax-ppss)))) ;; exclude comments (cmd1 (paced-file-population-command :file paced-first-test-file)) (cmd2 (paced-file-population-command :file paced-second-test-file @@ -351,7 +351,7 @@ (ert-deftest paced-populate-sort-order () "Test case for sorting after population." (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd (paced-file-population-command :file paced-first-test-file)) (test-dict (paced-make-dictionary "test-dict-case" paced-test-dict-save-file @@ -395,7 +395,7 @@ (ert-deftest paced-completions-try-completion () (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd (paced-file-population-command :file paced-first-test-file)) (test-dict (paced-make-dictionary "test-dict-case" paced-test-dict-save-file @@ -408,7 +408,7 @@ (ert-deftest paced-completions-try-completion-mixed-case () (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd (paced-file-population-command :file paced-first-test-file)) (test-dict (paced-make-dictionary "test-dict-case" paced-test-dict-save-file @@ -421,7 +421,7 @@ (ert-deftest paced-completions-all-completions () (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd (paced-file-population-command :file paced-first-test-file)) (test-dict (paced-make-dictionary "test-dict-case" paced-test-dict-save-file @@ -440,7 +440,7 @@ (ert-deftest paced-completions-test-completion () (let* ((paced--registered-dictionaries paced-test-default-registered-map) - (paced-global-dict-enable-alist '((text-mode . "test-dict-case"))) + (paced-global-dictionary-enable-alist '((text-mode . "test-dict-case"))) (cmd (paced-file-population-command :file paced-first-test-file)) (test-dict (paced-make-dictionary "test-dict-case" paced-test-dict-save-file diff --git a/paced.el b/paced.el index 81bd748..79fe483 100644 --- a/paced.el +++ b/paced.el @@ -7,9 +7,9 @@ ;; Keywords: convenience, completion ;; Package-Requires: ((emacs "25.1") (async "1.9.1")) ;; URL: https://savannah.nongnu.org/projects/paced-el/ -;; Version: 1.0.1 +;; Version: 1.1 ;; Created: 22 Jan 2017 -;; Modified: 08 Dec 2017 +;; Modified: 04 Feb 2018 ;; This file is part of GNU Emacs. @@ -270,10 +270,18 @@ If none exists, return nil." "Return non-nil if a dictionary with name KEY has been registered." (map-contains-key paced--registered-dictionaries key)) -(defsubst paced-ensure-registered (key) - "Throw an error if a dictionary with name KEY has not been registered." - (unless (paced-dictionary-key-registered-p key) - (error "No paced dictionary called '%s' has been registered" key))) +(defvar paced-throw-error-on-no-registered t + "Whether to throw an error when a named dictionary can't be found.") + +(defmacro paced-operate-on-named-dictionary (name &rest form) + "Run FORM on the dictionary with name NAME, bound to `dict'. + +If no dictionary named NAME exists, throw an error." + (declare (indent 1)) + `(if-let* ((dict (paced-named-dictionary ,name))) + (progn ,@form) + (when paced-throw-error-on-no-registered + (error "No paced dictionary called '%s' has been registered" ,name)))) (cl-defmethod paced-dictionary-register ((dict paced-dictionary)) "Registered dictionary DICT." @@ -323,7 +331,9 @@ customization interface." "Return the name of dictionary OBJ." (oref obj object-name)) -(defcustom paced-global-dict-enable-alist nil +;;; Current Dictionary + +(defcustom paced-global-dictionary-enable-alist nil "List that determines which dictionaries should be active. Each entry has the form (CONDITION . DICT-KEY), where CONDITION @@ -355,28 +365,40 @@ enabled unless `paced-mode' is active." :group 'paced :type '(alist :key-type sexp :value-type string)) -(defvar-local paced-local-dict-enable-alist nil +(define-obsolete-variable-alias 'paced-global-dict-enable-alist + 'paced-global-dictionary-enable-alist + "1.1") + +(defvar-local paced-local-dictionary-enable-alist nil "Local enable list. Has the same form as and takes priority over -`paced-global-dict-enable-alist'.") +`paced-global-dictionary-enable-alist'.") -(defun paced-dict-enable-list () +(define-obsolete-variable-alias 'paced-local-dict-enable-alist + 'paced-local-dictionary-enable-alist + "1.1") + +(defun paced-dictionary-enable-list () "Return the combination of the local and global enable-alists. -See `paced-local-dict-enable-alist' and -`paced-global-dict-enable-alist' for more information." - (append paced-local-dict-enable-alist - paced-global-dict-enable-alist)) +See `paced-local-dictionary-enable-alist' and +`paced-global-dictionary-enable-alist' for more information." + (append paced-local-dictionary-enable-alist + paced-global-dictionary-enable-alist)) + +(define-obsolete-function-alias 'paced-dict-enable-list + 'paced-dictionary-enable-list + "1.1") (defun paced-mode-symbol-p (sym) "Return non-nil if SYM is a mode symbol." (string-match-p (rx "-mode" string-end) (symbol-name sym))) -(defun paced-test-dict-enable-condition (condition) +(defun paced-test-dictionary-enable-condition (condition) "Determines if CONDITION passes in the current buffer. -See `paced-global-dict-enable-alist' for an explanation." +See `paced-global-dictionary-enable-alist' for an explanation." (pcase condition ((and (pred symbolp) (pred paced-mode-symbol-p)) @@ -390,9 +412,9 @@ See `paced-global-dict-enable-alist' for an explanation." ((pred functionp) (funcall condition)) (`(or . ,rest) - (seq-some 'paced-test-dict-enable-condition rest)) + (seq-some 'paced-test-dictionary-enable-condition rest)) (`(and . ,rest) - (seq-every-p 'paced-test-dict-enable-condition rest)))) + (seq-every-p 'paced-test-dictionary-enable-condition rest)))) (defun paced-current-dictionary () "Determine the current dictionary. @@ -400,18 +422,39 @@ See `paced-global-dict-enable-alist' for an explanation." Returns nil if no dictionary should be enabled. If a dictionary is found in the list that doesn't exist, it will -be skipped." - (let ((conditions (paced-dict-enable-list)) +be skipped. + +See `paced-global-dictionary-enable-alist' or +`paced-local-dictionary-enable-alist' for how to set the current +dictionary conditions." + (let ((conditions (paced-dictionary-enable-list)) (dictionary)) (while (and conditions (not dictionary)) (pcase-let* ((`(,condition . ,dict) (pop conditions))) (when (and (paced-dictionary-key-registered-p dict) - (paced-test-dict-enable-condition condition)) + (paced-test-dictionary-enable-condition condition)) (setq dictionary dict)))) (when dictionary (paced-named-dictionary dictionary)))) +(defvar paced-throw-error-on-no-current t + "Whether to throw an error when no current dictionary can be +found.") + +(defmacro paced-operate-on-current-dictionary (&rest form) + "Run FORM with the current dictionary bound to `dict'. + +If no dictionary can be found for the buffer, throw an error. To +suppress the error, set `paced-throw-error-on-no-current' to +nil." + `(if-let* ((dict (paced-current-dictionary))) + (progn ,@form) + (when paced-throw-error-on-no-current + (user-error "No dictionary found for current buffer")))) + +;;; Saving and Loading + (cl-defmethod paced-dictionary-save ((dict paced-dictionary) &optional force) "Save dictionary DICT according to its filename. @@ -428,10 +471,18 @@ If FORCE is non-nil (given with a prefix arg), forcibly save the dictionary if found." (declare (interactive-only paced-dictionary-save)) (interactive (list (paced-read-dictionary) current-prefix-arg)) - (paced-ensure-registered key) - (let ((dict (paced-named-dictionary key))) + (paced-operate-on-named-dictionary key (paced-dictionary-save dict force))) +(defun paced-save-current-dictionary (force) + "Save the dictionary for the current buffer. + +For how the current dictionary is determined, see +`paced-current-dictionary'." + (interactive "P") + (paced-operate-on-current-dictionary + (paced-dictionary-save dict force))) + (defun paced-load-dictionary-from-file (file) "Load dictionary from FILE." (interactive @@ -470,32 +521,7 @@ dictionary if found." (paced-dictionary-save dict t)) - -(defvar-local paced--current-source nil - "The source from which a dictionary is being populated. - -This is used internally to inform the user of the current source, -since population mostly uses temporary buffers.") - -(defvar-local paced-exclude-function (lambda () nil) - "Local predicate to determine if thing at point should be excluded. - -This should be a function of no arguments that returns non-nil if -the current thing-at-point should be excluded from paced -dictionaries. Exclusion is checked from the start or the end of -the current thing, depending on `paced-point-in-thing-at-point-for-exclusion'. -Point returns to its original position after the function is -called. - -By default, this allows everything. - -A useful function for this is `paced-in-comment-p'.") - -(defun paced-in-comment-p (&optional pos) - "Return non-nil if POS is in a comment. - -If POS is not specified, defaults to `point'." - (nth 8 (syntax-ppss (or pos (point))))) +;;; Handling of Thing at Point (defun paced-bounds-of-thing-at-point () "Get the bounds of the thing at point." @@ -530,6 +556,8 @@ Thing is based on `paced-thing-at-point-constituent'." Thing is based on `paced-thing-at-point-constituent'." (goto-char (cdr (paced-bounds-of-thing-at-point)))) +;;; Character Limits + (defun paced-length-of-thing-at-point () "Return the length, in characters, of the current thing at point." (length (paced-thing-at-point))) @@ -541,6 +569,28 @@ The limit requirement is set with `paced-character-limit'." (or (eq paced-character-limit 0) (<= (paced-length-of-thing-at-point) paced-character-limit))) +;;; Exclusion + +(defvar-local paced-exclude-function (lambda () nil) + "Local predicate to determine if thing at point should be excluded. + +This should be a function of no arguments that returns non-nil if +the current thing-at-point should be excluded from paced +dictionaries. Exclusion is checked from the start or the end of +the current thing, depending on `paced-point-in-thing-at-point-for-exclusion'. +Point returns to its original position after the function is +called. + +By default, this allows everything. + +A useful function for this is `paced-in-comment-p'.") + +(defun paced-in-comment-p (&optional pos) + "Return non-nil if POS is in a comment. + +If POS is not specified, defaults to `point'." + (nth 8 (syntax-ppss (or pos (point))))) + (defun paced-excluded-p () "Return non-nil to exclude current thing at point. @@ -562,6 +612,8 @@ This also handles character limits set by (paced-goto-end-of-thing-at-point))) (funcall paced-exclude-function)))) +;;; Case Handling + (defun paced-mixed-case-word-p (word) "Return non-nil if WORD is mixed-case. @@ -603,6 +655,14 @@ This is a separate function only for testing; use "Return WORD, modified based on DICT's case handling." (paced--handle-word-case (oref dict case-handling) word)) +;;; Population + +(defvar-local paced--current-source nil + "The source from which a dictionary is being populated. + +This is used internally to inform the user of the current source, +since population mostly uses temporary buffers.") + (cl-defmethod paced-dictionary-add-word ((dict paced-dictionary) word) "Add WORD to paced dictionary DICT." (let ((new-word (paced-dictionary-process-word dict word))) @@ -610,11 +670,13 @@ This is a separate function only for testing; use (cl-incf (map-elt (oref dict usage-hash) new-word 0)) (oset dict updated t))) -(defsubst paced-add-word-to-current-dict (word) - "Add WORD to the current paced dictionary." - (if-let* ((dict (paced-current-dictionary))) - (paced-dictionary-add-word dict word) - (error "No current dictionary found"))) +(defun paced-add-word-to-current-dictionary (word) + "Add WORD to the current dictionary. + +For how the current dictionary is determined, see +`paced-current-dictionary'." + (paced-operate-on-current-dictionary + (paced-dictionary-add-word dict word))) (cl-defmethod paced-dictionary-populate-from-buffer ((dict paced-dictionary) &optional buffer) "Repopulate DICT from BUFFER. @@ -637,41 +699,76 @@ If BUFFER is nil, use the current one." (paced-dictionary-add-word dict (paced-thing-at-point)))) (progress-reporter-done reporter))))) -(defun paced-populate-dictionary-from-region (dict start end) - "Populate DICT from the region in the current buffer between START and END. +(defun paced-populate-current-dictionary-from-buffer (&optional buffer) + "Populate the current dictionary from BUFFER. -Note that this doesn't add the current buffer to DICT's -population commands, so if DICT is later repopulated using +This means add a usage of each included thing in BUFFER. + +BUFFER is either the name of a buffer, or the buffer itself. If +not given, use the current buffer. + +In order to only populate the dictionary from a region, +`paced-populate-current-dictionary-from-region'. + +Note that this doesn't add the buffer to the dictionary's +population commands, so if it is later repopulated using `paced-dictionary-repopulate' or `paced-repopulate-named-dictionary', anything added with this -command will be lost." - (save-restriction - (narrow-to-region start end) - (paced-dictionary-populate-from-buffer dict))) +command will be lost. -(defun paced-populate-buffer-dictionary (&optional buffer) - "Populate BUFFER's current dictionary with BUFFER. +In order to make changes permanent, use +`paced-add-buffer-file-to-dictionary'. -This means add a usage of each included thing in buffer. +For how the current dictionary is determined, see +`paced-current-dictionary'." + (interactive "b") + (paced-operate-on-current-dictionary + (paced-dictionary-populate-from-buffer dict buffer))) -If called interactively, the current buffer is used. In order to -only populate the dictionary from a region, -`paced-populate-from-region'. +(define-obsolete-function-alias 'paced-populate-buffer-dictionary + 'paced-populate-current-dictionary-from-buffer + "1.1") -Note that this doesn't add BUFFER to the dictionary's population -commands, so if it is later repopulated using +(defun paced-populate-named-dictionary-from-buffer (name &optional buffer) + "Populate the dictionary named NAME from BUFFER. + +This means add a usage of each included thing in BUFFER. + +BUFFER is either the name of a buffer, or the buffer itself. If +not given, use the current buffer. + +In order to only populate the dictionary from a region, +`paced-populate-current-dictionary-from-region'. + +Note that this doesn't add the buffer to the dictionary's +population commands, so if it is later repopulated using `paced-dictionary-repopulate' or `paced-repopulate-named-dictionary', anything added with this command will be lost. In order to make changes permanent, use -`paced-add-buffer-file-to-dictionary'." - (interactive) - (if-let* ((dict (paced-current-dictionary))) - (paced-dictionary-populate-from-buffer dict buffer) - (user-error "No dictionary found"))) +`paced-add-buffer-file-to-dictionary'. + +For how the current dictionary is determined, see +`paced-current-dictionary'." + (interactive + (list (paced-read-dictionary) (read-buffer "Buffer: " nil t))) + (paced-operate-on-named-dictionary name + (paced-dictionary-populate-from-buffer dict buffer))) + +(cl-defmethod paced-dictionary-populate-from-region ((dict paced-dictionary) start end) + "Populate DICT from the region in the current buffer between START and END. -(defun paced-populate-from-region (start end) +Note that this doesn't add the current buffer to DICT's +population commands, so if DICT is later repopulated using +`paced-dictionary-repopulate' or +`paced-repopulate-named-dictionary', anything added with this +command will be lost." + (save-restriction + (narrow-to-region start end) + (paced-dictionary-populate-from-buffer dict))) + +(defun paced-populate-current-dictionary-from-region (start end) "Populate the current dictionary from the region START to END. Note that this doesn't add the current buffer to the dictionary's @@ -680,11 +777,14 @@ population commands, so if it is later repopulated using `paced-repopulate-named-dictionary', anything added with this command will be lost." (interactive "r") - (if-let* ((dict (paced-current-dictionary))) - (paced-populate-dictionary-from-region dict start end) - (user-error "No dictionary found"))) + (paced-operate-on-current-dictionary + (paced-dictionary-populate-from-region dict start end))) -(defun paced-add-current-thing-to-dict () +(define-obsolete-function-alias 'paced-populate-from-region + 'paced-populate-current-dictionary-from-region + "1.1") + +(defun paced-add-current-thing-to-dictionary () "Add the current thing at point to the current dictionary. No check is done to determine if the current thing should be @@ -696,7 +796,10 @@ population commands, so if it is later repopulated using `paced-repopulate-named-dictionary', anything added with this command will be lost." (interactive) - (paced-add-word-to-current-dict (paced-thing-at-point))) + (paced-operate-on-current-dictionary + (paced-dictionary-add-word dict (paced-thing-at-point)))) + +;;; Dictionary Reset (cl-defmethod paced-dictionary-reset ((dict paced-dictionary)) "Reset the usage-hash of paced-dictionary DICT." @@ -704,12 +807,23 @@ command will be lost." (defun paced-reset-named-dictionary (key) "Reset the paced dictionary with key KEY." + (declare (interactive-only paced-dictionary-reset)) (interactive (list (paced-read-dictionary))) - (paced-ensure-registered key) - (let ((dict (paced-named-dictionary key))) + (paced-operate-on-named-dictionary key (paced-dictionary-reset dict))) +(defun paced-reset-current-dictionary () + "Reset the current dictionary. + +For how the current dictionary is determined, see +`paced-current-dictionary'." + (interactive) + (paced-operate-on-current-dictionary + (paced-dictionary-reset dict))) + +;;; Dictionary Sorting + (cl-defmethod paced-dictionary-sort ((dict paced-dictionary)) "Sort the words in dictionary DICT by usage." (oset dict usage-hash @@ -718,12 +832,22 @@ command will be lost." (defun paced-sort-named-dictionary (key) "Sort the paced dictionary with key KEY." + (declare (interactive-only paced-dictionary-sort)) (interactive (list (paced-read-dictionary))) - (paced-ensure-registered key) - (let ((dict (paced-named-dictionary key))) + (paced-operate-on-named-dictionary key (paced-dictionary-sort dict))) +(defun paced-sort-current-dictionary () + "Sort the current paced dictionary. + +For how the current dictionary is determined, see +`paced-current-dictionary'." + (interactive) + (paced-operate-on-current-dictionary + (paced-dictionary-sort dict))) + +;;; The Minor Mode (define-minor-mode paced-mode "Toggle paced mode. @@ -740,10 +864,7 @@ This adds `paced-completion-at-point' to (define-globalized-minor-mode global-paced-mode paced-mode paced-mode :group 'paced) - - ; ;;;;;;;;;;;;;;;; ; - ; ;; Completion ;; ; - ; ;;;;;;;;;;;;;;;; ; +;;; Completion (cl-defmethod paced-dictionary-fix-completion-case ((dict paced-dictionary) prefix completions) "Account for case differences in the prefix by prepending PREFIX to COMPLETIONS. @@ -818,6 +939,8 @@ the prefix before completions are returned." (let* ((completion-ignore-case paced-completion-ignore-case)) (pcase action ((or `nil `t `lambda) + ;; Intentionally don't throw an error here, so as not to disrupt + ;; completion. (when-let* ((dict (paced-current-dictionary))) (paced-dictionary-completions dict string action pred))) (`(boundaries . _) nil) @@ -847,8 +970,10 @@ This should only be called from `paced-completion-at-point'." ;; Might not be the entire completion, so don't add it. ) (finished + ;; We know there's a current dictionary, since this should only be called + ;; from `paced-completion-at-point'. (when paced-auto-update-p - (paced-add-word-to-current-dict word))))) + (paced-add-word-to-current-dictionary word))))) (defun paced-completion-at-point () "Function for `completion-at-point-functions' to get the paced completions." @@ -860,9 +985,7 @@ This should only be called from `paced-completion-at-point'." :exclusive 'no)))) - ; ;;;;;;;;;;;;;;;;;; ; - ; ;; Repopulation ;; ; - ; ;;;;;;;;;;;;;;;;;; ; +;;; Repopulation (defun paced--insert-file-contents (file) "Insert the contents of FILE into the current buffer. @@ -1074,19 +1197,30 @@ repopulating it. If `paced-populate-warn-about-reset' is non-nil, confirmation will be requested before continuing." (interactive (list (paced-read-dictionary))) - (paced-ensure-registered key) - (let ((dict (paced-named-dictionary key))) + (paced-operate-on-named-dictionary key (when (or (not paced-populate-warn-about-reset) (y-or-n-p "Warning: Repopulating dictionary will reset it. Continue?")) (paced-dictionary-repopulate dict)))) +(defun paced-repopulate-current-dictionary () + "Repopulate the current dictionary. + +Note that this will empty the dictionary's contents before +repopulating it. If `paced-populate-warn-about-reset' is +non-nil, confirmation will be requested before continuing." + (interactive) + (paced-operate-on-current-dictionary + (when (or (not paced-populate-warn-about-reset) + (y-or-n-p "Warning: Repopulating dictionary will reset it. Continue?")) + (paced-dictionary-repopulate dict)))) + (cl-defmethod paced-dictionary-add-population-command ((dict paced-dictionary) (cmd paced-population-command)) "Add population command CMD to dictionary DICT." (cl-pushnew cmd (oref dict population-commands) :test 'equal)) (defun paced-add-buffer-file-to-dictionary (&optional buffer) - "Populate the dictionary of BUFFER with BUFFER. + "Populate the current dictionary of BUFFER with BUFFER. The file corresponding to BUFFER is then added to the current dictionary's population commands. @@ -1098,33 +1232,35 @@ must be set with `paced-edit-named-dictionary' or (with-current-buffer (or buffer (current-buffer)) (unless (buffer-file-name) (user-error "paced-add-buffer-file-to-dictionary called inside a non-file buffer.")) - (if-let* ((dict (paced-current-dictionary)) - (file-name (buffer-file-name)) - (cmd (paced-file-population-command :file file-name))) - (progn - (paced-dictionary-populate-from-buffer dict buffer) - (paced-dictionary-add-population-command dict cmd)) - (user-error "No dictionary found for current buffer")))) + (paced-operate-on-current-dictionary + (let* ((file-name (buffer-file-name)) + (cmd (paced-file-population-command :file file-name))) + (paced-dictionary-populate-from-buffer dict buffer) + (paced-dictionary-add-population-command dict cmd))))) +;;; Edit a Dictionary + (cl-defmethod paced-dictionary-edit ((dict paced-dictionary)) "Edit paced-dictionary DICT." (customize-object dict)) (defun paced-edit-named-dictionary (name) "Edit the paced-dictionary named NAME." + (declare (interactive-only paced-dictionary-edit)) (interactive (list (paced-read-dictionary))) - (if-let* ((dict (paced-named-dictionary name))) - (paced-dictionary-edit dict) - (error "No paced dictionary called '%s' has been registered" name))) + (paced-operate-on-named-dictionary name + (paced-dictionary-edit dict))) (defun paced-edit-current-dictionary () - "Edit the current paced dictionary." + "Edit the current paced dictionary. + +For how the current dictionary is determined, see +`paced-current-dictionary'." (interactive) - (if-let* ((dict (paced-current-dictionary))) - (paced-dictionary-edit dict) - (user-error "No dictionary found for current buffer"))) + (paced-operate-on-current-dictionary + (paced-dictionary-edit dict))) @@ -1172,16 +1308,15 @@ must be set with `paced-edit-named-dictionary' or (defun paced-print-current-dictionary () "Print the contents of the current dictionary in a dedicated buffer." (interactive) - (if-let* ((dict (paced-current-dictionary))) - (paced-dictionary-print dict) - (user-error "No dictionary found for current buffer"))) + (paced-operate-on-current-dictionary + (paced-dictionary-print))) (defun paced-print-named-dictionary (name) "Print the contents of the dictionary with name NAME." + (declare (interactive-only paced-dictionary-print)) (interactive (list (paced-read-dictionary))) - (if-let* ((dict (paced-named-dictionary name))) - (paced-dictionary-print dict) - (error "No paced dictionary called '%s' has been registered" name))) + (paced-operate-on-named-dictionary name + (paced-dictionary-print name))) diff --git a/paced.info b/paced.info index 1fb5c84..7ab606e 100644 --- a/paced.info +++ b/paced.info @@ -41,6 +41,7 @@ Dictionaries * Editing a Dictionary:: How to edit your new dictionary * Selective Dictionaries:: Enabling certain dictionaries under certain conditions * Dictionary Files:: Loading and Saving the Dictionaries +* Printing a Dictionary:: Seeing the contents of a dictionary Population Commands @@ -53,6 +54,9 @@ Example Setups * Org Agenda Files:: * Project Files:: +* Markdown Files:: +* Repopulating Dictionary After Saving:: +* Repopulating Dictionary After Spellchecking the Buffer:: Contributing @@ -63,6 +67,7 @@ Contributing Changelog +* 1.1: 11. * 1.0.1: 101. * 1.0: 10. @@ -191,17 +196,27 @@ Installation Emacs 25.1 async 1.9.1 - Right now, the only way to install paced is from source. + Paced may be installed from source, or from GNU ELPA. + + From ELPA: + + M-x package-install RET paced RET From Source: bzr branch https://bzr.savannah.gnu.org/r/paced-el paced - After that, add the following to your init file (typically .emacs): + After installing from source, add the following to your init file +(typically .emacs): (add-to-list 'load-path "/full/path/to/paced/") (require 'paced) + However you install paced, you must also make sure dictionaries are +loaded on startup: + + (paced-load-all-dictionaries) + File: paced.info, Node: Basic Setup, Prev: Installation, Up: Introduction @@ -213,8 +228,8 @@ is as follows: 1. Create a new dictionary, “Default” (See *note Creating a Dictionary::) - 2. Set paced-global-dict-enable-alist to ‘((t . "Default"))’ (See - *note Selective Dictionaries::) + 2. Set ‘paced-global-dictionary-enable-alist’ to ‘((t . "Default"))’ + (See *note Selective Dictionaries::) 3. Run ‘M-x global-paced-mode’ 4. To add a file to the dictionary, use ‘M-x paced-add-buffer-file-to-dictionary’ @@ -234,6 +249,7 @@ Dictionaries * Editing a Dictionary:: How to edit your new dictionary * Selective Dictionaries:: Enabling certain dictionaries under certain conditions * Dictionary Files:: Loading and Saving the Dictionaries +* Printing a Dictionary:: Seeing the contents of a dictionary File: paced.info, Node: Creating a Dictionary, Next: Editing a Dictionary, Up: Dictionaries @@ -284,9 +300,11 @@ Selective Dictionaries Paced provides a mechanism called the “enable list”, that allows a user to enable certain dictionaries for completion given certain conditions. - There are two enable lists: a global and local one. They both work -the same, with the local one taking precedence. Each entry in the list -has a condition and a key. + There are two enable lists: a global +(‘paced-global-dictionary-enable-alist’) and local +(‘paced-local-dictionary-enable-alist’) one. They both work the same, +with the local one taking precedence. Each entry in the list has a +condition and a key. The conditions are one of the following: @@ -317,7 +335,7 @@ active. The key is the dictionary name you set during dictionary creation. -File: paced.info, Node: Dictionary Files, Prev: Selective Dictionaries, Up: Dictionaries +File: paced.info, Node: Dictionary Files, Next: Printing a Dictionary, Prev: Selective Dictionaries, Up: Dictionaries Dictionary Files ================ @@ -348,6 +366,24 @@ setting ‘paced-repopulate-saves-dictionary’ to t. Population is covered in the next section. +File: paced.info, Node: Printing a Dictionary, Prev: Dictionary Files, Up: Dictionaries + +Printing a Dictionary +===================== + +Paced allows a user to print the contents of a dictionary to a buffer. +Uses for this might be to tweak population commands or exclude +functions, or to simply make sure a dictionary is populating correctly. + + To use this feature, run: + + M-x paced-print-named-dictionary RET NAME-OF-DICTIONARY RET + + Or for the current dictionary: + + M-x paced-print-current-dictionary RET + + File: paced.info, Node: Population Commands, Next: Example Setups, Prev: Dictionaries, Up: Top Population Commands @@ -407,7 +443,7 @@ When setting the population commands of a dictionary, one may also set certain properties. Each property is a variable binding, bound while the population command runs. - Two variables are of note here: + A few variables are of note here: paced-exclude-function Function of no arguments that returns non-nil if the thing at point @@ -415,6 +451,9 @@ paced-exclude-function paced-thing-at-point-constituent Symbol defining thing on which population works. Typically set to either ’symbol or ’word. +paced-character-limit + Maximum length of a thing to include it in a dictionary. If set to + 0 (default), no limit is imposed. For convenience, properties that are intended for all population commands of a given dictionary may be set in the dictionary itself. In @@ -498,10 +537,17 @@ A common problem is that population can take a long time. Some of us populate dictionaries from org agenda files, which can get pretty big. To solve this, paced uses the async -(https://github.com/jwiegley/emacs-async) package. Setup should be -seamless; just stick whatever code you need in -‘~/.emacs.d/paced-async.el’, type M-x -paced-repopulate-named-dictionary-async, and push enter. +(https://github.com/jwiegley/emacs-async) package. Setup is seamless; +just stick whatever code you need in ‘~/.emacs.d/paced-async.el’, and +use one of the two population commands: + + A named dictionary: + + M-x paced-repopulate-named-dictionary-async RET NAME RET + + Or the current dictionary: + + M-x paced-repopulate-current-dictionary-async RET A few things to note about this: @@ -509,7 +555,9 @@ paced-repopulate-named-dictionary-async, and push enter. population 2. Asynchronous population doesn’t change anything until after population is finished, so a user may continue to use their - dictionary while population is happening. + dictionary while population is happening. This also means that + multiple populations may run in parallel without interfering with + one another. 3. Because async runs population in a separate Emacs process, any custom code required for population must be in paced-async.el. This includes additional population command types, but doesn’t @@ -529,6 +577,9 @@ Example Setups * Org Agenda Files:: * Project Files:: +* Markdown Files:: +* Repopulating Dictionary After Saving:: +* Repopulating Dictionary After Spellchecking the Buffer:: File: paced.info, Node: Org Agenda Files, Next: Project Files, Up: Example Setups @@ -549,7 +600,20 @@ code and drawers. Done. - Now, the exclude command, which sits inside the properties option: + Now, the exclude command, which sits inside the properties option. +This can be added to ‘paced-async.el’: + + (require 'org) + + (defun org-at-tag-p () + (let* ((p (point))) + ;; Ignore errors from `org-get-tags-string'. + (ignore-errors + ;; Checks the match string for a tag heading, setting match-string 1 to the + ;; tags. Also sets match-beginning and match-end. + (org-get-tags-string) + (when (match-string 1) + (<= (match-beginning 1) p (match-end 1)))))) (defun org-paced-exclude () (or @@ -557,6 +621,8 @@ code and drawers. (org-between-regexps-p org-drawer-regexp ":END:") ;; Doesn't catch END (org-in-regexp ":END:") ;; but this does + (org-at-tag-p) ;; tags + (org-at-comment-p) ;; comments (org-in-regexp org-any-link-re) ;; links (org-in-block-p '("src" "quote" "verse")) ;; blocks @@ -574,7 +640,7 @@ customize buffer as such: And you’re done. See how easy that was? -File: paced.info, Node: Project Files, Prev: Org Agenda Files, Up: Example Setups +File: paced.info, Node: Project Files, Next: Markdown Files, Prev: Org Agenda Files, Up: Example Setups Project Files ============= @@ -605,6 +671,89 @@ dedicated solutions for almost everything, but it makes an excellent fallback. +File: paced.info, Node: Markdown Files, Next: Repopulating Dictionary After Saving, Prev: Project Files, Up: Example Setups + +Markdown Files +============== + +Another common request is markdown files. In order for this to work, +you’ll need to install ‘markdown-mode’: + + M-x package-install RET markdown-mode RET + + After that, add the following to your ‘paced-async.el’ file: + + (require 'markdown-mode) + + (defun paced-markdown-exclude-p () + "Taken from `markdown-flyspell-check-word-p'." + ;; Exclude anything markdown mode thinks flyspell should skip. + (or + ;; Ignore code blocks + (markdown-code-block-at-point-p) + (markdown-inline-code-at-point-p) + ;; Ignore comments + (markdown-in-comment-p) + ;; Ignore special text + (let ((faces (get-text-property (point) 'face))) + (if (listp faces) + (or (memq 'markdown-reference-face faces) + (memq 'markdown-markup-face faces) + (memq 'markdown-plain-url-face faces) + (memq 'markdown-inline-code-face faces) + (memq 'markdown-url-face faces)) + (memq faces '(markdown-reference-face + markdown-markup-face + markdown-plain-url-face + markdown-inline-code-face + markdown-url-face)))))) + + That excludes anything that the developers of markdown-mode felt +should be excluded from flyspell. + + Set this as your exclude function in your dictionary’s settings, then +add each markdown file by hand. + + +File: paced.info, Node: Repopulating Dictionary After Saving, Next: Repopulating Dictionary After Spellchecking the Buffer, Prev: Markdown Files, Up: Example Setups + +Repopulating Dictionary After Saving +==================================== + +This is a common request, although with the power of async, it’s an easy +one to fulfill. This will repopulate the current buffer’s dictionary +every time you save a file with a dictionary. This may seem daunting, +but the dictionary will remain usable during population, and multiple +populations won’t interfere with one another. + + ;; Repopulate the current dictionary after saving + (add-hook 'after-save-hook 'paced-repopulate-current-dictionary-async) + + Add that to your .emacs file, and paced will take it from there. + + If you decide that’s too much, do the following: + + M-: (remove-hook 'after-save-hook 'paced-repopulate-current-dictionary-async) RET + + +File: paced.info, Node: Repopulating Dictionary After Spellchecking the Buffer, Prev: Repopulating Dictionary After Saving, Up: Example Setups + +Repopulating Dictionary After Spellchecking the Buffer +====================================================== + +Another request, although much trickier to do. This one involves using +Emacs’s advice mechanism: + + (define-advice ispell-pdict-save (:after (&optional _no-query _force-save) paced-populate) + ;; Repopulate the current dictionary after running spell check + (paced-repopulate-current-dictionary-async)) + + If you decide this isn’t for you, do the following to revert the +changes: + + M-: (advice-remove #'ispell-pdict-save #'ispell-pdict-save@paced-populate) RET + + File: paced.info, Node: Contributing, Next: Changelog, Prev: Example Setups, Up: Top Contributing @@ -671,6 +820,19 @@ can then merge that into the main development branch. paced-POPULATION-COMMAND-TYPE-population-command • Run ’make check’ to verify that your mods don’t break anything • Avoid additional or altered dependencies if at all possible + • Dictionary commands come in threes (“the operation triad”): + 1. paced-dictionary-OPERATION, a cl-defmethod which performs + OPERATION on a dictionary + 2. paced-OPERATION-on-named-dictionary, an interactive only + function that prompts for a dictionary name and performs + OPERATION on that dictionary: + (interactive (list (paced-read-dictionary))) + (paced-ensure-registered name) + (paced-dictionary-OPERATION (paced-named-dictionary name)) + 3. paced-OPERATION-on-current-dictionary, an interactive function + that performs OPERATION on the current dictionary + (interactive) + (paced-dictionary-OPERATION (paced-current-dictionary-or-die)) File: paced.info, Node: Documentation, Next: Working with EDE, Prev: Development, Up: Contributing @@ -721,11 +883,32 @@ Changelog * Menu: +* 1.1: 11. * 1.0.1: 101. * 1.0: 10. -File: paced.info, Node: 101, Next: 10, Up: Changelog +File: paced.info, Node: 11, Next: 101, Up: Changelog + +1.1 +=== + + • Cleaned up the code to reflect the “operation triad” + • -OP, OP-on-named, OP-on-current + • Retained backwards compatibility by obsoleting a bunch of + functions, but didn’t remove any of them + • Also removed the use of dict- in global variables and + functions + + • Added the ability to print the contents of a dictionary in a + separate buffer + + • Added the option to limit the words added during population by size + + • Various documentation improvements + + +File: paced.info, Node: 101, Next: 10, Prev: 11, Up: Changelog 1.0.1 ===== @@ -746,34 +929,39 @@ Initial release. Tag Table: Node: Top228 -Node: Copying1772 -Node: Introduction2591 -Node: Similar Packages3711 -Node: pabbrev3997 -Node: predictive5140 -Node: Installation6188 -Node: Basic Setup6653 -Node: Dictionaries7256 -Node: Creating a Dictionary7660 -Node: Editing a Dictionary8700 -Node: Selective Dictionaries9176 -Node: Dictionary Files10825 -Node: Population Commands11916 -Node: Built-in Commands12852 -Node: Properties13649 -Node: Custom Commands14511 -Node: Asynchronous Population17238 -Node: Example Setups18431 -Node: Org Agenda Files18613 -Node: Project Files19869 -Node: Contributing20982 -Node: Bugs21756 -Node: Development22145 -Node: Documentation23112 -Node: Working with EDE23579 -Node: Changelog24622 -Node: 10124747 -Node: 1024944 +Node: Copying1971 +Node: Introduction2790 +Node: Similar Packages3910 +Node: pabbrev4196 +Node: predictive5339 +Node: Installation6387 +Node: Basic Setup7049 +Node: Dictionaries7664 +Node: Creating a Dictionary8137 +Node: Editing a Dictionary9177 +Node: Selective Dictionaries9653 +Node: Dictionary Files11391 +Node: Printing a Dictionary12512 +Node: Population Commands13034 +Node: Built-in Commands13970 +Node: Properties14767 +Node: Custom Commands15765 +Node: Asynchronous Population18492 +Node: Example Setups19941 +Node: Org Agenda Files20242 +Node: Project Files22002 +Node: Markdown Files23138 +Node: Repopulating Dictionary After Saving24763 +Node: Repopulating Dictionary After Spellchecking the Buffer25691 +Node: Contributing26436 +Node: Bugs27210 +Node: Development27599 +Node: Documentation29360 +Node: Working with EDE29827 +Node: Changelog30870 +Node: 1131006 +Node: 10131601 +Node: 1031809 End Tag Table diff --git a/paced.org b/paced.org index c3c4596..8a6e54e 100644 --- a/paced.org +++ b/paced.org @@ -98,7 +98,13 @@ need it to do that. | Emacs | 25.1 | | async | 1.9.1 | -Right now, the only way to install paced is from source. +Paced may be installed from source, or from GNU ELPA. + +From ELPA: + +#+begin_example +M-x package-install RET paced RET +#+end_example From Source: @@ -106,12 +112,20 @@ From Source: bzr branch https://bzr.savannah.gnu.org/r/paced-el paced #+end_src -After that, add the following to your init file (typically .emacs): +After installing from source, add the following to your init file (typically .emacs): #+BEGIN_SRC emacs-lisp (add-to-list 'load-path "/full/path/to/paced/") (require 'paced) #+END_SRC + +However you install paced, you must also make sure dictionaries are loaded on +startup: + +#+begin_src emacs-lisp +(paced-load-all-dictionaries) +#+end_src + ** Basic Setup :PROPERTIES: :DESCRIPTION: The simplest setup @@ -121,7 +135,7 @@ Paced needn't have a lot of setup to run. In fact, the simplest setup is as follows: 1. Create a new dictionary, "Default" (See [[#dictionary_creation][Creating a Dictionary]]) -2. Set paced-global-dict-enable-alist to ~((t . "Default"))~ (See [[#selective_dictionaries][Selective Dictionaries]]) +2. Set ~paced-global-dictionary-enable-alist~ to ~((t . "Default"))~ (See [[#selective_dictionaries][Selective Dictionaries]]) 3. Run ~M-x global-paced-mode~ 4. To add a file to the dictionary, use ~M-x paced-add-buffer-file-to-dictionary~ @@ -176,7 +190,8 @@ documented in the edit buffer. Paced provides a mechanism called the "enable list", that allows a user to enable certain dictionaries for completion given certain conditions. -There are two enable lists: a global and local one. They both work the same, +There are two enable lists: a global (~paced-global-dictionary-enable-alist~) +and local (~paced-local-dictionary-enable-alist~) one. They both work the same, with the local one taking precedence. Each entry in the list has a condition and a key. @@ -238,6 +253,26 @@ M-x paced-save-all-dictionaries RET Dictionaries may also be automatically saved whenever changed by setting ~paced-repopulate-saves-dictionary~ to t. Population is covered in the next section. +** Printing a Dictionary +:PROPERTIES: +:DESCRIPTION: Seeing the contents of a dictionary +:END: + +Paced allows a user to print the contents of a dictionary to a buffer. Uses for +this might be to tweak population commands or exclude functions, or to simply +make sure a dictionary is populating correctly. + +To use this feature, run: + +#+begin_example +M-x paced-print-named-dictionary RET NAME-OF-DICTIONARY RET +#+end_example + +Or for the current dictionary: + +#+begin_example +M-x paced-print-current-dictionary RET +#+end_example * Population Commands :PROPERTIES: @@ -286,12 +321,14 @@ When setting the population commands of a dictionary, one may also set certain properties. Each property is a variable binding, bound while the population command runs. -Two variables are of note here: +A few variables are of note here: - paced-exclude-function :: Function of no arguments that returns non-nil if the thing at point should be excluded from population. - paced-thing-at-point-constituent :: Symbol defining thing on which population works. Typically set to either 'symbol or 'word. +- paced-character-limit :: Maximum length of a thing to include it in a + dictionary. If set to 0 (default), no limit is imposed. For convenience, properties that are intended for all population commands of a given dictionary may be set in the dictionary itself. In the event of a @@ -374,16 +411,29 @@ in this case. A common problem is that population can take a long time. Some of us populate dictionaries from org agenda files, which can get pretty big. -To solve this, paced uses the [[https://github.com/jwiegley/emacs-async][async]] package. Setup should be seamless; just -stick whatever code you need in ~~/.emacs.d/paced-async.el~, type M-x -paced-repopulate-named-dictionary-async, and push enter. +To solve this, paced uses the [[https://github.com/jwiegley/emacs-async][async]] package. Setup is seamless; just stick +whatever code you need in ~~/.emacs.d/paced-async.el~, and use one of the two +population commands: + +A named dictionary: + +#+begin_example +M-x paced-repopulate-named-dictionary-async RET NAME RET +#+end_example + +Or the current dictionary: + +#+begin_example +M-x paced-repopulate-current-dictionary-async RET +#+end_example A few things to note about this: 1. Dictionaries will be automatically saved by this method after population 2. Asynchronous population doesn't change anything until after population is finished, so a user may continue to use their dictionary while population is - happening. + happening. This also means that multiple populations may run in parallel + without interfering with one another. 3. Because async runs population in a separate Emacs process, any custom code required for population must be in paced-async.el. This includes additional population command types, but doesn't include the following variables: @@ -411,15 +461,30 @@ The generator for file-list is easy: Done. -Now, the exclude command, which sits inside the properties option: +Now, the exclude command, which sits inside the properties option. This can be +added to ~paced-async.el~: #+begin_src emacs-lisp +(require 'org) + +(defun org-at-tag-p () + (let* ((p (point))) + ;; Ignore errors from `org-get-tags-string'. + (ignore-errors + ;; Checks the match string for a tag heading, setting match-string 1 to the + ;; tags. Also sets match-beginning and match-end. + (org-get-tags-string) + (when (match-string 1) + (<= (match-beginning 1) p (match-end 1)))))) + (defun org-paced-exclude () (or ;; Drawers (org-between-regexps-p org-drawer-regexp ":END:") ;; Doesn't catch END (org-in-regexp ":END:") ;; but this does + (org-at-tag-p) ;; tags + (org-at-comment-p) ;; comments (org-in-regexp org-any-link-re) ;; links (org-in-block-p '("src" "quote" "verse")) ;; blocks @@ -467,6 +532,86 @@ small function for excluding those: Use that for paced-exclude-function, and you're done. We can't necessarily recommend this for any programming language, as there are dedicated solutions for almost everything, but it makes an excellent fallback. +** Markdown Files + +Another common request is markdown files. In order for this to work, you'll need to install ~markdown-mode~: + +#+begin_example +M-x package-install RET markdown-mode RET +#+end_example + +After that, add the following to your ~paced-async.el~ file: + +#+begin_src emacs-lisp +(require 'markdown-mode) + +(defun paced-markdown-exclude-p () + "Taken from `markdown-flyspell-check-word-p'." + ;; Exclude anything markdown mode thinks flyspell should skip. + (or + ;; Ignore code blocks + (markdown-code-block-at-point-p) + (markdown-inline-code-at-point-p) + ;; Ignore comments + (markdown-in-comment-p) + ;; Ignore special text + (let ((faces (get-text-property (point) 'face))) + (if (listp faces) + (or (memq 'markdown-reference-face faces) + (memq 'markdown-markup-face faces) + (memq 'markdown-plain-url-face faces) + (memq 'markdown-inline-code-face faces) + (memq 'markdown-url-face faces)) + (memq faces '(markdown-reference-face + markdown-markup-face + markdown-plain-url-face + markdown-inline-code-face + markdown-url-face)))))) +#+end_src + +That excludes anything that the developers of markdown-mode felt should be +excluded from flyspell. + +Set this as your exclude function in your dictionary's settings, then add each +markdown file by hand. + +** Repopulating Dictionary After Saving + +This is a common request, although with the power of async, it's an easy one to +fulfill. This will repopulate the current buffer's dictionary every time you +save a file with a dictionary. This may seem daunting, but the dictionary will +remain usable during population, and multiple populations won't interfere with +one another. + +#+begin_src emacs-lisp +;; Repopulate the current dictionary after saving +(add-hook 'after-save-hook 'paced-repopulate-current-dictionary-async) +#+end_src + +Add that to your .emacs file, and paced will take it from there. + +If you decide that's too much, do the following: + +#+begin_example +M-: (remove-hook 'after-save-hook 'paced-repopulate-current-dictionary-async) RET +#+end_example + +** Repopulating Dictionary After Spellchecking the Buffer + +Another request, although much trickier to do. This one involves using Emacs's +advice mechanism: + +#+begin_src emacs-lisp +(define-advice ispell-pdict-save (:after (&optional _no-query _force-save) paced-populate) + ;; Repopulate the current dictionary after running spell check + (paced-repopulate-current-dictionary-async)) +#+end_src + +If you decide this isn't for you, do the following to revert the changes: + +#+begin_example +M-: (advice-remove #'ispell-pdict-save #'ispell-pdict-save@paced-populate) RET +#+end_example * Contributing :PROPERTIES: @@ -528,6 +673,21 @@ There are a few rules to follow: - New population commands should be named paced-POPULATION-COMMAND-TYPE-population-command - Run 'make check' to verify that your mods don't break anything - Avoid additional or altered dependencies if at all possible +- Dictionary commands come in threes ("the operation triad"): + 1. paced-dictionary-OPERATION, a cl-defmethod which performs OPERATION on a dictionary + 2. paced-OPERATION-on-named-dictionary, an interactive only function that prompts + for a dictionary name and performs OPERATION on that dictionary: + #+begin_src emacs-lisp + (interactive (list (paced-read-dictionary))) + (paced-ensure-registered name) + (paced-dictionary-OPERATION (paced-named-dictionary name)) + #+end_src + 3. paced-OPERATION-on-current-dictionary, an interactive function that + performs OPERATION on the current dictionary + #+begin_src emacs-lisp + (interactive) + (paced-dictionary-OPERATION (paced-current-dictionary-or-die)) + #+end_src ** Documentation :PROPERTIES: @@ -574,6 +734,18 @@ and letting one of us handle it is a good way to go. :PROPERTIES: :DESCRIPTION: List of changes by version :END: +** 1.1 +- Cleaned up the code to reflect the "operation triad" + - -OP, OP-on-named, OP-on-current + - Retained backwards compatibility by obsoleting a bunch of functions, but didn't remove any of them + - Also removed the use of dict- in global variables and functions + +- Added the ability to print the contents of a dictionary in a separate buffer + +- Added the option to limit the words added during population by size + +- Various documentation improvements + ** 1.0.1 Bug fix release - Save dictionaries right after they're created diff --git a/test.mk b/test.mk index 392cb81..fa886a3 100644 --- a/test.mk +++ b/test.mk @@ -16,7 +16,7 @@ # EDE only allows arbitrary code from an external makefile, so this is how we've # got to do testing. -test: +test: compile @$(EMACS) \ $(EMACSFLAGS) \ -L "." \