diff --git a/Makefile.in b/Makefile.in index 23bf4a5..b79eaf4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -84,7 +84,7 @@ MULEELC = @MULEELC@ AUCSRC = tex.el tex-buf.el tex-style.el plain-tex.el latex.el tex-info.el \ texmathp.el multi-prompt.el tex-mik.el font-latex.el tex-font.el \ context.el context-en.el context-nl.el tex-fold.el \ - toolbar-x.el tex-bar.el bib-cite.el + toolbar-x.el tex-bar.el bib-cite.el tex-ispell.el AUCELC = $(AUCSRC:.el=.elc) STYLESRC = style/prosper.el \ diff --git a/latex.el b/latex.el index babcd1a..c21699d 100644 --- a/latex.el +++ b/latex.el @@ -30,6 +30,7 @@ (require 'tex) (require 'tex-style) +(require 'tex-ispell) (eval-when-compile (require 'cl)) ;FIXME: Use cl-lib. ;;; Syntax diff --git a/tex-ispell.el b/tex-ispell.el new file mode 100644 index 0000000..ada8cfe --- /dev/null +++ b/tex-ispell.el @@ -0,0 +1,287 @@ +;;; tex-ispell.el --- AUCTeX skip additions for Ispell + +;; Copyright (C) 2016 Free Software Foundation, Inc. + +;; Author: Arash Esbati +;; Maintainer: address@hidden +;; Keywords: tex, wp, convenience + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: + +;; This file provides additions to skip list of Ispell (in this +;; context, Ispell is synonym for Ispell, Aspell and Hunspell spelling +;; checker programs). Macro arguments and environments skipped by +;; Ispell are stored in the car and/or cdr of +;; `ispell-tex-skip-alists'. This file uses two functions +;; `TeX-ispell-skip-setcar' and `TeX-ispell-skip-setcdr' defined in +;; `tex.el' to add new items to this variable. + +;; Ispell has a lot of LaTeX macros and environments already built-in. +;; E.g., check this link for Hunspell program: + +;; https://github.com/hunspell/hunspell/blob/master/src/parsers/latexparser.cxx + +;; Ispell does not check spelling in the preamble of a document. +;; Hence, only document macros and environments should be added here. +;; Currently, this file has support for the following macro packages: + +;; acro.sty +;; amsmath.sty +;; attachfile.sty +;; booktabs.sty +;; cleveref.sty +;; enumitem.sty +;; fancyref.sty +;; fancyvrb.sty +;; fontaxes.sty +;; fontspec.sty +;; listings.sty +;; mdframed.sty +;; nameref.sty +;; siunitx.sty +;; splitidx.sty +;; tabularx.sty +;; tabulary.sty +;; tikz.sty +;; varioref.sty + +;; If you have further additions, drop a line to . + +;;; Code: + +(require 'tex) + +;; Add new macros here: +(eval-when-compile + (defvar TeX-ispell-skip-cmds-list + '(;; acro.sty + ("ac" . 1) + ("ac*" . 1) + ("Ac" . 1) + ("Ac*" . 1) + ("acs" . 1) + ("acs*" . 1) + ("acl" . 1) + ("acl*" . 1) + ("Acl" . 1) + ("Acl*" . 1) + ("aca" . 1) + ("aca*" . 1) + ("acf" . 1) + ("acf*" . 1) + ("Acf" . 1) + ("Acf*" . 1) + ("acp" . 1) + ("acp*" . 1) + ("Acp" . 1) + ("Acp*" . 1) + ("acsp" . 1) + ("acsp*" . 1) + ("aclp" . 1) + ("aclp*" . 1) + ("Aclp" . 1) + ("Aclp*" . 1) + ("acap" . 1) + ("acap*" . 1) + ("acfp" . 1) + ("acfp*" . 1) + ("Acfp" . 1) + ("Acfp*" . 1) + ("Iac" . 1) + ("iacs" . 1) + ("iacl" . 1) + ("acflike" . 1) + ("acflike*" . 1) + ("acfplike" . 1) + ("acfplike*" . 1) + ("acsingle" . 1) + ("acsingle*" . 1) + ("Acsingle" . 1) + ("Acsingle*" . 1) + ("acreset" . 1) + ("acuse" . 1) + ("acsetup" . 1) + ;; attachfile.sty + ("attachfile" . 1) + ("attachfilesetup" . 1) + ("textattachfile" . 1) + ;; booktabs.sty + ("specialrule" . 3) + ;; cleveref.sty + ("cref" . 1) + ("Cref" . 1) + ("cref*" . 1) + ("Cref*" . 1) + ("cpageref" . 1) + ("Cpageref" . 1) + ("namecref" . 1) + ("nameCref" . 1) + ("lcnamecref" . 1) + ("labelcref" . 1) + ("crefrange" . 1) + ("Crefrange" . 1) + ("cpagerefrange" . 1) + ("Cpagerefrange" . 1) + ("crefrange*" . 1) + ("Crefrange*" . 1) + ("crefrange" . 2) + ("Crefrange" . 2) + ("cpagerefrange" . 2) + ("Cpagerefrange" . 2) + ("crefrange*" . 2) + ("Crefrange*" . 2) + ;; fancyref.sty + ("fref" . 1) + ("Fref" . 1) + ;; fancyvrb.sty + ("fvset" . 1) + ("VerbatimInput" . 1) + ;; fontaxes.sty + ("figureversion" . 1) + ;; fontspec.sty + ("addfontfeatures" . 1) + ;; ("fontspec" . 1) + ;; listings.sty + ("lstinputlisting" . 1) + ("lstset" . 1) + ;; mdframed.sty + ("mdfsetup" . 1) + ("mdfapptodefinestyle" . 2) + ;; nameref.sty + ("nameref" . 1) + ("Nameref" . 1) + ;; siunitx.sty + ("num" . 1) + ("si" . 1) + ("sisetup" . 1) + ("SI" . 2) + ;; splitidx.sty + ("sindex" . 1) + ;; varioref.sty + ("vref" . 1) + ("Vref" . 1) + ("vref*" . 1) + ("Ref" . 1) + ("vpageref" . 1) + ("vpageref*" . 1) + ("fullref" . 1) + ("vrefrange" . 1) + ("vrefrange*" . 1) + ("vpagerefrange" . 1) + ("vpagerefrange*" . 1) + ("vrefrange" . 2) + ("vrefrange*" . 2) + ("vpagerefrange" . 2) + ("vpagerefrange*" . 2) ) + "List of commands with arguments to be skipped. +Each element of the list is a cons cell with command name +\(string) as car and the number of mandatory arguments to be +skipped as cdr.")) + + +;; Add new environments with optional argument here: +(eval-when-compile + (defvar TeX-ispell-skip-envs-opt-arg-list + '(;; enumitem.sty + "description" + "description*" + "enumerate" + "enumerate*" + "itemize" + "itemize*" + ;; mdframed.sty + "mdframed") + "List of LaTeX environments with an opt argument to be skipped.")) + + +;; Add others delimited here: +(TeX-ispell-skip-setcar + '(;; LaTeX-base + ("\\\\raisebox" TeX-ispell-tex-arg-end 1 2 0) + ;; booktabs.sty + ("\\\\cmidrule" . "\\(([^)]*)\\)?{[-0-9]+}") + ;; siunitx.sty + ("\\\\fontspec" TeX-ispell-tex-arg-end 1 1 0))) + + +;; Add environments here: +(TeX-ispell-skip-setcdr + '(;; amsmath.sty + ("\\(align\\(\\*\\|at\\*?\\)?\\|flalign\\*?\\)" . + "\\\\end{\\(align\\(\\*\\|at\\*?\\)?\\|flalign\\*?\\)}") + ("gather\\*?" . "\\\\end{gather\\*?}") + ("multline\\*?" . "\\\\end{multline\\*?}") + ;; listings.sty + ("lstlisting" . "\\\\end{lstlisting}") + ;; tabularx.sty, tabulary.sty, Standard LaTeX tabular*-env + ("tabular[*xy]" TeX-ispell-tex-arg-end) + ;; tikz.sty + ("tikzpicture" . "\\\\end{tikzpicture}") + ;; fancyvrb.sty: In practice, all verbatim environments have a * + ;; variant, which sets showspaces=true + ("\\(Save\\|[BL]\\)?Verbatim\\(\\*\\|Out\\)?" . + "\\\\end{\\(Save\\|[BL]\\)?Verbatim\\(\\*\\|Out\\)?}"))) + + +;; No customization below this line + +(eval-when-compile + (defun TeX-ispell-sort-skip-cmds-list (arg) + "Return elements from `TeX-ispell-skip-cmds-list' acc. to ARG." + (when (member arg '(1 2 3)) + (let (cmds) + (dolist (elt TeX-ispell-skip-cmds-list) + (when (= (cdr elt) arg) + (push (car elt) cmds))) + (symbol-value 'cmds))))) + +(defvar TeX-ispell-skip-cmds-one-arg-regexp + (eval-when-compile + (concat "\\\\" + (regexp-opt (TeX-ispell-sort-skip-cmds-list 1) t))) + "Regexp of LaTeX commands with one argument to be skipped.") + +(defvar TeX-ispell-skip-cmds-two-args-regexp + (eval-when-compile + (concat "\\\\" + (regexp-opt (TeX-ispell-sort-skip-cmds-list 2) t))) + "Regexp of LaTeX commands with two arguments to be skipped.") + +(defvar TeX-ispell-skip-cmds-three-args-regexp + (eval-when-compile + (concat "\\\\" + (regexp-opt (TeX-ispell-sort-skip-cmds-list 3) t))) + "Regexp of LaTeX commands with three arguments to be skipped.") + +(defvar TeX-ispell-skip-envs-opt-arg-regexp + (eval-when-compile + (regexp-opt TeX-ispell-skip-envs-opt-arg-list t)) + "Regexp of LaTeX environments with an opt argument to be skipped.") + +;; Make them available to Ispell: +(TeX-ispell-skip-setcar + `((,TeX-ispell-skip-cmds-one-arg-regexp ispell-tex-arg-end) + (,TeX-ispell-skip-cmds-two-args-regexp ispell-tex-arg-end 2) + (,TeX-ispell-skip-cmds-three-args-regexp ispell-tex-arg-end 3))) + +(TeX-ispell-skip-setcdr + `((,TeX-ispell-skip-envs-opt-arg-regexp ispell-tex-arg-end 0))) + +(provide 'tex-ispell) + +;;; tex-ispell.el ends here diff --git a/tex.el b/tex.el index 6c302bb..7e41113 100644 --- a/tex.el +++ b/tex.el @@ -6521,6 +6521,87 @@ NAME may be a package, a command, or a document." (append '(plain-tex-mode ams-tex-mode latex-mode doctex-mode) ispell-tex-major-modes)) +(defcustom TeX-ispell-extend-skip-list t + "Whether to extend regions selected for skipping during spell checking." + :group 'TeX-misc + :type 'boolean) + +;; These functions are used to add new items to +;; `ispell-tex-skip-alists' -- see tex-ispell.el: +(defun TeX-ispell-skip-setcar (skip) + "Add SKIP to car of `ispell-tex-skip-alists'. +SKIP is an alist with the format described in +`ispell-tex-skip-alists'. Each element in SKIP is added on top +of the car of `ispell-tex-skip-alists'. This only happens if +`TeX-ispell-extend-skip-list' is non-nil." + (when TeX-ispell-extend-skip-list + (let ((raws (car ispell-tex-skip-alists)) + (envs (cadr ispell-tex-skip-alists))) + (dolist (x skip) + (pushnew x raws :test #'equal)) + (setq ispell-tex-skip-alists (list raws envs))))) + +(defun TeX-ispell-skip-setcdr (skip) + "Add SKIP to cdr of `ispell-tex-skip-alists'. +SKIP is an alist with the format described in +`ispell-tex-skip-alists'. Each element in SKIP is added on top +of the cdr of `ispell-tex-skip-alists'. This only happens if +`TeX-ispell-extend-skip-list' is non-nil." + (when TeX-ispell-extend-skip-list + (let ((raws (car ispell-tex-skip-alists)) + (envs (cadr ispell-tex-skip-alists))) + (dolist (x skip) + (pushnew x envs :test #'equal)) + (setq ispell-tex-skip-alists (list raws envs))))) + +(defun TeX-ispell-tex-arg-end (&optional arg1 arg2 arg3) + "Skip across ARG1, ARG2 and ARG3 number of braces and brackets. +This function is a variation of `ispell-tex-arg-end'. It should +be used when adding skip regions to `ispell-tex-skip-alists' for +constructs like: + + \\begin{tabularx}{300pt}[t]{lrc} ... + or + \\fontspec{font name}[font features] + +where optional and/or mandatory argument(s) follow(s) a mandatory +one. ARG1 is the number of mandatory arguments before the +optional one, ARG2 the max. number of following optional +arguments, ARG3 is the max. number of mandatory arguments +following. Omitting argument means 1. + +Here some examples for additions to `ispell-tex-skip-alists': + + \\begin{tabularx}{300pt}[t]{lrc} ... + ARG 1 2 3 + (\"tabularx\" TeX-ispell-tex-arg-end) or equivalent + (\"tabularx\" TeX-ispell-tex-arg-end 1 1 1) + + \\fontspec{font name}[font features] + ARG1 ARG2 ARG3=0 + (\"\\\\\\\\fontspec\" TeX-ispell-tex-arg-end 1 1 0) + + \\raisebox{lift}[height][depth]{contents} + ARG1 ARG2 ARG3=0 (checked by Ispell) + (\"\\\\\\\\raisebox\" TeX-ispell-tex-arg-end 1 2 0) + +Optional arguments before the first mandatory one are all +skipped." + (condition-case nil + (progn + (while (looking-at "[ \t\n]*\\[") (forward-sexp)) + (forward-sexp (or arg1 1)) + (let ((num 0)) + (while (and (looking-at "[ \t\n]*\\[") + (< num (or arg2 1))) + (setq num (1+ num)) + (forward-sexp))) + (forward-sexp (or arg3 1))) + (error + (message "Error skipping s-expressions at point %d." (point)) + (beep) + (sit-for 2)))) + ;;; Abbrev mode