9

我需要拼写检查包含许多驼峰单词的大型文档。我想要 ispell 或 aspell 检查单个单词是否拼写正确。

所以,如果是这个词:

ScientificProgrezGoesBoink

我很想让它建议这个:

ScientificProgressGoesBoink

有没有办法做到这一点?(我的意思是,在 Emacs 缓冲区上运行它时。)请注意,我不一定希望它提出完整的替代方案。但是,如果它知道 Progrez 未被识别,我希望能够至少替换该部分,或者将该词添加到我的私人词典中,而不是将每个骆驼大写的词都包含在词典中。

4

3 回答 3

3

我接受了@phils 的建议,并深入挖掘了一些。事实证明,如果你得到camelCase-mode并像这样重新配置一些 ispell :

(defun ispell-get-word (following)
  (when following
    (camelCase-forward-word 1))
  (let* ((start (progn (camelCase-backward-word 1)
                       (point)))
         (end (progn (camelCase-forward-word 1)
                     (point))))
    (list (buffer-substring-no-properties start end)
          start end)))

然后,在这种情况下,单个驼峰式单词,例如 AsThisOne,实际上将被正确地进行拼写检查。(除非你在文档的开头——我刚刚发现。)

所以这显然不是完整的解决方案,但至少它是一些东西。

于 2011-02-24T23:10:13.167 回答
2

aspell 中有“--run-together”选项。Hunspell 无法检查驼峰式单词。

如果你阅读 aspell 的代码,你会发现它的算法实际上并没有将驼峰单词拆分成一个子单词列表。也许这个算法更快,但它会错误地将包含两个字符子词的单词报告为拼写错误。不要浪费时间来调整其他 aspell 选项。我试过了,但它们没有用。

所以我们遇到了两个问题:

  1. aspell 将一些驼峰式单词报告为拼写错误

  2. hunspell 将所有驼峰式单词报告为拼写错误

解决这两个问题的解决方案是在 Emacs Lisp 中编写我们自己的谓词。

这是为 javascript 编写的示例谓词:

(defun split-camel-case (word)
  "Split camel case WORD into a list of strings.
Ported from 'https://github.com/fatih/camelcase/blob/master/camelcase.go'."
  (let* ((case-fold-search nil)
         (len (length word))
         ;; ten sub-words is enough
         (runes [nil nil nil nil nil nil nil nil nil nil])
         (runes-length 0)
         (i 0)
         ch
         (last-class 0)
         (class 0)
         rlt)

    ;; split into fields based on class of character
    (while (< i len)
      (setq ch (elt word i))
      (cond
       ;; lower case
       ((and (>= ch ?a) (<= ch ?z))
        (setq class 1))
       ;; upper case
       ((and (>= ch ?A) (<= ch ?Z))
        (setq class 2))
       ((and (>= ch ?0) (<= ch ?9))
        (setq class 3))
       (t
        (setq class 4)))

      (cond
       ((= class last-class)
        (aset runes
              (1- runes-length)
              (concat (aref runes (1- runes-length)) (char-to-string ch))))
       (t
        (aset runes runes-length (char-to-string ch))
        (setq runes-length (1+ runes-length))))
      (setq last-class class)
      ;; end of while
      (setq i (1+ i)))

    ;; handle upper case -> lower case sequences, e.g.
    ;;     "PDFL", "oader" -> "PDF", "Loader"
    (setq i 0)
    (while (< i (1- runes-length))
      (let* ((ch-first (aref (aref runes i) 0))
             (ch-second (aref (aref runes (1+ i)) 0)))
        (when (and (and (>= ch-first ?A) (<= ch-first ?Z))
                   (and (>= ch-second ?a) (<= ch-second ?z)))
          (aset runes (1+ i) (concat (substring (aref runes i) -1) (aref runes (1+ i))))
          (aset runes i (substring (aref runes i) 0 -1))))
      (setq i (1+ i)))

    ;; construct final result
    (setq i 0)
    (while (< i runes-length)
      (when (> (length (aref runes i)) 0)
        (setq rlt (add-to-list 'rlt (aref runes i) t)))
      (setq i (1+ i)))
     rlt))

(defun flyspell-detect-ispell-args (&optional run-together)
  "If RUN-TOGETHER is true, spell check the CamelCase words.
Please note RUN-TOGETHER will make aspell less capable. So it should only be used in prog-mode-hook."
  ;; force the English dictionary, support Camel Case spelling check (tested with aspell 0.6)
  (let* ((args (list "--sug-mode=ultra" "--lang=en_US"))args)
    (if run-together
        (setq args (append args '("--run-together" "--run-together-limit=16"))))
    args))

;; {{ for aspell only, hunspell does not need setup `ispell-extra-args'
(setq ispell-program-name "aspell")
(setq-default ispell-extra-args (flyspell-detect-ispell-args t))
;; }}

;; ;; {{ hunspell setup, please note we use dictionary "en_US" here
;; (setq ispell-program-name "hunspell")
;; (setq ispell-local-dictionary "en_US")
;; (setq ispell-local-dictionary-alist
;;       '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)))
;; ;; }}

(defvar extra-flyspell-predicate '(lambda (word) t)
  "A callback to check WORD.  Return t if WORD is typo.")

(defun my-flyspell-predicate (word)
  "Use aspell to check WORD.  If it's typo return t."
  (let* ((cmd (cond
               ;; aspell: `echo "helle world" | aspell pipe`
               ((string-match-p "aspell$" ispell-program-name)
                (format "echo \"%s\" | %s pipe"
                        word
                        ispell-program-name))
               ;; hunspell: `echo "helle world" | hunspell -a -d en_US`
               (t
                (format "echo \"%s\" | %s -a -d en_US"
                        word
                        ispell-program-name))))
         (cmd-output (shell-command-to-string cmd))
         rlt)
    ;; (message "word=%s cmd=%s" word cmd)
    ;; (message "cmd-output=%s" cmd-output)
    (cond
     ((string-match-p "^&" cmd-output)
      ;; it's a typo because at least one sub-word is typo
      (setq rlt t))
     (t
      ;; not a typo
      (setq rlt nil)))
    rlt))

(defun js-flyspell-verify ()
  (let* ((case-fold-search nil)
         (font-matched (memq (get-text-property (- (point) 1) 'face)
                             '(js2-function-call
                               js2-function-param
                               js2-object-property
                               js2-object-property-access
                               font-lock-variable-name-face
                               font-lock-string-face
                               font-lock-function-name-face
                               font-lock-builtin-face
                               rjsx-text
                               rjsx-tag
                               rjsx-attr)))
         subwords
         word
         (rlt t))
    (cond
     ((not font-matched)
      (setq rlt nil))
     ;; ignore two character word
     ((< (length (setq word (thing-at-point 'word))) 2)
      (setq rlt nil))
     ;; handle camel case word
     ((and (setq subwords (split-camel-case word)) (> (length subwords) 1))
      (let* ((s (mapconcat (lambda (w)
                             (cond
                              ;; sub-word wholse length is less than three
                              ((< (length w) 3)
                               "")
                               ;; special characters
                              ((not (string-match-p "^[a-zA-Z]*$" w))
                               "")
                              (t
                               w))) subwords " ")))
        (setq rlt (my-flyspell-predicate s))))
     (t
      (setq rlt (funcall extra-flyspell-predicate word))))
    rlt))

(put 'js2-mode 'flyspell-mode-predicate 'js-flyspell-verify)

或者只是使用我的新包https://github.com/redguardtoo/wucuo

于 2014-07-22T02:52:54.407 回答
0

您应该解析骆驼大小写的单词并将它们拆分,然后检查每个单词的单独拼写,并根据每个拼写错误的标记的单个建议组合一个建议。考虑到每个拼写错误的标记都可能有多个建议,这对我来说听起来有点低效。

于 2010-08-24T08:26:14.413 回答