;;; rw-ispell.el --- additional functions for ispell.el ;; ;; Copyright (C) 2009 Ralf Wachinger ;; ;; Author: Ralf Wachinger ;; Version: 0.1 ;; Keywords: ispell ;; Compatibility: GNU Emacs 22.x, GNU Emacs 23.x ;; ;; This file is NOT part of GNU Emacs. ;; ;; 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 2 ;; 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 . ;; ;;; Commentary: ;; ;; Associating freely personal dictionaries with ;; general dictionaries for Ispell, Aspell and Hunspell, ;; creating personal dictionary files if necessary, and ;; changing general and personal dictionaries at the same time. ;; ;; Save rw-ispell.el in a convenient directory, preferably in ;; your `load-path'. Add the following to your `user-init-file': ;; ;; (require 'rw-ispell) ;; ;; When the setup starts: ;; a) keyboard: 'M-x rw-ispell-set-up-pdicts RET' ;; b) menubar: Tools --> Spell Checking --> Set up Personal Dictionaries ;; c) automatically when the spellchecker is used the first time ;; d) `user-init-file', after setting the user options: ;; (rw-ispell-set-up-pdicts) ;; ;; Todo: ;; Settings in `ispell-message-dictionary-alist' are not considered yet. ;; Possibly full integration in ispell.el. ;; ;;; Change Log: ;; ;; 2009-03-20 (0.1) ;; Initial Release. ;; ;;; Code: (require 'ispell) (require 'easymenu) ;; User options. (defgroup rw-ispell nil "Additional ispell customization options." :group 'ispell) (defcustom rw-ispell-language-pdict-alist nil "*List used to select a new personal dictionary according to a dictionary regexp, normally a part of the dictionary name. It consists of pairs (REGEXP . DICTIONARY). If the REGEXP of the last pair is an empty string, then DICTIONARY is the default personal dictionary. If DICTIONARY is a file name without path, user's home directory is taken. E.g. you may use the following value: '((\"^en\" . \"~/.pdict_english\") (\"^de\" . \"~/.pdict_deutsch\") (\"\" . \"~/.pdict_default\"))" :type '(repeat (cons regexp string)) :group 'rw-ispell) (defcustom rw-ispell-create-pdict-files nil "*Create empty personal dictionary files, when they don't exist. Needed when the spellchecker can't create the files by itself." :type 'boolean :group 'rw-ispell) ;; Internal. (defvar rw-ispell-is-set-up nil "Flag if the personal dictionaries are set up.") (defun rw-ispell-create-pdict-file (element) "Create an empty personal dictionary file, if it doesn't exist already." (let ((file (expand-file-name (cdr element) "~/"))) (unless (file-exists-p file) (when rw-ispell-create-pdict-files (with-temp-file file t) (message "Created file %s" file))))) ;; User functions. (defun rw-ispell-set-up-pdicts () "Set up personal dictionaries according to `rw-ispell-language-pdict-alist'." (interactive) (ispell-check-version) (let ((default-pdict (or (assoc-default "" rw-ispell-language-pdict-alist) (getenv "WORDLIST") "~/.personal_dictionary"))) ;; At least one personal dictionary. (add-to-list 'rw-ispell-language-pdict-alist (cons "" default-pdict) 'append) ;; Absolute paths for every dictionary. (setq rw-ispell-language-pdict-alist (mapcar #'(lambda (pair) (cons (car pair) (expand-file-name (cdr pair) "~/"))) rw-ispell-language-pdict-alist)) ;; A file for every dictionary must exist. (mapc #'rw-ispell-create-pdict-file rw-ispell-language-pdict-alist) ;; A global dictionary must be set. (setq ispell-personal-dictionary (expand-file-name (assoc-default "" rw-ispell-language-pdict-alist)))) (setq rw-ispell-is-set-up t)) (add-hook 'ispell-initialize-spellchecker-hook 'rw-ispell-set-up-pdicts) (defun rw-ispell-change-dictionary (dict &optional arg) "Change to dictionary DICT for Ispell, and to associated personal dictionary according to `rw-ispell-language-pdict-alist'. With a prefix arg, set it \"globally\", for all buffers. Without a prefix arg, set it \"locally\", just for this buffer. By just answering RET you can find out what the current dictionary is." (interactive (list (completing-read "Use new dictionary (RET for current, SPC to complete): " (and (fboundp 'ispell-valid-dictionary-list) (mapcar 'list (ispell-valid-dictionary-list))) nil t) current-prefix-arg)) ;; General dictionary. (ispell-change-dictionary dict arg) ;; Personal dictionary. (unless (equal dict "") (let ((pdict (assoc-default dict rw-ispell-language-pdict-alist #'string-match))) (when pdict (if arg (setq ispell-personal-dictionary pdict) (setq ispell-local-pdict pdict)))))) (defun rw-ispell-change-personal-dictionary (dict &optional arg) "Change to personal dictionary DICT for the spellchecker. With a prefix arg, set it \"globally\", for all buffers. Without a prefix arg, set it \"locally\", just for this buffer." (interactive (list (completing-read "Use new personal dictionary (RET for current, SPC to complete): " (mapcar 'cdr rw-ispell-language-pdict-alist) nil t) current-prefix-arg)) ;; This relies on completing-read's bug of returning "" for no match (cond ((equal dict "") (message "Using %s personal dictionary %s" (if arg "global" "local") (if arg ispell-personal-dictionary ispell-local-pdict))) (t (if arg (setq ispell-personal-dictionary dict) (setq ispell-local-pdict dict)) (message "%s personal dictionary set to %s" (if arg "Global" "Local") dict)))) (easy-menu-add-item ispell-menu-map '() ["Set up Personals Dictionaries" rw-ispell-set-up-pdicts :visible (not rw-ispell-is-set-up)]) ;; Menu items analogous to "Change dictionary..." (easy-menu-add-item ispell-menu-map '() ["Change Dictionary with Personal..." rw-ispell-change-dictionary :visible rw-ispell-is-set-up]) (easy-menu-add-item ispell-menu-map '() ["Change Personal Dictionary..." rw-ispell-change-personal-dictionary :visible rw-ispell-is-set-up]) (provide 'rw-ispell) ;;; rw-ispell.el ends here.