reddit / emacs

Aspell, flyspell, and 'smart' quotes

graduale · 2022-02-15 23:10
(**Edit**: fixed unfortunate typo, pointed out by u/yurikhan...) I'm trying to get `flyspell` to play nice with `electric-quote-mode`. Basically, especially in `org-mode`, I want to be able to use 'smart' quotes without each instance of e.g. `isn’t` being marked as a spelling error. [Aspell only seems to like straight quotes](https://preview.redd.it/bp5jy0eow2i81.png?width=898&format=png&auto=webp&s=b9b917a72debcdd52034c319b1d157df8b350cb6) I've looked around the customization options for `flyspell` but haven't found anything that looks relevant. And it occurred to me that I cannot be the only person dealing with this problem. So I come here asking for help. Anyone? (I found [a post](http://endlessparentheses.com/ispell-and-apostrophes.html) by Artur Malabarba offering a solution to this problem but I'm wondering if others have other fixes.)
yurikhan · 2022-02-16 07:49
But `isn‘t` _is_ a spelling error. The correct spelling is `isn’t`. (FWIW I agree with your main point, and I’d like straight quotes to be marked as spelling errors as well.)
redguardtoo · 2022-02-16 13:22
The easiest way is to add `shouldn` into "~/.aspell.en.pws" Another solution is to set the varialbe `flyspell-generic-check-word-predicate`.
graduale · 2022-02-16 17:15
Oops. Unfortunate error indeed…
graduale · 2022-02-16 17:23
(I never get the keyboard shortcuts for inserting non-straight quotes on a Mac right… thank goodness for `electric-quote-mode` )
graduale · 2022-02-16 17:35
But wouldn't this require manually adding each possible contraction, possessive, etc. to the dictionary? If so, it's not very practical for my purposes. (From what I can tell setting `flyspell-generic-check-word-predicate` would involve basically the same thing, but I'm not super clear on how it works from just looking at the docstring.)
Illiamen · 2022-02-16 19:42
I think you can do this by customizing `ispell-local-dictionaries-alist`. The variable is a list of dictionaries and their settings. See the fourth element of the entry for each dictionary.
graduale · 2022-02-16 20:58
Oh, interesting. The solution by Artur Malabarba that I mentioned in the post involves setting that variable, but somehow does more than that: ```` (setq ispell-local-dictionary-alist `((nil "[[:alpha:]]" "[^[:alpha:]]" "['\x2019]" nil ("-B") nil utf-8))) ;;; Don't send ’ to the subprocess. (defun endless/replace-apostrophe (args) (cons (replace-regexp-in-string "’" "'" (car args)) (cdr args))) (advice-add #'ispell-send-string :filter-args #'endless/replace-apostrophe) ;;; Convert ' back to ’ from the subprocess. (defun endless/replace-quote (args) (if (not (derived-mode-p 'org-mode)) args (cons (replace-regexp-in-string "'" "’" (car args)) (cdr args)))) (advice-add #'ispell-parse-output :filter-args #'endless/replace-quote) ```` I started out by blindly copying and pasting from that blog post and got to what seemed like a solution, but didn't quite see what the two bits of advice were doing. Now that I read the docstring for `ispell-local-dictionary-alist` (as I should have!) and your comment, I'm trying to just set the value of `ispell-local-dictionary-alist` to all for `’` as part of words, and it looks like this works!
redguardtoo · 2022-02-16 22:26
The predicate is a function without parameters. Inside the funciton, - current buffer is the buffer being spell checked - the word at point is the typo candidate. If the predicate returns boolean value `t`, the candidate will be highlighted as typo The complexity of predicate is many major modes pre-configures `flyspell-generic-check-word-predicate`, so maybe you can't change it directly. You might need study the major mode's own predicate instead.
graduale · 2022-02-17 07:06
For those following at home, it turns out (unsurprisingly) the two bits of advice actually do something—they allow you to correct a word using flyspell without having the non-straight apostrophe being replaced with a straight one.
graduale · 2022-02-18 05:48
Thanks, I’ll have a look!
TheSitarHero · 2022-03-03 16:59
Having this same problem - what does your init.el/`ispell-local-dictionary-alist` look like now? As far as I can tell, my `ispell-local-dictionary-alist` already has the right symbol in the right place but clearly something is still wrong.
graduale · 2022-03-03 18:05
((nil "[[:alpha:]]" "[^[:alpha:]]" "['’]" nil ("-B") nil utf-8)) Why is something still wrong?
TheSitarHero · 2022-03-03 18:46
So did you get rid of the other bits of advice? I was still getting lines under correct words with apostrophes. I ended up fixing it by changing my spellchecking program to hunspell, following the instructions on [this page](https://www.emacswiki.org/emacs/InteractiveSpell). Converting the hunspell dicts to utf-8 and adding the lines: `ICONV 1` `ICONV ’ '` `WORDCHARS 0123456789'’` to the .aff file fixed it. In my init.el: `(add-to-list 'ispell-local-dictionary-alist '("english-hunspell"` `"[[:alpha:]]"` `"[^[:alpha:]]"` `"[']"` `t` `("-d" "en_GB")` `nil` `utf-8))`
graduale · 2022-03-03 19:07
Weird, I just used the solution from Malabarba's post and it works great. My config: (use-feature flyspell :ensure-system-package aspell :bind ("C-c e s" . my/flyspell-save-word) :custom (ispell-program-name "aspell") (flyspell-use-meta-tab nil) :init (add-hook 'text-mode-hook 'flyspell-mode) (add-hook 'prog-mode-hook 'flyspell-prog-mode) :config ;; Tell ispell.el that ’ can be part of a word, from http://endless parentheses.com/ispell-and-apostrophes.html (setq ispell-local-dictionary-alist `((nil "[[:alpha:]]" "[^[:alpha:]]" "['\x2019]" nil ("-B") nil utf-8))) ;;; Don't send ’ to the subprocess. (defun endless/replace-apostrophe (args) (cons (replace-regexp-in-string "’" "'" (car args)) (cdr args))) (advice-add #'ispell-send-string :filter-args #'endless/replace-apostrophe) ;;; Convert ' back to ’ from the subprocess. (defun endless/replace-quote (args) (if (not (derived-mode-p 'org-mode)) args (cons (replace-regexp-in-string "'" "’" (car args)) (cdr args)))) (advice-add #'ispell-parse-output :filter-args #'endless/replace-quote) (flyspell-mode 1)) (Here, `use-feature` is just a macro I got from somewhere which basically calls `use-package` with `:straight` set to `nil`.) (defmacro use-feature (name &rest args) "Like `use-package', but with `straight-use-package-by-default' disabled. NAME and ARGS are as in `use-package'." (declare (indent defun)) `(use-package ,name :straight nil ,@args))
TheSitarHero · 2022-03-03 20:57
Thanks. Seems like you're using aspell, which I didn't try. When I started, my default ispell program was set to enchant-2 i.e. ispell, which may be a bit primitive for this.