[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/spell-fu 4f9732492c 01/86: Initial code.
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/spell-fu 4f9732492c 01/86: Initial code. |
Date: |
Thu, 7 Jul 2022 12:03:38 -0400 (EDT) |
branch: elpa/spell-fu
commit 4f9732492c1afa9e69cb0910051422b5090a0512
Author: Campbell Barton <ideasman42@gmail.com>
Commit: Campbell Barton <ideasman42@gmail.com>
Initial code.
---
.elisp-autofmt | 0
readme.rst | 138 ++++++++++++++
spell-fu.el | 591 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 729 insertions(+)
diff --git a/.elisp-autofmt b/.elisp-autofmt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/readme.rst b/readme.rst
new file mode 100644
index 0000000000..1921e274b5
--- /dev/null
+++ b/readme.rst
@@ -0,0 +1,138 @@
+########
+Spell Fu
+########
+
+Fast highlighting of misspelled words.
+
+This is a light weight spell checker for Emacs,
+that runs from the syntax highlighter without starting external processes.
+
+
+Motivation
+==========
+
+There are two main areas this package focuses on.
+
+- Fast on-screen highlighting.
+
+ *Currently most alternative solutions run an external processes and don't
always update on-screen results.*
+
+- Hack-able spell-checking.
+
+ *Easily to customize for users, for different major-modes.*
+
+
+Usage
+=====
+
+You may enable this globally which has the following defaults.
+
+- Programming modes spell check comments and strings.
+- All other major modes check all words.
+
+.. code-block:: elisp
+
+ ;; This package has not yet been accepted into melpa.
+ (use-package spell-fu)
+ (global-spell-fu-mode)
+
+Or you may wish to configure this per-mode, e.g:
+
+.. code-block:: elisp
+
+ (use-package spell-fu)
+
+ (add-hook 'org-mode-hook
+ (lambda ()
+ (setq spell-fu-faces-exclude '(org-meta-line org-link org-code))
+ (spell-fu-mode)))
+
+
+Details
+-------
+
+- Currently this package requires ``aspell`` to generate the word-list.
+
+
+Customization
+-------------
+
+``spell-fu-directory``
+ The directory used for storing the dictionary cached.
+
+``spell-fu-idle-delay`` (0.25 seconds)
+ The idle time before marking words as misspelled.
+
+ This can be set to zero, in this case an idle timer won't be used,
+ and spelling will be checked as part of syntax highlighting.
+
+``spell-fu-incorrect-face`` (red, underline)
+ The font to use for the spell checking overlay.
+
+``spell-fu-syntax-table`` (buffer-local)
+ The syntax table used for spell-checking.
+
+ Useful when the current syntax-table for a major-mode is set for a
programming language
+ which doesn't make sense to use for natural language.
+
+``spell-fu-word-regexp``
+ The regular expression to use for scanning words.
+
+``spell-fu-faces-include``
+ When not ``nil``, only faces that in this list will be checked.
+
+``spell-fu-faces-exclude``
+ When not ``nil``, text with faces in this list will be excluded.
+
+``spell-fu-check-range``
+ This is the main function which checks words,
+ in most cases this can be left at it's default.
+
+ In some cases you may wish to scan the text in the given range using more
sophisticated checks,
+ skipping text based on your own rules.
+
+ This function takes ``(point-start point-end)`` arguments,
+ which are guaranteed to be on line boundaries.
+
+ ``(spell-fu-check-word point-start point-end word-string)`` should be
called for each word you wish to check.
+
+ Note that setting this function causes the following settings to be ignored:
+
+ - ``spell-fu-word-regexp``
+ - ``spell-fu-faces-include``
+ - ``spell-fu-faces-exclude``
+
+
+Other Settings
+--------------
+
+Some settings are used which aren't part of this package:
+
+``ispell-personal-dictionary``
+ When generating the word-list, this file is included when present.
+
+
+Limitations
+===========
+
+TODO.
+
+
+Installation
+============
+
+TODO.
+
+
+Other Packages
+==============
+
+TODO.
+
+
+TODO
+====
+
+- Support alternates to ``aspell`` for generating word lists.
+- Currently generating the initial word-list is hard-coded to ``en_US``,
+ this should read the users dictionary type.
diff --git a/spell-fu.el b/spell-fu.el
new file mode 100644
index 0000000000..64505ee154
--- /dev/null
+++ b/spell-fu.el
@@ -0,0 +1,591 @@
+;;; spell-fu.el --- Fast & light spelling highlighter -*- lexical-binding: t
-*-
+
+;; Copyright (C) 2020 Campbell Barton
+
+;; Author: Campbell Barton <ideasman42@gmail.com>
+
+;; URL: https://gitlab.com/ideasman42/emacs-spell-fu-mode
+;; Keywords: convenience
+;; Version: 0.1
+;; Package-Requires: ((emacs "26.2"))
+
+;; 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package checks the spelling of on-screen text.
+;;
+
+;;; Usage
+
+;;
+;; Write the following code to your .emacs file:
+;;
+;; (require 'spell-fu)
+;; (global-spell-fu-mode-mode)
+;;
+;; Or with `use-package':
+;;
+;; (use-package spell-fu)
+;; (global-spell-fu-mode-mode)
+;;
+;; If you prefer to enable this per-mode, you may do so using
+;; mode hooks instead of calling `global-spell-fu-mode-mode'.
+;; The following example enables this for org-mode:
+;;
+;; (add-hook 'org-mode-hook
+;; (lambda ()
+;; (setq spell-fu-faces-exclude '(org-meta-line))
+;; (spell-fu-mode)))
+;;
+
+;;; Code:
+
+(eval-when-compile
+ ;; For `ispell-personal-dictionary' and similar.
+ (require 'ispell)
+ ;; Quiet warning about `sort-fold-case'.
+ (require 'sort))
+
+
+;; ---------------------------------------------------------------------------
+;; Custom Variables
+
+(defcustom spell-fu-directory (locate-user-emacs-file "spell-fu"
".emacs-spell-fu")
+ "The directory to store undo data."
+ :group 'spell-fu
+ :type 'string)
+
+(defcustom spell-fu-idle-delay 0.25
+ "Idle time to wait before highlighting.
+Set to 0.0 to highlight immediately (as part of syntax highlighting)."
+ :group 'spell-fu
+ :type 'float)
+
+(defface spell-fu-incorrect-face
+ '((t (:underline (:color "red" :style wave))))
+ "Face for incorrect spelling."
+ :group 'spell-fu)
+
+;; See '-' as a word boundary \b, so 'full-screen' is detected as two words.
+(defvar-local spell-fu-syntax-table
+ (let ((table (standard-syntax-table)))
+ (modify-syntax-entry ?- "-" table)
+ table)
+ "The syntax table to use when scanning words.")
+
+(defvar-local spell-fu-word-regexp "\\b\\([[:alpha:]][[:alpha:]']*\\)\\b"
+ "The regular expression used to scan for words to check (used by
`spell-fu-check-range').")
+
+(defvar-local spell-fu-faces-include nil
+ "List of faces to check or nil to include all (used by
`spell-fu-check-range').")
+
+(defvar-local spell-fu-faces-exclude nil
+ "List of faces to check or nil to exclude none (used by
`spell-fu-check-range').")
+
+
+(defvar-local spell-fu-check-range 'spell-fu-check-range-default
+ "Function that takes a beginning and end points to check for the current
buffer.
+
+Users may want to write their own functions to have more control
+over which words are being checked.
+
+Notes:
+
+- The ranges passed in a guaranteed to be on line boundaries.
+- Calling `spell-fu-check-word' on each word.
+
+- You may explicitly mark a range as incorrect using
+ `spell-fu-mark-incorrect' which takes the range to mark as arguments.")
+
+
+;; ---------------------------------------------------------------------------
+;; Internal Variables
+
+;; Hash table, keep it global,
+;; although we could support buffer local dictionaries.
+(defvar spell-fu--cache-table nil)
+
+
+;; ---------------------------------------------------------------------------
+;; Dictionary Utility Functions
+
+(defun spell-fu--dictionary ()
+ "Access the current dictionary."
+ (or ispell-local-dictionary ispell-dictionary "default"))
+
+(defun spell-fu--cache-file ()
+ "Return the location of the cache file."
+ (expand-file-name (format "words_%s.el" (spell-fu--dictionary))
spell-fu-directory))
+
+(defun spell-fu--words-file ()
+ "Return the location of the word-list."
+ (expand-file-name (format "words_%s.txt" (spell-fu--dictionary))
spell-fu-directory))
+
+
+;; ---------------------------------------------------------------------------
+;; Generic Utility Functions
+;;
+;; Helpers, not directly related to checking spelling.
+;;
+
+(defmacro spell-fu--with-message-prefix (prefix &rest body)
+ "Add text before the message output.
+Argument PREFIX is the text to add at the start of the message.
+Optional argument BODY runs with the message prefix."
+ (declare (indent 1))
+ (let ((message-orig (make-symbol "--spell-fu--with-message-prefix--")))
+ `
+ (cl-letf*
+ (
+ (,message-orig (symbol-function 'message))
+ ((symbol-function 'message)
+ (lambda (arg &rest args)
+ (apply ,message-orig (append (list (concat "%s" arg)) (list
,prefix) args)))))
+ ,@body)))
+
+(defmacro spell-fu--with-add-hook-depth-override (depth-override &rest body)
+ "Support overriding the depth of a hook added by an indirect call.
+Argument DEPTH-OVERRIDE the depth value to call `add-hook' with.
+Optional argument BODY runs with the message prefix."
+ (declare (indent 1))
+ (let ((add-hook-orig (make-symbol
"--spell-fu--with-add-hook-depth-override--")))
+ `
+ (cl-letf*
+ (
+ (,add-hook-orig (symbol-function 'add-hook))
+ ((symbol-function 'add-hook)
+ (lambda (hook function &optional _depth local)
+ (funcall ,add-hook-orig hook function ,depth-override local))))
+ ,@body)))
+
+(defmacro spell-fu--expand-range-to-line-boundaries (point-start point-end)
+ "Set POINT-START the the line beginning, POINT-END to the line end."
+ (declare (indent 1))
+ ;; Ignore field boundaries.
+ (let ((inhibit-field-text-motion t))
+ `
+ (save-excursion
+ ;; Extend the ranges to line start/end, leaving the point at the start.
+ (goto-char ,point-end)
+ (setq ,point-end (line-end-position))
+ (goto-char ,point-start)
+ (setq ,point-start (line-beginning-position)))))
+
+(defun spell-fu--removed-changed-overlay (overlay after _beg _end &optional
_len)
+ "Hook for removing OVERLAY which is being edited.
+Argument AFTER, ignore when true."
+ (unless after
+ (delete-overlay overlay)))
+
+(defun spell-fu--faces-at-point (pos)
+ "Add the named faces that the `read-face-name' or `face' property use.
+Argument POS return faces at this point."
+ (let ((faces nil))
+ (let ((faceprop (or (get-char-property pos 'read-face-name)
(get-char-property pos 'face))))
+ (cond
+ ((facep faceprop)
+ (push faceprop faces))
+ ((face-list-p faceprop)
+ (dolist (face faceprop)
+ (if (facep face)
+ (push face faces))))))
+ faces))
+
+(defun spell-fu--file-is-older-list (file-test file-list)
+ "Return t when FILE-TEST is older than any files in FILE-LIST."
+ (catch 'result
+ (dolist (file-new file-list)
+ (when (time-less-p (nth 5 (file-attributes file-test)) (nth 5
(file-attributes file-new)))
+ (throw 'result t)))))
+
+(defun spell-fu--file-is-older (file-test &rest file-list)
+ "Return t when FILE-TEST is older than any files in FILE-LIST."
+ (spell-fu--file-is-older-list file-test file-list))
+
+
+;; ---------------------------------------------------------------------------
+;; Word List Generation
+
+(defun spell-fu--word-list-ensure (words-file)
+ "Ensure the word list is generated.
+Argument WORDS-FILE the file to write the word list into."
+ (let*
+ (
+ (has-words-file (file-exists-p words-file))
+ (has-dict-personal
+ (and ispell-personal-dictionary (file-exists-p
ispell-personal-dictionary)))
+ (is-dict-outdated
+ (and
+ has-words-file
+ has-dict-personal
+ (spell-fu--file-is-older words-file ispell-personal-dictionary))))
+
+ (when (or (not has-words-file) is-dict-outdated)
+ (message "Generating %S" words-file)
+
+ (with-temp-buffer
+ ;; Optional: insert personal dictionary, stripping header and
inserting a newline.
+ (when has-dict-personal
+ (insert-file-contents ispell-personal-dictionary)
+ (goto-char (point-min))
+ (when (looking-at "personal_ws\-")
+ (delete-region (line-beginning-position) (1+ (line-end-position))))
+ (goto-char (point-max))
+ (unless (eq ?\n (char-after))
+ (insert "\n")))
+
+ (call-process (executable-find "aspell") nil t nil "-d" "en_US" "dump"
"master")
+ ;; Case insensitive sort is important if this is used for
`ispell-complete-word-dict'.
+ ;; Which is a handy double-use for this file.
+ (let ((sort-fold-case t))
+ (sort-lines nil (point-min) (point-max)))
+ (write-region nil nil words-file nil 0)))))
+
+
+;; ---------------------------------------------------------------------------
+;; Word List Cache
+
+(defun spell-fu--cache-from-word-list (words-file cache-file)
+ "Create CACHE-FILE from WORDS-FILE.
+
+The resulting cache is returned as a minor optimization for first-time loading,
+where we need to create this data in order to write it,
+save some time by not spending time reading it back."
+ (let ((word-table nil))
+ (with-temp-buffer
+ (insert-file-contents-literally words-file)
+ (setq word-table (make-hash-table :test 'equal :size (count-lines
(point-min) (point-max))))
+ (while (not (eobp))
+ (let ((l (buffer-substring-no-properties (line-beginning-position)
(line-end-position))))
+ ;; Value of 't' is just for simplicity, it's no used except for
check the item exists.
+ (puthash (downcase l) t word-table)
+ (forward-line 1))))
+
+ ;; Write write it to a file.
+ (with-temp-buffer (prin1 word-table (current-buffer)) (write-region nil
nil cache-file nil 0))
+
+ ;; Return the resulting word table.
+ word-table))
+
+(defun spell-fu--cache-words-load (cache-file)
+ "Return the Lisp content from reading CACHE-FILE."
+ (with-temp-buffer
+ (insert-file-contents-literally cache-file)
+ (goto-char (point-min))
+ (read (current-buffer))))
+
+
+;; ---------------------------------------------------------------------------
+;; Word List Initialization
+;;
+;; Top level function, called when enabling the mode.
+
+(defun spell-fu--init-dictionary ()
+ "Setup the dictionary, initializing new files as necessary."
+
+ ;; Ensure our path exists.
+ (unless (file-directory-p spell-fu-directory)
+ (make-directory spell-fu-directory))
+
+ ;; Generate word-list on demand.
+ (spell-fu--with-message-prefix "Spell-Fu: "
+
+ (let
+ ( ;; Get the paths of both files, ensure the cache file is newer,
+ ;; otherwise regenerate it.
+ (words-file (spell-fu--words-file))
+ (cache-file (spell-fu--cache-file)))
+
+ (spell-fu--word-list-ensure words-file)
+
+ ;; Load cache or create it, creating it returns the cache
+ ;; to avoid some slow-down on first load.
+ (setq spell-fu--cache-table
+ (if (and (file-exists-p cache-file) (spell-fu--file-is-older
cache-file words-file))
+ (spell-fu--cache-words-load cache-file)
+ (spell-fu--cache-from-word-list words-file cache-file))))))
+
+
+;; ---------------------------------------------------------------------------
+;; Shared Functions
+
+(defun spell-fu--remove-overlays (&optional point-start point-end)
+ "Remove symbol `spell-fu-mode' overlays from current buffer.
+If optional arguments POINT-START and POINT-END exist remove overlays from
+range POINT-START to POINT-END. Otherwise remove all overlays."
+ (remove-overlays point-start point-end 'spell-fu-mode t))
+
+(defun spell-fu-mark-incorrect (point-start point-end)
+ "Mark the text from POINT-START to POINT-END with the default incorrect
spelling overlay."
+ (let ((item-ov (make-overlay point-start point-end)))
+ (overlay-put item-ov 'spell-fu-mode t)
+ (overlay-put item-ov 'face 'spell-fu-incorrect-face)
+ (overlay-put item-ov 'modification-hooks
'spell-fu--removed-changed-overlay)
+ (overlay-put item-ov 'insert-in-front-hooks
'spell-fu--removed-changed-overlay)
+ (overlay-put item-ov 'insert-behind-hooks
'spell-fu--removed-changed-overlay)
+ (overlay-put item-ov 'evaporate t)
+ item-ov))
+
+(defun spell-fu-check-word (point-start point-end word)
+ "Run the spell checker on a word.
+
+Marking the spelling as incorrect using `spell-fu-incorrect-face' on failure.
+Argument POINT-START the beginning position of WORD.
+Argument POINT-END the end position of WORD."
+ (unless (gethash (downcase word) spell-fu--cache-table nil)
+ ;; Ignore all uppercase words.
+ (unless (equal word (upcase word))
+ (spell-fu-mark-incorrect point-start point-end))))
+
+
+;; ---------------------------------------------------------------------------
+;; Range Checking Commands
+;;
+;; These functions are value values for the `spell-fu-check-range' buffer
local variable.
+;;
+;; Note that the callers of these function extends the range to line
delimiters,
+;; to ensure there no chance of the points being in the middle of a word.
+;;
+
+(defun spell-fu--check-faces-at-point (pos faces-include faces-exclude)
+ "Check if the position POS has faces that match the include/exclude
arguments.
+
+Argument FACES-INCLUDE faces to check POS includes or ignored when nil.
+Argument FACES-EXCLUDE faces to check POS excludes or ignored when nil."
+ (let
+ (
+ (result (null faces-include))
+ (faces-at-pos (spell-fu--faces-at-point pos))
+ (face nil))
+ (while (setq face (pop faces-at-pos))
+ (when (memq face faces-exclude)
+ (setq faces-at-pos nil)
+ (setq result nil))
+ (when (and (null result) (memq face faces-include))
+ (setq result t)))
+ result))
+
+
+(defun spell-fu--check-range-with-faces (point-start point-end)
+ "Check spelling for POINT-START & POINT-END, checking text matching face
rules."
+ (spell-fu--remove-overlays point-start point-end)
+ (with-syntax-table spell-fu-syntax-table
+ (save-match-data
+ (save-excursion
+ (goto-char point-start)
+ (while (re-search-forward spell-fu-word-regexp point-end t)
+ (let
+ (
+ (word-start (match-beginning 0))
+ (word-end (match-end 0)))
+ (when
+ (spell-fu--check-faces-at-point
+ word-start
+ spell-fu-faces-include
+ spell-fu-faces-exclude)
+ (spell-fu-check-word word-start word-end
(match-string-no-properties 0)))))))))
+
+(defun spell-fu--check-range-without-faces (point-start point-end)
+ "Check spelling for POINT-START & POINT-END, checking all text."
+ (spell-fu--remove-overlays point-start point-end)
+ (with-syntax-table spell-fu-syntax-table
+ (save-match-data
+ (save-excursion
+ (goto-char point-start)
+ (while (re-search-forward spell-fu-word-regexp point-end t)
+ (let
+ (
+ (word-start (match-beginning 0))
+ (word-end (match-end 0)))
+ (spell-fu-check-word word-start word-end
(match-string-no-properties 0))))))))
+
+(defun spell-fu-check-range-default (point-start point-end)
+ "Check spelling POINT-START & POINT-END, checking comments and strings."
+ (if (or spell-fu-faces-include spell-fu-faces-exclude)
+ (spell-fu--check-range-with-faces point-start point-end)
+ (spell-fu--check-range-without-faces point-start point-end)))
+
+
+;; ---------------------------------------------------------------------------
+;; Immediate Style (spell-fu-idle-delay zero or lower)
+
+(defun spell-fu--font-lock-fontify-region (point-start point-end)
+ "Update spelling for POINT-START & POINT-END to the queue, checking all
text."
+ (spell-fu--expand-range-to-line-boundaries point-start point-end)
+ (funcall spell-fu-check-range point-start point-end))
+
+(defun spell-fu--immediate-enable ()
+ "Enable immediate spell checking."
+
+ ;; It's important this is added with a depth of 100,
+ ;; because we want the font faces (comments, string etc) to be set so
+ ;; the spell checker can read these values which may include/exclude words.
+ (spell-fu--with-add-hook-depth-override 100
+ (jit-lock-register 'spell-fu--font-lock-fontify-region)))
+
+(defun spell-fu--immediate-disable ()
+ "Disable immediate spell checking."
+ (spell-fu--remove-overlays)
+ (jit-lock-unregister 'spell-fu--font-lock-fontify-region))
+
+
+;; ---------------------------------------------------------------------------
+;; Timer Style (spell-fu-idle-delay over zero)
+
+(defvar spell-fu--idle-timer nil)
+
+(defun spell-fu--idle-remove-overlays (&optional point-start point-end)
+ "Remove `spell-fu-pending' overlays from current buffer.
+If optional arguments POINT-START and POINT-END exist remove overlays from
+range POINT-START to POINT-END. Otherwise remove all overlays."
+ (remove-overlays point-start point-end 'spell-fu-pending t))
+
+(defun spell-fu--idle-handle-pending-ranges ()
+ "Spell check the on-screen overlay ranges."
+ (when (bound-and-true-p spell-fu-mode)
+ (let*
+ ( ;; Don't show the cursor motion from spell checking.
+ (visible-start (window-start))
+ (visible-end (window-end))
+
+ (overlays-in-view
+ (seq-filter
+ (lambda (item-ov) (eq (overlay-get item-ov 'spell-fu-pending) t))
+ (overlays-in visible-start visible-end))))
+
+ (while overlays-in-view
+ (let*
+ ( ;; Window clamped range.
+ (item-ov (pop overlays-in-view))
+ (point-start (max visible-start (overlay-start item-ov)))
+ (point-end (min visible-end (overlay-end item-ov))))
+
+ ;; Expand so we don't spell check half a word.
+ (spell-fu--expand-range-to-line-boundaries point-start point-end)
+
+ (when
+ (condition-case err
+ ;; Needed so the idle timer won't quit mid-spelling.
+ (let ((inhibit-quit nil))
+ (funcall spell-fu-check-range point-start point-end)
+ t)
+ (error
+ (progn
+ ;; Keep since this should be very rare.
+ (message "Early exit 'spell-fu-mode': %s"
(error-message-string err))
+ ;; Break out of the loop.
+ (setq overlays-in-view nil)
+ nil)))
+
+ ;; Don't delete the overlay since it may extend outside the window
bounds,
+ ;; always delete the range instead.
+ (spell-fu--idle-remove-overlays point-start point-end)
+
+ ;; Ensure the next overlay hasn't been removed.
+ (while (and overlays-in-view (null (overlay-buffer (car
overlays-in-view))))
+ (pop overlays-in-view))))))))
+
+(defun spell-fu--idle-font-lock-region-pending (point-start point-end)
+ "Track the range to spell check, adding POINT-START & POINT-END to the
queue."
+ (let ((item-ov (make-overlay point-start point-end)))
+ ;; Handy for debugging pending regions to be checked.
+ ;; (overlay-put item-ov 'face '(:background "#000000" :extend t))
+ (overlay-put item-ov 'spell-fu-pending t)
+ (overlay-put item-ov 'evaporate 't)))
+
+(defun spell-fu--idle-timer-enable ()
+ "Add the global idle timer."
+ (unless spell-fu--idle-timer
+ (setq spell-fu--idle-timer
+ (run-with-idle-timer spell-fu-idle-delay t
'spell-fu--idle-handle-pending-ranges))))
+
+(defun spell-fu--idle-timer-disable ()
+ "Remove the global idle timer."
+ (when spell-fu--idle-timer
+ (cancel-timer spell-fu--idle-timer)
+ (setq spell-fu--idle-timer nil)))
+
+(defun spell-fu--idle-enable ()
+ "Enable the idle style of updating."
+ ;; Unlike with immediate style, idle / deferred checking isn't as likely to
+ ;; run before fonts have been checked.
+ ;; Nevertheless, this avoids the possibility of spell checking
+ ;; running before font-faces have been set.
+ (spell-fu--with-add-hook-depth-override 100
+ (jit-lock-register 'spell-fu--idle-font-lock-region-pending))
+ (spell-fu--idle-timer-enable))
+
+(defun spell-fu--idle-disable ()
+ "Disable the idle style of updating."
+ (jit-lock-unregister 'spell-fu--idle-font-lock-region-pending)
+ (spell-fu--idle-timer-disable)
+ (spell-fu--idle-remove-overlays))
+
+
+;; ---------------------------------------------------------------------------
+;; Define Minor Mode
+;;
+;; Developer note, use global hooks since these run before buffers are loaded.
+;; Each function checks if the local mode is active before operating.
+
+(defun spell-fu-mode-enable ()
+ "Turn on option `spell-fu-mode' for the current buffer."
+ (spell-fu--init-dictionary)
+
+ ;; We may want defaults for other modes,
+ ;; although keep this general.
+ (cond
+ ((derived-mode-p 'prog-mode)
+ (unless spell-fu-faces-include
+ (setq spell-fu-faces-include
+ '(font-lock-comment-face font-lock-doc-face font-lock-string-face)))
+ (unless spell-fu-faces-exclude
+ (setq spell-fu-faces-exclude '(font-lock-constant-face)))))
+
+ (cond
+ ((<= spell-fu-idle-delay 0.0)
+ (spell-fu--immediate-enable))
+ (t
+ (spell-fu--idle-enable))))
+
+(defun spell-fu-mode-disable ()
+ "Turn off option `spell-fu-mode' for the current buffer."
+ (cond
+ ((<= spell-fu-idle-delay 0.0)
+ (spell-fu--immediate-enable))
+ (t
+ (spell-fu--idle-disable))))
+
+;;;###autoload
+(define-minor-mode spell-fu-mode
+ "Toggle `spell-fu-mode' in the current buffer."
+ :global nil
+
+ (cond
+ (spell-fu-mode
+ (spell-fu-mode-enable))
+ (t
+ (spell-fu-mode-disable))))
+
+(defun spell-fu-mode-turn-on ()
+ "Enable the option `spell-fu-mode' where possible."
+ (when (and (not (minibufferp)) (not spell-fu-mode))
+ (spell-fu-mode 1)))
+
+;;;###autoload
+(define-globalized-minor-mode global-spell-fu-mode spell-fu-mode
spell-fu-mode-turn-on)
+
+(provide 'spell-fu)
+;;; spell-fu.el ends here
- [nongnu] elpa/spell-fu 1698c51740 79/86: Store a list of hashes instead of symbol lookups for each word, (continued)
- [nongnu] elpa/spell-fu 1698c51740 79/86: Store a list of hashes instead of symbol lookups for each word, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 47d92c1989 84/86: Fix wrong function name for multiple languages example, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 2590ee3002 80/86: Cleanup: remove cl-remove-if-not, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 1299bfca2e 85/86: Merge pull request 'Fix wrong function name for multiple languages example' (#27) from kepi/emacs-spell-fu:master into master, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 907271a557 03/86: Cleanup: unnecessary 'let' nesting, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 3898e6f591 04/86: Cleanup: remove use of exception handling, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu ae4504f14d 07/86: Fix using hard coded en_US dictionary, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu f2b7d58e87 08/86: Cleanup: correct comment, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 1e6928f77d 05/86: Cleanup: add message for generating cache, some comments, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu bd76cb262e 15/86: Avoid 2x loops over the pending screen overlays, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 4f9732492c 01/86: Initial code.,
ELPA Syncer <=
- [nongnu] elpa/spell-fu 6a7440044e 19/86: Cleanup: rename variables to make the assignment explicit, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 5915e0ae2a 24/86: Cleanup: use safer sharp-quoted function names, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 3f4fc34204 32/86: Add 'spell-fu-buffer' utility, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu ddad489f2c 43/86: Adds affix dict support via aspell expand cmd, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu ce64f4bc4d 51/86: Cleanup: remove arguments that are always fixed, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 1b765f8029 58/86: Cleanup: replace setq with let binding, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 1159eeec13 63/86: Fix including trailing single-quote in the word, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 32fcbd9e8e 68/86: Initial changes for multiple dictionaries, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 1f3e5b8f05 72/86: readme: Fix typos, ELPA Syncer, 2022/07/07
- [nongnu] elpa/spell-fu 2f2fd6de00 83/86: Change URL to codeberg, ELPA Syncer, 2022/07/07