Download Git

;;; batch-mode.el --- major mode for editing batch scrips
;;; Copyright (C) 2002, Agnar Renolen <>
;;; Modified (c) 2009, Matthew Fidler <matthew.fidler at>
;;; Fixed indents (and labels)

;; batch-mode.el 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 2 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

;; This is version 1.0 of 21 August 2002.

;;; Comentary:

;; The batch-mode provides syntax hilighting and auto-indentation for
;; DOS batch files (.bat).  and auto-idendation.  

;; Agnar Renolen, <>

;;; Code:

(defgroup batch nil
  "Major mode for editing batch code"
  :prefix "batch-"
  :group 'languages)

; (defvar batch-mode-hook nil
;   "Hooks called when batch mode fires up."
;   :type 'hook
;   :group 'batch)

(defvar batch-mode-map nil
  "Keymap used with batch code")

(defcustom batch-indent-level 4
  "Amount by which batch subexpressions are indented."
  :type 'integer
  :group 'batch)

(defvar batch-font-lock-keywords
     ; since we can't specify batch comments through the syntax table,
     ; we have to specify it here, and override whatever is highlighted
     '( "^[ \t]*@?rem\\>.*" (0 font-lock-comment-face t))

     ; since the argument to the echo command is a string, we format it
     ; as a string
     '( "\\<echo\\>[ \t]*\\(.*\\)" (1 font-lock-string-face t))

     ; the argument of the goto statement is a label
     '( "\\<goto\\>[ \t]*\\([a-zA-Z0-9_]+\\)" (1
     ; the keywords of batch (which are not built-in commands)
     (concat "\\<\\(cmdextversion\\|"

     ; built-in DOS commands
     (cons (concat "\\<\\(a\\(ssoc\\|t\\(\\|trib\\)\\)\\|break\\|"
     ; variables are embeded in percent chars
     '( "%[a-zA-Z0-9_]+%?" . font-lock-variable-name-face)
     ; labels are formatted as constants
     '( ":[a-zA-Z0-9_]+" . font-lock-constant-face)

     ; command line switches are hilighted as type-face
     '( "[-/][a-zA-Z0-9_]+" . font-lock-type-face)

     ; variables set should also be hilighted with variable-name-face
     '( "\\<set\\>[ \t]*\\([a-zA-Z0-9_]+\\)" (1 font-lock-variable-name-face))

(defvar batch-mode-syntax-table
  (let ((st (make-syntax-table)))
    (modify-syntax-entry ?:  ". 12" st)
    (modify-syntax-entry ?\n ">" st)
    (modify-syntax-entry ?%  "." st)
    (modify-syntax-entry ?=  "." st)
  "Syntax table used while in Batch mode.")

(defun batch-mode ()
  "Major mode for editing batch scripts."
  (setq major-mode 'batch-mode)
  (setq mode-name "Batch")
  (set (make-local-variable 'indent-line-function) 'batch-indent-line)
  (set (make-local-variable 'comment-start) "rem")
  (set (make-local-variable 'comment-end)   "")
  ;;(set (make-local-variable 'comment-start-skip) "rem[ \t]*")
  (set (make-local-variable 'font-lock-defaults)
       '(batch-font-lock-keywords nil t nil))
  (set-syntax-table batch-mode-syntax-table)
  (run-hooks 'batch-mode-hook))
(defun batch-indent-line ()
  "Indent current line as batch script"
  (let ((indent (batch-calculate-indent))
	beg shift-amt 
	(old-pos (- (point-max) (point))))
    (setq beg (point))
    (skip-chars-forward " \t")
    (if (looking-at ")")
	(setq indent (max (- indent batch-indent-level))))
    (message "prev indent: %d" indent)
    (setq shift-amt (- indent (current-column)))
    (if (not (zerop shift-amt))
	  (delete-region beg (point))
	  ; ArcView replaces tabs with single spaces, so we only insert
	  ; spaces to make indentation correct in ArcView.
	  (insert-char ?  indent)
	  (if (> (- (point-max) old-pos) (point))
	      (goto-char (- (point-max) old-pos)))))
(defun batch-calculate-indent ()
  "Return appropriate indentation for the current line as batch code."
    (if (bobp)
      (if (re-search-backward "^[ \t]*[^ \t\n\r]" nil t)
	  (if (looking-at "[ \t]*\\()[ \t]*else\\|for\\|if\\)\\>[^(\n]*([^)\n]*")
	      (+ (current-indentation) batch-indent-level)
	    (if (looking-at "[ \t]*[^(]*)[ \t]*")
		(- (current-indentation) batch-indent-level)

(add-to-list 'auto-mode-alist '("\\.bat\\'" . batch-mode))
(add-to-list 'auto-mode-alist '("\\.cmd\\'" . batch-mode))
(provide 'batch-mode)

;;; batch-mode.el ends here