-
Notifications
You must be signed in to change notification settings - Fork 3
/
zeek-mode.el
200 lines (160 loc) · 7.61 KB
/
zeek-mode.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
;; An Emacs mode for the Zeek scripting language.
(defvar zeek-mode-hook nil)
;; For access to string-trim below.
(eval-when-compile (require 'subr-x))
;; (defvar zeek-mode-map
;; (let ((zeek-mode-map (make-keymap)))
;; (define-key zeek-mode-map "\C-j" 'newline-and-indent)
;; zeek-mode-map)
;; "Keymap for ZEEK major mode")
(add-to-list 'auto-mode-alist '("\\.bro\\'" . zeek-mode))
(add-to-list 'auto-mode-alist '("\\.zeek\\'" . zeek-mode))
(add-to-list 'interpreter-mode-alist '("zeek" . zeek-mode))
;; Crude way to match "env (args) zeek (args) --", i.e. the construct
;; to allow Zeek use in a shebang line.
(add-to-list 'magic-mode-alist '("#![ \t]*.*/bin/env.+[ \t]zeek.+--[ \t]*$" . zeek-mode))
;; ---- Syntax Highlighting --------------------------------------------
(defvar zeek-mode-keywords
`(("\\(@[^#\n]+\\)" (0 font-lock-preprocessor-face t))
(,(concat "\\<"
(regexp-opt '("const" "option" "redef") t)
"\\>") (0 font-lock-constant-face))
(,(concat "\\<"
(regexp-opt '("addr" "any" "bool" "count" "counter" "double"
"enum" "file" "int" "interval" "list" "net"
"opaque" "paraglob" "pattern" "port" "record"
"set" "string" "subnet" "table" "timer" "time"
"union" "vector") t)
"\\>") (0 font-lock-type-face))
(,(concat "\\<"
(regexp-opt '("add" "alarm" "break" "case" "default"
"delete" "else" "event" "export" "fmt" "for"
"function" "global" "global_attr" "hook" "if" "in"
"local" "match" "module" "next" "of" "print"
"return" "schedule" "switch" "this" "type"
"using" "when") t)
"\\>") (0 font-lock-keyword-face))
(,(concat "\\<"
(regexp-opt '("day" "days" "hr" "hrs" "min" "mins" "sec" "secs"
"msec" "msecs" "usec" "usecs") t)
"\\>") (0 font-lock-function-name-face))
("\\(&[a-zA-Z_0-9]+\\)" (0 font-lock-builtin-face))
)
"Keyword highlighting spec for Zeek mode")
(font-lock-add-keywords 'zeek-mode zeek-mode-keywords)
;; ---- The Syntax Table -----------------------------------------------
(defvar zeek-mode-syntax-table
(let ((zeek-mode-syntax-table (make-syntax-table)))
;; Additional valid token characters
(modify-syntax-entry ?_ "w" zeek-mode-syntax-table)
(modify-syntax-entry ?. "w" zeek-mode-syntax-table)
(modify-syntax-entry ?& "w" zeek-mode-syntax-table)
;; Make $ a punctuation character
(modify-syntax-entry ?$ "." zeek-mode-syntax-table)
;; Comment starts/ends in the Zeek language.
;; Does not distinguish Zeekygen comments.
(modify-syntax-entry ?# "<" zeek-mode-syntax-table)
(modify-syntax-entry ?\n ">" zeek-mode-syntax-table)
zeek-mode-syntax-table)
"Syntax table for Zeek mode")
;; ---- Zeek script formatting and parsing via "zeek-script" -----------
;;
;; This requires the zeekscript Python package, see here for details:
;; https://github.com/zeek/zeekscript
(defvar zeek-script
(executable-find "zeek-script")
"Full path to the zeek-script command.
If nil, please install the zeekscript Python package and ensure its
zeek-script command is in your PATH.")
(when zeek-script
(defun zeek-script-cmd (&rest args)
"Returns full zeek-script invocation string for the given arguments."
(mapconcat 'identity (cons zeek-script args) " "))
(defun zeek-command-on-buffer (command destination &optional replace keep-errbuf)
"Run a command on the buffer and report errors to echo area.
This is a wrapper around shell-command-on-region, with a subset
of its arguments. The stderr stream of the command ends up in
buffer *zeek command errors* and its first line gets reported
to the echo area. By default the buffer is temporary and killed
upon return, but the keep-errbuf argument, when t, preserves it."
(let ((errbuf "*zeek-script errors*"))
(when (get-buffer errbuf)
(kill-buffer errbuf))
;; Run the given command on the buffer, plugging in stdout and stderr
;; destination buffers.
(shell-command-on-region (point-min) (point-max)
command destination replace errbuf)
(when (get-buffer errbuf)
(with-current-buffer errbuf
(goto-char (point-min))
(message (string-trim (thing-at-point 'line))))
(unless keep-errbuf (kill-buffer errbuf)))))
(defun zeek-format-buffer ()
"Format the current buffer using `zeek-script format'."
(interactive)
(let (;; Count non-whitespace characters up to point. Formatting only
;; modifies whitespace, so we can use this count to place point at the
;; "same" location after formatting.
(numchars (how-many "[^ \t\r\n]" (point-min) (point)))
;; Count number of lines that point is currently down from the top of
;; the visible window.
(toplines (count-lines (window-start) (point))))
;; Format the whole buffer, replacing its content.
(zeek-command-on-buffer (zeek-script-cmd "format" "-") (current-buffer) t t)
;; Put point back to "same" spot: search for "numchars" instances of
;; optional whitespace, a single non-whitespace, plus optional
;; whitespace. (The latter is necessary to make point land correctly right
;; after any whitespace.) The search leaves point at the end of the search
;; result, i.e. where we want to be.
(goto-char (point-min))
(re-search-forward "[ \t\r\n]*[^ \t\r\n][ \t\r\n]*" nil t numchars)
;; Point is now correct, but we may have scrolled the window. Re-scroll
;; so point is back on the same number of lines down from the top of the
;; window that it was before formatting.
(recenter-top-bottom toplines)))
(defun zeek-parse-buffer ()
"Report parse tree via `zeek-script parse' in a new buffer.
Potential parsing problems appear in the echo area and are
reflected in the parse tree."
(interactive)
(let ((outbuf "*zeek-script parse tree*"))
(zeek-command-on-buffer (zeek-script-cmd "parse" "-") outbuf)
(switch-to-buffer-other-window outbuf)
(special-mode)))
(defun zeek-format-before-save ()
"Add this to .emacs to run zeek-format on the current buffer when saving:
\(add-hook 'before-save-hook #'zeek-format-before-save)"
(interactive)
(when (eq major-mode 'zeek-mode) (zeek-format-buffer)))
(add-hook 'zeek-mode-hook
(lambda ()
(local-set-key (kbd "C-c C-f") 'zeek-format-buffer)
(local-set-key (kbd "C-c C-p") 'zeek-parse-buffer))))
;; ---- Main definitions -----------------------------------------------
;; We add whitespace minor mode by default and configure it to only show us
;; spaces after tabs or right from the start of a line. The face is called
;; whitespace-space-after-tab.
(add-hook 'zeek-mode-hook 'whitespace-mode)
(add-hook 'zeek-mode-hook
(lambda ()
(setq whitespace-space-after-tab-regexp '("^\t*\\( +\\)"))
(setq whitespace-style '(face space-after-tab))
))
;; The update-changes script maintains this version number; do not edit.
(defconst zeek-mode-version "1.0.0-11"
"The current version of Zeek mode.")
(defun zeek-mode ()
"Major mode for editing Zeek scripts"
(interactive)
(kill-all-local-variables)
(set-syntax-table zeek-mode-syntax-table)
(set (make-local-variable 'font-lock-defaults) '(zeek-mode-keywords))
(set (make-local-variable 'comment-start) "#")
(setq indent-tabs-mode t)
(setq c-basic-offset 8)
(setq tab-width 8)
(local-set-key (kbd "TAB") 'self-insert-command)
(setq major-mode 'zeek-mode)
(setq mode-name "Zeek")
(run-hooks 'zeek-mode-hook))
(provide 'zeek-mode)