;;; enumitem.el --- AUCTeX style for `enumitem.sty' (v3.5.2) ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Arash Esbati ;; Maintainer: auctex-devel@gnu.org ;; Created: 2014-10-20 ;; Keywords: tex ;; This file is part of AUCTeX. ;; AUCTeX 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. ;; AUCTeX 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 AUCTeX; 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 adds support for `enumitem.sty' (v3.5.2) from 2011/09/28. ;; `enumitem.sty' is part of TeXLive. ;; Tassilo Horn's `minted.el' was a major source of inspiration for ;; this style, many thanks to him (also for patiently answering my ;; many other questions, incl. the stupid ones.) ;; If things do not work or when in doubt, press `C-c C-n'. Comments ;; for improvement are welcome. ;;; Code: ;; Needed for compiling `pushnew': (eval-when-compile (require 'cl)) ;; Needed for auto-parsing. (require 'tex) (defvar LaTeX-enumitem-key-val-options '(;; Vertical Spacing ("topsep") ("partopsep") ("parsep") ("itemsep") ;; Horizontal Spacing ("leftmargin" ("*" "!")) ("itemindent" ("*" "!")) ("labelsep" ("*" "!")) ("labelwidth" ("*" "!")) ("labelindent" ("*" "!")) ("labelsep*") ("labelindent*") ("widest") ("widest*") ("rightmargin") ;; Labels and cross reference format ("label") ("label*") ("ref") ("font") ("format") ("align" ("left" "right" "parleft")) ;; Numbering, stopping, resuming ("start") ("resume") ("resume*") ;; Series ("series") ;; Penalties ("beginpenalty") ("midpenalty") ("endpenalty") ("before") ("before*") ("after") ("after*") ;; Description styles ("style" ("standard" "multiline" "nextline" "sameline" "unboxed")) ;; Compact lists ("noitemsep") ("nosep") ;; Wide lists ("wide") ;; Inline lists ("itemjoin") ("itemjoin*") ("afterlabel") ("mode" ("boxed" "unboxed"))) "Key=value options for enumitem macros and environments.") (defvar LaTeX-enumitem-key-val-options-local nil "Buffer-local key=value options for enumitem macros and environments.") (make-variable-buffer-local 'LaTeX-enumitem-key-val-options-local) (defvar LaTeX-enumitem-newlist-list-local nil "Local list of all environments definded with `\\newlist' plus the ones initially available through `enumitem' package.") (make-variable-buffer-local 'LaTeX-enumitem-newlist-list-local) ;; Setup for \newlist: (TeX-auto-add-type "enumitem-newlist" "LaTeX") (defvar LaTeX-enumitem-newlist-regexp '("\\\\newlist{\\([^}]+\\)}{\\([^}]+\\)}" (1 2) LaTeX-auto-enumitem-newlist) "Matches the arguments of `\\newlist' from `enumitem' package.") ;; Setup for \SetEnumitemKey: (TeX-auto-add-type "enumitem-SetEnumitemKey" "LaTeX") (defvar LaTeX-enumitem-SetEnumitemKey-regexp '("\\\\SetEnumitemKey{\\([^}]+\\)}" 1 LaTeX-auto-enumitem-SetEnumitemKey) "Matches the arguments of `\\SetEnumitemKey' from `enumitem' package.") ;; Setup for \SetEnumitemValue: (TeX-auto-add-type "enumitem-SetEnumitemValue" "LaTeX") ;; Upon Tassilo's recommendation, we include also `0' so that we can ;; use the function `LaTeX-enumitem-SetEnumitemValue-list' while we ;; make sure that `TeX-auto-list-information' doesn't remove multiple ;; defined values to a specific key. For this reason, we also ignore ;; the 3. argument to the `\SetEnumitemValue' macro (i.e., a third ;; {\\([^}]+\\)} in regex) so that we don't pollute the generated ;; `docname.el' with unnecessary (La)TeX code. (defvar LaTeX-enumitem-SetEnumitemValue-regexp '("\\\\SetEnumitemValue{\\([^}]+\\)}{\\([^}]+\\)}" (0 1 2) LaTeX-auto-enumitem-SetEnumitemValue) "Matches the arguments of `\\SetEnumitemValue' from `enumitem' package.") ;; Plug them into the machinery. (defun LaTeX-enumitem-auto-prepare () "Clear various `LaTeX-enumitem-*' before parsing." (setq LaTeX-auto-enumitem-newlist nil LaTeX-auto-enumitem-SetEnumitemKey nil LaTeX-auto-enumitem-SetEnumitemValue nil)) (defun LaTeX-enumitem-auto-cleanup () "Move parsing results into right places for further usage." ;; \newlist{}{}{} ;; env=, type=, ignored= (dolist (env-type (LaTeX-enumitem-newlist-list)) (let* ((env (car env-type)) (type (cadr env-type))) (LaTeX-add-environments (list env 'LaTeX-enumitem-env-with-opts)) ;; Tell AUCTeX about parsed description like environments. (when (or (string-equal type "description") (string-equal type "description*")) (add-to-list 'LaTeX-item-list `(,env . LaTeX-item-argument))))) ;; Now add the parsed env's to the local list. (when (LaTeX-enumitem-newlist-list) (setq LaTeX-enumitem-newlist-list-local (append (mapcar 'list (mapcar 'car (LaTeX-enumitem-newlist-list))) LaTeX-enumitem-newlist-list-local)))) (add-hook 'TeX-auto-prepare-hook #'LaTeX-enumitem-auto-prepare t) (add-hook 'TeX-auto-cleanup-hook #'LaTeX-enumitem-auto-cleanup t) (add-hook 'TeX-update-style-hook #'TeX-auto-parse t) (defun LaTeX-enumitem-env-with-opts (env) "Update available key-val options, then insert ENV and optional key-val and the first item." (LaTeX-enumitem-update-key-val-options) (LaTeX-insert-environment env (let ((opts (TeX-read-key-val t LaTeX-enumitem-key-val-options-local))) (when (and opts (not (string-equal opts ""))) (format "[%s]" opts)))) (if (TeX-active-mark) (progn (LaTeX-find-matching-begin) (end-of-line 1)) (end-of-line 0)) (delete-char 1) (when (looking-at (concat "^[ \t]+$\\|" "^[ \t]*" TeX-comment-start-regexp "+[ \t]*$")) (delete-region (point) (line-end-position))) (delete-horizontal-space) ;; Deactivate the mark here in order to prevent `TeX-parse-macro' ;; from swapping point and mark and the \item ending up right after ;; \begin{...}. (TeX-deactivate-mark) (LaTeX-insert-item) ;; The inserted \item may have outdented the first line to the ;; right. Fill it, if appropriate. (when (and (not (looking-at "$")) (not (assoc environment LaTeX-indent-environment-list)) (> (- (line-end-position) (line-beginning-position)) (current-fill-column))) (LaTeX-fill-paragraph nil))) (defun LaTeX-arg-SetEnumitemKey (optional) "Ask for a new key to be defined and add it to `LaTeX-enumitem-key-val-options-local'." (LaTeX-enumitem-update-key-val-options) (let ((key (TeX-read-string "New Key: ")) (replace (TeX-read-key-val optional LaTeX-enumitem-key-val-options-local "Replacement"))) (TeX-argument-insert key optional) (TeX-argument-insert replace optional) (add-to-list 'LaTeX-enumitem-key-val-options-local (list key)) (LaTeX-add-enumitem-SetEnumitemKeys key))) ;; In `LaTeX-enumitem-SetEnumitemValue-regexp', we match (0 1 2). ;; When adding a new `key=val', we need something unique for `0'-match ;; until the next `C-c C-n'. We mimic that regex-match bei concat'ing ;; the elements and pass the result to ;; `LaTeX-add-enumitem-SetEnumitemValues'. It will vanish upon next ;; invocation of `C-c C-n'. (defun LaTeX-arg-SetEnumitemValue (optional) "Ask for a new value added to an existing key incl. the final replacement of the value." (LaTeX-enumitem-update-key-val-options) (let* ((key (TeX-read-key-val optional LaTeX-enumitem-key-val-options-local "Key")) (val (TeX-read-string "String value: ")) ;; (key-match (car (assoc key LaTeX-enumitem-key-val-options-local))) (val-match (cdr (assoc key LaTeX-enumitem-key-val-options-local))) (temp (copy-alist LaTeX-enumitem-key-val-options-local)) (opts (assq-delete-all (car (assoc key temp)) temp))) (if val-match (pushnew (list key (delete-dups (apply 'append (list val) val-match))) opts :test #'equal) (pushnew (list key (list val)) opts :test #'equal)) (setq LaTeX-enumitem-key-val-options-local (copy-alist opts)) (TeX-argument-insert key optional) (TeX-argument-insert val optional) (LaTeX-add-enumitem-SetEnumitemValues (list (concat "\\SetEnumitemValue{" key "}{" val "}") key val)))) (defun LaTeX-enumitem-update-key-val-options () "Update the buffer-local key-val options before offering them in `enumitem'-completions." (dolist (key (LaTeX-enumitem-SetEnumitemKey-list)) (add-to-list 'LaTeX-enumitem-key-val-options-local key)) (dolist (keyvals (LaTeX-enumitem-SetEnumitemValue-list)) (let* ((key (nth 1 keyvals)) (val (nth 2 keyvals)) ;; (key-match (car (assoc key LaTeX-enumitem-key-val-options-local))) (val-match (cdr (assoc key LaTeX-enumitem-key-val-options-local))) (temp (copy-alist LaTeX-enumitem-key-val-options-local)) (opts (assq-delete-all (car (assoc key temp)) temp))) (if val-match (pushnew (list key (delete-dups (apply 'append (list val) val-match))) opts :test #'equal) (pushnew (list key (list val)) opts :test #'equal)) (setq LaTeX-enumitem-key-val-options-local (copy-alist opts))))) (TeX-add-style-hook "enumitem" (lambda () ;; Add enumitem to the parser. (TeX-auto-add-regexp LaTeX-enumitem-newlist-regexp) (TeX-auto-add-regexp LaTeX-enumitem-SetEnumitemKey-regexp) (TeX-auto-add-regexp LaTeX-enumitem-SetEnumitemValue-regexp) ;; Activate the buffer-local version of key-vals. (setq LaTeX-enumitem-key-val-options-local (copy-alist LaTeX-enumitem-key-val-options)) ;; Set the standard env's to the local list. (setq LaTeX-enumitem-newlist-list-local '(("itemize") ("enumerate") ("description"))) ;; Add the starred versions to the local list. (when (LaTeX-provided-package-options-member "enumitem" "inline") (setq LaTeX-enumitem-newlist-list-local (append '(("itemize*") ("enumerate*") ("description*")) LaTeX-enumitem-newlist-list-local))) ;; Standard env's take key-val as optional argument. (LaTeX-add-environments '("itemize" LaTeX-enumitem-env-with-opts) '("enumerate" LaTeX-enumitem-env-with-opts) '("description" LaTeX-enumitem-env-with-opts)) ;; Make inline env's available with package option "inline" (when (LaTeX-provided-package-options-member "enumitem" "inline") (LaTeX-add-environments '("itemize*" LaTeX-enumitem-env-with-opts) '("enumerate*" LaTeX-enumitem-env-with-opts) '("description*" LaTeX-enumitem-env-with-opts)) (add-to-list 'LaTeX-item-list '("description*" . LaTeX-item-argument))) ;; Cloning lists (TeX-add-symbols ;; The easy way would be: ;; '("newlist" ;; "Name" (TeX-arg-eval ;; completing-read "Type: " ;; '(("itemize") ("enumerate") ("description") ;; ("itemize*") ("enumerate*") ("description*"))) ;; "Max-depth") ;; But we go the extra mile to improve the user experience and add ;; the arguments directly to appropriate lists. ;; \newlist{}{}{} '("newlist" (TeX-arg-eval (lambda () (let ((name (TeX-read-string "Name: ")) (type (completing-read "Type: " '(("itemize") ("enumerate") ("description") ("itemize*") ("enumerate*") ("description*")))) (depth (TeX-read-string "Max-depth: "))) (setq LaTeX-enumitem-newlist-list-local (append `(,(list name)) LaTeX-enumitem-newlist-list-local)) (when (or (string-equal type "description") (string-equal type "description*")) (add-to-list 'LaTeX-item-list `(,name . LaTeX-item-argument))) (LaTeX-add-environments `(,name LaTeX-enumitem-env-with-opts)) (LaTeX-add-enumitem-newlists (list name type)) (insert (format "{%s}" name) (format "{%s}" type)) (format "%s" depth))))) ;; \renewlist{}{}{} '("renewlist" (TeX-arg-eval completing-read "Name: " LaTeX-enumitem-newlist-list-local) (TeX-arg-eval completing-read "Type: " '(("itemize") ("enumerate") ("description") ("itemize*") ("enumerate*") ("description*"))) "Max-depth") ;; \setlist[]{} '("setlist" [TeX-arg-eval mapconcat 'identity (TeX-completing-read-multiple "Environment(s), level(s): " `(,@LaTeX-enumitem-newlist-list-local ("1") ("2") ("3") ("4"))) ","] (TeX-arg-eval (lambda () (LaTeX-enumitem-update-key-val-options) (let ((opts (TeX-read-key-val nil LaTeX-enumitem-key-val-options-local))) (format "%s" opts))))) ;; \setlist*[]{} '("setlist*" [TeX-arg-eval mapconcat 'identity (TeX-completing-read-multiple "Environment, level: " `(,@LaTeX-enumitem-newlist-list-local ("1") ("2") ("3") ("4"))) ","] (TeX-arg-eval (lambda () (LaTeX-enumitem-update-key-val-options) (let ((opts (TeX-read-key-val nil LaTeX-enumitem-key-val-options-local))) (format "%s" opts))))) ) ;; General commands: (TeX-add-symbols ;; Ask for an Integer and insert it. '("setlistdepth" "Integer") ;; Just add the braces and let the user do the rest. '("AddEnumerateCounter" 3) '("AddEnumerateCounter*" 3) ;; "\restartlist" only works for lists defined with "resume" key. ;; We will not extract that information and leave that to users. ;; For completion, extract enumerated environments from ;; `LaTeX-enumitem-newlist-list' and add "enumerate" to them. '("restartlist" (TeX-arg-eval (lambda () (let ((enums '("enumerate"))) (when (LaTeX-provided-package-options-member "enumitem" "inline") (pushnew "enumerate*" enums :test #'equal)) (dolist (env-type (LaTeX-enumitem-newlist-list)) (let ((env (car env-type)) (type (cadr env-type))) (when (or (string-equal type "enumerate") (string-equal type "enumerate*")) (pushnew env enums :test #'equal)))) (completing-read "List name: " enums))))) ;; "Key" will be parsed and added to key-val list. '("SetEnumitemKey" LaTeX-arg-SetEnumitemKey) ;; "Key" and "Value" are added to our key-val list '("SetEnumitemValue" LaTeX-arg-SetEnumitemValue "Replacement")) ;; Setting enumerate short label (when (LaTeX-provided-package-options-member "enumitem" "shortlabels") (TeX-add-symbols '("SetEnumerateShortLabel" (TeX-arg-eval completing-read "Key: " '(("A") ("a") ("I") ("i") ("1"))) "Replacement"))) ;; Fontification (when (and (featurep 'font-latex) (eq TeX-install-font-lock 'font-latex-setup)) (font-latex-add-keywords '(("newlist" "{{{") ("renewlist" "{{{") ("setlist" "*[{") ("AddEnumerateCounter" "*{{{") ("SetEnumitemKey" "{{" ) ("SetEnumitemValue" "{{{")) 'function) (font-latex-add-keywords '(("restartlist" "{" ) ("setlistdepth" "{" ) ("SetEnumerateShortLabel" "{{")) 'variable))) LaTeX-dialect) (defvar LaTeX-enumitem-package-options '("inline" "ignoredisplayed" "shortlabels" "loadonly") "Package options for the enumitem package.") ;;; enumitem.el ends here