Created
November 4, 2016 03:30
-
-
Save lawlist/dfc75240cc06c66f19f48eaa3e570d7c to your computer and use it in GitHub Desktop.
Native splellchecking on OSX -- extracted from Aquamacs -- patch applies to Emacs 25 branch as of November 1, 2016 commit 5043e0a7e06dbf6507a59736e55bdde69d9a2a4e
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lisp/term/common-win.el b/lisp/term/common-win.el | |
index 937e47f..165263a 100644 | |
--- a/lisp/term/common-win.el | |
+++ b/lisp/term/common-win.el | |
@@ -73,6 +73,8 @@ | |
(cons (logior (lsh 0 16) 12) 'ns-new-frame) | |
(cons (logior (lsh 0 16) 13) 'ns-toggle-toolbar) | |
(cons (logior (lsh 0 16) 14) 'ns-show-prefs) | |
+ (cons (logior (lsh 0 16) 20) 'ns-check-spelling) | |
+ (cons (logior (lsh 0 16) 21) 'ns-spelling-change) | |
)))) | |
(set-terminal-parameter frame 'x-setup-function-keys t))) | |
diff --git a/lisp/term/ns-win.el b/lisp/term/ns-win.el | |
index e737131..d9795da 100644 | |
--- a/lisp/term/ns-win.el | |
+++ b/lisp/term/ns-win.el | |
@@ -164,6 +164,8 @@ The properties returned may include `top', `left', `height', and `width'." | |
(define-key global-map [ns-open-file] 'ns-find-file) | |
(define-key global-map [ns-open-temp-file] [ns-open-file]) | |
(define-key global-map [ns-change-font] 'ns-respond-to-change-font) | |
+(define-key global-map [ns-check-spelling] 'ns-respond-to-find-next-misspelling) | |
+(define-key global-map [ns-spelling-change] 'ns-respond-to-change-spelling) | |
(define-key global-map [ns-open-file-line] 'ns-open-file-select-line) | |
(define-key global-map [ns-spi-service-call] 'ns-spi-service-call) | |
(define-key global-map [ns-new-frame] 'make-frame) | |
@@ -660,6 +662,31 @@ See the documentation of `create-fontset-from-fontset-spec' for the format.") | |
(defvar ns-reg-to-script) ; nsfont.m | |
+(autoload 'ns-toggle-spellchecker-panel "flyspell" | |
+ "Show NSSpellChecker spellingPanel, and call | |
+ns-highlight-misspelling-and-suggest. If panel | |
+is already visible, close it." t) | |
+ | |
+(autoload 'ns-highlight-misspelling-and-suggest "flyspell" | |
+ "Search forward in current buffer for first misspelling, looping if end | |
+is reached. If found, set region to the misspelling, apply face | |
+flyspell-incorrect, and show word in OS X spelling panel" nil) | |
+ | |
+(defun ns-respond-to-change-spelling (start end) | |
+ "Respond to changeSpelling: event, expecting ns-spelling-text | |
+to substitute for selected buffer text." | |
+ (interactive "r") | |
+ (if mark-active | |
+ (delete-region start end)) | |
+ (insert ns-spelling-text)) | |
+ | |
+(defun ns-respond-to-find-next-misspelling () | |
+ "Respond to checkSpelling: event. Also called by Spellchecker | |
+panel immediately after correcting a word in a buffer." | |
+ (interactive) | |
+ (ns-highlight-misspelling-and-suggest) | |
+ ) | |
+ | |
;; This maps font registries (not exposed by NS APIs for font selection) to | |
;; Unicode scripts (which can be mapped to Unicode character ranges which are). | |
;; See ../international/fontset.el | |
diff --git a/lisp/textmodes/flyspell.el b/lisp/textmodes/flyspell.el | |
index 730b55f..eaa5357 100644 | |
--- a/lisp/textmodes/flyspell.el | |
+++ b/lisp/textmodes/flyspell.el | |
@@ -40,6 +40,7 @@ | |
(require 'ispell) | |
(eval-when-compile (require 'cl-lib)) | |
+(require 'thingatpt) ;; use (word-at-point) in ns-spellchecking functions | |
;;*---------------------------------------------------------------------*/ | |
;;* Group ... */ | |
@@ -282,11 +283,452 @@ If this variable is nil, all regions are treated as small." | |
'flyspell-auto-correct-word)))) | |
(defcustom flyspell-auto-correct-binding | |
- [(control ?\;)] | |
+ [(control ?\')] | |
"The key binding for flyspell auto correction." | |
:type 'key-sequence | |
:group 'flyspell) | |
+(defcustom flyspell-mode-auto-on t | |
+ "Non-nil means that executing `flyspell-region' or `flyspell-buffer' | |
+will automatically turn on `flyspell-mode' for that buffer." | |
+ :group 'flyspell | |
+ :type 'boolean) | |
+ | |
+;; ********************************************************************** | |
+;; functions that use NSSpellChecker as the spellchecking | |
+;; engine, instead of ispell or aspell | |
+ | |
+(defun ns-spellcheck-and-flyspell-word (beg end) | |
+ "Use NSSpellChecker to locate misspelled words within range | |
+BEG to END in current buffer. Run flyspell-word on the misspelling, | |
+and repeat search if word is not considered a misspelling by flyspell. | |
+Returns buffer location of misspelled word if found, or nil. As a side | |
+effect, marks the misspelled word (if found) with face flyspell-incorrect." | |
+ (let ((pos beg) | |
+ misspell-location | |
+ misspell-end | |
+ done) | |
+ ;; update dictionary if needed | |
+ (unless (string= ispell-current-dictionary | |
+ (ns-spellchecker-current-language)) | |
+ (ispell-change-dictionary (ns-spellchecker-current-language))) | |
+ ;; loop through until we find a misspelling or end of string | |
+ (while (not done) | |
+ (setq misspell-location | |
+ (ns-spellchecker-check-spelling (buffer-substring pos end) | |
+ (current-buffer))) | |
+ (if misspell-location | |
+ (save-excursion | |
+ (setq misspell-end | |
+ (+ pos (car misspell-location) (cdr misspell-location))) | |
+ (goto-char misspell-end) | |
+ (if (flyspell-word) ;; returns t if not misspelled | |
+ ;; ignore misspelling if flyspell-word says it's OK, | |
+ ;; but continue checking | |
+ (setq misspell-location nil) | |
+ ;; if flyspell-word concurs, we've found a misspelling & are done | |
+ (setq done t) | |
+ )) | |
+ ;; no misspellings in string; finish. | |
+ (setq done t)) | |
+ (unless done | |
+ ;; check remainder of string | |
+ (setq pos misspell-end))) | |
+ ;; if a misspelling has been found, report is location (otherwise nil) | |
+ (when misspell-location | |
+ (cons (+ pos (car misspell-location)) misspell-end)) | |
+ )) | |
+ | |
+(defun ns-find-next-misspelling (&optional noskip) | |
+ "Move forward in buffer to next misspelling; set region to the word, | |
+and apply flyspell-incorrect face. If NOSKIP is non-nil, don't skip | |
+the current highlighted word (if any)." | |
+;; search forward for a spelling error according to NSSpellChecker | |
+;; do flyspell-word or equivalent to see if it is really misspelled | |
+;; (e.g. not TeX or other filtered expression) | |
+;; if it is, then also highlight it, and put it in the spelling panel | |
+ (interactive) | |
+ (unless (string= ispell-current-dictionary | |
+ (ns-spellchecker-current-language)) | |
+ (ispell-change-dictionary (ns-spellchecker-current-language))) | |
+ (let* ((pos (if mark-active | |
+ ;; use beginning of region as start point for spellchecking, | |
+ ;; if there is an active region | |
+ (min (mark) (point)) | |
+ (point))) | |
+ (beg (point-min)) | |
+ (end (point-max)) | |
+ ;; (chunk-begin pos) | |
+ misspell-location | |
+ ;; chunk-end | |
+ ;; approx-end | |
+ ) | |
+ (save-excursion ;retain point & region if no misspelling found | |
+ (goto-char pos) | |
+ ;; this bit is needed to unhighlight the previous word, if it | |
+ ;; is ignored or learned in the spelling panel | |
+ (let ((word (word-at-point))) | |
+ (if (and word | |
+ (not (ns-spellchecker-check-spelling word (current-buffer)))) | |
+ (flyspell-unhighlight-at (point)))) | |
+ ;; If midway through a word, start at search at next word; | |
+ ;; but don't skip an entire word | |
+ (if (backward-word) | |
+ (forward-word)) | |
+ ;; When a selection is active, always skip the first word or | |
+ ;; partial word (as TextEdit does), so we don't spellcheck | |
+ ;; the same word again | |
+ (if (and mark-active (not noskip)) (forward-word)) | |
+ (flyspell-word) | |
+ (setq pos (point)) | |
+ ;; if region from point to end is larger than 1.5x | |
+ ;; NS-SPELLCHECKER-CHUNK-SIZE chars, then check text in smaller chunks | |
+ ;; (if (< (- end pos) (* 1.5 ns-spellchecker-chunk-size)) | |
+ ;; (setq chunk-end end) | |
+ ;; (setq chunk-end (+ pos ns-spellchecker-chunk-size)) | |
+ ;; ;; try not to spellcheck across a sentence boundary, to keep | |
+ ;; ;; grammar checking happy; make sure we at least don't split words. | |
+ ;; (save-excursion | |
+ ;; (goto-char approx-end) | |
+ ;; (forward-sentence) | |
+ ;; (setq chunk-end (point)) | |
+ ;; ;; If sentence boundary not found within 10% after | |
+ ;; ;; specified chunk size, look for first sentence | |
+ ;; ;; boundary up to 10% before desired chunk size. | |
+ ;; ;; If not found, use first word boundary after desired chunk size | |
+ ;; (if (> (abs (- chunk-end approx-end)) | |
+ ;; (* 0.1 ns-spellchecker-chunk-size)) | |
+ ;; (progn | |
+ ;; ;; find nearest previous sentence boundary | |
+ ;; (backward-sentence) | |
+ ;; (setq chunk-end (point)) | |
+ ;; ;; check whether it's within 10% of specified value | |
+ ;; (if (> (abs (- approx-end chunk-end)) | |
+ ;; (* 0.1 ns-spellchecker-chunk-size)) | |
+ ;; (progn | |
+ ;; ;; if not, use next word boundary | |
+ ;; (goto-char approx-end) | |
+ ;; (forward-word) | |
+ ;; (setq chunk-end (point)))))))) | |
+ ;; (while (and | |
+ ;; (eq (car misspelled-location) -1) | |
+ ;; (not overlap)) | |
+ ;; (setq misspell-location | |
+ ;; (ns-spellchecker-check-spelling | |
+ ;; (buffer-substring chunk-begin chunk-end))) | |
+ ;; ;; ) | |
+ ;; (setq misspell-location | |
+ ;; (ns-spellchecker-check-spelling (buffer-substring pos end))) | |
+ ;; (if (= (car misspell-location) -1) | |
+ ;; nil | |
+ ;; (setq misspell-beg | |
+ ;; (+ pos (car misspell-location)) | |
+ ;; misspell-end | |
+ ;; (+ pos (car misspell-location) (cdr misspell-location))) | |
+ ;; (goto-char misspell-end) | |
+ ;; (setq misspell-found-p (not (flyspell-word)))) | |
+ ;; (unless misspell-found-p | |
+ ;; (setq misspell-location | |
+ ;; (ns-spellchecker-check-spelling (buffer-substring beg pos))) | |
+ ;; (if (= (car misspell-location) -1) | |
+ ;; nil | |
+ ;; (setq misspell-beg | |
+ ;; (+ beg (car misspell-location)) | |
+ ;; misspell-end | |
+ ;; (+ beg (car misspell-location) (cdr misspell-location))) | |
+ ;; (goto-char misspell-end) | |
+ ;; (setq misspell-found-p (not (flyspell-word))))) | |
+ ;; (if misspell-found-p | |
+ ;; return start and end of the misspelled word, or nil if none found | |
+ ;; (cons misspell-beg misspell-end)) | |
+ (setq misspell-location | |
+ (ns-spellcheck-and-flyspell-word pos end)) | |
+ (if (not misspell-location) | |
+ (setq misspell-location (ns-spellcheck-and-flyspell-word beg pos))) | |
+ ;; returns nil if we haven't found a misspelling | |
+ misspell-location | |
+ ))) | |
+ | |
+(defun ns-highlight-misspelling-and-suggest (&optional noskip) | |
+ "Search forward in current buffer for first misspelling, looping if end | |
+is reached. If found, set region to the misspelling, apply face | |
+flyspell-incorrect, and show word in OS X spelling panel. If | |
+NOSKIP is non-nil, don't skip the current highlighted word (if any)." | |
+ (interactive) | |
+ (let* ((misspell-region (ns-find-next-misspelling noskip)) | |
+ (misspell-beg (car misspell-region)) | |
+ (misspell-end (cdr misspell-region)) | |
+ word) | |
+ (if (not misspell-region) | |
+ ;; no misspelling found; blank and beep the spelling panel | |
+ (progn | |
+ (ns-spellchecker-show-word "") | |
+ (message "Spell checker: found no errors")) | |
+ ;; misspelling found; set region to mispelled word, and show | |
+ ;; in spelling panel | |
+ (goto-char misspell-end) | |
+ (push-mark misspell-beg 'no-msg 'activate) | |
+ (setq word (buffer-substring misspell-beg misspell-end)) | |
+ (ns-spellchecker-show-word word))) | |
+ (if (and flyspell-mode-auto-on (not flyspell-mode)) | |
+ (turn-on-flyspell))) | |
+ | |
+(defun ns-start-spellchecker () | |
+ "Show NSSpellChecker spellingPanel, and call | |
+ns-highlight-misspelling-and-suggest, which see." | |
+ (interactive) | |
+ (ns-popup-spellchecker-panel) | |
+ (ns-highlight-misspelling-and-suggest)) | |
+ | |
+(defun ns-toggle-spellchecker-panel () | |
+ "Show NSSpellChecker spellingPanel, and call | |
+ns-highlight-misspelling-and-suggest. If panel | |
+is already visible, close it instead." | |
+ (interactive) | |
+ (if (ns-spellchecker-panel-visible-p) | |
+ (ns-close-spellchecker-panel) | |
+ (ns-popup-spellchecker-panel) | |
+ ;; panel shouldn't skip past currently selected word, if there is one | |
+ (ns-highlight-misspelling-and-suggest 'noskip))) | |
+ | |
+;;;###autoload | |
+(defun spellcheck-now () | |
+ "Start spellchecking, using OS X spellchecker | |
+(`ns-highlight-misspelling-and-suggest') or `ispell-buffer' (which see), | |
+depending on value of `ispell-program-name'." | |
+ (interactive) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-highlight-misspelling-and-suggest) | |
+ (ispell-buffer))) | |
+ | |
+;;;###autoload | |
+(defun spellchecker-panel-or-ispell () | |
+ "Calls `ns-toggle-spellchecker-panel' or `ispell' (which see), depending | |
+on current value of `ispell-program-name'." | |
+ (interactive) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-toggle-spellchecker-panel) | |
+ (ispell))) | |
+ | |
+(defun ns-flyspell-region (beg end) | |
+ "Flyspell text between BEG and END using ns-spellchecker-check-spelling." | |
+ (interactive "r") | |
+ (unless (string= ispell-current-dictionary | |
+ (ns-spellchecker-current-language)) | |
+ (ispell-change-dictionary (ns-spellchecker-current-language))) | |
+ (save-excursion | |
+ (let ((spellcheck-position beg) | |
+ (count 0) | |
+ ;; if called by ns-flyspell-large-region, then report progress | |
+ ;; relative to "large region" extents instead of simply extents of | |
+ ;; the current sub-region | |
+ (progress-beg | |
+ (if (boundp 'ns-flyspell-large-region-beg) | |
+ ns-flyspell-large-region-beg | |
+ beg)) | |
+ (progress-end | |
+ (if (boundp 'ns-flyspell-large-region-end) | |
+ ns-flyspell-large-region-end | |
+ end)) | |
+ spellcheck-text | |
+ ns-spellcheck-output | |
+ misspelled-location | |
+ misspelled-length) | |
+ ;; remove any existing flyspell overlays before checking | |
+ (flyspell-delete-region-overlays beg end) | |
+ (while (< spellcheck-position end) | |
+ ;; report progress | |
+ (if flyspell-issue-message-flag | |
+ (message "Spell Checking...%d%% [%s]" | |
+ (* 100 (/ (float (- spellcheck-position progress-beg)) | |
+ (- progress-end progress-beg))) | |
+ (word-at-point))) | |
+ ;; extract text from last checked word to end | |
+ (setq spellcheck-text (buffer-substring spellcheck-position end)) | |
+ ;; find (position . length) of first misspelled word in extracted text | |
+ (setq ns-spellcheck-output | |
+ ;; returns nil if no misspellings found | |
+ (ns-spellchecker-check-spelling spellcheck-text (current-buffer))) | |
+ (if ns-spellcheck-output | |
+ ;; found misspelled word | |
+ (progn | |
+ (setq misspelled-location | |
+ (+ (car ns-spellcheck-output) spellcheck-position)) | |
+ (setq misspelled-length (cdr ns-spellcheck-output)) | |
+ ;; start next check after current found word | |
+ (setq spellcheck-position | |
+ (+ misspelled-location misspelled-length)) | |
+ ;; use flyspell-word to filter and mark misspellings | |
+ (goto-char spellcheck-position) | |
+ (flyspell-word)) | |
+ ;; no misspellings found; we've reached the end of chunk | |
+ (setq spellcheck-position end)) | |
+ ) | |
+ (if (and (not (boundp 'ns-flyspell-large-region-end)) | |
+ flyspell-issue-message-flag) | |
+ (message "Spell Checking completed.")) | |
+ ;; function returns end of this chunk | |
+ end))) | |
+ | |
+(defun ns-flyspell-large-region (beg end) | |
+ "Flyspell text between BEG and END using ns-spellchecker-check-spelling. | |
+Break long text into chunks of approximate size NS-SPELLCHECKER-CHUNK-SIZE, | |
+dividing at sentence boundaries where possible, or at word boundaries if | |
+sentence boundaries are too far between." | |
+ (interactive "r") | |
+ (save-excursion | |
+ (let ((inhibit-redisplay t) | |
+ (chunk-beg beg) | |
+ (ns-flyspell-large-region-beg beg) | |
+ (ns-flyspell-large-region-end end) | |
+ chunk-end | |
+ approx-end) | |
+ (while | |
+ (< | |
+ (progn | |
+ ;; if length from chunk start to overall end is less | |
+ ;; than 1.5x chunk size, set chunk end to overall end | |
+ (if (< (- end chunk-beg) (* 1.5 ns-spellchecker-chunk-size)) | |
+ (setq chunk-end end) | |
+ ;; otherwise, set end to sentence boundary near desired chunk size | |
+ (save-excursion | |
+ (setq approx-end (+ chunk-beg ns-spellchecker-chunk-size)) | |
+ (goto-char approx-end) | |
+ (forward-sentence) | |
+ (setq chunk-end (point)) | |
+ ;; If sentence boundary not found within 10% after | |
+ ;; specified chunk size, look for first sentence | |
+ ;; boundary up to 10% before desired chunk size. | |
+ ;; If not found, use first word boundary after desired chunk size | |
+ (if (> (- chunk-end approx-end) | |
+ (* 0.1 ns-spellchecker-chunk-size)) | |
+ (progn | |
+ ;; find nearest previous sentence boundary | |
+ (backward-sentence) | |
+ (setq chunk-end (point)) | |
+ ;; check whether it's within 10% of specified value | |
+ (if (> (- approx-end chunk-end) | |
+ (* 0.1 ns-spellchecker-chunk-size)) | |
+ (progn | |
+ ;; if not, use next word boundary | |
+ (goto-char approx-end) | |
+ (forward-word) | |
+ (setq chunk-end (point)))))))) | |
+ ;; make sure we haven't extended the chunk beyond the region | |
+ (if (> chunk-end end) | |
+ (setq chunk-end end)) | |
+ ;; check spelling of chunk, returning chunk end location | |
+ (ns-flyspell-region chunk-beg chunk-end)) | |
+ end) | |
+ ;; if not done, start new chunk at previous chunk end | |
+ (setq chunk-beg chunk-end)) | |
+ ;; when done, report completion | |
+ (if flyspell-issue-message-flag (message "Spell Checking completed.")) | |
+ ))) | |
+ | |
+;; ********************************************************************** | |
+;; global-flyspell-mode and automatic text-mode flyspelling | |
+ | |
+(defun maybe-turn-on-flyspell () | |
+ "Run `turn-on-flyspell' for current buffer, unless one of | |
+`global-flyspell-inhibit-functions' returns t" | |
+ (unless (run-hook-with-args-until-success | |
+ 'global-flyspell-inhibit-functions) | |
+ (turn-on-flyspell))) | |
+ | |
+;; turn on flyspell-mode for all buffers, with exception of new fundamental-mode | |
+;; buffers and those returning t for any function listed in | |
+;; `global-flyspell-inhibit-functions' | |
+;;;###autoload | |
+(define-globalized-minor-mode global-flyspell-mode flyspell-mode | |
+ maybe-turn-on-flyspell) | |
+ | |
+(defcustom global-flyspell-inhibit-functions | |
+ '(global-flyspell-default-inhibit-function) | |
+ "List of functions to be called before `global-flyspell-mode' activates | |
+flyspell in a buffer. Those functions are called one by one, with no | |
+arguments, until one of them returns a non-nil value, and thus, prevents | |
+activation of `flyspell-mode' (but only via `global-flyspell-mode')." | |
+ :group 'flyspell | |
+ :type 'hook) | |
+ | |
+(defcustom global-flyspell-inhibit-buffer-names | |
+ nil | |
+ "List of buffer names in which `global-flyspell-mode' will not | |
+ activate `flyspell-mode'" | |
+ :group 'flyspell | |
+ :type '(repeat (string))) | |
+ | |
+(defun global-flyspell-default-inhibit-function () | |
+ "Return non-nil if current buffer: has a major mode with a | |
+property named mode-class with value special; has a name starting | |
+with a space and is not visiting a file; is designated by its | |
+name as a buffer to be displayed specially; or is listed by name | |
+in `global-flyspell-inhibit-buffer-names'. This is used to | |
+indicate that the buffer should not have `flyspell-mode' turned on | |
+by `global-flyspell-mode'." | |
+ (or | |
+ ;; buffers whose major-mode is marked as "special" | |
+ (eq (get major-mode 'mode-class) 'special) | |
+ ;; buffers whose name starts with space, if not visiting a file | |
+ (and (char-equal ?\ (aref (buffer-name) 0)) | |
+ (not (buffer-file-name))) | |
+ ;; buffers designated for special display (e.g. dedicated frame) | |
+ (special-display-p (buffer-name)) | |
+ ;; buffer with name listed in global-flyspell-inhibit-buffer-names | |
+ (member (buffer-name) global-flyspell-inhibit-buffer-names) | |
+ )) | |
+ | |
+;;;###autoload | |
+(defun flyspell-text-modes () | |
+ "Use Flyspell in Text mode and related modes. | |
+Applies to all buffers that use modes related to Text mode, | |
+both existing buffers and buffers that you subsequently create. | |
+Turns off `flyspell-all-modes' if on." | |
+ (interactive) | |
+ (if global-flyspell-mode | |
+ (global-flyspell-mode -1)) | |
+ (add-hook 'text-mode-hook 'turn-on-flyspell) | |
+ (customize-mark-as-set 'text-mode-hook) | |
+ (dolist (buffer (buffer-list)) | |
+ (with-current-buffer buffer | |
+ (let ((textual (or (derived-mode-p 'text-mode) text-mode-variant))) | |
+ (if textual | |
+ (turn-on-flyspell) | |
+ (if flyspell-mode (turn-off-flyspell)))))) | |
+ (message "Flyspell activated in Text modes")) | |
+ | |
+;;;###autoload | |
+(defun flyspell-all-modes () | |
+ "Use Flyspell in all major modes. | |
+Applies both to existing buffers and buffers that you subsequently | |
+create. Turns off `flyspell-text-modes' if on." | |
+ (interactive) | |
+ (let ((disable-for-text (memq 'turn-on-flyspell | |
+ text-mode-hook))) | |
+ (if disable-for-text | |
+ (progn | |
+ (remove-hook 'text-mode-hook 'turn-on-flyspell) | |
+ (customize-mark-as-set 'text-mode-hook))) | |
+ (global-flyspell-mode 1) | |
+ (message "Flyspell activated in all modes"))) | |
+ | |
+;;;###autoload | |
+(defun flyspell-no-modes () | |
+ "Turn off `flyspell-text-modes' or `flyspell-all-modes' if on. | |
+Also turns off flyspell-mode in all existing buffers." | |
+ (interactive) | |
+ (let ((disable-for-text (memq 'turn-on-flyspell | |
+ text-mode-hook))) | |
+ (if disable-for-text | |
+ (progn | |
+ (remove-hook 'text-mode-hook 'turn-on-flyspell) | |
+ (customize-mark-as-set 'text-mode-hook))) | |
+ ;; this turns off flyspell everywhere, whether or not the global mode was | |
+ ;; previously on: | |
+ (global-flyspell-mode -1) | |
+ (message "Flyspell deactivated in all modes"))) | |
+ | |
;;*---------------------------------------------------------------------*/ | |
;;* Mode specific options */ | |
;;* ------------------------------------------------------------- */ | |
@@ -579,7 +1021,8 @@ in your init file. | |
(defun flyspell-hack-local-variables-hook () | |
;; When local variables are loaded, see if the dictionary context | |
;; has changed. | |
- (flyspell-accept-buffer-local-defs 'force)) | |
+ ;; (flyspell-accept-buffer-local-defs 'force) | |
+ ) | |
(defun flyspell-kill-ispell-hook () | |
(setq flyspell-last-buffer nil) | |
@@ -1151,6 +1594,8 @@ misspelling and skips redundant spell-checking step." | |
(setq flyspell-word-cache-end end) | |
(setq flyspell-word-cache-word word) | |
;; now check spelling of word. | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (setq poss (ns-spellchecker-parse-output word)) | |
(if (not known-misspelling) | |
(progn | |
(ispell-send-string "%\n") | |
@@ -1177,7 +1622,7 @@ misspelling and skips redundant spell-checking step." | |
(setq poss (ispell-parse-output (car ispell-filter))))) | |
;; Else, this was a known misspelling to begin with, and | |
;; we should forge an ispell return value. | |
- (setq poss (list word 1 nil nil))) | |
+ (setq poss (list word 1 nil nil)))) | |
(let ((res (cond ((eq poss t) | |
;; correct | |
(setq flyspell-word-cache-result t) | |
@@ -1534,7 +1979,12 @@ The buffer to mark them in is `flyspell-large-region-buffer'." | |
;;*---------------------------------------------------------------------*/ | |
(defun flyspell-large-region (beg end) | |
(let* ((curbuf (current-buffer)) | |
- (buffer (get-buffer-create "*flyspell-region*"))) | |
+ (buffer (get-buffer-create "*flyspell-region*")) | |
+ (current-dict-name (or ispell-local-dictionary ispell-dictionary-internal)) | |
+ (current-dict | |
+ (if (eq ispell-use-cocoaspell-internal 'full) | |
+ (aspell-dict-abbrev current-dict-name) | |
+ current-dict-name))) | |
(setq flyspell-external-ispell-buffer buffer) | |
(setq flyspell-large-region-buffer curbuf) | |
(setq flyspell-large-region-beg beg) | |
@@ -1547,8 +1997,7 @@ The buffer to mark them in is `flyspell-large-region-buffer'." | |
(set-buffer curbuf) | |
(ispell-set-spellchecker-params) ; Initialize variables and dicts alists | |
;; Local dictionary becomes the global dictionary in use. | |
- (setq ispell-current-dictionary | |
- (or ispell-local-dictionary ispell-dictionary)) | |
+ (setq ispell-current-dictionary current-dict-name) | |
(setq ispell-current-personal-dictionary | |
(or ispell-local-pdict ispell-personal-dictionary)) | |
(let ((args (ispell-get-ispell-args)) | |
@@ -1557,7 +2006,7 @@ The buffer to mark them in is `flyspell-large-region-buffer'." | |
(if (and ispell-current-dictionary ; use specified dictionary | |
(not (member "-d" args))) ; only define if not overridden | |
(setq args | |
- (append (list "-d" ispell-current-dictionary) args))) | |
+ (append (list "-d" current-dict) args))) | |
(if ispell-current-personal-dictionary ; use specified pers dict | |
(setq args | |
(append args | |
@@ -1625,9 +2074,21 @@ The buffer to mark them in is `flyspell-large-region-buffer'." | |
(let ((old beg)) | |
(setq beg end) | |
(setq end old))) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (progn | |
+ (if (> (- end beg) (* ns-spellchecker-chunk-size 1.5)) | |
+ ;; large; spellcheck in chunks. | |
+ (ns-flyspell-large-region beg end) | |
+ ;; not so large; spellcheck all at once. | |
+ (ns-flyspell-region beg end)) | |
+ ;; check for and mark consecutive repeated words | |
+ (flyspell-check-region-doublons beg end)) | |
(if (and flyspell-large-region (> (- end beg) flyspell-large-region)) | |
(flyspell-large-region beg end) | |
- (flyspell-small-region beg end))))) | |
+ (flyspell-small-region beg end)))) | |
+ (if (and flyspell-mode-auto-on (not flyspell-mode)) | |
+ (turn-on-flyspell)) | |
+ )) | |
;;*---------------------------------------------------------------------*/ | |
;;* flyspell-buffer ... */ | |
@@ -1751,10 +2212,10 @@ FACE and MOUSE-FACE specify the `face' and `mouse-face' properties | |
for the overlay." | |
(let ((overlay (make-overlay beg end nil t nil))) | |
(overlay-put overlay 'face face) | |
- (overlay-put overlay 'mouse-face mouse-face) | |
+ ;; (overlay-put overlay 'mouse-face mouse-face) | |
(overlay-put overlay 'flyspell-overlay t) | |
(overlay-put overlay 'evaporate t) | |
- (overlay-put overlay 'help-echo "mouse-2: correct word at point") | |
+ ;; (overlay-put overlay 'help-echo "mouse-2: correct word at point") | |
(overlay-put overlay 'keymap flyspell-mouse-map) | |
(when (eq face 'flyspell-incorrect) | |
(and (stringp flyspell-before-incorrect-word-string) | |
@@ -1955,8 +2416,10 @@ This command proposes various successive corrections for the current word." | |
(word (car word)) | |
poss ispell-filter) | |
(setq flyspell-auto-correct-word word) | |
- ;; Now check spelling of word.. | |
- (ispell-send-string "%\n") ;Put in verbose mode. | |
+ ;; now check spelling of word. | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (setq poss (ns-spellchecker-parse-output word)) | |
+ (ispell-send-string "%\n") ;put in verbose mode | |
(ispell-send-string (concat "^" word "\n")) | |
;; Wait until ispell has processed word. | |
(while (progn | |
@@ -1969,7 +2432,7 @@ This command proposes various successive corrections for the current word." | |
(or ispell-filter | |
(setq ispell-filter '(*))) | |
(if (consp ispell-filter) | |
- (setq poss (ispell-parse-output (car ispell-filter)))) | |
+ (setq poss (ispell-parse-output (car ispell-filter))))) | |
(cond | |
((or (eq poss t) (stringp poss)) | |
;; Don't correct word. | |
@@ -2115,6 +2578,8 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
(word (car word)) | |
poss ispell-filter) | |
;; now check spelling of word. | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (setq poss (ns-spellchecker-parse-output word)) | |
(ispell-send-string "%\n") ;put in verbose mode | |
(ispell-send-string (concat "^" word "\n")) | |
;; wait until ispell has processed word | |
@@ -2128,7 +2593,7 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
(or ispell-filter | |
(setq ispell-filter '(*))) | |
(if (consp ispell-filter) | |
- (setq poss (ispell-parse-output (car ispell-filter)))) | |
+ (setq poss (ispell-parse-output (car ispell-filter))))) | |
(cond | |
((or (eq poss t) (stringp poss)) | |
;; don't correct word | |
@@ -2141,8 +2606,10 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
poss word cursor-location start end opoint)) | |
(t | |
;; The word is incorrect, we have to propose a replacement. | |
- (flyspell-do-correct (flyspell-emacs-popup event poss word) | |
- poss word cursor-location start end opoint))) | |
+ ;; restore point now, in case user selects a non-flyspell option. | |
+ (goto-char opoint) | |
+ (flyspell-emacs-popup | |
+ event poss word cursor-location start end opoint))) | |
(ispell-pdict-save t))))) | |
;;*---------------------------------------------------------------------*/ | |
@@ -2157,14 +2624,19 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
nil) | |
((eq replace 'save) | |
(goto-char save) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-learn-word word) | |
(ispell-send-string (concat "*" word "\n")) | |
;; This was added only to the XEmacs side in revision 1.18 of | |
;; flyspell. I assume its absence on the Emacs side was an | |
;; oversight. --Stef | |
(ispell-send-string "#\n") | |
+ (setq ispell-pdict-modified-p '(t))) | |
(flyspell-unhighlight-at cursor-location) | |
- (setq ispell-pdict-modified-p '(t))) | |
+ ) | |
((or (eq replace 'buffer) (eq replace 'session)) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-ignore-word word (current-buffer)) | |
(ispell-send-string (concat "@" word "\n")) | |
(add-to-list 'ispell-buffer-session-localwords word) | |
(or ispell-buffer-local-name ; session localwords might conflict | |
@@ -2173,9 +2645,11 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
(if (null ispell-pdict-modified-p) | |
(setq ispell-pdict-modified-p | |
(list ispell-pdict-modified-p))) | |
- (goto-char save) | |
+ (goto-char save)) | |
(if (eq replace 'buffer) | |
- (ispell-add-per-file-word-list word))) | |
+ (ispell-add-per-file-word-list word)) | |
+ (flyspell-unhighlight-at cursor-location) | |
+ ) | |
(replace | |
;; This was added only to the Emacs side. I assume its absence on | |
;; the XEmacs side was an oversight. --Stef | |
@@ -2190,7 +2664,8 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
(delete-region start end) | |
(goto-char start) | |
(funcall flyspell-insert-function new-word) | |
- (if flyspell-abbrev-p | |
+ (if (and (not (string= ispell-program-name "NSSpellChecker")) | |
+ flyspell-abbrev-p) | |
(flyspell-define-abbrev word new-word))) | |
;; In the original Emacs code, this was only called in the body | |
;; of the if. I arbitrarily kept the XEmacs behavior instead. | |
@@ -2216,7 +2691,9 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
;;*---------------------------------------------------------------------*/ | |
;;* flyspell-emacs-popup ... */ | |
;;*---------------------------------------------------------------------*/ | |
-(defun flyspell-emacs-popup (event poss word) | |
+;; flyspell-emacs-popup modified to define & use a keymap, so we can | |
+;; easily append another keymap (as "parent-keymap") | |
+(defun flyspell-emacs-popup (event poss word cursor-location start end save) | |
"The Emacs popup menu." | |
(if (and (not event) | |
(display-mouse-p)) | |
@@ -2230,32 +2707,106 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
(1+ (cdr (cdr mouse-pos)))) | |
(car mouse-pos))))) | |
(let* ((corrects (if flyspell-sort-corrections | |
- (sort (car (cdr (cdr poss))) 'string<) | |
- (car (cdr (cdr poss))))) | |
- (cor-menu (if (consp corrects) | |
- (mapcar (lambda (correct) | |
- (list correct correct)) | |
- corrects) | |
- '())) | |
+ ;; reverse word order for proper menu display | |
+ (reverse (sort (car (cdr (cdr poss))) 'string<)) | |
+ (reverse (car (cdr (cdr poss)))))) | |
(affix (car (cdr (cdr (cdr poss))))) | |
- show-affix-info | |
- (base-menu (let ((save (if (and (consp affix) show-affix-info) | |
- (list | |
- (list (concat "Save affix: " (car affix)) | |
- 'save) | |
- '("Accept (session)" session) | |
- '("Accept (buffer)" buffer)) | |
- '(("Save word" save) | |
- ("Accept (session)" session) | |
- ("Accept (buffer)" buffer))))) | |
- (if (consp cor-menu) | |
- (append cor-menu (cons "" save)) | |
- save))) | |
- (menu (cons "flyspell correction menu" base-menu))) | |
- (car (x-popup-menu event | |
- (list (format "%s [%s]" word (or ispell-local-dictionary | |
- ispell-dictionary)) | |
- menu))))) | |
+ show-affix-info) | |
+ | |
+ (setq flyspell-context-menu-map | |
+ (make-sparse-keymap | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (if (not (car (cdr (cdr poss)))) | |
+ "No Guesses Found" | |
+ (format "%s" word)) | |
+ (format "%s [%s]" word (or ispell-local-dictionary | |
+ ispell-dictionary))))) | |
+ | |
+ (define-key flyspell-context-menu-map [flyspell-corr-sep2] | |
+ ;; only include this separator when we'll also have more menu below it | |
+ `(menu-item "--" 'ignore :visible (not (eq | |
+ osx-key-mode-mouse-3-behavior | |
+ 'mouse-save-then-kill)))) | |
+ (define-key flyspell-context-menu-map [buffer] | |
+ `(menu-item (if (string= ispell-program-name "NSSpellChecker") | |
+ "Ignore Spelling & Comment Buffer" | |
+ "Accept (buffer)") | |
+ (lambda () (interactive) | |
+ (flyspell-do-correct | |
+ 'buffer | |
+ ',poss | |
+ ,word | |
+ ,cursor-location | |
+ ,start | |
+ ,end | |
+ ,save)) | |
+ :help "Always consider spelling as correct in this buffer")) | |
+ (define-key flyspell-context-menu-map [session] | |
+ `(menu-item (if (string= ispell-program-name "NSSpellChecker") | |
+ "Ignore Spelling" | |
+ "Accept (session)") | |
+ (lambda () (interactive) | |
+ (flyspell-do-correct | |
+ 'session | |
+ ',poss | |
+ ,word | |
+ ,cursor-location | |
+ ,start | |
+ ,end | |
+ ,save)) | |
+ :help (if (string= ispell-program-name "NSSpellChecker") | |
+ "Consider spelling as correct in this buffer and session" | |
+ "Consider spelling correct for buffers in current session"))) | |
+ (define-key flyspell-context-menu-map [save] | |
+ `(menu-item (if (string= ispell-program-name "NSSpellChecker") | |
+ "Learn Spelling" | |
+ "Save word") | |
+ (lambda () (interactive) | |
+ (flyspell-do-correct | |
+ 'save | |
+ ',poss | |
+ ,word | |
+ ,cursor-location | |
+ ,start | |
+ ,end | |
+ ,save)) | |
+ :help "Save spelling to dictionary")) | |
+ (define-key flyspell-context-menu-map [affix] | |
+ ;; affix is nil for NSSpellChecker, so not visible | |
+ `(menu-item (concat "Save affix: " ,(car affix)) | |
+ (lambda () (interactive) | |
+ (flyspell-do-correct | |
+ 'save | |
+ ',poss | |
+ ,word | |
+ ,cursor-location | |
+ ,start | |
+ ,end | |
+ ,save)) | |
+ :help "Save affix construction to dictionary" | |
+ :visible (and ,(consp affix) ,show-affix-info))) | |
+ (when (consp corrects) | |
+ (define-key flyspell-context-menu-map [flyspell-corr-sep] '(menu-item "--")) | |
+ (let ((count 0) | |
+ cor-count-str) | |
+ (mapc (lambda (correct) | |
+ ;; generate custom symbol to serve as each key | |
+ (setq count (1+ count) | |
+ cor-count-str (concat "correct" (number-to-string count))) | |
+ (define-key flyspell-context-menu-map `[,(intern cor-count-str)] | |
+ `(menu-item ,correct | |
+ (lambda () (interactive) | |
+ (flyspell-do-correct | |
+ ,correct | |
+ ',poss | |
+ ,word | |
+ ,cursor-location | |
+ ,start | |
+ ,end | |
+ ,save)) | |
+ :help "Replace word with guess"))) | |
+ corrects))) | |
+ (popup-menu flyspell-context-menu-map event nil))) | |
;;*---------------------------------------------------------------------*/ | |
;;* flyspell-xemacs-popup ... */ | |
diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el | |
index 0cedf86..f6b7b36 100644 | |
--- a/lisp/textmodes/ispell.el | |
+++ b/lisp/textmodes/ispell.el | |
@@ -4,6 +4,7 @@ | |
;; Author: Ken Stevens <k.stevens@ieee.org> | |
;; Maintainer: Ken Stevens <k.stevens@ieee.org> | |
+;; in Aquamacs: Nathaniel Cunningham <nathaniel.cunningham@gmail.com> | |
;; Stevens Mod Date: Mon Jan 7 12:32:44 PST 2003 | |
;; Stevens Revision: 3.6 | |
;; Status : Release with 3.1.12+ and 3.2.0+ ispell. | |
@@ -11,7 +12,7 @@ | |
;; Web Site : http://kdstevens.com/~stevens/ispell-page.html | |
;; Keywords: unix wp | |
-;; This file is part of GNU Emacs. | |
+;; This file is part of GNU Emacs and of Aquamacs Emacs. | |
;; GNU Emacs is free software: you can redistribute it and/or modify | |
;; it under the terms of the GNU General Public License as published by | |
@@ -350,11 +351,14 @@ Must be greater than 1." | |
:type 'integer | |
:group 'ispell) | |
+;;;###autoload | |
(defcustom ispell-program-name | |
- (or (executable-find "aspell") | |
- (executable-find "ispell") | |
- (executable-find "hunspell") | |
- "ispell") | |
+;; set NSSpellChecker as the default; no need to see if it's installed | |
+ "NSSpellChecker" | |
+ ;; (or (executable-find "aspell") | |
+ ;; (executable-find "ispell") | |
+ ;; (executable-find "hunspell") | |
+ ;; "ispell") | |
"Program invoked by \\[ispell-word] and \\[ispell-region] commands." | |
:type 'string | |
:set (lambda (symbol value) | |
@@ -532,6 +536,10 @@ is automatically set when defined in the file with either | |
(const :tag "default" nil)) | |
:group 'ispell) | |
+(defvar ispell-dictionary-internal ispell-dictionary | |
+ "Internal copy of `ispell-dictionary'; can be modified by NSSpellChecker | |
+panel without disturbing `ispell-dictionary' setting.") | |
+ | |
(defcustom ispell-extra-args nil | |
"If non-nil, a list of extra switches to pass to the Ispell program. | |
For example, (\"-W\" \"3\") to cause it to accept all 1-3 character | |
@@ -817,6 +825,459 @@ here just for backwards compatibility.") | |
"[[:alpha:]] if Emacs supports [:alpha:] regexp, nil | |
otherwise (current XEmacs does not support it).") | |
+;; ********************************************************************** | |
+;; settings to control the use of NSSpellChecker as the spellchecking | |
+;; engine, instead of ispell/aspell/hunspell | |
+ | |
+(defvar ns-spellchecker-language-alist | |
+'(("es" . "castellano") | |
+ ("da" . "dansk") | |
+ ("de" . "deutsch") | |
+ ("en" . "english") | |
+ ("fr" . "francais") | |
+ ("it" . "italiano") | |
+ ("nl" . "nederlands") | |
+ ("pt" . "portugues") | |
+ ("ru" . "russian") | |
+ ("sv" . "svenska")) | |
+ "alist pairing NSSpellChecker languages with corresponding entries | |
+in ispell's default ispell-dictionary-alist. This is only for the | |
+purposes of extracting `otherchars' and `many-otherchars-p'") | |
+ | |
+(defun ns-spellchecker-ispell-equiv-language (language) | |
+ "Returns the name of the language from | |
+ispell-dictionary-base-alist that corresponds to LANGUAGE | |
+of NSSpellChecker. Returns nil if no corresponding language found." | |
+ (let ((lang-short-abbrev (substring language 0 2)) | |
+ (lang-abbrev-p (or (equal (length language) 2) | |
+ (equal (substring language 2 3) "_")))) | |
+ (if lang-abbrev-p | |
+ (cdr (assoc lang-short-abbrev ns-spellchecker-language-alist))))) | |
+ | |
+(defun ns-spellchecker-dictionary-otherchars (language) | |
+ "Returns the pair (otherchars . many-otherchars-p) for the specified | |
+NSSpellChecker LANGUAGE, by finding the corresponding entries in | |
+ispell-dictionary-base-alist. If no corresponding entry is found, assumes | |
+' for otherchars and t for many-otherchars-p." | |
+ (let* ((ispell-language (ns-spellchecker-ispell-equiv-language language)) | |
+ (ispell-lang-list (assoc ispell-language ispell-dictionary-base-alist)) | |
+ otherchars | |
+ many-otherchars-p) | |
+ (if ispell-language | |
+ (setq otherchars | |
+ (car (cdr (cdr (cdr ispell-lang-list)))) | |
+ many-otherchars-p | |
+ (car (cdr (cdr (cdr (cdr ispell-lang-list)))))) | |
+ (setq otherchars "[']" | |
+ many-otherchars-p t)) | |
+ (cons otherchars many-otherchars-p))) | |
+ | |
+(defun ns-spellchecker-dictionary-details (language) | |
+ (let* ((otherchars-pair (ns-spellchecker-dictionary-otherchars language)) | |
+ (otherchars (car otherchars-pair)) | |
+ (many-otherchars-p (cdr otherchars-pair))) | |
+ (list | |
+ language | |
+ "[[:alpha:]]" | |
+ "[^[:alpha:]]" | |
+ otherchars | |
+ many-otherchars-p | |
+ nil | |
+ nil | |
+ 'iso-8859-1))) | |
+ | |
+(defun ns-spellchecker-list-dictionaries () | |
+ (let ((lang-list (ns-spellchecker-list-languages)) | |
+ dictionary-list) | |
+ ;; if (ns-spellchecker-current-language) returns a language not | |
+ ;; included by (ns-spellchecker-list-languages) -- | |
+ ;; e.g. "Multilingual" in OS 10.6 -- append it to the list. | |
+ (add-to-list 'lang-list (ns-spellchecker-current-language)) | |
+ (dolist (lang lang-list) | |
+ (setq dictionary-list | |
+ (cons (ns-spellchecker-dictionary-details lang) | |
+ dictionary-list))) | |
+ dictionary-list)) | |
+ | |
+(defcustom ns-spellchecker-chunk-size 100000 | |
+ "approximate size in characters of the chunks of text to be | |
+passed to `ns-spellchecker-check-spelling' when checking large | |
+regions." | |
+ :type '(choice (const :tag "Default" 100000) | |
+ number )) | |
+ | |
+(defun ns-spellchecker-parse-output (word) | |
+ "NSSpellChecker replacement for ispell-parse-output. Spellcheck WORD | |
+and Return: | |
+1: t for an exact match. | |
+2: A list of possible correct spellings of the format: | |
+ (\"ORIGINAL-WORD\" OFFSET MISS-LIST) | |
+ ORIGINAL-WORD is a string of the possibly misspelled word. | |
+ OFFSET is an integer giving the character offset of the word from | |
+ the beginning of the line. | |
+ MISS-LIST is a possibly null list of guesses." | |
+ (unless (string= ispell-current-dictionary | |
+ (ns-spellchecker-current-language)) | |
+ (ispell-change-dictionary (ns-spellchecker-current-language))) | |
+ (let* ((output (ns-spellchecker-check-spelling word (current-buffer))) | |
+ (offset (car output))) | |
+ (if offset | |
+ ;; word is incorrect -- return | |
+ ;; (\"ORIGINAL-WORD\" OFFSET MISS-LIST GUESS-LIST) | |
+ ;; GUESS-LIST built from known affixes is nil for NSSpellChecker | |
+ (list word (1+ offset) (ns-spellchecker-get-suggestions word) nil) | |
+ ;; offset is nil: word is correct -- return t | |
+ t))) | |
+ | |
+(defun ispell-ns-spellcheck-string (string) | |
+ "NSSpellChecker replacement for ispell-parse-output. Spellcheck STRING | |
+and return a list of lists (one for each misspelled word) of the format: | |
+ (\"ORIGINAL-WORD\" OFFSET MISS-LIST nil) | |
+ ORIGINAL-WORD is a string of the possibly misspelled word. | |
+ OFFSET is an integer giving the line offset of the word. | |
+ MISS-LIST is a possibly null list of guesses." | |
+ (unless (string= ispell-current-dictionary | |
+ (ns-spellchecker-current-language)) | |
+ (ispell-change-dictionary (ns-spellchecker-current-language))) | |
+ (let ((strlen (length string)) | |
+ (prev-offset 0) | |
+ ns-spellcheck-output | |
+ offset | |
+ length | |
+ word | |
+ return-list) | |
+ (while (progn | |
+ (setq ns-spellcheck-output | |
+ (ns-spellchecker-check-spelling string (current-buffer)) | |
+ offset (car ns-spellcheck-output) | |
+ length (cdr ns-spellcheck-output)) | |
+ ;; if no misspelled words, terminate while loop | |
+ (when offset | |
+ ;; misspelled word found; get word; | |
+ ;; set string to not-yet-checked portion; | |
+ ;; add details of misspelling to head of return-list | |
+ (setq word (substring string offset (+ offset length)) | |
+ string (substring string (+ offset length))) | |
+ (add-to-list 'return-list | |
+ (list word (+ prev-offset offset) | |
+ (ns-spellchecker-get-suggestions word) | |
+ nil)) | |
+ (setq prev-offset (+ prev-offset offset length)) | |
+ ))) | |
+ return-list)) | |
+ | |
+;;; ********************************************************************** | |
+;;; settings to use cocoAspell preferences (from Spelling prefpane) | |
+;;; or cocoAspell-installed aspell dictionaries | |
+ | |
+;; we need an alist or hash table pairing up language names and | |
+;; abbreviations | |
+(defvar ispell-language-abbrev-alist | |
+ (list | |
+ (cons "Afrikaans" "af") | |
+ (cons "Amharic" "am") | |
+ (cons "Arabic" "ar") | |
+ (cons "Azerbaijani" "az") | |
+ (cons "Belarusian" "be") | |
+ (cons "Bulgarian" "bg") | |
+ (cons "Bengali" "bn") | |
+ (cons "Breton" "br") | |
+ (cons "Catalan" "ca") | |
+ (cons "Czech" "cs") | |
+ (cons "Kashubian" "csb") | |
+ (cons "Welsh" "cy") | |
+ (cons "Danish" "da") | |
+ (cons "German" "de") | |
+ (cons "Greek" "el") | |
+ (cons "English" "en") | |
+ (cons "Esperanto" "eo") | |
+ (cons "Spanish" "es") | |
+ (cons "Estonian" "et") | |
+ (cons "Persian" "fa") | |
+ (cons "Finnish" "fi") | |
+ (cons "Faroese" "fo") | |
+ (cons "French" "fr") | |
+ (cons "Frisian" "fy") | |
+ (cons "Irish" "ga") | |
+ (cons "Scottish Gaelic" "gd") | |
+ (cons "Gallegan" "gl") | |
+ (cons "Gujarati" "gu") | |
+ (cons "Manx" "gv") | |
+ (cons "Hebrew" "he") | |
+ (cons "Hindi" "hi") | |
+ (cons "Hiligaynon" "hil") | |
+ (cons "Croatian" "hr") | |
+ (cons "Upper Sorbian" "hsb") | |
+ (cons "Hungarian" "hu") | |
+ (cons "Armenian" "hy") | |
+ (cons "Interlingua" "ia") | |
+ (cons "Indonesian" "id") | |
+ (cons "Icelandic" "is") | |
+ (cons "Italian" "it") | |
+ (cons "Kurdish" "ku") | |
+ (cons "Latin" "la") | |
+ (cons "Lithuanian" "lt") | |
+ (cons "Latvian" "lv") | |
+ (cons "Malagasy" "mg") | |
+ (cons "Maori" "mi") | |
+ (cons "Macedonian" "mk") | |
+ (cons "Malayalam" "ml") | |
+ (cons "Mongolian" "mn") | |
+ (cons "Marathi" "mr") | |
+ (cons "Malay" "ms") | |
+ (cons "Maltese" "mt") | |
+ (cons "Norwegian Bokmål" "nb") | |
+ (cons "Low German; Low Saxon" "nds") | |
+ (cons "Dutch" "nl") | |
+ (cons "Norwegian Nynorsk" "nn") | |
+ (cons "Nyanja; Chichewa; Chewa" "ny") | |
+ (cons "Oriya" "or") | |
+ (cons "Punjabi" "pa") | |
+ (cons "Polish" "pl") | |
+ (cons "Portuguese" "pt") | |
+ (cons "Quechua" "qu") | |
+ (cons "Romanian" "ro") | |
+ (cons "Russian" "ru") | |
+ (cons "Kinyarwanda" "rw") | |
+ (cons "Sardinian" "sc") | |
+ (cons "Slovak" "sk") | |
+ (cons "Slovenian" "sl") | |
+ (cons "Serbian" "sr") | |
+ (cons "Swedish" "sv") | |
+ (cons "Swahili" "sw") | |
+ (cons "Tamil" "ta") | |
+ (cons "Telugu" "te") | |
+ (cons "Tetum" "tet") | |
+ (cons "Turkmen" "tk") | |
+ (cons "Tagalog" "tl") | |
+ (cons "Tswana" "tn") | |
+ (cons "Turkish" "tr") | |
+ (cons "Ukrainian" "uk") | |
+ (cons "Uzbek" "uz") | |
+ (cons "Vietnamese" "vi") | |
+ (cons "Walloon" "wa") | |
+ (cons "Yiddish" "yi") | |
+ (cons "Zulu" "zu")) | |
+ "Paired language names and ISO abbreviations. | |
+Used to match cocoAspell language names to dictionary | |
+file names.") | |
+ | |
+;; another alist for language region abbreviations | |
+(defvar ispell-lregion-abbrev-alist | |
+ (list | |
+ (cons "United States" "US") | |
+ (cons "Canada" "CA") | |
+ (cons "United Kingdom" "GB") | |
+ (cons "France" "FR") | |
+ (cons "Switzerland" "CH") | |
+ (cons "Austria" "AT") | |
+ (cons "Germany" "DE") | |
+ (cons "Brazil" "BR") | |
+ (cons "Portugal" "PT")) | |
+ "Paired language region names and abbreviations. | |
+Used to match cocoAspell language names to dictionary | |
+file names.") | |
+ | |
+(defvar aspell-knows-no-dicts nil | |
+ "Records whether or not aspell's default configuration can | |
+locate any dictionaries.") | |
+ | |
+(defcustom ispell-use-cocoaspell | |
+ 'auto | |
+ "Specify whether spell-checking with aspell should use | |
+cocoaAspell installation. The following options are available: | |
+- `full' | |
+ Use cocoAspell preferences and dictionaries. | |
+- `dicts' | |
+ Use cocoAspell dictionaries only (e.g. when | |
+ Spelling prefpane isn't installed). | |
+- nil | |
+ Use generic aspell without cocoAspell. | |
+- `auto' | |
+ Determine the proper setting at runtime, | |
+ based on detected aspell/cocoAspell installation." | |
+ :group 'ispell | |
+ :type '(choice :tag "Aspell should use..." | |
+ (const :tag "cocoAspell Preferences and Dictionaries" full) | |
+ (const :tag "cocoAspell Dictionaries Only" dicts) | |
+ (const :tag "generic install (Don't use cocoAspell)" nil) | |
+ (const :tag "automatic setting" auto))) | |
+ | |
+(defvar ispell-use-cocoaspell-internal nil | |
+ "Internal value based on `ispell-use-cocoaspell', after checking | |
+aspell/cocoaspell installation if needed. Do not set directly.") | |
+ | |
+(defvar ispell-cocoaspell-prefs-dir nil | |
+ "Full path to cocoAspell's preferences directory, if cocoAspell installed") | |
+ | |
+(defvar ispell-cocoaspell-prefpane-plist nil | |
+ "Full path to file recording cocoAspell Spelling prefpane's preferences. | |
+File contents are used to determine which languages are available for | |
+spell checking.") | |
+ | |
+(defvar ispell-cocoaspell-dict-list nil | |
+ "List of dictionaries checked in cocoAspell's Spelling prefpane.") | |
+ | |
+(defvar ispell-cocoaspell-dict-dir-list nil | |
+ "List of paths of directories for all cocoAspell-installed | |
+aspell dictionaries.") | |
+ | |
+(defvar ispell-cocoaspell-dict-aliases nil | |
+ "List of base filenames of all *.alias files in cocoAspell-installed aspell | |
+dictionary directories. These will be used to generate ispell-dictionary-alist | |
+when cocoAspell dictionaries are installed, but no Spelling prefpane.") | |
+ | |
+(defun ispell-update-cocoaspell-settings () | |
+ (setq aspell-knows-no-dicts | |
+ (condition-case nil | |
+ (with-temp-buffer | |
+ ;; is there a stored cocoaSpell configuration? | |
+ (call-process ispell-program-name nil t nil "dicts") | |
+ (eq (point-min) (point-max))) | |
+ (error nil))) | |
+ | |
+ ;; determine how to configure aspell | |
+ (setq ispell-use-cocoaspell-internal | |
+ (if (eq ispell-use-cocoaspell 'auto) | |
+ ;; automatically determine appropriate setting | |
+ (when aspell-knows-no-dicts | |
+ ;; assume cocoaspell if no dictionaries known to aspell | |
+ (if (file-accessible-directory-p | |
+ (expand-file-name "~/Library/Services/cocoAspell.service/")) | |
+ ;; if cocoaSpell service is installed, use full cocoaspell support | |
+ 'full | |
+ ;; else just use cocoaSpell-installed dictionaries | |
+ 'dicts)) | |
+ ;; use setting as specified by user | |
+ ispell-use-cocoaspell)) | |
+ | |
+ (setq ispell-cocoaspell-prefs-dir | |
+ (when (eq ispell-use-cocoaspell-internal 'full) | |
+ (expand-file-name "~/Library/Preferences/cocoAspell/"))) | |
+ | |
+ (setq ispell-cocoaspell-prefpane-plist | |
+ (when (eq ispell-use-cocoaspell-internal 'full) | |
+ ;; overall preferences set in Spelling prefpane | |
+ (expand-file-name | |
+ "~/Library/Services/cocoAspell.service/Contents/Info.plist"))) | |
+ | |
+ (setq ispell-cocoaspell-dict-list | |
+ (when (eq ispell-use-cocoaspell-internal 'full) | |
+ (let ((lang-list)) | |
+ (with-temp-buffer | |
+ ;; extract language names selected in Spelling prefpane | |
+ (insert-file-contents ispell-cocoaspell-prefpane-plist) | |
+ ;; (call-process "plutil" nil t t "-convert" "xml1" "-o" "-" | |
+ ;; ispell-cocoaspell-prefpane-plist) | |
+ ;; (goto-char (point-min)) | |
+ ;; move point to just before first language name | |
+ (re-search-forward "<key>NSLanguages</key>\\s +<array>\\s +<string>") | |
+ (while (progn | |
+ ;; find each language name, and move point to just before next | |
+ ;; tag after current string | |
+ (re-search-forward "\\(.+\\)</string>\\s +<\\([/a-z]+\\)>") | |
+ ;; tack current language onto lang-list | |
+ (add-to-list 'lang-list (match-string 1)) | |
+ ;; continue if next tag indicates another language string | |
+ (equal (match-string 2) "string"))) | |
+ ;; return the language list | |
+ lang-list)))) | |
+ | |
+ (setq ispell-cocoaspell-dict-dir-list | |
+ (when (eq ispell-use-cocoaspell-internal 'dicts) | |
+ (file-expand-wildcards | |
+ "/Library/Application Support/cocoAspell/aspell*-*-*"))) | |
+ | |
+ (setq ispell-cocoaspell-dict-aliases | |
+ (when (eq ispell-use-cocoaspell-internal 'dicts) | |
+ (mapcar (lambda (dict-alias) | |
+ (file-name-sans-extension | |
+ (file-name-nondirectory dict-alias))) | |
+ (file-expand-wildcards | |
+ "/Library/Application Support/cocoAspell/aspell*-*-*/*.multi")))) | |
+ ) | |
+ ;; convert from names e.g. English (United States) to en_US | |
+ ;; append stuff in brackets | |
+ ;; also include length if it exists | |
+(defun aspell-dict-abbrev (langstring) | |
+ "Given LANGSTRING, a string name for a cocoAspell dictionary, return | |
+abbreviated dict name, which cocoAspell uses as base filename for that | |
+dictionary's files. e.g., passed `English (United States) [w_accents]' | |
+return 'en_US-w_accents'." | |
+ (unless (null langstring) | |
+ (let* ((lang-elts | |
+ ;; parse out: language | |
+ (string-match (concat "\\([[:alpha:]]+;?\\( [[:alpha:]]+;?\\)*\\)" | |
+ ;; region | |
+ "\\( (\\([[:alpha:] ]+\\))\\)?" | |
+ ;; modifiers | |
+ "\\( \\[\\(.+\\)\\]\\)?" | |
+ ) langstring)) | |
+ (lang (match-string 1 langstring)) | |
+ (lregion (match-string 4 langstring)) | |
+ (mods (match-string 6 langstring)) | |
+ (lang-abbrev (cdr (assoc lang ispell-language-abbrev-alist))) | |
+ (lregion-abbrev (or (cdr (assoc lregion ispell-lregion-abbrev-alist)) | |
+ lregion))) ;; if no abbrev in alist, keep name | |
+ (if lregion-abbrev (setq lregion-abbrev (concat "_" lregion-abbrev))) | |
+ (if mods (setq mods (concat "-" mods))) | |
+ (concat lang-abbrev lregion-abbrev mods)))) ;; generate aspell conf filename | |
+ | |
+(defun ispell-cocoaspell-dict-dir (dict-abbrev) | |
+ "Return the full path of the directory in which cocoAspell dictionary | |
+files for dictionary DICT-ABBREV are located." | |
+ (let ((filename (concat ispell-cocoaspell-prefs-dir dict-abbrev ".conf"))) | |
+ (if (file-readable-p filename) | |
+ (with-temp-buffer | |
+ (insert-file-contents filename) | |
+ (when (search-forward-regexp "^dict-dir " nil t) | |
+ (buffer-substring (point) (progn (end-of-line) (point))))) | |
+ (let* ((dict-abbrev-parts (split-string dict-abbrev "-")) | |
+ (dict-abbrev-root (car dict-abbrev-parts)) | |
+ (dict-abbrev-mods (nth 1 dict-abbrev-parts)) | |
+ (dict-abbrev-root-parts (split-string dict-abbrev-root "_")) | |
+ (dict-lang-abbrev (car dict-abbrev-root-parts)) | |
+ (dict-region-abbrev (nth 1 dict-abbrev-root-parts)) | |
+ (dict-parent-dir "/Library/Application Support/cocoAspell/") | |
+ (dict-dir (car (or (directory-files | |
+ dict-parent-dir | |
+ nil | |
+ (concat "aspell[0-9]?-" | |
+ dict-lang-abbrev "-" | |
+ dict-abbrev-mods "-.+")) | |
+ (directory-files | |
+ dict-parent-dir | |
+ nil | |
+ (concat "aspell[0-9]?-" | |
+ dict-lang-abbrev "-" | |
+ dict-region-abbrev "-.+")) | |
+ (directory-files | |
+ dict-parent-dir | |
+ nil | |
+ (concat "aspell[0-9]?-" | |
+ dict-lang-abbrev "-.+")))))) | |
+ (concat dict-parent-dir dict-dir))))) | |
+ | |
+(defun ispell-cocoaspell-aspell-args (dict-abbrev) | |
+ "Return the arguments to be passed to the aspell command to use dictionary | |
+DICT-ABBREV." | |
+ (let* ((lang-conf (concat ispell-cocoaspell-prefs-dir dict-abbrev ".conf")) | |
+ (readable (file-readable-p lang-conf))) | |
+ (if readable | |
+ (list (concat "--conf=" ispell-cocoaspell-prefs-dir "filters.conf") | |
+ (concat "--per-conf=" ispell-cocoaspell-prefs-dir dict-abbrev ".conf")) | |
+ (let* ((dict-abbrev-parts (split-string dict-abbrev "-")) | |
+ (dict-abbrev-root (car dict-abbrev-parts)) | |
+ (dict-abbrev-mods (nth 1 dict-abbrev-parts)) | |
+ (dict-dir (ispell-cocoaspell-dict-dir dict-abbrev))) | |
+ (list (concat "--dict-dir=" dict-dir) | |
+ (concat "--home-dir=" ispell-cocoaspell-prefs-dir) | |
+ (concat "--jargon=" dict-abbrev-mods) | |
+ (concat "--lang=" dict-abbrev-root) | |
+ (concat "--personal=" dict-abbrev-root ".pws") | |
+ (concat "--repl=" dict-abbrev-root ".prepl")))))) | |
+ | |
+ | |
;;; ********************************************************************** | |
;;; The following are used by ispell, and should not be changed. | |
;;; ********************************************************************** | |
@@ -845,6 +1306,15 @@ Otherwise returns the library directory name, if that is defined." | |
;; all versions, since versions earlier than 3.0.09 didn't identify | |
;; themselves on startup. | |
(interactive "p") | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ ;; If using NSSpellChecker, just initialize variables and return. | |
+ (progn | |
+ ;; Make sure these variables are (re-)initialized to the default value | |
+ (setq ispell-really-aspell nil | |
+ ispell-aspell-supports-utf8 nil | |
+ ispell-really-hunspell nil | |
+ ispell-encoding8-command nil) | |
+ t) | |
(let ((default-directory (or (and (boundp 'temporary-file-directory) | |
temporary-file-directory) | |
default-directory)) | |
@@ -926,7 +1396,7 @@ Otherwise returns the library directory name, if that is defined." | |
(if (ispell-check-minver hunspell8-minver ispell-really-hunspell) | |
(setq ispell-encoding8-command "-i") | |
(setq ispell-really-hunspell nil)))))) | |
- result)) | |
+ result))) | |
(defun ispell-call-process (&rest args) | |
"Like `call-process' but defend against bad `default-directory'." | |
@@ -1000,7 +1470,7 @@ and added as a submenu of the \"Edit\" menu.") | |
(defvar ispell-async-processp (and (fboundp 'delete-process) | |
(fboundp 'process-send-string) | |
(fboundp 'accept-process-output) | |
- ;;(fboundp 'make-process) | |
+ ;;(fboundp 'start-process) | |
;;(fboundp 'set-process-filter) | |
;;(fboundp 'process-kill-without-query) | |
) | |
@@ -1013,14 +1483,19 @@ and added as a submenu of the \"Edit\" menu.") | |
Internal use.") | |
(defun ispell-find-aspell-dictionaries () | |
- "Find Aspell's dictionaries, and record in `ispell-dictionary-alist'." | |
+ "Find Aspell's dictionaries, and record in `ispell-aspell-dictionary-alist'." | |
(unless (and ispell-really-aspell ispell-encoding8-command) | |
(error "This function only works with Aspell >= 0.60")) | |
(let* ((dictionaries | |
- (split-string | |
+ (cond | |
+ ((eq ispell-use-cocoaspell-internal 'full) | |
+ ispell-cocoaspell-dict-list) | |
+ ((eq ispell-use-cocoaspell-internal 'dicts) | |
+ ispell-cocoaspell-dict-aliases) | |
+ (t (split-string | |
(with-temp-buffer | |
(ispell-call-process ispell-program-name nil t nil "dicts") | |
- (buffer-string)))) | |
+ (buffer-string)))))) | |
;; Search for the named dictionaries. | |
(found | |
(delq nil | |
@@ -1030,13 +1505,30 @@ Internal use.") | |
(setq found (ispell-aspell-add-aliases found)) | |
;; Merge into FOUND any elements from the standard ispell-dictionary-base-alist | |
;; which have no element in FOUND at all. | |
- (dolist (dict ispell-dictionary-base-alist) | |
- (unless (assoc (car dict) found) | |
- (setq found (nconc found (list dict))))) | |
+ ;; ==SKIP this step -- don't add entries corresponding to dicts we don't have!!!== | |
+;; (dolist (dict ispell-dictionary-base-alist) | |
+;; (unless (assoc (car dict) found) | |
+;; (setq found (nconc found (list dict))))) | |
(setq ispell-aspell-dictionary-alist found) | |
;; Add a default entry | |
- (let ((default-dict | |
- '(nil "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-B") nil utf-8))) | |
+ (let* ((english-dict-cdr | |
+ (if ispell-use-cocoaspell-internal | |
+ (or | |
+ (assoc-default "^[Ee]nglish$" ispell-aspell-dictionary-alist | |
+ (lambda (dict-name key) | |
+ (string-match key dict-name))) | |
+ (assoc-default "^[Ee]n$" ispell-aspell-dictionary-alist | |
+ (lambda (dict-name key) | |
+ (string-match key dict-name))) | |
+ (assoc-default "^[Ee]ngl.*" ispell-aspell-dictionary-alist | |
+ (lambda (dict-name key) | |
+ (and (string-match key dict-name) | |
+ (not (string-match ".*variant.*" | |
+ dict-name)))))) | |
+ (cdr (assoc "en" ispell-aspell-dictionary-alist)))) | |
+ (default-dict | |
+ (cons nil (or english-dict-cdr | |
+ (cdr (car ispell-aspell-dictionary-alist)))))) | |
(push default-dict ispell-aspell-dictionary-alist)))) | |
(defvar ispell-aspell-data-dir nil | |
@@ -1066,26 +1558,39 @@ of `ispell-dictionary-base-alist' elements." | |
(or ispell-aspell-data-dir | |
(setq ispell-aspell-data-dir | |
(ispell-get-aspell-config-value "data-dir"))) | |
- | |
- ;; Try finding associated datafile. aspell will look for master .dat | |
- ;; file in `dict-dir' and `data-dir'. Associated .dat files must be | |
- ;; in the same directory as master file. | |
- (let ((data-file | |
- (catch 'datafile | |
- (dolist ( tmp-path (list ispell-aspell-dict-dir | |
- ispell-aspell-data-dir )) | |
- ;; Try xx.dat first, strip out variant, country code, etc, | |
- ;; then try xx_YY.dat (without stripping country code), | |
- ;; then try xx-alt.dat, for de-alt etc. | |
- (dolist (tmp-regexp (list "^[[:alpha:]]+" | |
- "^[[:alpha:]_]+" | |
- "^[[:alpha:]]+-\\(alt\\|old\\)")) | |
- (let ((fullpath | |
- (concat tmp-path "/" | |
- (and (string-match tmp-regexp dict-name) | |
- (match-string 0 dict-name)) ".dat"))) | |
- (if (file-readable-p fullpath) | |
- (throw 'datafile fullpath))))))) | |
+ ;; Try finding associated datafile | |
+ (let* ((dict-abbrev (when (eq ispell-use-cocoaspell-internal 'full) | |
+ (aspell-dict-abbrev dict-name))) | |
+ (lang ;; Strip out region, variant, etc. | |
+ (and (string-match "^[[:alpha:]]+" (or dict-abbrev dict-name)) | |
+ (match-string 0 (or dict-abbrev dict-name)))) | |
+ ;; When using cocoaspell, need to specify dict-dir directly. | |
+ (dict-dir (cond | |
+ ((eq ispell-use-cocoaspell-internal 'full) | |
+ (ispell-cocoaspell-dict-dir dict-abbrev)) | |
+ ((eq ispell-use-cocoaspell-internal 'dicts) | |
+ (file-name-directory | |
+ (car | |
+ (file-expand-wildcards | |
+ (concat | |
+ "/Library/Application Support/cocoAspell/aspell*-*-*/" | |
+ dict-name ".multi"))))))) | |
+ (datafile0 | |
+ (if ispell-use-cocoaspell-internal (concat dict-dir "/" lang ".dat"))) | |
+ (datafile1 | |
+ (concat ispell-aspell-data-dir "/" lang ".dat")) | |
+ (datafile2 | |
+ (concat ispell-aspell-data-dir "/" | |
+ ;; Strip out anything but xx_YY. | |
+ (and (string-match "^[[:alpha:]_]+" dict-name) | |
+ (match-string 0 dict-name)) ".dat")) | |
+ (data-file | |
+ (if (and datafile0 (file-readable-p datafile0)) | |
+ datafile0 | |
+ (if (file-readable-p datafile1) | |
+ datafile1 | |
+ (if (file-readable-p datafile2) | |
+ datafile2)))) | |
otherchars) | |
(if data-file | |
@@ -1111,7 +1616,14 @@ of `ispell-dictionary-base-alist' elements." | |
"[^[:alpha:]]" | |
(regexp-opt otherchars) | |
t ; We can't tell, so set this to t | |
- (list "-d" dict-name) | |
+ ;; set arguments for aspell | |
+ ;; if we use cocoAspell prefs, use conf files associated with language | |
+ (cond ((eq ispell-use-cocoaspell-internal 'full) | |
+ (ispell-cocoaspell-aspell-args dict-abbrev)) | |
+ ((eq ispell-use-cocoaspell-internal 'dicts) | |
+ (list "-d" dict-name | |
+ "--dict-dir" dict-dir)) | |
+ (t (list "-d" dict-name))) | |
nil ; aspell doesn't support this | |
;; Here we specify the encoding to use while communicating with | |
;; aspell. This doesn't apply to command line arguments, so | |
@@ -1381,15 +1893,10 @@ aspell is used along with Emacs).") | |
(error nil)) | |
ispell-encoding8-command | |
ispell-emacs-alpha-regexp) | |
- ;; auto-detection will only be used if spellchecker is not | |
- ;; ispell, supports a way to set communication to UTF-8 and | |
- ;; Emacs flavor supports [:alpha:] | |
- (if ispell-really-aspell | |
- (or ispell-aspell-dictionary-alist | |
- (ispell-find-aspell-dictionaries)) | |
- (if ispell-really-hunspell | |
- (or ispell-hunspell-dictionary-alist | |
- (ispell-find-hunspell-dictionaries))))) | |
+ (progn | |
+ (ispell-update-cocoaspell-settings) | |
+ (unless ispell-aspell-dictionary-alist | |
+ (ispell-find-aspell-dictionaries)))) | |
;; Substitute ispell-dictionary-alist with the list of | |
;; dictionaries corresponding to the given spellchecker. | |
@@ -1397,13 +1904,12 @@ aspell is used along with Emacs).") | |
;; installed dictionaries and add to it elements of the original | |
;; list that are not present there. Allow distro info. | |
(let ((found-dicts-alist | |
- (if (and ispell-encoding8-command | |
- ispell-emacs-alpha-regexp) | |
- (if ispell-really-aspell | |
- ispell-aspell-dictionary-alist | |
- (if ispell-really-hunspell | |
- ispell-hunspell-dictionary-alist)) | |
- nil)) | |
+ (cond ((and ispell-really-aspell | |
+ ispell-encoding8-command) | |
+ ispell-aspell-dictionary-alist) | |
+ ((string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-list-dictionaries)) | |
+ (t nil))) | |
(ispell-dictionary-base-alist ispell-dictionary-base-alist) | |
ispell-base-dicts-override-alist ; Override only base-dicts-alist | |
all-dicts-alist) | |
@@ -1460,11 +1966,18 @@ aspell is used along with Emacs).") | |
;; Add dicts to ``ispell-dictionary-alist'' unless already present. | |
(dolist (dict (append found-dicts-alist | |
- ispell-base-dicts-override-alist | |
- ispell-dictionary-base-alist)) | |
+ ispell-base-dicts-override-alist)) | |
+ ;; Why would we add all dictionaries to the list that already has available ones? | |
+ ;; ispell-dictionary-base-alist)) | |
(unless (assoc (car dict) all-dicts-alist) | |
(add-to-list 'all-dicts-alist dict))) | |
(setq ispell-dictionary-alist all-dicts-alist)) | |
+ (setq ispell-dictionary-internal | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ ;; get working value of ispell-dictionary from spellingPanel | |
+ (ns-spellchecker-current-language) | |
+ ;; or set from global value | |
+ ispell-dictionary)) | |
;; If Emacs flavor supports [:alpha:] use it for global dicts. If | |
;; spellchecker also supports UTF-8 via command-line option use it | |
@@ -1499,7 +2012,9 @@ The variable `ispell-library-directory' defines their location." | |
(ispell-set-spellchecker-params)) | |
(let ((dicts (append ispell-local-dictionary-alist ispell-dictionary-alist)) | |
- (dict-list (cons "default" nil)) | |
+ (dict-list (if (string= ispell-program-name "NSSpellChecker") | |
+ nil | |
+ (cons "default" nil))) | |
name dict-bname) | |
(dolist (dict dicts) | |
(setq name (car dict) | |
@@ -1510,6 +2025,7 @@ The variable `ispell-library-directory' defines their location." | |
name | |
;; For Aspell, we already know which dictionaries exist. | |
(or ispell-really-aspell | |
+ (string= ispell-program-name "NSSpellChecker") | |
;; Include all dictionaries if lib directory not known. | |
;; Same for Hunspell, where ispell-library-directory is nil. | |
(not ispell-library-directory) | |
@@ -1520,10 +2036,97 @@ The variable `ispell-library-directory' defines their location." | |
(push name dict-list))) | |
dict-list)) | |
+(defun toggle-text-mode-flyspell () | |
+ "Toggle whether to use Flyspell in Text mode and related modes. | |
+This command affects all buffers that use modes related to Text mode, | |
+both existing buffers and buffers that you subsequently create." | |
+ (interactive) | |
+ (let ((enable-mode (not (memq 'turn-on-flyspell text-mode-hook)))) | |
+ (if enable-mode | |
+ (add-hook 'text-mode-hook 'turn-on-flyspell) | |
+ (remove-hook 'text-mode-hook 'turn-on-flyspell)) | |
+ (dolist (buffer (buffer-list)) | |
+ (with-current-buffer buffer | |
+ (if (or (derived-mode-p 'text-mode) text-mode-variant) | |
+ (flyspell-mode (if enable-mode 1 0))))) | |
+ (message "Flyspell %s in Text modes" | |
+ (if enable-mode "enabled" "disabled")))) | |
+ | |
+;;;###autoload | |
+(defun menu-bar-text-mode-flyspell () | |
+ (interactive) | |
+ (toggle-text-mode-flyspell) | |
+ (customize-mark-as-set 'text-mode-hook)) | |
+ | |
;; Define commands in menu in opposite order you want them to appear. | |
;;;###autoload | |
(if ispell-menu-map-needed | |
(progn | |
+ (defvar ispell-submenu-map (make-sparse-keymap "Ispell")) | |
+ (define-key ispell-submenu-map [ispell-complete-word] | |
+ `(menu-item ,(purecopy "Complete Word") ispell-complete-word | |
+ :help ,(purecopy "Complete word at cursor using dictionary"))) | |
+ (define-key ispell-submenu-map [ispell-complete-word-interior-frag] | |
+ `(menu-item ,(purecopy "Complete Word Fragment") | |
+ ispell-complete-word-interior-frag | |
+ :help ,(purecopy "Complete word fragment at cursor"))) | |
+ (define-key ispell-submenu-map [ispell-continue] | |
+ `(menu-item ,(purecopy "Continue Spell-Checking") ispell-continue | |
+ :enable (and (boundp 'ispell-region-end) | |
+ (marker-position ispell-region-end) | |
+ (equal (marker-buffer ispell-region-end) | |
+ (current-buffer))) | |
+ :help ,(purecopy "Continue spell checking last region"))) | |
+ (define-key ispell-submenu-map [ispell-word] | |
+ `(menu-item ,(purecopy "Spell-Check Word") ispell-word | |
+ :help ,(purecopy "Spell-check word at cursor"))) | |
+ (define-key ispell-submenu-map [ispell-comments-and-strings] | |
+ `(menu-item ,(purecopy "Spell-Check Comments") ispell-comments-and-strings | |
+ :help ,(purecopy "Spell-check only comments and strings"))) | |
+ (define-key ispell-submenu-map [ispell-region] | |
+ `(menu-item ,(purecopy "Spell-Check Region") ispell-region | |
+ :enable mark-active | |
+ :help ,(purecopy "Spell-check text in marked region"))) | |
+ (define-key ispell-submenu-map [ispell-message] | |
+ `(menu-item ,(purecopy "Spell-Check Message") ispell-message | |
+ :visible (eq major-mode 'mail-mode) | |
+ :help ,(purecopy "Skip headers and included message text"))) | |
+ (define-key ispell-submenu-map [ispell-buffer] | |
+ `(menu-item ,(purecopy "Spell-Check Buffer") ispell-buffer | |
+ :help ,(purecopy "Check spelling of selected buffer"))) | |
+ ;;(put 'ispell-region 'menu-enable 'mark-active) | |
+ | |
+ (fset 'ispell-submenu-map (symbol-value 'ispell-submenu-map)))) | |
+ | |
+;;;###autoload | |
+(if ispell-menu-map-needed | |
+ (progn | |
+ (defvar flyspell-modes-submenu-map (make-sparse-keymap "Flyspell modes")) | |
+ (define-key flyspell-modes-submenu-map [flyspell-no-modes] | |
+ `(menu-item ,(purecopy "No Modes") flyspell-no-modes | |
+ :button (:radio . (and (not (memq 'turn-on-flyspell | |
+ text-mode-hook)) | |
+ (not (bound-and-true-p | |
+ global-flyspell-mode)))) | |
+ :help ,(purecopy "Turn off automatic spellchecking by mode"))) | |
+ (define-key flyspell-modes-submenu-map [flyspell-text-modes] | |
+ `(menu-item ,(purecopy "Text Modes") flyspell-text-modes | |
+ :button (:radio . (and (memq 'turn-on-flyspell text-mode-hook) | |
+ (not (bound-and-true-p | |
+ global-flyspell-mode)))) | |
+ :help ,(purecopy "Turn on flyspell in text modes only"))) | |
+ (define-key flyspell-modes-submenu-map [flyspell-all-modes] | |
+ `(menu-item ,(purecopy "All Modes") flyspell-all-modes | |
+ :button (:radio . (bound-and-true-p global-flyspell-mode)) | |
+ :help ,(purecopy "Turn on flyspell in all modes"))) | |
+ | |
+ (fset 'flyspell-modes-submenu-map | |
+ (symbol-value 'flyspell-modes-submenu-map)))) | |
+ | |
+;;; define commands in menu in opposite order you want them to appear. | |
+;;;###autoload | |
+(if ispell-menu-map-needed | |
+ (progn | |
(setq ispell-menu-map (make-sparse-keymap "Spell")) | |
(define-key ispell-menu-map [ispell-change-dictionary] | |
`(menu-item ,(purecopy "Change Dictionary...") ispell-change-dictionary | |
@@ -1533,10 +2136,12 @@ The variable `ispell-library-directory' defines their location." | |
(lambda () (interactive) (ispell-kill-ispell nil 'clear)) | |
:enable (and (boundp 'ispell-process) ispell-process | |
(eq (ispell-process-status) 'run)) | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Terminate Ispell subprocess"))) | |
(define-key ispell-menu-map [ispell-pdict-save] | |
`(menu-item ,(purecopy "Save Dictionary") | |
(lambda () (interactive) (ispell-pdict-save t t)) | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Save personal dictionary"))) | |
(define-key ispell-menu-map [ispell-customize] | |
`(menu-item ,(purecopy "Customize...") | |
@@ -1547,17 +2152,35 @@ The variable `ispell-library-directory' defines their location." | |
`(menu-item ,(purecopy "Help") | |
(lambda () (interactive) (describe-function 'ispell-help)) | |
:help ,(purecopy "Show standard Ispell keybindings and commands"))) | |
+ | |
+ (define-key ispell-menu-map [ispell-submenu] | |
+ `(menu-item ,(purecopy "Ispell") ,ispell-submenu-map | |
+ :visible (string= ispell-program-name "NSSpellChecker"))) | |
+ (define-key ispell-menu-map [spellcheck-menu-separator] | |
+ `(menu-item ,(purecopy "--"))) | |
+ | |
+ (define-key ispell-menu-map [flyspell-modes-submenu] | |
+ `(menu-item ,(purecopy "Check Spelling While Typing in Modes") | |
+ ,flyspell-modes-submenu-map)) | |
(define-key ispell-menu-map [flyspell-mode] | |
- `(menu-item ,(purecopy "Automatic spell checking (Flyspell)") | |
+ `(menu-item ,(purecopy "Check Spelling While Typing (in this buffer)") | |
flyspell-mode | |
:help ,(purecopy "Check spelling while you edit the text") | |
+ ;; allow toggling regardless of mode-related flyspell settings | |
:button (:toggle . (bound-and-true-p flyspell-mode)))) | |
+ (define-key ispell-menu-map [flyspell-buffer] | |
+ `(menu-item ,(purecopy "Mark All Misspellings") | |
+ flyspell-buffer | |
+ :help ,(purecopy "Scan full buffer for misspellings"))) | |
+ | |
(define-key ispell-menu-map [ispell-complete-word] | |
`(menu-item ,(purecopy "Complete Word") ispell-complete-word | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Complete word at cursor using dictionary"))) | |
(define-key ispell-menu-map [ispell-complete-word-interior-frag] | |
`(menu-item ,(purecopy "Complete Word Fragment") | |
ispell-complete-word-interior-frag | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Complete word fragment at cursor"))))) | |
;;;###autoload | |
@@ -1569,13 +2192,15 @@ The variable `ispell-library-directory' defines their location." | |
(marker-position ispell-region-end) | |
(equal (marker-buffer ispell-region-end) | |
(current-buffer))) | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Continue spell checking last region"))) | |
(define-key ispell-menu-map [ispell-word] | |
`(menu-item ,(purecopy "Spell-Check Word") ispell-word | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Spell-check word at cursor"))) | |
(define-key ispell-menu-map [ispell-comments-and-strings] | |
- `(menu-item ,(purecopy "Spell-Check Comments") | |
- ispell-comments-and-strings | |
+ `(menu-item ,(purecopy "Spell-Check Comments") ispell-comments-and-strings | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Spell-check only comments and strings"))))) | |
;;;###autoload | |
@@ -1584,15 +2209,35 @@ The variable `ispell-library-directory' defines their location." | |
(define-key ispell-menu-map [ispell-region] | |
`(menu-item ,(purecopy "Spell-Check Region") ispell-region | |
:enable mark-active | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Spell-check text in marked region"))) | |
(define-key ispell-menu-map [ispell-message] | |
`(menu-item ,(purecopy "Spell-Check Message") ispell-message | |
- :visible (eq major-mode 'mail-mode) | |
+ :visible (and (eq major-mode 'mail-mode) | |
+ (not (string= ispell-program-name | |
+ "NSSpellChecker"))) | |
:help ,(purecopy "Skip headers and included message text"))) | |
(define-key ispell-menu-map [ispell-buffer] | |
- `(menu-item ,(purecopy "Spell-Check Buffer") ispell-buffer | |
+ `(menu-item ,(purecopy "Spell-Check Buffer") spellcheck-now | |
+ :visible (not (string= ispell-program-name "NSSpellChecker")) | |
:help ,(purecopy "Check spelling of selected buffer"))) | |
;;(put 'ispell-region 'menu-enable 'mark-active) | |
+ | |
+ (define-key ispell-menu-map [nsspellchecker-panel-hide] | |
+ `(menu-item ,(purecopy "Hide Spelling Panel") spellchecker-panel-or-ispell | |
+ :visible (and (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-panel-visible-p)) | |
+ :help ,(purecopy "Toggle OS X spellcheck panel visibility"))) | |
+ (define-key ispell-menu-map [nsspellchecker-panel-show] | |
+ `(menu-item ,(purecopy "Show Spelling Panel") spellchecker-panel-or-ispell | |
+ :visible (and (string= ispell-program-name "NSSpellChecker") | |
+ (not (ns-spellchecker-panel-visible-p))) | |
+ :help ,(purecopy "Toggle OS X spellcheck panel visibility"))) | |
+ (define-key ispell-menu-map [nsspellcheck] | |
+ `(menu-item ,(purecopy "Spellcheck Now") spellcheck-now | |
+ :visible (string= ispell-program-name "NSSpellChecker") | |
+ :help ,(purecopy "Check spelling with OS X spellchecker"))) | |
+ | |
(fset 'ispell-menu-map (symbol-value 'ispell-menu-map)))) | |
;;; XEmacs versions 19 & 20 | |
@@ -2117,6 +2762,8 @@ quit spell session exited." | |
(or quietly | |
(message "Checking spelling of %s..." | |
(funcall ispell-format-word-function word))) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (setq poss (ns-spellchecker-parse-output word)) | |
(ispell-send-string "%\n") ; put in verbose mode | |
(ispell-send-string (concat "^" word "\n")) | |
;; wait until ispell has processed word | |
@@ -2128,7 +2775,7 @@ quit spell session exited." | |
(if (and ispell-filter (listp ispell-filter)) | |
(if (> (length ispell-filter) 1) | |
(error "Ispell and its process have different character maps") | |
- (setq poss (ispell-parse-output (car ispell-filter))))) | |
+ (setq poss (ispell-parse-output (car ispell-filter)))))) | |
(cond ((eq poss t) | |
(or quietly | |
(message "%s is correct" | |
@@ -2420,12 +3067,16 @@ Global `ispell-quit' set to start location to continue spell session." | |
(cond | |
((= char ? ) nil) ; accept word this time only | |
((= char ?i) ; accept and insert word into pers dict | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-learn-word word) | |
(ispell-send-string (concat "*" word "\n")) | |
- (setq ispell-pdict-modified-p '(t)) ; dictionary modified! | |
+ (setq ispell-pdict-modified-p '(t))) ; dictionary modified! | |
(and (fboundp 'flyspell-unhighlight-at) | |
(flyspell-unhighlight-at start)) | |
nil) | |
((or (= char ?a) (= char ?A)) ; accept word without insert | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-ignore-word word (current-buffer)) | |
(ispell-send-string (concat "@" word "\n")) | |
(add-to-list 'ispell-buffer-session-localwords word) | |
(and (fboundp 'flyspell-unhighlight-at) | |
@@ -2434,7 +3085,7 @@ Global `ispell-quit' set to start location to continue spell session." | |
(setq ispell-buffer-local-name (buffer-name))) | |
(if (null ispell-pdict-modified-p) | |
(setq ispell-pdict-modified-p | |
- (list ispell-pdict-modified-p))) | |
+ (list ispell-pdict-modified-p)))) | |
(if (= char ?A) 0)) ; return 0 for ispell-add buffer-local | |
((or (= char ?r) (= char ?R)) ; type in replacement | |
(and (eq 'block ispell-highlight-p) ; refresh tty's | |
@@ -2524,13 +3175,17 @@ Global `ispell-quit' set to start location to continue spell session." | |
'block)) | |
t) ; reselect from new choices | |
((= char ?u) ; insert lowercase into dictionary | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-learn-word (downcase word)) | |
(ispell-send-string (concat "*" (downcase word) "\n")) | |
- (setq ispell-pdict-modified-p '(t)) ; dictionary modified! | |
+ (setq ispell-pdict-modified-p '(t))) ; dictionary modified! | |
nil) | |
((= char ?m) ; type in what to insert | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-learn-word (read-string "Insert: " word)) | |
(ispell-send-string | |
(concat "*" (read-string "Insert: " word) "\n")) | |
- (setq ispell-pdict-modified-p '(t)) | |
+ (setq ispell-pdict-modified-p '(t))) | |
(cons word nil)) | |
((and (>= num 0) (< num count)) | |
(if ispell-query-replace-choices ; Query replace flag | |
@@ -2980,9 +3635,11 @@ Optional third arg SHIFT is an offset to apply based on previous corrections." | |
(defun ispell-process-status () | |
"Return the status of the Ispell process. | |
When asynchronous processes are not supported, `run' is always returned." | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ 'run | |
(if ispell-async-processp | |
(process-status ispell-process) | |
- (and ispell-process 'run))) | |
+ (and ispell-process 'run)))) | |
(defun ispell-start-process () | |
@@ -3001,12 +3658,17 @@ Keeps argument list for future Ispell invocations for no async support." | |
default-directory | |
;; Defend against bad `default-directory'. | |
(expand-file-name "~/"))) | |
+ ;; cocoAspell dict basename is abbreviated version | |
+ (current-dict | |
+ (if (eq ispell-use-cocoaspell-internal 'full) | |
+ (aspell-dict-abbrev ispell-current-dictionary) | |
+ ispell-current-dictionary)) | |
(orig-args (ispell-get-ispell-args)) | |
(args | |
(append | |
(if (and ispell-current-dictionary ; Not for default dict (nil) | |
(not (member "-d" orig-args))) ; Only define if not overridden. | |
- (list "-d" ispell-current-dictionary)) | |
+ (list "-d" current-dict)) | |
orig-args | |
(if ispell-current-personal-dictionary ; Use specified pers dict. | |
(list "-p" ispell-current-personal-dictionary)) | |
@@ -3083,7 +3745,7 @@ Keeps argument list for future Ispell invocations for no async support." | |
(ispell-kill-ispell t) | |
(message "Starting new Ispell process %s with %s dictionary..." | |
ispell-program-name | |
- (or ispell-local-dictionary ispell-dictionary "default")) | |
+ (or ispell-local-dictionary ispell-dictionary-internal "default")) | |
(sit-for 0) | |
(setq ispell-library-directory (ispell-check-version) | |
;; Assign a non-nil value to ispell-process-directory | |
@@ -3203,23 +3865,26 @@ 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 | |
+ (let ((completion-ignore-case t)) | |
(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)) | |
+ current-prefix-arg))) | |
(ispell-set-spellchecker-params) ; Initialize variables and dicts alists | |
(unless arg (ispell-buffer-local-dict 'no-reload)) | |
(if (equal dict "default") (setq dict nil)) | |
;; This relies on completing-read's bug of returning "" for no match | |
- (cond ((equal dict "") | |
+ (cond ((and (equal dict nil) (string= ispell-program-name "NSSpellChecker")) | |
+ (setq ispell-dictionary-internal (ns-spellchecker-current-language))) | |
+ ((equal dict "") | |
(ispell-internal-change-dictionary) | |
(message "Using %s dictionary" | |
(or (and (not arg) ispell-local-dictionary) | |
- ispell-dictionary "default"))) | |
+ ispell-dictionary-internal "default"))) | |
((equal dict (or (and (not arg) ispell-local-dictionary) | |
- ispell-dictionary "default")) | |
+ ispell-dictionary-internal "default")) | |
;; Specified dictionary is the default already. Could reload | |
;; the dictionaries if needed. | |
(ispell-internal-change-dictionary) | |
@@ -3232,7 +3897,8 @@ By just answering RET you can find out what the current dictionary is." | |
(assoc dict ispell-dictionary-alist)) | |
(if arg | |
;; set default dictionary | |
- (setq ispell-dictionary dict) | |
+ (setq ispell-dictionary dict | |
+ ispell-dictionary-internal dict) | |
;; set local dictionary | |
(setq ispell-local-dictionary dict) | |
(setq ispell-local-dictionary-overridden t)) | |
@@ -3247,15 +3913,15 @@ By just answering RET you can find out what the current dictionary is." | |
"Update the dictionary and the personal dictionary used by Ispell. | |
This may kill the Ispell process; if so, a new one will be started | |
when needed." | |
- (let* ((dict (or ispell-local-dictionary ispell-dictionary)) | |
- (pdict (or ispell-local-pdict ispell-personal-dictionary)) | |
- (expanded-pdict (if pdict (expand-file-name pdict)))) | |
+ (let ((dict (or ispell-local-dictionary ispell-dictionary-internal)) | |
+ (pdict (or ispell-local-pdict ispell-personal-dictionary))) | |
(unless (and (equal ispell-current-dictionary dict) | |
- (equal ispell-current-personal-dictionary | |
- expanded-pdict)) | |
+ (equal ispell-current-personal-dictionary pdict)) | |
(ispell-kill-ispell t) | |
(setq ispell-current-dictionary dict | |
- ispell-current-personal-dictionary expanded-pdict)))) | |
+ ispell-current-personal-dictionary pdict) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (ns-spellchecker-set-language dict))))) | |
;; Avoid error messages when compiling for these dynamic variables. | |
(defvar ispell-start) | |
@@ -3620,6 +4286,8 @@ Returns the sum SHIFT due to changes in word replacements." | |
(if (not (numberp shift)) | |
(setq shift 0)) | |
;; send string to spell process and get input. | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (setq ispell-filter (nreverse (ispell-ns-spellcheck-string string))) | |
(ispell-send-string string) | |
(while (progn | |
(ispell-accept-output) | |
@@ -3627,11 +4295,17 @@ Returns the sum SHIFT due to changes in word replacements." | |
(not (string= "" (car ispell-filter))))) | |
;; parse all inputs from the stream one word at a time. | |
;; Place in FIFO order and remove the blank item. | |
- (setq ispell-filter (nreverse (cdr ispell-filter))) | |
+ (setq ispell-filter (nreverse (cdr ispell-filter)))) | |
(while (and (not ispell-quit) ispell-filter) | |
;; get next word, accounting for accepted words and start shifts | |
- (setq poss (ispell-parse-output (car ispell-filter) | |
- accept-list shift)) | |
+ (setq poss (if (string= ispell-program-name "NSSpellChecker") | |
+ ;; add shift to offset from ispell-ns-spellcheck-string | |
+ (progn | |
+ (setcar (cdr (car ispell-filter)) | |
+ (+ shift (cadr (car ispell-filter)))) | |
+ (car ispell-filter)) | |
+ (ispell-parse-output (car ispell-filter) | |
+ accept-list shift))) | |
(if (and poss (listp poss)) ; spelling error occurred. | |
;; Whenever we have misspellings, we can change | |
;; the buffer. Keep boundaries as markers. | |
@@ -4255,6 +4929,9 @@ You can bind this to the key C-c i in GNUS or mail by adding to | |
(defun ispell-accept-buffer-local-defs () | |
"Load all buffer-local information, restarting Ispell when necessary." | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (setq ispell-current-dictionary | |
+ (or ispell-local-dictionary ispell-dictionary-internal))) | |
(ispell-buffer-local-dict) ; May kill ispell-process. | |
(ispell-buffer-local-words) ; Will initialize ispell-process. | |
(ispell-buffer-local-parsing)) | |
@@ -4265,7 +4942,8 @@ You can bind this to the key C-c i in GNUS or mail by adding to | |
Overrides the default parsing mode. | |
Includes LaTeX/Nroff modes and extended character mode." | |
;; (ispell-init-process) must already be called. | |
- (ispell-send-string "!\n") ; Put process in terse mode. | |
+ (if (not (string= ispell-program-name "NSSpellChecker")) | |
+ (ispell-send-string "!\n")) ; Put process in terse mode. | |
;; We assume all major modes with "tex-mode" in them should use latex parsing | |
;; When exclusively checking comments, set to raw text mode (nroff). | |
(if (and (not (eq 'exclusive ispell-check-comments)) | |
@@ -4274,16 +4952,19 @@ Includes LaTeX/Nroff modes and extended character mode." | |
(symbol-name major-mode))) | |
(eq ispell-parser 'tex))) | |
(progn | |
- (ispell-send-string "+\n") ; set ispell mode to tex | |
+ (if (not (string= ispell-program-name "NSSpellChecker")) | |
+ (ispell-send-string "+\n")) ; set ispell mode to tex | |
(if (not (eq ispell-parser 'tex)) | |
(set (make-local-variable 'ispell-parser) 'tex))) | |
- (ispell-send-string "-\n")) ; set mode to normal (nroff) | |
+ (if (not (string= ispell-program-name "NSSpellChecker")) | |
+ (ispell-send-string "-\n"))) ; set mode to normal (nroff) | |
;; If needed, test for SGML & HTML modes and set a buffer local nil/t value. | |
(if (and ispell-skip-html (not (eq ispell-skip-html t))) | |
(setq ispell-skip-html | |
(not (null (string-match "sgml\\|html\\|xml" | |
(downcase (symbol-name major-mode))))))) | |
;; Set default extended character mode for given buffer, if any. | |
+ (when (not (string= ispell-program-name "NSSpellChecker")) | |
(let ((extended-char-mode (ispell-get-extended-character-mode))) | |
(if extended-char-mode | |
(ispell-send-string (concat extended-char-mode "\n")))) | |
@@ -4306,7 +4987,7 @@ Includes LaTeX/Nroff modes and extended character mode." | |
((string-match "~" string) ; Set extended character mode. | |
(ispell-send-string (concat string "\n"))) | |
(t (message "Invalid Ispell Parsing argument!") | |
- (sit-for 2)))))))) | |
+ (sit-for 2))))))))) | |
;; Can kill the current ispell process | |
@@ -4354,12 +5035,13 @@ Both should not be used to define a buffer-local dictionary." | |
(ispell-kill-ispell t)) | |
;; Actually start a new ispell process, because we need | |
;; to send commands now to specify the local words to it. | |
+ (if (not (string= ispell-program-name "NSSpellChecker")) | |
(ispell-init-process) | |
(dolist (session-localword ispell-buffer-session-localwords) | |
(ispell-send-string (concat "@" session-localword "\n"))) | |
(or ispell-buffer-local-name | |
(if ispell-buffer-session-localwords | |
- (setq ispell-buffer-local-name (buffer-name)))) | |
+ (setq ispell-buffer-local-name (buffer-name))))) | |
(save-excursion | |
(goto-char (point-min)) | |
(while (search-forward ispell-words-keyword nil t) | |
@@ -4376,7 +5058,13 @@ Both should not be used to define a buffer-local dictionary." | |
;; Error handling needs to be added between ispell and Emacs. | |
(if (and (< 1 (length string)) | |
(equal 0 (string-match ispell-casechars string))) | |
- (ispell-send-string (concat "@" string "\n")))))))) | |
+ (if (string= ispell-program-name "NSSpellChecker") | |
+ (progn | |
+ ;; dummy spellcheck to ensure that NSSpellChecker | |
+ ;; is initialized | |
+ (ns-spellchecker-check-spelling "test") | |
+ (ns-spellchecker-ignore-word string (current-buffer))) | |
+ (ispell-send-string (concat "@" string "\n"))))))))) | |
;; Returns optionally adjusted region-end-point. | |
diff --git a/src/nsfns.m b/src/nsfns.m | |
index 9bc6c1d..3b4dfc6 100644 | |
--- a/src/nsfns.m | |
+++ b/src/nsfns.m | |
@@ -1095,6 +1095,363 @@ get_geometry_from_preferences (struct ns_display_info *dpyinfo, | |
========================================================================== */ | |
+/* Spelling */ | |
+ | |
+DEFUN ("ns-popup-spellchecker-panel", Fns_popup_spellchecker_panel, Sns_popup_spellchecker_panel, | |
+ 0, 0, "", | |
+ doc: /* Pop up the spell checking panel. | |
+Shows the NS spell checking panel and brings it to the front.*/) | |
+ (void) | |
+{ | |
+ id sc; | |
+ | |
+ check_window_system (NULL); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ block_input(); | |
+ [[sc spellingPanel] orderFront: NSApp]; | |
+ | |
+ // Spelling panel should appear with previous content, not empty. | |
+ // [sc updateSpellingPanelWithMisspelledWord:@""]; // no word, no spelling errors | |
+ | |
+ // found here: http://trac.webkit.org/changeset/19670 | |
+ // // FIXME 4811447: workaround for lack of API | |
+ // NSSpellChecker *spellChecker = [NSSpellChecker sharedSpellChecker]; | |
+ // does not work | |
+ // if ([sc respondsToSelector:@selector(_updateGrammar)]) | |
+ // [sc performSelector:@selector(_updateGrammar)]; | |
+ unblock_input(); | |
+ return Qnil; | |
+} | |
+ | |
+DEFUN ("ns-close-spellchecker-panel", Fns_close_spellchecker_panel, Sns_close_spellchecker_panel, | |
+ 0, 0, "", | |
+ doc: /* Close the spell checking panel.*/) | |
+ (void) | |
+{ | |
+ id sc; | |
+ | |
+ check_window_system (NULL); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ block_input(); | |
+ [[sc spellingPanel] close]; | |
+ | |
+ unblock_input(); | |
+ return Qnil; | |
+} | |
+ | |
+DEFUN ("ns-spellchecker-panel-visible-p", Fns_spellchecker_panel_visible_p, Sns_spellchecker_panel_visible_p, | |
+ 0, 0, "", | |
+ doc: /* Return t if spellchecking panel is visible, | |
+nil otherwise.*/) | |
+ (void) | |
+{ | |
+ id sc; | |
+ BOOL visible; | |
+ | |
+ check_window_system (NULL); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ block_input(); | |
+ visible = [[sc spellingPanel] isVisible]; | |
+ | |
+ unblock_input(); | |
+ return visible ? Qt : Qnil; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-show-word", Fns_spellchecker_show_word, Sns_spellchecker_show_word, | |
+ 1, 1, 0, | |
+ doc: /* Show word WORD in the spellchecking panel. | |
+Give empty string to delete word.*/) | |
+ (str) | |
+ Lisp_Object str; | |
+{ | |
+ id sc; | |
+ | |
+ CHECK_STRING (str); | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ [sc updateSpellingPanelWithMisspelledWord:[NSString stringWithUTF8String: SDATA (str)]]; // no word, no spelling errors | |
+ | |
+ unblock_input(); | |
+ return Qnil; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-learn-word", Fns_spellchecker_learn_word, Sns_spellchecker_learn_word, | |
+ 1, 1, 0, | |
+ doc: /* Learn word WORD. | |
+Returns learned word if successful. | |
+Not available on 10.4.*/) | |
+ (str) | |
+ Lisp_Object str; | |
+{ | |
+ CHECK_STRING (str); | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ id sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+#ifdef NS_IMPL_COCOA | |
+ if ([sc respondsToSelector:@selector(learnWord:)]) // (NSAppKitVersionNumber >= 824.0) | |
+ { | |
+ | |
+ [sc learnWord:[NSString stringWithUTF8String: SDATA (str)]]; | |
+ unblock_input(); | |
+ return str; | |
+ } | |
+#endif | |
+ unblock_input(); | |
+ return Qnil; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-ignore-word", Fns_spellchecker_ignore_word, Sns_spellchecker_ignore_word, | |
+ 1, 2, 0, | |
+ doc: /* Ignore word WORD in buffer BUFFER.*/) | |
+ (str, buffer) | |
+ Lisp_Object str, buffer; | |
+{ | |
+ id sc; | |
+ | |
+ CHECK_STRING (str); | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ NSInteger tag = 1; | |
+ if (! NILP (buffer)) | |
+ { | |
+ tag = sxhash (buffer, 0); | |
+ } | |
+ | |
+ [sc ignoreWord:[NSString stringWithUTF8String: SDATA (str)] inSpellDocumentWithTag:tag]; | |
+ unblock_input(); | |
+ return Qnil; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-ignored-words", Fns_spellchecker_ignored_words, Sns_spellchecker_ignored_words, | |
+ 1, 1, 0, | |
+ doc: /* Return list of words ignored by NSSpellChecker | |
+for buffer BUFFER */) | |
+ (buffer) | |
+ Lisp_Object buffer; | |
+{ | |
+ id sc; | |
+ | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ NSInteger tag = 1; | |
+ if (! NILP (buffer)) | |
+ { | |
+ tag = sxhash (buffer, 0); | |
+ } | |
+ | |
+ Lisp_Object retval = Qnil; | |
+ NSArray *words = [sc ignoredWordsInSpellDocumentWithTag:tag]; | |
+ int arrayCount = [words count]; | |
+ int i; | |
+ for (i = 0; i < arrayCount; i++) { | |
+ // build Lisp list of strings | |
+ retval = Fcons (build_string ([[words objectAtIndex:i] UTF8String]), | |
+ retval); | |
+ } | |
+ unblock_input(); | |
+ return retval; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-check-spelling", Fns_spellchecker_check_spelling, Sns_spellchecker_check_spelling, | |
+ 1, 2, 0, | |
+ doc: /* Check spelling of STRING | |
+Returns the location of the first misspelled word in a | |
+cons cell of form (beginning . length), or nil if all | |
+words are spelled as in the dictionary.*/) | |
+ (string, buffer) | |
+ Lisp_Object string, buffer; | |
+{ | |
+ id sc; | |
+ | |
+ CHECK_STRING (string); | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ /* NSRange first_word = nil; // Invalid initializer! NSRange is a struct */ | |
+ NSInteger tag = 1; | |
+ if (! NILP (buffer) ) | |
+ { | |
+ tag = sxhash (buffer, 0); | |
+ } | |
+ | |
+ /* unfinished - | |
+ if ([sc respondsToSelector:@selector(checkString:range:types:options:inSpellDocumentWithTag:orthography:wordCount:)]) | |
+ { | |
+ NSString *nsString = [NSString stringWithUTF8String: SDATA (string)]; | |
+ NSArray *spelling_result = [sc | |
+ checkString:nsString | |
+ range:NSMakeRange(0,[nsString size]-1) | |
+ types:NSTextCheckingAllSystemTypes - NSTextCheckingTypeGrammar | |
+ options:nil | |
+ inSpellDocumentWithTag:tag | |
+ orthography:nil // difficult to produce | |
+ wordCount:nil]; | |
+ | |
+ } else */ | |
+ // { | |
+ | |
+ NSRange first_word = [sc checkSpellingOfString:[NSString stringWithUTF8String: SDATA (string)] startingAt:((NSInteger) 0) | |
+ language:nil wrap:NO inSpellDocumentWithTag:tag wordCount:nil]; | |
+ | |
+ // } | |
+ unblock_input(); | |
+ if (first_word.location == NSNotFound || (int) first_word.location < 0) | |
+ return Qnil; | |
+ else | |
+ return Fcons (make_number (first_word.location), make_number (first_word.length)); | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-check-grammar", Fns_spellchecker_check_grammar, Sns_spellchecker_check_grammar, | |
+ 1, 2, 0, | |
+ doc: /* Check spelling of SENTENCE. | |
+BUFFER, if given, idenitifies the document containing list | |
+of ignored grammatical constructions. */) | |
+ (sentence, buffer) | |
+ Lisp_Object sentence, buffer; | |
+{ | |
+ id sc; | |
+ | |
+ CHECK_STRING (sentence); | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ NSInteger tag = 1; | |
+ if (! NILP (buffer) ) | |
+ { | |
+ tag = sxhash (buffer, 0); | |
+ } | |
+ | |
+ NSArray *errdetails; | |
+ | |
+ /* to do: use long version */ | |
+ NSRange first_word = [sc checkGrammarOfString: [NSString stringWithUTF8String: SDATA (sentence)] startingAt:((NSInteger) 0) | |
+ language:nil wrap:NO inSpellDocumentWithTag:tag details:&errdetails]; | |
+ | |
+ unblock_input(); | |
+ if (first_word.length == 0) // Is this how "no location" is indicated? | |
+ return Qnil; | |
+ else | |
+ return Fcons (make_number ((int) first_word.location), make_number ((int) first_word.length)); | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-get-suggestions", Fns_spellchecker_get_suggestions, Sns_spellchecker_get_suggestions, | |
+ 1, 1, 0, | |
+ doc: /* Get suggestions for WORD. | |
+If word contains all capital letters, or its first | |
+letter is capitalized, the suggested words are | |
+capitalized in the same way. */) | |
+ (word) | |
+ Lisp_Object word; | |
+{ | |
+ id sc; | |
+ | |
+ CHECK_STRING (word); | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ Lisp_Object retval = Qnil; | |
+ NSArray *guesses = [sc guessesForWord: [NSString stringWithUTF8String: SDATA (word)]]; | |
+ int arrayCount = [guesses count]; | |
+ int i = arrayCount; | |
+ while (--i >= 0) | |
+ retval = Fcons (build_string ([[guesses objectAtIndex:i] UTF8String]), | |
+ retval); | |
+ unblock_input(); | |
+ return retval; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-list-languages", Fns_spellchecker_list_languages, Sns_spellchecker_list_languages, | |
+ 0, 0, 0, | |
+ doc: /* Get all available spell-checking languages. | |
+Returns nil if not successful.*/) | |
+ (void) | |
+{ | |
+ id sc; | |
+ Lisp_Object retval = Qnil; | |
+ | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+#ifdef NS_IMPL_COCOA | |
+ if ([sc respondsToSelector:@selector(availableLanguages)]) // (NSAppKitVersionNumber >= 824.0) | |
+ { | |
+ NSArray *langs = [sc availableLanguages]; | |
+ int arrayCount = [langs count]; | |
+ int i; | |
+ for (i = 0; i < arrayCount; i++) { | |
+ // build Lisp list of strings | |
+ retval = Fcons (build_string ([[langs objectAtIndex:i] UTF8String]), | |
+ retval); | |
+ } | |
+ } | |
+#endif | |
+ unblock_input(); | |
+ return retval; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-current-language", Fns_spellchecker_current_language, Sns_spellchecker_current_language, | |
+ 0, 0, 0, | |
+ doc: /* Get the current spell-checking language.*/) | |
+ (void) | |
+{ | |
+ id sc; | |
+ | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ Lisp_Object retval = Qnil; | |
+ NSString *lang = [sc language]; | |
+ retval = build_string ([lang UTF8String]); | |
+ | |
+ unblock_input(); | |
+ return retval; | |
+} | |
+ | |
+ | |
+DEFUN ("ns-spellchecker-set-language", Fns_spellchecker_set_language, Sns_spellchecker_set_language, | |
+ 1, 1, 0, | |
+ doc: /* Set spell-checking language. | |
+LANGUAGE must be one of the languages returned by | |
+`ns-spellchecker-list-langauges'.*/) | |
+ (language) | |
+ Lisp_Object language; | |
+{ | |
+ id sc; | |
+ | |
+ CHECK_STRING (language); | |
+ check_window_system (NULL); | |
+ block_input(); | |
+ sc = [NSSpellChecker sharedSpellChecker]; | |
+ | |
+ [sc setLanguage: [NSString stringWithUTF8String: SDATA (language)]]; | |
+ unblock_input(); | |
+ return Qnil; | |
+} | |
+ | |
DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, | |
1, 1, 0, | |
doc: /* Make a new Nextstep window, called a "frame" in Emacs terms. | |
@@ -3136,6 +3493,20 @@ be used as the image of the icon representing the frame. */); | |
doc: /* Toolkit version for NS Windowing. */); | |
Vns_version_string = ns_appkit_version_str (); | |
+ defsubr (&Sns_popup_spellchecker_panel); | |
+ defsubr (&Sns_close_spellchecker_panel); | |
+ defsubr (&Sns_spellchecker_panel_visible_p); | |
+ defsubr (&Sns_spellchecker_learn_word); | |
+ defsubr (&Sns_spellchecker_ignore_word); | |
+ defsubr (&Sns_spellchecker_ignored_words); | |
+ defsubr (&Sns_spellchecker_show_word); | |
+ defsubr (&Sns_spellchecker_check_spelling); | |
+ defsubr (&Sns_spellchecker_check_grammar); | |
+ defsubr (&Sns_spellchecker_get_suggestions); | |
+ defsubr (&Sns_spellchecker_list_languages); | |
+ defsubr (&Sns_spellchecker_current_language); | |
+ defsubr (&Sns_spellchecker_set_language); | |
+ | |
defsubr (&Sns_read_file_name); | |
defsubr (&Sns_get_resource); | |
defsubr (&Sns_set_resource); | |
diff --git a/src/nsterm.h b/src/nsterm.h | |
index 4b246bd..c0d15a7 100644 | |
--- a/src/nsterm.h | |
+++ b/src/nsterm.h | |
@@ -757,6 +757,8 @@ extern EmacsMenu *mainMenu, *svcsMenu, *dockMenu; | |
#define KEY_NS_NEW_FRAME ((1<<28)|(0<<16)|12) | |
#define KEY_NS_TOGGLE_TOOLBAR ((1<<28)|(0<<16)|13) | |
#define KEY_NS_SHOW_PREFS ((1<<28)|(0<<16)|14) | |
+#define KEY_NS_CHECK_SPELLING ((1<<28)|(0<<16)|20) | |
+#define KEY_NS_SPELLING_CHANGE ((1<<28)|(0<<16)|21) | |
/* could use list to store these, but rest of emacs has a big infrastructure | |
for managing a table of bitmap "records" */ | |
diff --git a/src/nsterm.m b/src/nsterm.m | |
index 4d9d105..4e41856 100644 | |
--- a/src/nsterm.m | |
+++ b/src/nsterm.m | |
@@ -5746,6 +5746,54 @@ not_in_argv (NSString *arg) | |
} | |
+/* called on spelling text change (part of NSChangeSpelling protocol) */ | |
+- (void)changeSpelling: (id)sender | |
+{ | |
+ NSEvent *e =[[self window] currentEvent]; | |
+ | |
+ NSTRACE (changeSpelling); | |
+ if (!emacs_event) | |
+ return; | |
+ | |
+ /* SET_FRAME_GARBAGED (emacsframe); needed? */ | |
+ | |
+ emacs_event->kind = NS_NONKEY_EVENT; | |
+ emacs_event->modifiers = 0; | |
+ emacs_event->code = KEY_NS_SPELLING_CHANGE; | |
+ ns_spelling_text = build_string ([[[(NSControl*)sender selectedCell] stringValue] UTF8String]); | |
+ EV_TRAILER (e); | |
+} | |
+ | |
+- (void)ignoreSpelling:(id)sender { | |
+ NSInteger tag = sxhash (Fcurrent_buffer (), 0); | |
+ | |
+ [[NSSpellChecker sharedSpellChecker] ignoreWord:[[sender selectedCell] stringValue] | |
+ inSpellDocumentWithTag: tag]; | |
+ | |
+ /* Note (To Do): To make the ignored words feature useful, the | |
+ application must store a document’s ignored words list with the | |
+ document. See the NSSpellChecker class description for more | |
+ information. [From Apple's Cocoa documentation]*/ | |
+} | |
+ | |
+ | |
+/* Find Next button */ | |
+- (void)checkSpelling:(id)sender { | |
+ NSEvent *e =[[self window] currentEvent]; | |
+ | |
+ NSTRACE (checkSpelling); | |
+ if (!emacs_event) | |
+ return; | |
+ | |
+ SET_FRAME_GARBAGED (emacsframe); | |
+ | |
+ emacs_event->kind = NS_NONKEY_EVENT; | |
+ emacs_event->modifiers = 0; | |
+ emacs_event->code = KEY_NS_CHECK_SPELLING; | |
+ EV_TRAILER (e); | |
+} | |
+ | |
+ | |
- (BOOL)acceptsFirstResponder | |
{ | |
NSTRACE ("[EmacsView acceptsFirstResponder]"); | |
@@ -8652,6 +8700,10 @@ syms_of_nsterm (void) | |
"String for visualizing working composition sequence."); | |
ns_working_text =Qnil; | |
+ DEFVAR_LISP ("ns-spelling-text", ns_spelling_text, | |
+ "The substitute text corresponding to the ns-spelling-change event."); | |
+ ns_spelling_text =Qnil; | |
+ | |
DEFVAR_LISP ("ns-input-font", ns_input_font, | |
"The font specified in the last NS event."); | |
ns_input_font =Qnil; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment