aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/textmodes
diff options
context:
space:
mode:
authorRichard M. Stallman1993-10-26 20:01:56 +0000
committerRichard M. Stallman1993-10-26 20:01:56 +0000
commitbd28fa5941643e3f7e3f00ddbf7f734176dfac5b (patch)
tree5de030e7856de627f0d8fb7a301cdb844ad5dd0d /lisp/textmodes
parent4578d35d505afeb549b162db40f5d2be4f25d094 (diff)
downloademacs-bd28fa5941643e3f7e3f00ddbf7f734176dfac5b.tar.gz
emacs-bd28fa5941643e3f7e3f00ddbf7f734176dfac5b.zip
(ispell-look-command): New user variable.
(ispell-do-look, ispell-lookup-build-list): Use it as PROGRAM for call-process instead of just "look". (ispell-complete-word-interior-frag): New command. (ispell-complete-word): New command. (ispell-menu-map): Add bindings for them. (ispell-gnu-look-still-broken-p, ispell-look-dictionary): New vars.
Diffstat (limited to 'lisp/textmodes')
-rw-r--r--lisp/textmodes/ispell4.el313
1 files changed, 311 insertions, 2 deletions
diff --git a/lisp/textmodes/ispell4.el b/lisp/textmodes/ispell4.el
index a4f55710ceb..63724f0d0c0 100644
--- a/lisp/textmodes/ispell4.el
+++ b/lisp/textmodes/ispell4.el
@@ -45,6 +45,9 @@ You can use this to specify the name of your private dictionary.
45The -S option is always passed to Ispell as the last parameter, 45The -S option is always passed to Ispell as the last parameter,
46and need not be mentioned here.") 46and need not be mentioned here.")
47 47
48(defvar ispell-look-command "look"
49 "*Command for running look.")
50
48;Each marker in this list points to the start of a word that 51;Each marker in this list points to the start of a word that
49;ispell thought was bad last time it did the :file command. 52;ispell thought was bad last time it did the :file command.
50;Notice that if the user accepts or inserts a word into his 53;Notice that if the user accepts or inserts a word into his
@@ -216,6 +219,12 @@ that have not already been dumped will be lost."
216(defvar ispell-menu-map (make-sparse-keymap "Spell")) 219(defvar ispell-menu-map (make-sparse-keymap "Spell"))
217(defalias 'ispell-menu-map ispell-menu-map) 220(defalias 'ispell-menu-map ispell-menu-map)
218 221
222(define-key ispell-menu-map [ispell-complete-word-interior-frag]
223 '("Complete Interior Fragment" . ispell-complete-word-interior-frag))
224
225(define-key ispell-menu-map [ispell-complete-word]
226 '("Complete Word" . ispell-complete-word))
227
219(define-key ispell-menu-map [reload-ispell] 228(define-key ispell-menu-map [reload-ispell]
220 '("Reload Dictionary" . reload-ispell)) 229 '("Reload Dictionary" . reload-ispell))
221 230
@@ -572,8 +581,8 @@ L lookup; Q quit\n")
572 (set-buffer buf) 581 (set-buffer buf)
573 (delete-region (point-min) (point-max)) 582 (delete-region (point-min) (point-max))
574 (if ispell-have-new-look 583 (if ispell-have-new-look
575 (call-process "look" nil buf nil "-r" regex) 584 (call-process ispell-look-command nil buf nil "-r" regex)
576 (call-process "look" nil buf nil regex)) 585 (call-process ispell-look-command nil buf nil regex))
577 (goto-char (point-min)) 586 (goto-char (point-min))
578 (forward-line 10) 587 (forward-line 10)
579 (delete-region (point) (point-max)) 588 (delete-region (point) (point-max))
@@ -608,6 +617,306 @@ L lookup; Q quit\n")
608 (kill-emacs 1)) 617 (kill-emacs 1))
609 (write-region (point-min) (point-max) "ispell.info")) 618 (write-region (point-min) (point-max) "ispell.info"))
610 619
620;;;; ispell-complete-word
621
622;;; Brief Description:
623;;; Complete word fragment at point using dictionary and replace with full
624;;; word. Expansion done in current buffer like lisp-complete-symbol.
625;;; Completion of interior word fragments possible with prefix argument.
626
627;;; Known Problem:
628;;; Does not use private dictionary because GNU `look' does not use it. It
629;;; would be nice if GNU `look' took standard input; this would allow gzip'ed
630;;; dictionaries to be used. GNU `look' also has a bug, see
631;;; `ispell-gnu-look-still-broken-p'.
632
633;;; Motivation:
634;;; The `l', "regular expression look up", keymap option of ispell-word
635;;; (ispell-do-look) can only be run after finding a misspelled word. So
636;;; ispell-do-look can not be used to look for words starting with `cat' to
637;;; find `catechetical' since `cat' is a correctly spelled word. Furthermore,
638;;; ispell-do-look does not return the entire list returned by `look'.
639;;;
640;;; ispell-complete-word allows you to get a completion list from the system
641;;; dictionary and expand a word fragment at the current position in a buffer.
642;;; These examples assume ispell-complete-word is bound to M-TAB as it is in
643;;; text-mode; the `Complete Word' and `Complete Interior Fragment' entries of
644;;; the "Spell" submenu under the "Edit" menu may also be used instead of
645;;; M-TAB and C-u M-TAB, respectively.
646;;;
647;;; EXAMPLE 1: The word `Saskatchewan' needs to be spelled. The user may
648;;; type `Sas' and hit M-TAB and a completion list will be built using the
649;;; shell command `look' and displayed in the *Completions* buffer:
650;;;
651;;; Possible completions are:
652;;; sash sashay
653;;; sashayed sashed
654;;; sashes sashimi
655;;; Saskatchewan Saskatoon
656;;; sass sassafras
657;;; sassier sassing
658;;; sasswood sassy
659;;;
660;;; By viewing this list the user will hopefully be motivated to insert the
661;;; letter `k' after the `sas'. When M-TAB is hit again the word `Saskat'
662;;; will be inserted in place of `sas' (note case) since this is a unique
663;;; substring completion. The narrowed completion list can be viewed with
664;;; another M-TAB
665;;;
666;;; Possible completions are:
667;;; Saskatchewan Saskatoon
668;;;
669;;; Inserting the letter `c' and hitting M-TAB will narrow the completion
670;;; possibilities to just `Saskatchewan' and this will be inserted in the
671;;; buffer. At any point the user may click the mouse on a completion to
672;;; select it.
673;;;
674;;; EXAMPLE 2: The user has typed `Sasaquane' and M-$ (ispell-word) gives no
675;;; "near-misses" in which case you back up to `Sas' and hit M-TAB and find
676;;; the correct word as above. The `Sas' will be replaced by `Saskatchewan'
677;;; and the remaining word fragment `aquane' can be deleted.
678;;;
679;;; EXAMPLE 3: If a version of `look' is used that supports regular
680;;; expressions, then `ispell-have-new-look' should be t (its default) and
681;;; interior word fragments may also be used for the search. The word
682;;; `pneumonia' needs to be spelled. The user can only remember the
683;;; interior fragment `mon' in which case `C-u M-TAB' on `mon' gives a list
684;;; of all words containing the interior word fragment `mon'. Typing `p'
685;;; and M-TAB will narrow this list to all the words starting with `p' and
686;;; containing `mon' from which `pneumonia' can be found as above.
687
688;;; The user-defined variables are:
689;;;
690;;; ispell-look-command
691;;; ispell-look-dictionary
692;;; ispell-gnu-look-still-broken-p
693
694;;; Algorithm (some similarity to lisp-complete-symbol):
695;;;
696;;; * call-process on command ispell-look-command (default: "look") to find
697;;; words in ispell-look-dictionary matching `string' (or `regexp' if
698;;; ispell-have-new-look is t). Parse output and store results in
699;;; ispell-lookup-completions-alist.
700;;;
701;;; * Build completion list using try-completion and `string'
702;;;
703;;; * Replace `string' in buffer with matched common substring completion.
704;;;
705;;; * Display completion list only if there is no matched common substring.
706;;;
707;;; * Rebuild ispell-lookup-completions-alist, on a next call, only when
708;;; beginning of word fragment has changed.
709;;;
710;;; * Interior fragments searches are performed similarly with the exception
711;;; that the entire fragment at point is initially removed from the buffer,
712;;; the STRING passed to try-completion and all-completions is just "" and
713;;; not the interior fragment; this allows all completions containing the
714;;; interior fragment to be shown. The location in the buffer is stored to
715;;; decide whether future completion narrowing of the current list should be
716;;; done or if a new list should be built. See interior fragment example
717;;; above.
718;;;
719;;; * Robust searches are done using a `look' with -r (regular expression)
720;;; switch if ispell-have-new-look is t.
721
722;;;; User-defined variables.
723
724(defvar ispell-look-dictionary nil
725 "*If non-nil then spelling dictionary as string for `ispell-complete-word'.
726Overrides default dictionary file such as \"/usr/dict/words\" or GNU look's
727\"${prefix}/lib/ispell/ispell.words\"")
728
729(defvar ispell-gnu-look-still-broken-p nil
730 "*t if GNU look -r can give different results with and without trialing `.*'.
731Example: `look -dfr \"^ya\" foo' returns nothing, while `look -dfr \"^ya.*\" foo'
732returns `yacc', where `foo' is a dictionary file containing the three lines
733
734 y
735 y's
736 yacc
737
738Both commands should return `yacc'. If `ispell-complete-word' erroneously
739states that no completions exist for a string, then setting this variable to t
740will help find those completions.")
741
742;;;; Internal variables.
743
744;;; Possible completions for last word fragment.
745(defvar ispell-lookup-completions-alist nil)
746
747;;; Last word fragment processed by `ispell-complete-word'.
748(defvar ispell-lookup-last-word nil)
749
750;;; Buffer local variables.
751
752;;; Value of interior-frag in last call to `ispell-complete-word'.
753(defvar ispell-lookup-last-interior-p nil)
754(make-variable-buffer-local 'ispell-lookup-last-interior-p)
755(put 'ispell-lookup-last-interior-p 'permanent-local t)
756
757;;; Buffer position in last call to `ispell-complete-word'.
758(defvar ispell-lookup-last-bow nil)
759(make-variable-buffer-local 'ispell-lookup-last-bow)
760(put 'ispell-lookup-last-bow 'permanent-local t)
761
762;;;; Interactive functions.
763;;;###autoload
764(defun ispell-complete-word (&optional interior-frag)
765 "Complete word using letters at point to word beginning using `look'.
766With optional argument INTERIOR-FRAG, word fragment at point is assumed to be
767an interior word fragment in which case `ispell-have-new-look' should be t.
768See also `ispell-look-dictionary' and `ispell-gnu-look-still-broken-p'."
769
770 (interactive "P")
771
772 ;; `look' must support regexp expressions in order to perform an interior
773 ;; fragment search.
774 (if (and interior-frag (not ispell-have-new-look))
775 (error (concat "Sorry `ispell-have-new-look' is nil. "
776 "You also will need GNU Ispell's `look'.")))
777
778 (let* ((completion-ignore-case t)
779
780 ;; Get location of beginning of word fragment.
781 (bow (save-excursion (skip-chars-backward "a-zA-Z'") (point)))
782
783 ;; Get the string to look up.
784 (string (buffer-substring bow (point)))
785
786 ;; Get regexp for which we search and, if necessary, an interior word
787 ;; fragment.
788 (regexp (if interior-frag
789 (concat "^.*" string ".*")
790 ;; If possible use fast binary search: no trailing `.*'.
791 (concat "^" string
792 (if ispell-gnu-look-still-broken-p ".*"))))
793
794 ;; We want all completions for case of interior fragments so set
795 ;; prefix to an empty string.
796 (prefix (if interior-frag "" string))
797
798 ;; Are we continuing from a previous interior fragment search?
799 ;; Check last value of interior-word and if the point has moved.
800 (continuing-an-interior-frag-p
801 (and ispell-lookup-last-interior-p
802 (equal ispell-lookup-last-bow bow)))
803
804 ;; Are we starting a unique word fragment search? Always t for
805 ;; interior word fragment search.
806 (new-unique-string-p
807 (or interior-frag (null ispell-lookup-last-word)
808 (let ((case-fold-search t))
809 ;; Can we locate last word fragment as a substring of current
810 ;; word fragment? If the last word fragment is larger than
811 ;; the current string then we will have to rebuild the list
812 ;; later.
813 (not (string-match
814 (concat "^" ispell-lookup-last-word) string)))))
815
816 completion)
817
818 ;; Check for perfect completion already. That is, maybe the user has hit
819 ;; M-x ispell-complete-word one too many times?
820 (if (string-equal string "")
821 (if (string-equal (concat ispell-lookup-last-word " ")
822 (buffer-substring
823 (save-excursion (forward-word -1) (point)) (point)))
824 (error "Perfect match...still. Please move on.")
825 (error "No word fragment at point.")))
826
827 ;; Create list of words from system dictionary starting with `string' if
828 ;; new string and not continuing from a previous interior fragment search.
829 (if (and (not continuing-an-interior-frag-p) new-unique-string-p)
830 (setq ispell-lookup-completions-alist
831 (ispell-lookup-build-list string regexp)))
832
833 ;; Check for a completion of `string' in the list and store `string' and
834 ;; other variables for the next call.
835 (setq completion (try-completion prefix ispell-lookup-completions-alist)
836 ispell-lookup-last-word string
837 ispell-lookup-last-interior-p interior-frag
838 ispell-lookup-last-bow bow)
839
840 ;; Test the completion status.
841 (cond
842
843 ;; * Guess is a perfect match.
844 ((eq completion t)
845 (insert " ")
846 (message "Perfect match."))
847
848 ;; * No possibilities.
849 ((null completion)
850 (message "Can't find completion for \"%s\"" string)
851 (beep))
852
853 ;; * Replace string fragment with matched common substring completion.
854 ((and (not (string-equal completion ""))
855 ;; Fold case so a completion list is built when `string' and common
856 ;; substring differ only in case.
857 (let ((case-fold-search t))
858 (not (string-match (concat "^" completion "$") string))))
859 (search-backward string bow)
860 (replace-match completion nil t) ; FIXEDCASE doesn't work? or LITERAL?
861 (message "Proposed unique substring. Repeat for completions list."))
862
863 ;; * String is a common substring completion already. Make list.
864 (t
865 (message "Making completion list...")
866 (if (string-equal completion "") (delete-region bow (point)))
867 (let ((list (all-completions prefix ispell-lookup-completions-alist)))
868 (with-output-to-temp-buffer " *Completions*"
869 (display-completion-list list)))
870 (message "Making completion list...done")))))
871
872;;;###autoload
873(defun ispell-complete-word-interior-frag ()
874 "Runs `ispell-complete-word' with a non-nil INTERIOR-FRAG.
875A completion list is built for word fragment at point which is assumed to be
876an interior word fragment. `ispell-have-new-look' should be t."
877 (interactive)
878 (ispell-complete-word t))
879
880;;;; Internal Function.
881
882;;; Build list of words using ispell-look-command from dictionary
883;;; ispell-look-dictionary (if this is a non-nil string). Look for words
884;;; starting with STRING if ispell-have-new-look is nil or look for REGEXP if
885;;; ispell-have-new-look is t. Returns result as an alist suitable for use by
886;;; try-completion, all-completions, and completing-read.
887(defun ispell-lookup-build-list (string regexp)
888 (save-excursion
889 (message "Building list...")
890 (set-buffer (get-buffer-create " *ispell look*"))
891 (erase-buffer)
892
893 (if (stringp ispell-look-dictionary)
894 (if ispell-have-new-look
895 (call-process ispell-look-command nil t nil "-fr" regexp
896 ispell-look-dictionary)
897 (call-process ispell-look-command nil t nil "-f" string
898 ispell-look-dictionary))
899 (if ispell-have-new-look
900 (call-process ispell-look-command nil t nil "-fr" regexp)
901 (call-process ispell-look-command nil t nil "-f" string)))
902
903 ;; Build list for try-completion and all-completions by storing each line
904 ;; of output starting from bottom of buffer and deleting upwards.
905 (let (list)
906 (goto-char (point-min))
907 (while (not (= (point-min) (point-max)))
908 (end-of-line)
909 (setq list (cons (buffer-substring (point-min) (point)) list))
910 (forward-line)
911 (delete-region (point-min) (point)))
912
913 ;; Clean.
914 (erase-buffer)
915 (message "Building list...done")
916
917 ;; Make the list into an alist and return.
918 (mapcar 'list (nreverse list)))))
919
611(defvar ispell-message-cite-regexp "^ " 920(defvar ispell-message-cite-regexp "^ "
612 "*Regular expression to match lines cited from one message into another.") 921 "*Regular expression to match lines cited from one message into another.")
613 922