conf-flyspell.el 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. ;;; conf-flyspell.el --- Flyspell with multi-language/multi-dictionary
  2. ;;; support for `text-mode', `org-mode' etc, and `prog-mode'.
  3. ;;; Commentary:
  4. ;; Usually I write code on english (comments/vars/commits etc)
  5. ;; but my `org-mode' notes/todos or my attempt of blog are a
  6. ;; mix between English/Spanish, so I was looking for an alternative
  7. ;; to have spellchecker in both languages, ideally at the same time
  8. ;; or per buffer, after tested `auto-dictionary' and `guess-language'
  9. ;; even `spell-fu' no of those solved my needs, so after read the docs
  10. ;; of `aspell' I found out it has a option for multi-dictionaries
  11. ;; `--add-extra-dicts' but not worked to me, in reddit I found
  12. ;; the solution with `hunspell' the works really great with
  13. ;; multi-language after some additional tweaks/advices.
  14. ;; see `https://www.reddit.com/r/emacs/comments/8kbdhq/emails_with_spellcheck_in_two_languages')
  15. ;;; Code:
  16. (require 'flyspell)
  17. (require 'flyspell-correct)
  18. (require 'flyspell-correct-ido)
  19. (defcustom distopico:default-dictionaries '("en_GB" "en_US" "en" "es_ANY" "es_CO" "es_MX")
  20. "List of dictionaries to load global by default."
  21. :group 'flyspell
  22. :type 'list)
  23. (defvar distopico:ispell-current-dictionary nil
  24. "Current dictionary to check if is necesary print meesage about new dictionary.")
  25. ;; Functions
  26. (defun distopico:flyspell-get-available-dictionaries ()
  27. "Check available dictionaries from `distopico:default-dictionaries'.
  28. return return a string of those available."
  29. (let ((dictionaries nil)
  30. (missing nil))
  31. (dolist (own-dic distopico:default-dictionaries dictionaries)
  32. (if (assoc own-dic ispell-hunspell-dict-paths-alist)
  33. (setq dictionaries (cons own-dic dictionaries))
  34. (setq missing (cons own-dic missing))))
  35. (when missing
  36. (message (format "flyspell: Dictionary [%s] is not available" (mapconcat 'concat missing ","))))
  37. (when dictionaries
  38. (mapconcat 'concat dictionaries ","))))
  39. (defun distopico:flyspell-update-dictionaries ()
  40. "Set and update available dictionaries."
  41. (interactive)
  42. (ispell-set-spellchecker-params)
  43. (setq ispell-dictionary (distopico:flyspell-get-available-dictionaries))
  44. ;; ispell-set-spellchecker-params has to be called
  45. ;; to setup the dictionaries path
  46. (ispell-hunspell-add-multi-dic ispell-dictionary)
  47. ;; Defines as local dictionary to avoid kill/start ispell all the time
  48. (setq ispell-local-pdict ispell-dictionary))
  49. (defun distopico:flyspell-enable-hook ()
  50. "Enable spell in different modes hooks."
  51. (when (executable-find ispell-program-name)
  52. (flyspell-mode)))
  53. ;; Advice
  54. (defun distopico:ispell-init-process (orig-fun &rest args)
  55. "Advice to avoid unnecessary `ispell' if the message not changed.
  56. this call the `ORIG-FUN' with `ARGS' but inhibit the message
  57. that show on each buffer switch, now only display the message if the
  58. dictionary `ispell-current-dictionary' really changed."
  59. (if (equal ispell-current-dictionary distopico:ispell-current-dictionary)
  60. (let ((inhibit-message t)
  61. (message-log-max nil))
  62. (apply orig-fun args))
  63. (progn
  64. (setq distopico:ispell-current-dictionary ispell-current-dictionary)
  65. (apply orig-fun args))))
  66. (defun distopico:ispell-kill-ispell (orig-fun &rest args)
  67. "Advice to avoid annoying kill `ìspell' process if not a interactive call.
  68. this call the `ORIG-FUN' with the original `ARGS' but inhibit kill message
  69. because looks like an error, kill the process on each buffer switch make sense
  70. to avoid call the incorrect dictionary per buffer but is not really necessary
  71. show the message if not a interactive call."
  72. (if (called-interactively-p 'interactive)
  73. (apply orig-fun args)
  74. (let ((inhibit-message t)
  75. (message-log-max nil))
  76. (apply orig-fun args))))
  77. (advice-add 'ispell-init-process :around #'distopico:ispell-init-process)
  78. (advice-add 'ispell-kill-ispell :around #'distopico:ispell-kill-ispell)
  79. ;; Init
  80. (with-eval-after-load "ispell"
  81. (setq ispell-program-name (executable-find "hunspell")
  82. flyspell-use-meta-tab nil
  83. flyspell-prog-text-faces '(font-lock-comment-face font-lock-doc-face))
  84. ;; Faces
  85. (set-face-attribute 'flyspell-incorrect nil :underline '(:style wave :color "#6D0900"))
  86. ;; Update available dictionaries
  87. (distopico:flyspell-update-dictionaries)
  88. ;; Re-define some keys
  89. (define-key flyspell-mode-map (kbd "C-c $") #'flyspell-correct-at-point)
  90. (define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper)
  91. (define-key flyspell-mode-map (kbd "C-M-i") nil))
  92. ;;; conf-flyspell ends here
  93. (provide 'conf-flyspell)