Code Monkey home page Code Monkey logo

flyspell-correct's Introduction

Boris Buliga / Technical Blog / Barberry Garden

Stand With Ukraine

  • I'm an engineering manager from Chișinău, Moldova; based in Kyiv, Ukraine. My time is often consumed by work, tinkering with Emacs, delving into the world of wine, and embarking on quests in Final Fantasy XIV (or other video games).
  • I share much of my Emacs journey on GitHub, alongside musings on my technical blog.
  • I also detail my experiences with wine and the various tasting events I partake in or organise on 🍇 Barberry Garden 🦄.

If you appreciate my content, consider supporting me through Buy Me A Coffee or Payoneer.

flyspell-correct's People

Contributors

blue0513 avatar boruch-baum avatar clemera avatar d12frosted avatar damiencassou avatar dependabot[bot] avatar ergus avatar gusbrs avatar minad avatar mrbliss avatar syohex avatar tpadioleau avatar vermiculus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

flyspell-correct's Issues

Wrong type argument in `flyspell-correct-move`

When a buffer is first created, the mark is nil.
If the function flyspell-correct-move is called under this conditions, the following error will be shown:

Wrong type argument: number-or-marker-p, nil

This is due to this source line. The (mark) function returns nil and the command errors out.

A hydra for flyspell with flyspell-correct

Hi,

following the discussion at #58, I share here a hydra I cooked for flyspell spellchecking making use of flyspell-correct-at-point as basic correction interface. As I mentioned there, it is not strictly within the logic of flyspell-correct but I believe it may complement it in the particular task of whole buffer checking, so it might be useful. So, this is not a "request" nor a "bug report". It is meant only as sharing some ideas I found worked well for me, for your use in flyspell-correct as you please, if you please. Of course, if some of the ideas or code are deemed interesting enough to be somehow incorporated to flyspell-correct, I'll be more than happy with that.

If I understand the design choice correctly, flyspell-correct is mainly targeted at making fast spell corrections on a local scope, as you type, and excels at that. This is pretty much the same logic of flyspell itself. But sometimes we also need to do a thorough "whole buffer" check, particularly when we reach a revision stage of a given document. The standard Emacs tool for this task is ispell. Here, in the sense of being designed for it: it keeps track of a ispell session, it defaults to ispell-buffer if there is no region, etc. Of course, we can use ispell-word to correct a word at point or go about a whole buffer with flyspell-goto-next-error and then using flyspell-correct-at-point (or vanilla flyspell facilities for that), but in either case things are less than optimal. (I probably wouldn't start flyspell-correct-wrapper for a whole buffer spell-checking task myself, but it is of course possible, and seems to be so used, e.g. #60 (comment)).

Sure, we could use either tool where it is most appropriate, and probably this is what many people actually do. But I wanted to keep a single UI for spellchecking to reduce the cognitive strain of switching between quite different interfaces. Flyspell would be then the natural candidate, for it is hard to beat for the "spell-checking as you type" task. I've also been reaching best results for LaTeX with it. And I use flyspell-correct(-ivy) as my Flyspell interface, and like it very much.

So I wanted to improve upon flyspell for this particular task of "whole buffer checking" while keeping the goodness of flyspell-correct. This is what this hydra is about.

That given, a couple of things stood out as differences with respect to the behavior of flyspell-correct for this particular task I wanted to address:

  • point handling: flyspell-correct restores point after its task is finished. I find this brilliant for "correct while typing" kind of task (It was actually the killer behavior that brought me to use flyspell-correct in the first place). But it is not what I want when doing a "whole buffer" kind of task. Sometimes the spell-checking requires some sort of special intervention/editing for which the want to actually leave the checker, do something else, and then take things back from where we were with spell-checking. Ispell handles this: it offers a way to leave point at the word which was being checked, and we can restart it again with a prefix etc.

  • skipping: when doing a local "correct while typing" kind of task we see what the problem is and want to correct fast, but sometimes we have to skip some words to get there; when doing a "whole buffer" kind of task we are typically at a revision/polishing phase when the ratio of "skipping/correcting" is much larger. So for the first case we want "mostly correcting, and sometimes skipping", whereas for the second "mostly skipping, and sometimes correcting". Ispell is also sensitive to this: it is not by chance that the space-bar skips in ispell, whereas it replaces in query-replace.

One thing I wanted to keep from flyspell-correct is it's bidirectional functioning, which is not really granted by either flyspell or ispell. True, this is probably less important for a "whole buffer" kind of task, but I like this flexibility and, in my experience, sometimes things that show up further down the road of a checking session makes us regret a previous decision of skipping or accepting and I want to go back. So it's a nice flexibility I wanted to have: navigating back and forth the spelling mistakes.

Those things given, I came up with the hydra plus auxiliary functions below. It admittedly is not within the logic of flyspell-correct-move, and just makes use of flyspell-correct-at-point when needed, which grants the UI experience in line with flyspell-correct in general. The core are the skip functions (just adapted versions of flyspell-goto-next-error). The result is more akin to the ispell logic (given the task envisaged), but with even more flexibility and nicer interface granted by flyspell-correct.

See how you like it and, as said before, if there's anything you can take from it (including all of it) for flyspell-correct, I'd be very glad. If not, it is fine too, the fun in sharing and discussing is more than good enough.

(defhydra hydra-flyspell (:color amaranth
                                 :body-pre
                                 (progn
                                   (when mark-active
                                     (deactivate-mark))
                                   (when (or (not (mark t))
                                             (/= (mark t) (point)))
                                     (push-mark (point) t)))
                                 :hint nil)
  "
 ^Flyspell^         ^Errors^            ^Word^
---------------------------------------------------------
 _b_ check buffer   _c_ correct         _s_ save (buffer)
 _d_ change dict    _n_ goto next       _l_ lowercase (buffer)
 _u_ undo           _p_ goto previous   _a_ accept (session)
 _q_ quit
"
  ("b" flyspell-buffer)
  ("d" ispell-change-dictionary)
  ("u" undo-tree-undo)
  ("q" nil :color blue)
  ("C-/" undo-tree-undo)

  ("c" my/flyspell-correct-at-point-maybe-next)
  ("n" my/flyspell-goto-next-error)
  ("p" my/flyspell-goto-previous-error)
  ("." my/flyspell-correct-at-point-maybe-next)
  ("SPC" my/flyspell-goto-next-error)
  ("DEL" my/flyspell-goto-previous-error)

  ("s" my/flyspell-accept-word-buffer)
  ("l" my/flyspell-accept-lowercased-buffer)
  ("a" my/flyspell-accept-word)

  ("M->" end-of-buffer)
  ("M-<" beginning-of-buffer)
  ("C-v" scroll-up-command)
  ("M-v" scroll-down-command))

(defun my/flyspell-error-p (&optional position)
  "Return non-nil if at a flyspell misspelling, and nil otherwise."
  ;; The check technique comes from 'flyspell-goto-next-error'.
  (let* ((pos (or position (point)))
         (ovs (overlays-at pos))
         r)
    (while (and (not r) (consp ovs))
      (if (flyspell-overlay-p (car ovs))
          (setq r t)
        (setq ovs (cdr ovs))))
    r))

(defvar my/hydra-flyspell-direction 'forward)
(defun my/flyspell-correct-at-point-maybe-next ()
  (interactive)
  (cond ((my/flyspell-error-p)
         (save-excursion
           (flyspell-correct-at-point)))
        ((equal my/hydra-flyspell-direction 'forward)
         (my/flyspell-goto-next-error)
         ;; recheck, for 'my/flyspell-goto-next-error' can legitimately stop
         ;; at the end of buffer
         (when (my/flyspell-error-p)
           (save-excursion
             (flyspell-correct-at-point))))
        ((equal my/hydra-flyspell-direction 'backward)
         (my/flyspell-goto-previous-error)
         ;; recheck, for 'my/flyspell-goto-previous-error' can legitimately
         ;; stop at the beginning of buffer
         (when (my/flyspell-error-p)
           (save-excursion
             (flyspell-correct-at-point))))))

;; Just an adapted version of 'flyspell-goto-next-error'.
(defun my/flyspell-goto-previous-error ()
  "Go to the previous previously detected error.
In general FLYSPELL-GOTO-PREVIOUS-ERROR must be used after
FLYSPELL-BUFFER."
  (interactive)
  (setq my/hydra-flyspell-direction 'backward)
  (let ((pos (point))
        (min (point-min)))
    (if (and (eq (current-buffer) flyspell-old-buffer-error)
             (eq pos flyspell-old-pos-error))
        (progn
          (if (= flyspell-old-pos-error min)
              ;; goto end of buffer
              (progn
                (message "Restarting from end of buffer")
                (goto-char (point-max)))
            (backward-word 1))
          (setq pos (point))))
    ;; seek the previous error
    (while (and (> pos min)
                (not (my/flyspell-error-p pos)))
      (setq pos (1- pos)))
    (goto-char pos)
    (when (eq (char-syntax (preceding-char)) ?w)
      (backward-word 1))
    ;; save the current location for next invocation
    (setq flyspell-old-pos-error (point))
    (setq flyspell-old-buffer-error (current-buffer))
    (if (= pos min)
        (message "No more miss-spelled word!")))
  ;; After moving, check again if we are at a misspelling (accepting a word
  ;; might have changed this, since the last check).  If not, go to the next
  ;; error again, unless we are at point-min (otherwise we might enter into
  ;; infinite loop, if there are no remaining errors).
  (flyspell-word)
  (unless (or (= (point) (point-min))
              (my/flyspell-error-p))
    (my/flyspell-goto-previous-error))
  (when (my/flyspell-error-p)
    (swiper--ensure-visible)))

(defun my/flyspell-goto-next-error ()
  "Go to the next previously detected error.
In general FLYSPELL-GOTO-NEXT-ERROR must be used after
FLYSPELL-BUFFER."
  ;; Just a recursive wrapper on the original flyspell function, which takes
  ;; into account possible changes of accepted words since the last check.
  (interactive)
  (setq my/hydra-flyspell-direction 'forward)
  (flyspell-goto-next-error)
  ;; After moving, check again if we are at a misspelling.  If not, go to
  ;; the next error again.
  (flyspell-word)
  (unless (or (= (point) (point-max))
              (my/flyspell-error-p))
    (my/flyspell-goto-next-error))
  (when (my/flyspell-error-p)
    (swiper--ensure-visible)))

(defun my/flyspell-accept-word (&optional local-dict lowercase)
  "Accept word at point for this session.
If LOCAL-DICT is non-nil, also add it to the buffer-local
dictionary. And if LOWERCASE is non-nil, do so with the word
lower-cased."
  (interactive)
  (let ((word (flyspell-get-word)))
    (if (not (and (consp word)
                  ;; Check if we are actually at a flyspell error.
                  (my/flyspell-error-p (car (cdr word)))))
        (message "Point is not at a misspelling.")
      (let ((start (car (cdr word)))
            (word (car word)))
        (when (and local-dict lowercase)
          (setq word (downcase word)))
        ;; This is just taken (slightly adjusted) from
        ;; 'flyspell-do-correct', which is essentially what
        ;; 'ispell-command-loop' also does.
        (ispell-send-string (concat "@" word "\n"))
        (add-to-list 'ispell-buffer-session-localwords word)
        (or ispell-buffer-local-name ; session localwords might conflict
            (setq ispell-buffer-local-name (buffer-name)))
        (flyspell-unhighlight-at start)
        (if (null ispell-pdict-modified-p)
            (setq ispell-pdict-modified-p
                  (list ispell-pdict-modified-p)))
        (if local-dict
            (ispell-add-per-file-word-list word))))))

(defun my/flyspell-accept-word-buffer ()
  "See `my/flyspell-accept-word'."
  (interactive)
  (my/flyspell-accept-word 'local-dict))
(defun my/flyspell-accept-lowercased-buffer ()
  "See `my/flyspell-accept-word'."
  (interactive)
  (my/flyspell-accept-word 'local-dict 'lowercase))

Notes: 1) I use undo-tree, place there whatever you use; 2) this lacks the facility of saving the word to the "global" dictionary, which I don't use, but it could easily be added; 3) this is an amaranth hydra, so the session will be mostly "sticky", which is meant, unless you explicitly want to leave it; 4) the hydra includes some navigation commands and alternative bindings for base actions which are not in explicit in the "menu".

Allow to configure behaviour of C-g

Current behaviour: when hitting C-g during flyspell-correct-wrapper, the point is restored to the place where it was /before/ calling flyspell-correct-wrapper.

Desired behaviour: allow to configure flyspell-correct-wrapper in a way that C-g behaves like STOP action by default. In this case we might want to have an action to restore the point. 🤷

This concern was raised multiple times, the last occurrences that I remember are: #48 (by @Boruch-Baum) and #73 (by @WorldsEndless).

`flyspell-correct-avy-menu` unavailable

I may be missing something but I cannot find flyspell-correct-avy-menu anywhere. I've tried browsing the package list, searching on MELPA, and doing a simple Google search (only a handful of hits for "flyspell-correct-avy-menu". Of course, trying use-package flyspell-avy-menu tells me that the package cannot be found.

flyspell-correct-ivy

Hi,

The popup options with save to dictionary etc., have stopped working. Not sure why, I cannot get an error report out of it. However, when I press M-o, I get the text Suggestions for … in dictionary "Default":, but cannot see the options. Although, pressing one of the predefined options e.g., M-o b allows me to accept the word in buffer, but I just cannot see the options.

Here is my config:

  (use-package flyspell-correct-ivy
    :bind ("C-M-;" . flyspell-correct-wrapper)
    :init
    (setq flyspell-correct-interface #'flyspell-correct-ivy))

Anyway I can fix this? I'm on emacs 28.0.50 on MacOS.

Cheers.

Differences between our two versions

After your cherry-pick and all your subsequent work, I finally now have decided to integrate, merge and compare our two versions, and I notice two functional differences that affect the "user experience". If the differences are intentional decisions, that's cool, but I thought to point them out in case they were oversights.

  1. In your version, when a user completes or aborts a spell-correct 'session', you return POINT to where it was before beginning the spell-correct 'session'. My version stays put at abort / finish point. I find this desirable because when one is in the process of correcting errors, one may abort mid-way upon seeing that a context needs more editing work than the simple correction provided by fly spell-correct.

  2. Your version doesn't implement flyspell-direction. This was a feature to allow the user to determine the default direction of error-checking, and to allow toggling of direction to be persistent. Among my recent splurge of minor commits is a redefinition of the variable to a defcustom, named
    flyspell-correct-forward-direction. With this, among other things, if one interrupts a correction operation in the middle of a document, the continuing command will remember the recent direction.

ref: https://github.com/Boruch-Baum/flyspell-correct

Setup using use-package

Hi,

I tried to use flyspell-correct-helm flavor using the setup you propose

(use-package flyspell-correct-helm
  :bind ("C-;" . flyspell-correct-wrapper)
  :init
  (setq flyspell-correct-interface #'flyspell-correct-helm))

It doesn't seem to work for me. The key binding is not set, it is actually still bound to flyspell-auto-correct-previous-word. Moreover if I use manually the flyspell-correct-wrapper function, it complains saying helm-M-x-execute-command: Symbol’s function definition is void: flyspell-correct-helm.

The simpler setup you suggest

(require 'flyspell-correct-helm)
(define-key flyspell-mode-map (kbd "C-;") 'flyspell-correct-wrapper)

is working for me (key binding and it uses helm version). But I notice that it doesn't seem to do operations in the same order as in the use-package setup.

I think there are two problems with the use-package setup:

  • The key binding is hidden by flyspell-mode-map, a first fix could be
(use-package flyspell-correct-helm
  :after flyspell ;; to make sure flyspell-mode-map is defined
  :bind (:map flyspell-mode-map ("C-;" . flyspell-correct-wrapper))
  :init
  (setq flyspell-correct-interface #'flyspell-correct-helm))

it makes it closer to the simple setup you suggest.

  • Even if the key binding works, the package is not loaded, maybe because flyspell-correct-wrapper is not part of flyspell-correct-helm ? A workaround could be to split into two distinct use-package declarations
(use-package flyspell-correct
  :after flyspell
  :bind (:map flyspell-mode-map ("C-;" . flyspell-correct-wrapper)))

(use-package flyspell-correct-helm
  :after flyspell-correct)

This way flyspell-correct-helm is loaded as soon as flyspell-correct is, for example when C-; is pressed. (setq flyspell-correct-interface #'flyspell-correct-helm) doesn't seem needed because it is already in flyspell-correct-helm.el. Finally if one doesn't want to use a key-binding, it can be replaced by explicitly deferring with :defer t.

Thanks

Suggest binding?

Hi,

I just set up this binding and have found it really useful:

(use-package flyspell-correct-popup
  :bind (:map popup-menu-keymap
              ("TAB" . popup-next)
              ("S-TAB" . popup-previous)))

I use it in combination with this function which I bind to C-;.

(defun ap/iedit-or-flyspell ()
  "Call `iedit-mode' or correct misspelling with flyspell, depending..."
  (interactive)
  (if (or iedit-mode
          (and (derived-mode-p 'prog-mode)
               (not (or (nth 4 (syntax-ppss))
                        (nth 3 (syntax-ppss))))))
      ;; prog-mode is active and point is in a comment, string, or
      ;; already in iedit-mode
      (iedit-mode)
    ;; Not prog-mode or not in comment or string
    (if (not (equal flyspell-previous-command this-command))
        ;; FIXME: This mostly works, but if there are two words on the
        ;; same line that are misspelled, it doesn't work quite right
        ;; when correcting the earlier word after correcting the later
        ;; one

        ;; First correction; autocorrect
        (call-interactively 'flyspell-auto-correct-previous-word)
      ;; First correction was not wanted; use popup to choose
      (progn
        (save-excursion
          (undo))  ; This doesn't move point, which I think may be the problem.
        (flyspell-region (line-beginning-position) (line-end-position))
        (call-interactively 'flyspell-correct-previous-word-generic)))))

Here's how this works: if I press the key in a comment or a string, it calls flyspell-auto-correct-previous-word at first, automatically correcting the previous marked misspelling to the initial suggestion. If I press the key again, it undoes the correction and calls flyspell-correct-previous-word-generic, which is set to flyspell-correct-popup, which shows the popup list of suggested corrections, and I can then press TAB and S-TAB to choose one and RET to select it. And if I press C-; in code, it activates iedit-mode instead.

I don't know that you'd want to add all of that to the readme, but I thought that the TAB/S-TAB binding would be generally appreciated by most people, as it's a lot easier than C-n/p for choosing. :)

Thanks for your work on this package! Spell correction is so nice on Emacs now!

Ivy actions not available?

Reading the documents, it seems like the Ivy implementation uses the Ivy actions system, and mentions pressing M-o (when the minibuffer is offering a list of suggested words I assume). When I do this though it just accepts the selected word - so actually calls the default action, correct.

Am I doing something wrong or misunderstanding? How should access to save, etc. work? Sorry if this is a silly mistake on my part!

Can't use flyspell-correct-word

Hi,

first of all thanks for the package and for the chance to use Ivy with Flyspell.

I'm using latest flyspell-correct from MELPA. Whenever I issue flyspell-correct-word on a mispelled word, this is the message that I get:

flyspell-correct-word must be bound to an event with parameters

I am using GNU Emacs 25.0.93.1 (x86_64-debian-linux-gnu, GTK+ Version 3.14.5) of 2016-05-06.

No ivy actions

Hi,

I am using latest ivy and flyspell-correct-ivy from MELPA. I can't see the actions displayed here anymore.

This is what I see:

screenshot

This is how I set up flyspell-correct-ivy:

(use-package flyspell-correct-ivy       ; Better interface for corrections
  :ensure t
  :after flyspell
  :bind (:map flyspell-mode-map
              ("C-c $" . flyspell-correct-at-point)))

And these are some details about my system, in case they're needed:

GNU Emacs 27.0.50 (build 1, x86_64-debian-linux-gnu, GTK+ Version 3.22.30)
 of 2018-11-21

Repository revision: 166f6274b4118344612e60fba831e223728f3e89
Configured using:
--host=x86_64-debian-linux-gnu --with-modules

Emacs uptime: 12 minutes, 10 seconds
Colour theme: sanityinc-tomorrow-night
Operating system: Ubuntu 18.04.1 LTS
Window system: x11
Desktop environment: GNOME Shell 3.28.3

flyspell-correct-wrapper does nothing

Version: GNU Emacs 26.3 (build 1, x86_64-suse-linux-gnu, GTK+ Version 3.24.12)

Error: attempting flyspell-correct-wrapper does nothing at all

Steps to reproduce:
0. use-package the two things below, I think according to the readme instructions

  1. insert text with mistypes into a buffer
  2. execute flyspell-buffer to cause errors to be highlighted
  3. Execute flyspell-correct-wrapper. Nothing happens and there is no message.

If I instead execute flyspell-correct-next, things work as expected for a single correction; but I cannot try to use "rapid mode"

emacs init setup:

	(use-package flyspell-correct
	  :ensure t
	  :bind
	  :config
	  (define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper))

	(use-package flyspell-correct-helm
	  :ensure t        
	  :init
	  (setq flyspell-correct-interface #'flyspell-correct-helm))

Add screenshots

Just like helm-flyspell, flyspell-correct also should have some nice screenshots to attract users.

Migrate to buttercup

Reasons to move from ERT:

  1. No good out of box solution for mocking, stubbing and set-up code.
  2. Constant issues with stack traces in the console (ert-runner + cask).
  3. Plethora of packages.
  4. No grouping, so my function names are becoming huge.

Right now it's hard for me to maintain the tests and to add new cases. So I want to give buttercup a try.

A handier skip for flyspell-correct-ivy

Hi,

I've been a happy user of flyspell-correct, and of flyspell-correct-ivy in particular, for some time. Thank you very much.

Since it came around, I also rely mainly on flyspell-correct-wrapper for my "on the spot" typing mistakes. However, I see myself somewhat frequently stumbling around the prefixes, and wishing I had thought all the way through before I started the command. Alas, I mostly don't... Commonly, I'd start flyspell-correct-wrapper just to find out I was actually aiming for the second (or more) spelling mistake before point. If so, the alternative would be to cancel the current operation, start it all over, now with the prefix, "M-o k" to skip the first (again, if it's more), to then reach the desired typo.

Also, I was also thinking the "Skip" action could be more useful when not in "rapid", for it just quits. Finally, ivy-actions are neat for eventual operations, but "M-o k" can be too much for an operation I want to be fast and might want repeatedly. So I had an itch to scratch, I did scratch it today, and thought I might as well share.

The result is bellow, with main features:

  • Action "Skip" does not leave the loop even when rapid is nil, that is, when the command is called without prefix.
  • It introduces command my/flyspell-correct-ivy-skip, bound in flyspell-correct-ivy-map (which had to be created), so that we can skip in succession by simply repeating the key initially used to call flyspell-correct-wrapper (or -previous or -next). This makes it easier to adjust the task after starting a flyspell-correct operation, at least to some degree.

This is still only lightly tested, but so far I'm quite glad with the results. I'd be even happier if you guys also like it, and to eventually find something along these lines incorporated to flyspell-correct.

The code uses el-patch syntax, because that's what I'm using here, and also because it makes it easy to see where I changed stuff. But if that's a problem, I can make it otherwise.

(defvar my/flyspell-correct--skip nil)
(el-patch-defun flyspell-correct-move (position &optional forward rapid)
  "Correct the first misspelled word that occurs before POSITION.

Uses `flyspell-correct-at-point' function for correction.

With FORWARD set non-nil, check forward instead of backward.

With RAPID set non-nil, automatically continues in direction
until all errors in buffer have been addressed."
  ;; NOTE: The way I may be pushing the mark may possibly be more
  ;; idiomatically done using the opoint arg of
  ;; `flyspell-correct-word-before-point'.
  (interactive "d")
  (el-patch-add
    ;; this is done in 'flyspell-correct-wrapper', but not in
    ;; 'flyspell-correct-next' or 'flyspell-correct-previous', which thus
    ;; result in error if there is no mark.
    (when (or (not (mark t))
              (/= (mark t) (point)))
      (push-mark (point) t)))
  (save-excursion
    (let ((incorrect-word-pos))

      ;; narrow the region
      (overlay-recenter (point))

      (let ((overlay-list
             (if forward
                 (overlays-in (- position 1) (point-max))
               (overlays-in (point-min) (+ position 1))))
            (overlay 'dummy-value))
        (while overlay
          (setq overlay (car-safe overlay-list))
          (setq overlay-list (cdr-safe overlay-list))
          (when (and overlay
                     (flyspell-overlay-p overlay))
            (setq incorrect-word-pos (overlay-start overlay))
            (let ((scroll (> incorrect-word-pos (window-end))))
              (goto-char incorrect-word-pos)
              (when scroll (ignore-errors (recenter))))

            ;; try to correct word `flyspell-correct-at-point' returns t when
            ;; there is nothing to correct. In such case we just skip current
            ;; word.
            (unless (flyspell-correct-at-point)
              (when (/= (mark t) (point)) (push-mark (point) t))
              (when (el-patch-swap
                      (not rapid)
                      (and (not my/flyspell-correct--skip)
                           (not rapid)))
                (setq overlay nil))))))

      (when incorrect-word-pos
        (goto-char incorrect-word-pos)
        (forward-word)
        (when (= (mark t) (point)) (pop-mark))))))

(defun my/flyspell-correct-ivy-skip ()
  (interactive)
  (setq my/flyspell-correct--skip t)
  (setq ivy-exit 'done)
  (exit-minibuffer))

;; These are the same keys I use for `flyspell-correct-wrapper',
;; `flyspell-correct-previous' and `flyspell-correct-next'.
(defvar flyspell-correct-ivy-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C-;") #'my/flyspell-correct-ivy-skip)
    (define-key map (kbd "C-.") #'my/flyspell-correct-ivy-skip)
    (define-key map (kbd "C-,") #'my/flyspell-correct-ivy-skip)
    map))

(el-patch-defun flyspell-correct-ivy (candidates word)
  "Run `ivy-read' for the given CANDIDATES.

List of CANDIDATES is given by flyspell for the WORD.

Return a selected word to use as a replacement or a tuple
of (command, word) to be used by `flyspell-do-correct'."
  (el-patch-add
    (setq my/flyspell-correct--skip nil))
  (let* (result
         (action-default (lambda (x) (setq result x)))
         (action-save-word (lambda (_) (setq result (cons 'save word))))
         (action-accept-session (lambda (_) (setq result (cons 'session word))))
         (action-accept-buffer (lambda (_) (setq result (cons 'buffer word))))
         (action-skip-word (lambda (_)
                             (el-patch-swap
                               (setq result (cons 'skip word))
                               (setq my/flyspell-correct--skip t))))
         (action `(1
                   ("o" ,action-default "correct")
                   ("s" ,action-save-word "Save")
                   ("S" ,action-accept-session "Accept (session)")
                   ("b" ,action-accept-buffer "Accept (buffer)")
                   ("k" ,action-skip-word "Skip"))))
    (ivy-read (format "Suggestions for \"%s\" in dictionary \"%s\": "
                      word (or ispell-local-dictionary
                               ispell-dictionary
                               "Default"))
              candidates
              :action action
              (el-patch-add :keymap flyspell-correct-ivy-map)
              :caller 'flyspell-correct-ivy)
    (el-patch-add
      (when my/flyspell-correct--skip
        (setq result (cons 'skip word))))
    result))

A simpler flyspell-correct-wrapper

I've been using flyspell-correct-wrapper as my main spell-checking interface for some time, and though I like it very much, the way it uses up to three universal-arguments, with two different behavior dimensions at play (repetition and direction) does put some cognitive load into calling it. And I admit I end up usually refraining from calling it with arguments for this reason.

Since the inclusion of the ability to skip a word on demand, independently of having called flyspell-correct-wrapper with an argument (in #58 (comment)), the rapid-mode became much less important to me. When I want do do a longer spell-checking session, I go to the hydra #69 instead.

So I came up with a simpler flyspell-correct-wrapper, which takes up only one argument, and changes the direction with the argument. I'm using it in the form:

(defun flyspell-correct-once-wrapper (&optional arg)
  "Correct spelling error once in a dwim fashion.
One \\[universal-argument] changes direction of spelling errors search."
  (interactive "P")
  (when (or (not (mark t))
	    (/= (mark t) (point)))
    (push-mark (point) t))
  (flyspell-correct-move (point) arg))

which is, of course, just a simplified version of the current wrapper, and it does not enable rapid-mode.

A single binding, up to a single argument. And it solves 95% of my relevant cases, and for the other 5% I can fall-back to a more general command. The point, of course, is to reduce friction in the most common use case.

I'm still in the phase of "getting used to it", but I like it. And I thought it might also interest other users of flyspell-correct.

Add flyspell-correct-next-word-generic

I would want a similar function to flyspell-correct-previous-word-generic that searches forward instead of backwards and flyspell-correct-next-word-generic sounds like a good name. :)

Not sure if I have time/energy to add this myself anytime soon, but logging it here.

Allow to configure default direction of `flyspell-correct-wrapper`

It would be nice to have a configuration variable for the default direction of flyspell-correct-wrapper.

Optionally (I am not sure if it's a good idea, any input would be helpful), value of this variable should be modified when changing direction using universal arguments. For example, if the value of that variable is set to forward, invocation of flyspell-correct-wrapper with C-u C-u starts backward spell correction and the value of direction is also changed, so the next execution of flyspell-correct-wrapper without any universal arguments will lead to backward spell correction. What I personally dislike about this feature, it creates cognitive load, as you don't know current value of direction unless you call flyspell-correct-wrapper or inspect the value of variable. For complex cases where you constantly need to switch direction, it's better to use hydra (see #69). But I would love to hear more from users.

See the second point in #48 for more information.

Package should not depend on installation order

Recently I introduced pretty controversial change in 40bdd2f.

It's just a quick workaround and I am looking into proper solution.

In short, flyspell-correct must be byte compiled after helm. In some setups there is no way to force specific order of package installation. So flyspell-correct ends up in situation where helm-build-sync-source is nil. So you have to recompile flyspell-correct in order to fix runtime error.

The proper way of solving such issues is setting helm dependency in 'Package Requires' block. But flyspell-correct doesn't know which package will be used by user. So it's pretty lame to depend on all possible packages (ivy, helm and popup).

One solution would be to divide this package into 4 different - one base package flyspell-correct-generic and three other depending on generic package - flyspell-correct-ivy, flyspell-correct-helm and flyspell-correct-popup. It's very likely that I'll do so, but I would like to hear more on available solutions (if any).

`use-package` defer setup

If I am using always-defer with use-package is there anything extra I have to do so that the correct interface function is called?

I am using

(use-package flyspell-correct-ivy
  :demand
  :bind ("C-M-;" . flyspell-correct-wrapper)
  :init
  (setq flyspell-correct-interface #'flyspell-correct-ivy))

and I get

Error (use-package): Cannot load flyspell-correct-ivy

When trying to use flyspell ivy

Thanks for any pointers

Cursor jump on long-range error finds [patch included]

flyspell-correct-previous-word-generic will jump to the previous error no matter how far back in the buffer the error is located. For me, that's not a bug, but a desirable feature, but what I don't like is that after exiting the function, point jumps back to where it started instead of staying where it made the correction. If you agree that behavior is desirable, attached is a patch that seems to make the change.

Also, on a related note. I decided to use your package in conjunction with flyspell-buffer and flyspell-region to navigate forward and backward among discovered spelling errors. It works great. Here's what I do (you're welcome to use it and share it):

(setq  my-flyspell-direction t)
(defun my-flyspell-popup-wrapper (arg)
  "Search for the previous or next spelling error and suggest
corrections via popup interface. Use the prefix argument to
toggle direction."
  (interactive "P")
  (when arg (setq my-flyspell-direction (not my-flyspell-direction)))
  (if my-flyspell-direction
    (flyspell-correct-previous-word-generic (point))
   (flyspell-correct-next-word-generic (point))))

(define-key flyspell-mode-map (kbd "M-$") 'my-flyspell-popup-wrapper)

patch_fly.txt

Flyspell-correct-ivy doesn't work is previous error is a duplicate

Steps to reproduce

Enter this sentence in a random buffer:

The sixth word in this sentnece is misspelled, but because the the twelfth word is a duplicate, flyspell-correct-ivy doesn't work.

Two words are marked by Flyspell (I put them in bold): the obviously misspelled "sentnece" and the duplicated "the" ("the the twelfth"), because Flyspell warns about duplicated words. Trying to run flyspell-correct-ivy with the point at the end of the sentence (or anywhere after the double "the") has absolutely no effect.

Expected behavior

  • Either suggest to delete the duplicated word (probably not such a good idea).
  • Or suggest corrections for the misspelled word (that would probably be the best option, IMHO)

Thanks!
Th

Highlight the word that is currently being corrected

First, thank you for this package! Perhaps it can be achieved by other means, but I was wondering if it would be possible to add a custom face that would highlight the word that is currently being corrected?

Describe ivy interface minibuffer commands

Thanks for the package!

As I just started using Ivy, I am not very familiar with it, and I was having trouble figuring out how to switch from the list of dictionary candidates to the option menu to save, accept, etc. I looked at the Ivy manual and found the mini buffer command M-o or ivy-dispatching-done, which is what I needed. Perhaps you should note this in the documentation since, unlike the Helm and Popup interfaces, the Ivy interface hides the options menu.

"save <CHANGE>" is broken

Using -helm. When a mistype is found and I type-in what it should be, I can then click "Save" my correction, but it doesn't actually correct my word. Example

;; Buffer Text: cook3s

(M-x flyspell-correct-buffer) ;; word "cook3s" is underlined as incorrect
(flyspell-correct-wrapper) ;; goes to "cook3s" and offers options
;; Type "cookies" and  press enter on "Save cookies"
;; Message: Personal dictionary saved.
;; The word cook3s is no longer underlined as incorrect, but is not changed to cookies as expected

void-variable word

When trying to correct the spelling of a word (flyspell-correct-word-generic), flyspell-correct errors out with:

In `Options' source: `(lambda nil (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp)))' 
 (void-variable word)

Backtrace:

Debugger entered--Lisp error: (void-variable word)
  (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp))
  (lambda nil (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp)))()
  apply((lambda nil (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp))) nil)
  helm-funcall-with-source(((name . "Options") (candidates lambda nil (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp))) (action . identity) (filtered-candidate-transformer helm-flx-fuzzy-matching-sort helm-fuzzy-highlight-matches) (candidate-number-limit . 9999) (volatile) (match helm-mm-exact-match helm-mm-match flyspell-correct--helm-always-match helm-fuzzy-match) (fuzzy-match) (header-line . "TAB: Do Nothing (keeping session)") (dont-plug helm-compile-source--multi-match helm-compile-source--persistent-help helm-compile-source--migemo) (matchplugin)) (lambda nil (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp))))
  helm-interpret-value((lambda nil (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp))) ((name . "Options") (candidates lambda nil (let ((tmp word)) (flyspell-correct--helm-option-candidates tmp))) (action . identity) (filtered-candidate-transformer helm-flx-fuzzy-matching-sort helm-fuzzy-highlight-matches) (candidate-number-limit . 9999) (volatile) (match helm-mm-exact-match helm-mm-match flyspell-correct--helm-always-match helm-fuzzy-match) (fuzzy-match) (header-line . "TAB: Do Nothing (keeping session)") (dont-plug helm-compile-source--multi-match helm-compile-source--persistent-help helm-compile-source--migemo) (matchplugin)))

Can't break out of rapid mode

Helm user here (don't know if that matters).

I often need to stop a spell-correcting session in mid stride, for example if my spell error is "liket his" and I need to just go and to a C-t to fix it. However, it seems impossible to break out of rapid mode since C-g just skips a single step. If I'm spellchecking a large chunk of text with many errors, I need to C-g for every single mistype. There should definitely be some way to break out of rapid mode mid-process.

flyspell-correct-word-generic only shows possible corrections

as the header says flyspell-correct only shows possible corrections but not the options to
to save unknown word to my dictionary, accept this spelling in current buffer or whole session. This is my setup

(use-package flyspell-correct
  :ensure t
  :after flyspell
  :bind (:map flyspell-mode-map
              ("C-<f8>" . flyspell-correct-word-generic))
  :config 
  (setq flyspell-correct-interface 'flyspell-correct-ivy)
  )

What I'm doing wrong?

Regards
Thorsten

rapid mode

Hi I am using the Ivy interface and after the configuration I was not having the rapid behaviour with C-u. I Changed the code in the wrapper to make it simpler and now it works. The problem is that the prefix was not passed among the functions.

;;;###autoload
(defun flyspell-correct-wrapper (arg)
  (interactive "P")
  (if (or (not (mark)) (/= (mark) (point)))
	  (push-mark (point) t))
  (let ((flyspell-forward-direction t)
		(flyspell-rapid nil))
  (cond
   ((equal current-prefix-arg '(4)) ; C-u = rapid
	(setq flyspell-rapid t))
   ((equal current-prefix-arg '(16))    ; C-u C-u = change direction
    (setq flyspell-forward-direction nil))
   ((equal current-prefix-arg '(64))    ; C-u C-u C-u = do both
	(setq flyspell-rapid t)
	(setq flyspell-forward-direction nil)))
  (flyspell-correct-move (point) flyspell-forward-direction flyspell-rapid)))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.