使用ispell / aspell拼写检查camelcased单词

时间:2010-08-24 07:50:16

标签: emacs aspell camelcasing ispell

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

所以,如果是这个词:

  

ScientificProgrezGoesBoink

我很乐意建议这样做:

  

ScientificProgressGoesBoink

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

3 个答案:

答案 0 :(得分: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)))

然后,在这种情况下,单个骆驼套装的单词,asAsThisOne 实际上将被正确拼写检查。 (除非你在文件的开头 - 我才发现。)

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

答案 1 :(得分:2)

aspell中有“ - 运行在一起”选项。 Hunspell无法检查camelcased字。

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

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

  1. aspell报告了一些来自拼写错误的词语

  2. hunspell报告所有camelcased单词为拼写错误

  3. 解决两个问题的解决方案是在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)
    

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

答案 2 :(得分:0)

你应该解析骆驼套管的单词然后拆分它们,然后检查每个单词的拼写,并在考虑每个拼写错误的令牌的单一建议的情况下收集建议。考虑到每个拼写错误的令牌可以有多个建议,这对我来说听起来有点低效。