diff options
| author | Richard M. Stallman | 1993-10-26 20:01:56 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1993-10-26 20:01:56 +0000 |
| commit | bd28fa5941643e3f7e3f00ddbf7f734176dfac5b (patch) | |
| tree | 5de030e7856de627f0d8fb7a301cdb844ad5dd0d | |
| parent | 4578d35d505afeb549b162db40f5d2be4f25d094 (diff) | |
| download | emacs-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.
| -rw-r--r-- | lisp/textmodes/ispell4.el | 313 |
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. | |||
| 45 | The -S option is always passed to Ispell as the last parameter, | 45 | The -S option is always passed to Ispell as the last parameter, |
| 46 | and need not be mentioned here.") | 46 | and 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'. | ||
| 726 | Overrides 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 `.*'. | ||
| 731 | Example: `look -dfr \"^ya\" foo' returns nothing, while `look -dfr \"^ya.*\" foo' | ||
| 732 | returns `yacc', where `foo' is a dictionary file containing the three lines | ||
| 733 | |||
| 734 | y | ||
| 735 | y's | ||
| 736 | yacc | ||
| 737 | |||
| 738 | Both commands should return `yacc'. If `ispell-complete-word' erroneously | ||
| 739 | states that no completions exist for a string, then setting this variable to t | ||
| 740 | will 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'. | ||
| 766 | With optional argument INTERIOR-FRAG, word fragment at point is assumed to be | ||
| 767 | an interior word fragment in which case `ispell-have-new-look' should be t. | ||
| 768 | See 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. | ||
| 875 | A completion list is built for word fragment at point which is assumed to be | ||
| 876 | an 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 | ||