[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/spell-fu 68d33508ef 04/21: Add support for buffer local wo
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/spell-fu 68d33508ef 04/21: Add support for buffer local word-lists |
Date: |
Sat, 7 Jan 2023 07:12:28 -0500 (EST) |
branch: elpa/spell-fu
commit 68d33508efa9740cc0355e18668c92332e3389d9
Author: Campbell Barton <ideasman42@gmail.com>
Commit: Campbell Barton <ideasman42@gmail.com>
Add support for buffer local word-lists
---
changelog.rst | 1 +
readme.rst | 14 +++++
spell-fu.el | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 195 insertions(+), 1 deletion(-)
diff --git a/changelog.rst b/changelog.rst
index b11094967b..1d20171cea 100644
--- a/changelog.rst
+++ b/changelog.rst
@@ -4,6 +4,7 @@ Change Log
##########
- In development.
+ - Support buffer local word-lists via ``spell-fu-buffer-session-localwords``.
- Fix faces of overlays (such as ``hl-line-mode``) no longer mask other
faces when selecting words to check.
- Support for multiple dictionaries at once.
- Reduce idle overlay fragmentation.
diff --git a/readme.rst b/readme.rst
index fc1cb468fb..caa9157d5f 100644
--- a/readme.rst
+++ b/readme.rst
@@ -136,6 +136,20 @@ You may wish to set these values differently based on the
current major-mode.
(global-spell-fu-mode)
+Buffer Local Words
+^^^^^^^^^^^^^^^^^^
+
+You may optionally define a buffer-local word list.
+
+``spell-fu-buffer-session-localwords``
+ A list of strings to accept as correctly spelled words.
+ These may be set by file or directory locals.
+
+ Changing this while spell-fu is already active requires calling:
+ ``spell-fu-buffer-session-localwords-update``.
+ Calling outside of ``spell-fu-mode`` has no effect.
+
+
Advanced Buffer Local Settings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/spell-fu.el b/spell-fu.el
index c5a06ff788..4bfdb0d630 100644
--- a/spell-fu.el
+++ b/spell-fu.el
@@ -70,6 +70,14 @@ Set to 0.0 to highlight immediately (as part of syntax
highlighting)."
"List of major-modes to exclude when `spell-fu' has been enabled globally."
:type '(repeat symbol))
+(defvar-local spell-fu-buffer-session-localwords nil
+ "Optional buffer-local word-list of words.
+This is intended to be set by file-locals or dir-locals.
+Call `spell-fu-buffer-session-localwords-refresh' after run-time
modifications.")
+
+;;;###autoload
+(put 'spell-fu-buffer-session-localwords 'safe-local-variable
#'spell-fu-list-of-strings-p)
+
(defvar-local global-spell-fu-ignore-buffer nil
"When non-nil, the global mode will not be enabled for this buffer.
This variable can also be a predicate function, in which case
@@ -157,6 +165,17 @@ Notes:
;; Cache the result of: `(mapcar (lambda (dict) (symbol-value dict))
spell-fu-dictionaries)'
(defvar-local spell-fu--cache-table-list nil)
+;; The buffer local dictionary generated from
`spell-fu-buffer-session-localwords'.
+(defvar-local spell-fu--buffer-localwords-cache-table nil)
+
+;; Map `spell-fu-buffer-session-localwords' identity to existing
+;; `spell-fu--buffer-localwords-cache-table' entries to avoid conversions from
+;; word lists to dictionaries by checking if the conversion has already been
done.
+;;
+;; NOTE: The keys are the objects for the local-word list,
+;; so this relies on the lists being shared between buffers (not just matching
contents).
+(defvar spell-fu--buffer-localwords-global-cache-table-map nil)
+
;; ---------------------------------------------------------------------------
;; Dictionary Utility Functions
@@ -170,7 +189,34 @@ Notes:
(list
(spell-fu-get-ispell-dictionary (or ispell-local-dictionary
ispell-dictionary "default")))
(when (and ispell-personal-dictionary (file-exists-p
ispell-personal-dictionary))
- (list (spell-fu-get-personal-dictionary "default"
ispell-personal-dictionary)))))
+ (list (spell-fu-get-personal-dictionary "default"
ispell-personal-dictionary)))
+ (when spell-fu-buffer-session-localwords
+ (list (spell-fu-get-buffer-session-localwords-dictionary)))))
+
+(defun spell-fu--dictionaries-test-any (test-fn)
+ "Remove any dictionaries that match TEST-FN."
+ (let ((result nil))
+ (let ((dict-list spell-fu-dictionaries))
+ (while dict-list
+ (let ((dict (pop dict-list)))
+ (when (funcall test-fn dict)
+ (setq result t)
+ (setq dict-list nil)))))
+ result))
+
+(defun spell-fu--dictionaries-remove-any (test-fn)
+ "Return non-nil if any dictionaries match TEST-FN."
+ (setq spell-fu-dictionaries
+ (remq
+ nil
+ (mapcar
+ (lambda (dict)
+ (cond
+ ((funcall test-fn dict)
+ dict)
+ (t
+ nil)))
+ spell-fu-dictionaries))))
(defun spell-fu--cache-file (dict)
"Return the location of the cache file with dictionary DICT."
@@ -355,6 +401,11 @@ Argument POS return faces at this point."
"Return t when FILE-TEST is older than any files in FILE-LIST."
(spell-fu--file-is-older-list file-test file-list))
+;; Auto load as this is a callback for `safe-local-variable'.
+;;;###autoload
+(defun spell-fu-list-of-strings-p (obj)
+ "Return t when OBJ is a list of strings."
+ (and (listp obj) (not (memq nil (mapcar #'stringp obj)))))
;; ---------------------------------------------------------------------------
;; Word List Cache
@@ -1418,6 +1469,134 @@ Argument DICT-FILE is the absolute path to the
dictionary."
(lambda (word) (spell-fu--personal-word-add-or-remove word dict
dict-file 'remove))))
dict))
+;; ---------------------------------------------------------------------------
+;; Buffer Local Words
+
+(defun spell-fu--buffer-localwords-cache-table-update ()
+ "Set `spell-fu--buffer-localwords-cache-table' from the local word list."
+ (let
+ ( ;; Reuse the previous table if possible.
+ (word-table
+ (and
+ spell-fu--buffer-localwords-global-cache-table-map
+ (gethash
+ spell-fu-buffer-session-localwords
+ spell-fu--buffer-localwords-global-cache-table-map
+ nil))))
+
+ (unless word-table
+ (setq word-table
+ (make-hash-table :test #'equal :size (length
spell-fu-buffer-session-localwords)))
+ (dolist (word spell-fu-buffer-session-localwords)
+ (puthash (spell-fu--canonicalize-word word) t word-table))
+ (unless spell-fu--buffer-localwords-global-cache-table-map
+ (setq spell-fu--buffer-localwords-global-cache-table-map
+ (make-hash-table :test #'eq :weakness 'value)))
+ (puthash
+ spell-fu-buffer-session-localwords
+ word-table
+ spell-fu--buffer-localwords-global-cache-table-map))
+ (setq spell-fu--buffer-localwords-cache-table word-table)))
+
+(defun spell-fu--buffer-localwords-add-or-remove (word action)
+ "Add or remove WORD from buffer local names depending on ACTION."
+ (catch 'result
+ (spell-fu--with-message-prefix "Spell-fu: "
+ (unless word
+ (message "word not found!")
+ (throw 'result nil))
+ ;; Case insensitive.
+ (let
+ (
+ (encoded-word (spell-fu--canonicalize-word word))
+ (changed nil))
+ (let ((word-in-dict (gethash encoded-word
spell-fu--buffer-localwords-cache-table nil)))
+ (cond
+ ((eq action 'add)
+ (when word-in-dict
+ (message "\"%s\" already in the local dictionary." word)
+ (throw 'result nil))
+
+ (push encoded-word spell-fu-buffer-session-localwords)
+ (spell-fu--buffer-localwords-cache-table-update)
+
+ (message "\"%s\" successfully added!" word)
+ (setq changed t))
+
+ ((eq action 'remove)
+ (unless word-in-dict
+ (message "\"%s\" not in the personal dictionary." word)
+ (throw 'result nil))
+
+ (setq spell-fu-buffer-session-localwords
+ (delete encoded-word
+ spell-fu-buffer-session-localwords))
+ (spell-fu--buffer-localwords-cache-table-update)
+
+ (message "\"%s\" successfully removed!" word)
+ (setq changed t))
+
+ (t ;; Internal error, should never happen.
+ (error "Invalid action %S" action)))
+
+ (when changed
+ ;; TODO: update file local variables?
+ t))))))
+
+(defun spell-fu-get-buffer-session-localwords-dictionary ()
+ "Get the personal dictionary NAME.
+Argument DICT-FILE is the absolute path to the dictionary."
+ (let ((dict 'spell-fu--buffer-localwords-cache-table))
+ ;; Start with no words - construct them lazily
+ (set dict nil)
+ ;; Set description
+ (put dict 'description "Buffer local dictionary")
+ ;; Set update function
+ (put dict 'update #'spell-fu--buffer-localwords-cache-table-update)
+ ;; Set add/remove functions
+ (put dict 'add-word (lambda (word)
(spell-fu--buffer-localwords-add-or-remove word 'add)))
+ (put
+ dict
+ 'remove-word
+ (lambda (word) (spell-fu--buffer-localwords-add-or-remove word 'remove)))
+ dict))
+
+(defun spell-fu--buffer-localwords-dictionary-test (dict)
+ "Return non-nil when DICT is a local-words dictionary."
+ (eq (get dict 'update) #'spell-fu--buffer-localwords-cache-table-update))
+
+(defun spell-fu--buffer-localwords-update-impl ()
+ "Implementation for `spell-fu-buffer-session-localwords-update'."
+ (let
+ (
+ (do-refresh-cache-table-list nil)
+ (has-localwords-dict
+ (spell-fu--dictionaries-test-any
#'spell-fu--buffer-localwords-dictionary-test)))
+ (cond
+ (spell-fu-buffer-session-localwords
+ (unless has-localwords-dict ;; Add dict.
+ (setq spell-fu-dictionaries
+ (append
+ spell-fu-dictionaries
+ (list (spell-fu-get-buffer-session-localwords-dictionary))))
+ (setq do-refresh-cache-table-list t)))
+ (t
+ (when has-localwords-dict ;; Remove dict.
+ (spell-fu--dictionaries-remove-any
#'spell-fu--buffer-localwords-dictionary-test)
+ (setq do-refresh-cache-table-list t))))
+ (cond
+ (spell-fu-buffer-session-localwords
+ (spell-fu--buffer-localwords-cache-table-update))
+ (t
+ (kill-local-variable 'spell-fu--buffer-localwords-cache-table)))
+ (when do-refresh-cache-table-list
+ (spell-fu--refresh-cache-table-list))))
+
+;;;###autoload
+(defun spell-fu-buffer-session-localwords-update ()
+ "Refresh after changing `spell-fu-buffer-session-localwords'."
+ (when spell-fu-mode
+ (spell-fu--buffer-localwords-update-impl)))
;; ---------------------------------------------------------------------------
;; Define Minor Mode
- [nongnu] elpa/spell-fu ed504863f5 05/21: Fix #13: Every word marked as incorrect on MS-Windows, (continued)
- [nongnu] elpa/spell-fu ed504863f5 05/21: Fix #13: Every word marked as incorrect on MS-Windows, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 219bc124a6 14/21: Cleanup: use private convention for mode management functions, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu df9abe16e2 02/21: Cleanup: sharp-quote function, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 57a678a3c4 07/21: Cleanup: sharp-quote function, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu f62b6d3770 12/21: Cleanup: doc-string length, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu be719051a6 13/21: readme: note when reset may be needed, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu f0d49dcc5a 16/21: Cleanup: emacs native format, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu d0e67cdb25 06/21: When using jit-lock-stealth, ensure words out of the view are checked, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 341575b1d0 10/21: Adding spell-fu-debug variable & spell-fu-reset command, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu bddea99f9c 17/21: Fix #36: byte compilation error with Emacs 28.2, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 68d33508ef 04/21: Add support for buffer local word-lists,
ELPA Syncer <=
- [nongnu] elpa/spell-fu b89bfe035d 20/21: Cleanup: correct typo in description, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 0dc0568186 11/21: Update doc-string and readme for spell-fu-faces-exclude, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 8412ba02df 03/21: Cleanup: use `zerop`, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 95e005969d 09/21: Cleanup: replace 'if' with 'cond', ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 635c5c2eed 19/21: Cleanup: sharp-quote function, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 38f4a4a275 08/21: Fix #31: Failure to detect updated symlinked dictionaries, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 89b014194a 18/21: Cleanup: emacs native format (update), ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 39549871c7 15/21: Cleanup: format, ELPA Syncer, 2023/01/07
- [nongnu] elpa/spell-fu 3caf7047ea 21/21: Rename functions & variables that used a global- prefix, ELPA Syncer, 2023/01/07