; Created by Brian Templeton (bpt@hcoop.net) ; Extended by Adam Chlipala (adamc@hcoop.net) (eval-when-compile (require 'cl)) (defvar domtool-indent 2) (defvar domtool-mode-syntax-table (let ((table (make-syntax-table))) (loop for i from ?a to ?z do (modify-syntax-entry i "w" table)) (loop for i from ?A to ?Z do (modify-syntax-entry i "w" table)) (loop for i from ?0 to ?9 do (modify-syntax-entry i "w" table)) (mapc (lambda (pair) (loop for ch across (if (stringp (car pair)) (car pair) (string (car pair))) do (modify-syntax-entry ch (cadr pair) table))) '((" \t\n\14" " ") ; \14 is ^L (?_ "_") (?* ". 23n") (?\( "()1") (?\) ")(4") (?\" "\"") (?\\ "\\") (?\[ "(]") (?\] ")[") ;; We identify single-line comments using ;; font-lock-syntactic-keywords, because it's easier to ;; recognize documentation comments using the syntax table. (?\{ "(}12b") (?\} "){34b") ("->=<,:;^!&" ".") ; -> => <- = , : ; ^ ! & )) table)) (defun domtool-syms-re (&rest syms) (concat "\\<" (regexp-opt syms t) "\\>")) (defconst domtool-types-regexp (domtool-syms-re "proxy_target" "proxy_port" "mod_rewrite_cond_flag" "mod_rewrite_flag" "rewrite_arg" "redirect_code" "autoindex_option" "autoindex_width" "homedir_path" "dnsRecord" "apache_option" "authType" "location" "ssl" "ssl_cert_path" "suexec_flag" "web_node" "aliasTarget" "aliasSource" "email" "emailUser" "mail_node" "dnsKind" "master" "dns_node" "soa" "serial" "your_path" "your_group" "your_user" "group" "user" "node" "your_domain_host" "your_domain" "domain" "host" "ip" "no_newlines" "no_spaces" "bool" "string" "int") "A regexp that matches Domtool types from the standard library.") (defconst domtool-contexts-regexp (domtool-syms-re "Location" "Vhost" "Domain") "A regexp that matches Domtool contexts from the standard library.") (defconst domtool-actions-regexp (domtool-syms-re "errorDocument" "scriptAlias" "alias" "proxyPassReverse" "proxyPass" "rewriteLogLevel" "localProxyRewrite" "rewriteBase" "rewriteCond" "rewriteRule" "setEnv" "davFilesystem" "readmeName" "headerName" "unset_indexOptions" "set_indexOptions" "indexOptions" "addDescription" "mailmanWebHost" "dnsDefault" "dnsAlias" "dnsMail" "dnsIP" "nameserver" "dom" "web" "webAt" "relayMail" "handleMail" "dns" "addDefaultCharset" "action" "forceTypeOff" "forceType" "directoryIndex" "unset_options" "set_options" "options" "satisfyAny" "satisfyAll" "denyFrom" "denyFromAll" "allowFrom" "allowFromAll" "orderDenyAllow" "orderAllowDeny" "requireGroup" "requireUser" "requireValidUser" "authUserFile" "authName" "authType" "serverAliasDefault" "serverAlias" "serverAliasHost" "directory" "location" "vhost" "defaultAlias" "aliasDrop" "aliasMulti" "emailAlias" "aliasPrim" "domain") "A regexp that matches Domtool actions from the standard library.") (defconst domtool-vals-regexp (domtool-syms-re "ornext" "cond_nocase" "env" "skip" "redirectWith" "mimeType" "passthrough" "noescape" "qsappend" "nocase" "nosubreq" "chain" "last" "gone" "forbidden" "redirect" "redir307" "redir305" "redir304" "redir303" "redir302" "redir301" "redir300" "seeother" "permanent" "temp" "xhtml" "versionSort" "trackModified" "suppressSize" "suppressRules" "suppressLastModified" "suppressIcon" "suppressHtmlPreamble" "suppressDescription" "suppressColumnSorting" "scanHtmlTitles" "nameWidth" "ignoreClient" "ignoreCase" "iconWidth" "iconHeight" "iconsAreLinks" "htmlTable" "foldersFirst" "fancyIndexing" "descriptionWidth" "characters" "autofit" "home" "web_node" "default_node" "dnsDefaultA" "dnsNS" "dnsMX" "dnsCNAME" "dnsA" "indexes" "includesNOEXEC" "execCGI" "kerberos" "digest" "basic" "use_cert" "no_ssl" "web_node_to_node" "dropTarget" "addressesTarget" "addressTarget" "catchAllSource" "defaultSource" "userSource" "mail_node_to_node" "noDns" "useDns" "internalMaster" "externalMaster" "dns_node_to_node" "defaultSoa" "soa" "serialConst" "serialAuto" "ip_of_node" "true" "false") "A regexp that matches Domtool vals from the standard library.") (defconst domtool-env-vars-regexp (domtool-syms-re "Aliases" "DNS" "DocumentRoot" "Group" "MailNodes" "Mailbox" "SSL" "ServerAdmin" "SuExec" "TTL" "User" "WebNodes") "A regexp that matches Domtool env-vars from the standard library.") (defvar domtool-font-lock-keywords `(,(concat "\\_<" (regexp-opt '("let" "in" "begin" "end" "with" "where" "extern" "type" "val" "context" "Root") t) "\\_>") (,domtool-actions-regexp . font-lock-builtin-face) (,domtool-vals-regexp . font-lock-variable-name-face) (,domtool-contexts-regexp . font-lock-constant-face) (,domtool-env-vars-regexp . font-lock-constant-face) (,domtool-types-regexp . font-lock-type-face) ("type[ \t]+\\(\\(\\sw\\|\\s_\\)+\\)" 1 font-lock-type-face) ("val[ \t]+\\(\\(\\sw\\|\\s_\\)+\\)" 1 font-lock-variable-name-face))) (defvar domtool-font-lock-syntactic-keywords '(("\\(#\\).*\\(\n\\|\\'\\)" (1 "!") (2 "!")))) (defun domtool-font-lock-syntactic-face-function (state) "Major mode for editing Domtool files." (cond ((nth 3 state) font-lock-string-face) ((eq (nth 7 state) t) font-lock-doc-face) (t font-lock-comment-face))) (define-derived-mode domtool-mode fundamental-mode "Domtool" ;; For some reason, whatever twiddling `define-derived-mode' does ;; with the syntax table breaks recognition of (*...*) comments. So ;; we need to tell d-d-m to leave our syntax table alone. :syntax-table domtool-mode-syntax-table (set (make-local-variable 'indent-line-function) 'domtool-indent-line) (set (make-local-variable 'font-lock-defaults) '(domtool-font-lock-keywords nil nil nil nil (font-lock-syntactic-keywords . domtool-font-lock-syntactic-keywords) (font-lock-syntactic-face-function . domtool-font-lock-syntactic-face-function)))) (defun domtool-indent-line () (let ((savep (> (current-column) (current-indentation))) (indent (domtool-calculate-indent))) (cond ((eq indent 'noindent) indent) (savep (save-excursion (indent-line-to indent))) (t (indent-line-to indent))))) (defun until-closed (level) (if (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\)\\_>" nil t) (cond ((string= (match-string 0) "end") (until-closed (+ level 1))) ((= level 0) (current-indentation)) (t (until-closed (- level 1)))) nil)) (defun domtool-calculate-indent () (save-excursion (back-to-indentation) (multiple-value-bind (previous-keyword base-indent) (save-excursion (if (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\)\\_>" nil t) (values (match-string 0) (current-indentation)) (values nil 0))) (let ((state (syntax-ppss))) (cond ((nth 3 state) 'noindent) ((nth 4 state) (domtool-calculate-comment-indent state)) ((looking-at "\\_<\\(with\\|end\\)\\_>") (until-closed 0)) ((not previous-keyword) base-indent) ((string= previous-keyword "end") base-indent) (t (+ base-indent domtool-indent))))))) (defun domtool-calculate-comment-indent (state) (ecase (nth 7 state) ((t) 'noindent) ((syntax-table) 'noindent) ; can't happen ((nil) (let ((start (nth 8 state)) (depth 0)) (while (> (point) start) (re-search-backward "(\\*\\|\\*)" start t) (if (looking-at "(\\*") (incf depth) (decf depth))) (+ (current-indentation) depth)))))