;;; exam.el --- AUCTeX style for the (LaTeX) exam class  -*- lexical-binding: t; -*-

;; Copyright (C) 2016--2022 Free Software Foundation, Inc.

;; Author: Uwe Brauer <oub@mat.ucm.es>
;; Created: 2016-03-06
;; 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 the exam class.

;; Acknowledgements
;; Arash Esbati <arash@gnu.org> for a almost complete rewrite.

;;; Code:

(require 'tex)
(require 'latex)

;; Silence the compiler:
(declare-function font-latex-add-keywords
                  "font-latex"
                  (keywords class))

(defvar LaTeX-article-class-options)

(defvar LaTeX-exam-class-options
  '("answers" "noanswers" "cancelspace" "nocancelspace" "addpoints")
  "Class options for the exam class.")

(TeX-load-style "article")
;; Add options from `LaTeX-article-class-options' only once:
(dolist (opt LaTeX-article-class-options)
  (add-to-list 'LaTeX-exam-class-options opt))

(defun LaTeX-exam-insert-item ()
  "Insert a new item in the current environment from exam class.
Item inserted depends on the environment."
  (let ((env (LaTeX-current-environment)))
    (TeX-insert-macro
     (cond ((string= env "questions")
            "question")
           ((string= env "parts")
            "part")
           ((string= env "subparts")
            "subpart")
           ((string= env "subsubparts")
            "subsubpart")
           ((member env '("choices" "oneparchoices"
                          "checkboxes" "oneparcheckboxes"))
            "choice")
           ;; Fallback
           (t "item")))))

(defun LaTeX-exam-insert-label (_optional &optional name type)
  "Indent the line and query/insert a label incl. the \"\\label\" macro.
Arguments NAME and TYPE are the same as for the function
`LaTeX-label'.  OPTIONAL is ignored."
  (indent-according-to-mode)
  (let ((currenv (LaTeX-current-environment)))
    (LaTeX-label (or name currenv) (or type 'environment))))

(TeX-add-style-hook
 "exam"
 (lambda ()
   (TeX-run-style-hooks "article")
   ;; Make our label prefix available ...
   (let ((envs '("questions")))
     (dolist (env envs)
       ;; to AUCTeX
       (add-to-list 'LaTeX-label-alist
                    (cons env 'LaTeX-exam-label))
       ;; to RefTeX with `reftex-add-label-environments'
       (when (fboundp 'reftex-add-label-environments)
         (reftex-add-label-environments
          `((,env ,LaTeX-exam-reftex-quick-id-key ,LaTeX-exam-label
                  "~\\ref{%s}" nil
                  (regexp "[Qq]uestions?" "[Nn]umbers?")))))))
   (when (or (member "xcolor" (TeX-style-list))
             (member "color" (TeX-style-list)))
     (TeX-add-symbols '("shadedsolutions" 0)))

   (LaTeX-add-environments
    '("solution" [ "Height" ])
    '("select")
    '("choices" LaTeX-env-item)
    '("oneparchoices" LaTeX-env-item)
    '("checkboxes" LaTeX-env-item)
    '("oneparcheckboxes" LaTeX-env-item)
    '("solutionorbox" [ "Height" ])
    '("solutionorlines" [ "Height" ])
    '("solutionordottedlines" [ "Height" ])
    '("solutionorgrid" [ "Height" ])
    '("questions" LaTeX-env-item)
    '("parts" LaTeX-env-item)
    '("subparts" LaTeX-env-item)
    '("subsubparts" LaTeX-env-item))

   ;; Tell AUCTeX about special environments:
   (let ((envs '("questions"
                 "parts"      "subparts"         "subsubparts"
                 "choices"    "oneparchoices"
                 "checkboxes" "oneparcheckboxes")))
     (dolist (env envs)
       (add-to-list 'LaTeX-item-list
                    (cons env #'LaTeX-exam-insert-item)
                    t)))

   ;; Append us only once:
   (unless (and (string-match "question" LaTeX-item-regexp)
                (string-match "sub" LaTeX-item-regexp))
     (set (make-local-variable 'LaTeX-item-regexp)
          (concat
           LaTeX-item-regexp
           "\\|"
           "choice\\b"
           "\\|"
           "\\(titled\\)?question\\b"
           "\\|"
           "\\(sub\\)*part\\b"))
     (LaTeX-set-paragraph-start))

   (TeX-add-symbols
    '("part" [ "Points" ] (TeX-arg-literal " "))
    '("subpart" [ "Points" ] (TeX-arg-literal " "))
    '("gradetable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])
    '("bonusgradetable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])
    '("bonuspointtable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])
    '("partialgradetable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])
    '("partialbonusgradetable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])
    '("partialbonuspointtable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])
    '("pointtable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])
    '("partialpointtable"
      [TeX-arg-completing-read ("v" "h") "Orientation"]
      [TeX-arg-completing-read ("questions" "pages") "Table index"])

    '("subsubpart" [ "Points" ] (TeX-arg-literal " "))
    '("question"  ["Points"] (TeX-arg-literal " "))
    '("bonusquestion"  ["Points"] (TeX-arg-literal " "))
    '("extrafootheight"  [ TeX-arg-length "Extra height 1. page footer" ]
      (TeX-arg-length "Extra height footers"))
    '("titledquestion" "Title" ["Points"] LaTeX-exam-insert-label (TeX-arg-literal " "))
    '("ContinuedQuestion" 0)
    '("CorrectChoice" 0)
    '("CorrectChoiceEmphasis" 1)
    '("IncompleteQuestion" 0)
    '("SolutionEmphasis" 1)
    '("addpoints" 0)
    '("addquestionobject" 0)
    '("answerclearance" 1)
    '("answerline"  ["Points"] (TeX-arg-literal " "))
    '("begingradingrange" 1)
    '("bhpgword" 1)
    '("bhpword" 1)
    '("bhqword" 1)
    '("bhsword" 1)
    '("bhtword" 1)
    '("bonuspart" 0)
    '("bonuspointformat" 0)
    '("bonuspointname" 1)
    '("bonuspointpoints" 2)
    '("bonuspointsinrange" 0)
    '("bonuspointsofquestion" 1)
    '("bonuspointsonpage" 0)
    '("bonusqformat" 1)
    '("bonussubpart" 0)
    '("bonussubsubpart" 0)
    '("bonustitledquestion" "Title" ["Points"] LaTeX-exam-insert-label (TeX-arg-literal " "))
    '("bonustotalformat" 0)
    '("boxedpoints" 0)
    '("bracketedpoints" 0)
    '("bvpgword" 1)
    '("bvpword" 1)
    '("bvqword" 1)
    '("bvsword" 1)
    '("bvtword" 1)
    '("cancelspace" 0)
    '("cellwidth" 1)
    '("cfoot" 1)
    '("chbpword" 1)
    '("chead" 1)
    '("checkboxchar" 1)
    '("checkboxeshook" 0)
    '("checkedchar" 1)
    '("choice" (TeX-arg-literal " "))
    '("choicelabel" 0)
    '("choiceshook" 0)
    '("chpgword" 1)
    '("chpword" 1)
    '("chqword" 1)
    '("chsword" 1)
    '("chsword" 1)
    '("correctchoice" 0)
    '("correctchoiceemphasis" 1)
    '("covercfoot" 3)
    '("coverchead" 3)
    '("coverextrafootheight" 3)
    '("coverextraheadheight" 3)
    '("coverfirstpagefooter" 3)
    '("coverfirstpageheader" 3)
    '("coverfooter" 3)
    '("coverheader" 3)
    '("coverlfoot" 3)
    '("coverlhead" 3)
    '("coverrfoot" 3)
    '("coverrhead" 3)
    '("coverrunningfooter" 3)
    '("coverrunningheader" 3)
    '("cvbpword" 1)
    '("cvpgword" 1)
    '("cvpword" 1)
    '("cvqword" 1)
    '("cvsword" 1)
    '("cvtword" 1)
    '("dottedlinefillheight" 1)
    '("droppoints" 0)
    '("droptotalbonuspoints" 0)
    '("droptotalpoints" 0)
    '("endgradingrange" 1)
    '("extraheadheight" 1)
    '("extrawidth" 1)
    '("fillwithdottedlines" 1)
    '("fillwithlines" 1)
    '("firstpagefooter" 1)
    '("firstpagefootrule" 0)
    '("firstpageheader" 3)
    '("firstpageheadrule" 0)
    '("footer" 3)
    '("footrule" 0)
    '("framedsolutions" 0)
    '("fullwidth" 1)
    '("gradetablestretch" 0)
    '("greeknum" 0)
    '("half" 0)
    '("hpgword" 1)
    '("hpword" 1)
    '("hqword" 1)
    '("hsword" 1)
    '("htword" 1)
    '("ifcontinuation" 2)
    '("ifincomplete" 2)
    '("iflastpage" 2)
    '("ifprintanswers" 0)
    '("lfoot" 1)
    '("lhead" 1)
    '("linefillheight" 1)
    '("linefillthickness" 1)
    '("makeemptybox" 1)
    '("marginbonuspointname" 1)
    '("marginpointname" 1)
    '("marginpointsep" 1)
    '("marksnotpoints" 0)
    '("noaddpoints" 0)
    '("nobonusqformat" 1)
    '("noboxedpoints" 0)
    '("nobracketedpoints" 0)
    '("nocancelspace" 0)
    '("nomorequestions" 0)
    '("nopointsinmargin" 0)
    '("nopointsinrightmargin" 0)
    '("noprintanswers" 0)
    '("noqformat" 1)
    '("numbonuspoints" 0)
    '("numcoverpages" 0)
    '("numpages" 0)
    '("numparts" 0)
    '("numpoints" 0)
    '("numquestions" 0)
    '("numsubparts" 0)
    '("numsubsubparts" 0)
    '("oddeven" 2)
    '("partlabel" 0)
    '("partopsep" 0)
    '("partshook" 0)
    '("pointname" 1)
    '("pointpoints" 2)
    '("pointsdroppedatright" 0)
    '("pointsinmargin" 0)
    '("pointsinrightmargin" 0)
    '("pointsofquestion" 1)
    '("pointsonpage" 1)
    '("printanswers" 0)
    '("printselectedfalse" 0)
    '("printselectedtrue" 0)
    '("qformat" 1)
    '("questionlabel" 0)
    '("questionshook" 0)
    '("rfoot" 1)
    '("rhead" 1)
    '("rightpointsmargin" 0)
    '("roman" 0)
    '("romannumeral" 0)
    '("runningfooter" 3)
    '("runningfootrule" 0)
    '("runningheader" 3)
    '("runningheadrule" 0)
    '("settabletotalbonuspoints" 1)
    '("settabletotalpoints" 1)
    '("shadedsolutions" 0)
    '("solutiontitle" 0)
    '("subpartlabel" 0)
    '("subpartshook" 0)
    '("subsubpartlabel" 0)
    '("subsubpartshook" 0)
    '("thechoice" 0)
    '("themarginpoints" 0)
    '("thepartno" 0)
    '("thequestion" 0)
    '("thequestiontitle" 0)
    '("thesubpart" 0)
    '("thesubsubpart" 0)
    '("totalbonuspoints" 0)
    '("totalformat" 0)
    '("totalnumpages" 0)
    '("totalpoints" 0)
    '("unframedsolutions" 0)
    '("uplevel" 1)
    '("usehorizontalhalf" 0)
    '("useslantedhalf" 0)
    '("vpgword" 1)
    '("vpword" 1)
    '("vqword" 1)
    '("vsword" 1)
    '("vtword" 1)
    '("thepoints" 0)
    ;; ... more stuff here
    )
   (LaTeX-add-lengths "answerlinelength" "answerskip")

   ;; Don't increase indentation at various \if* macros:
   (let ((exceptions '("ifcontinuation"
                       "ifincomplete"
                       "iflastpage")))
     (dolist (elt exceptions)
       (add-to-list 'LaTeX-indent-begin-exceptions-list elt t))
     (LaTeX-indent-commands-regexp-make))

   ;; Fontification
   (when (and (featurep 'font-latex)
              (eq TeX-install-font-lock 'font-latex-setup))
     (font-latex-add-keywords '(("question"        "[")
                                ("titledquestion"  "{[")
                                ("subpart"         "[")
                                ("subsubpart"      "["))
                              'textual)))
 TeX-dialect)

;;; exam.el ends here
