Commit | Line | Data |
---|---|---|
7679edb1 | 1 | ;;; bat-mode.el --- Major mode for editing DOS/Windows scripts |
400a3178 | 2 | |
ba318903 | 3 | ;; Copyright (C) 2003, 2008-2014 Free Software Foundation, Inc. |
400a3178 AM |
4 | |
5 | ;; Author: Arni Magnusson <arnima@hafro.is> | |
6 | ;; Keywords: languages | |
7 | ||
8 | ;; This file is part of GNU Emacs. | |
9 | ||
10 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
11 | ;; it under the terms of the GNU General Public License as published by | |
12 | ;; the Free Software Foundation, either version 3 of the License, or | |
13 | ;; (at your option) any later version. | |
14 | ||
15 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | ;; GNU General Public License for more details. | |
19 | ||
20 | ;; You should have received a copy of the GNU General Public License | |
21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
22 | ||
23 | ;;; Commentary: | |
24 | ;; | |
7679edb1 SM |
25 | ;; Major mode for editing DOS/Windows scripts (batch files). Provides syntax |
26 | ;; highlighting, a basic template, access to DOS help pages, imenu/outline | |
312b1740 | 27 | ;; navigation, and the ability to run scripts from within Emacs. The syntax |
400a3178 AM |
28 | ;; groups for highlighting are: |
29 | ;; | |
30 | ;; Face Example | |
7679edb1 | 31 | ;; bat-label-face :LABEL |
400a3178 AM |
32 | ;; font-lock-comment-face rem |
33 | ;; font-lock-builtin-face copy | |
34 | ;; font-lock-keyword-face goto | |
35 | ;; font-lock-warning-face cp | |
36 | ;; font-lock-constant-face [call] prog | |
37 | ;; font-lock-variable-name-face %var% | |
38 | ;; font-lock-type-face -option | |
39 | ;; | |
40 | ;; Usage: | |
41 | ;; | |
7679edb1 | 42 | ;; See documentation of function `bat-mode'. |
400a3178 AM |
43 | ;; |
44 | ;; Separate package `dos-indent' (Matthew Fidler) provides rudimentary | |
45 | ;; indentation, see http://www.emacswiki.org/emacs/dos-indent.el. | |
46 | ;; | |
47 | ;; Acknowledgements: | |
48 | ;; | |
49 | ;; Inspired by `batch-mode' (Agnar Renolen) and `cmd-mode' (Tadamegu Furukawa). | |
50 | ||
51 | ;;; Code: | |
52 | ||
53 | ;; 1 Preamble | |
54 | ||
7679edb1 | 55 | (defgroup bat-mode nil |
cdc1ebb9 | 56 | "Major mode for editing DOS/Windows batch files." |
400a3178 AM |
57 | :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces) |
58 | :group 'languages) | |
59 | ||
60 | ;; 2 User variables | |
61 | ||
7679edb1 SM |
62 | (defface bat-label-face '((t :weight bold)) |
63 | "Font Lock mode face used to highlight labels in batch files.") | |
400a3178 AM |
64 | |
65 | ;; 3 Internal variables | |
66 | ||
7679edb1 | 67 | (defvar bat-font-lock-keywords |
400a3178 AM |
68 | (eval-when-compile |
69 | (let ((COMMANDS | |
70 | '("assoc" "at" "attrib" "cd" "cls" "color" "copy" "date" "del" "dir" | |
71 | "doskey" "echo" "endlocal" "erase" "fc" "find" "findstr" "format" | |
72 | "ftype" "label" "md" "mkdir" "more" "move" "net" "path" "pause" | |
73 | "popd" "prompt" "pushd" "rd" "ren" "rename" "replace" "rmdir" "set" | |
74 | "setlocal" "shift" "sort" "subst" "time" "title" "tree" "type" | |
75 | "ver" "vol" "xcopy")) | |
76 | (CONTROLFLOW | |
77 | '("call" "cmd" "defined" "do" "else" "equ" "exist" "exit" "for" "geq" | |
78 | "goto" "gtr" "if" "in" "leq" "lss" "neq" "not" "start")) | |
cdc1ebb9 EZ |
79 | (UNIX |
80 | '("bash" "cat" "cp" "fgrep" "grep" "ls" "sed" "sh" "mv" "rm"))) | |
312b1740 | 81 | `(("\\<_\\(call\\|goto\\)\\_>[ \t]+%?\\([A-Za-z0-9-_\\:.]+\\)%?" |
400a3178 | 82 | (2 font-lock-constant-face t)) |
312b1740 | 83 | ("^:[^:].*" |
7679edb1 | 84 | . 'bat-label-face) |
312b1740 | 85 | ("\\<_\\(defined\\|set\\)\\_>[ \t]*\\(\\w+\\)" |
400a3178 | 86 | (2 font-lock-variable-name-face)) |
312b1740 | 87 | ("%\\(\\w+\\)%?" |
400a3178 | 88 | (1 font-lock-variable-name-face)) |
312b1740 | 89 | ("!\\(\\w+\\)!?" ; delayed-expansion !variable! |
400a3178 | 90 | (1 font-lock-variable-name-face)) |
312b1740 | 91 | ("[ =][-/]+\\(\\w+\\)" |
400a3178 | 92 | (1 font-lock-type-face append)) |
312b1740 SM |
93 | (,(concat "\\_<" (regexp-opt COMMANDS) "\\_>") . font-lock-builtin-face) |
94 | (,(concat "\\_<" (regexp-opt CONTROLFLOW) "\\_>") | |
95 | . font-lock-keyword-face) | |
cdc1ebb9 | 96 | (,(concat "\\_<" (regexp-opt UNIX) "\\_>") |
312b1740 | 97 | . font-lock-warning-face))))) |
400a3178 | 98 | |
7679edb1 SM |
99 | (defvar bat-menu |
100 | '("Bat" | |
101 | ["Run" bat-run :help "Run script"] | |
102 | ["Run with Args" bat-run-args :help "Run script with args"] | |
400a3178 AM |
103 | "--" |
104 | ["Imenu" imenu :help "Navigate with imenu"] | |
105 | "--" | |
7679edb1 | 106 | ["Template" bat-template :help "Insert template"] |
400a3178 | 107 | "--" |
7679edb1 | 108 | ["Help (Command)" bat-cmd-help :help "Show help page for DOS command"])) |
400a3178 | 109 | |
7679edb1 | 110 | (defvar bat-mode-map |
400a3178 | 111 | (let ((map (make-sparse-keymap))) |
7679edb1 SM |
112 | (easy-menu-define nil map nil bat-menu) |
113 | (define-key map [?\C-c ?\C-/] 'bat-cmd-help) ;FIXME: Why not C-c C-? ? | |
114 | (define-key map [?\C-c ?\C-a] 'bat-run-args) | |
115 | (define-key map [?\C-c ?\C-c] 'bat-run) | |
116 | (define-key map [?\C-c ?\C-t] 'bat-template) | |
117 | (define-key map [?\C-c ?\C-v] 'bat-run) | |
400a3178 AM |
118 | map)) |
119 | ||
7679edb1 | 120 | (defvar bat-mode-syntax-table |
400a3178 | 121 | (let ((table (make-syntax-table))) |
f001e98e | 122 | (modify-syntax-entry ?\n ">" table) |
40f7e0e8 | 123 | (modify-syntax-entry ?\" "\"" table) |
312b1740 SM |
124 | ;; Beware: `w' should not be used for non-alphabetic chars. |
125 | (modify-syntax-entry ?~ "_" table) | |
400a3178 | 126 | (modify-syntax-entry ?% "." table) |
312b1740 SM |
127 | (modify-syntax-entry ?- "_" table) |
128 | (modify-syntax-entry ?_ "_" table) | |
129 | ;; FIXME: { and } can appear in identifiers? Really? | |
130 | (modify-syntax-entry ?{ "_" table) | |
131 | (modify-syntax-entry ?} "_" table) | |
400a3178 AM |
132 | (modify-syntax-entry ?\\ "." table) |
133 | table)) | |
134 | ||
f001e98e SM |
135 | (defconst bat--syntax-propertize |
136 | (syntax-propertize-rules | |
137 | ("^[ \t]*\\(?:\\(@?r\\)em\\_>\\|\\(?1::\\):\\).*" (1 "<")))) | |
138 | ||
400a3178 AM |
139 | ;; 4 User functions |
140 | ||
7679edb1 | 141 | (defun bat-cmd-help (cmd) |
cdc1ebb9 | 142 | "Show help for batch file command CMD." |
400a3178 AM |
143 | (interactive "sHelp: ") |
144 | (if (string-equal cmd "net") | |
312b1740 | 145 | ;; FIXME: liable to quoting nightmare. Use call-process? |
400a3178 AM |
146 | (shell-command "net /?") (shell-command (concat "help " cmd)))) |
147 | ||
7679edb1 | 148 | (defun bat-run () |
cdc1ebb9 | 149 | "Run a batch file." |
400a3178 | 150 | (interactive) |
312b1740 | 151 | ;; FIXME: liable to quoting nightmare. Use call/start-process? |
400a3178 AM |
152 | (save-buffer) (shell-command buffer-file-name)) |
153 | ||
7679edb1 | 154 | (defun bat-run-args (args) |
cdc1ebb9 | 155 | "Run a batch file with ARGS." |
400a3178 | 156 | (interactive "sArgs: ") |
312b1740 | 157 | ;; FIXME: Use `compile'? |
400a3178 AM |
158 | (shell-command (concat buffer-file-name " " args))) |
159 | ||
7679edb1 | 160 | (defun bat-template () |
cdc1ebb9 | 161 | "Insert minimal batch file template." |
400a3178 AM |
162 | (interactive) |
163 | (goto-char (point-min)) (insert "@echo off\nsetlocal\n\n")) | |
164 | ||
312b1740 | 165 | ;;;###autoload |
7679edb1 | 166 | (add-to-list 'auto-mode-alist '("\\.\\(bat\\|cmd\\)\\'" . bat-mode)) |
312b1740 | 167 | |
400a3178 AM |
168 | ;; 5 Main function |
169 | ||
170 | ;;;###autoload | |
7679edb1 | 171 | (define-derived-mode bat-mode prog-mode "Bat" |
cdc1ebb9 | 172 | "Major mode for editing DOS/Windows batch files.\n |
7679edb1 SM |
173 | Start a new script from `bat-template'. Read help pages for DOS commands |
174 | with `bat-cmd-help'. Navigate between sections using `imenu'. | |
175 | Run script using `bat-run' and `bat-run-args'.\n | |
176 | \\{bat-mode-map}" | |
312b1740 | 177 | (setq-local comment-start "rem ") |
f001e98e | 178 | (setq-local syntax-propertize-function bat--syntax-propertize) |
312b1740 | 179 | (setq-local font-lock-defaults |
7679edb1 | 180 | '(bat-font-lock-keywords nil t)) ; case-insensitive keywords |
312b1740 SM |
181 | (setq-local imenu-generic-expression '((nil "^:[^:].*" 0))) |
182 | (setq-local outline-regexp ":[^:]")) | |
400a3178 | 183 | |
7679edb1 | 184 | (provide 'bat-mode) |
400a3178 | 185 | |
7679edb1 | 186 | ;;; bat-mode.el ends here |