1 ;;; vcl-mode.el --- Major mode for Varnish Configuration Language -*- lexical-binding:t -*-
3 ;; Author: Sergey Poznyakoff <gray@gnu.org.ua>
5 ;; Keywords: Varnish, VCL
7 ;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
24 ;; Emacs support for Varnish's configuration language:
25 ;; https://varnish-cache.org/docs/trunk/users-guide/vcl.html
26 ;; This version of vcl-mode supports VCL-4.0.
28 ;; The features provided are auto-indentation (based on CC-mode's
29 ;; engine), keyword highlighting, and matching of {"..."} multi-line
32 ;; If you need support for VCL-2.0, you might have more luck with the older
33 ;; package: https://github.com/ssm/elisp/blob/master/vcl-mode.el
41 (let ((map (make-sparse-keymap)))
42 (set-keymap-parent map c-mode-base-map)
43 (define-key map "\C-c%" 'vcl-match-paren)
45 "Keymap used in vcl-mode buffers.")
47 (defvar vcl-mode-syntax-table
48 (let ((st (make-syntax-table)))
49 (modify-syntax-entry ?\n "> b" st)
50 ;; Use comment style `b' to match the style used for \n!
51 (modify-syntax-entry ?\# "< b" st)
52 (modify-syntax-entry ?/ ". 124b" st)
53 (modify-syntax-entry ?* ". 23" st)
54 (modify-syntax-entry ?+ "." st)
55 (modify-syntax-entry ?- "." st)
56 (modify-syntax-entry ?~ "." st)
57 (modify-syntax-entry ?= "." st)
58 (modify-syntax-entry ?% "." st)
59 (modify-syntax-entry ?< "." st)
60 (modify-syntax-entry ?> "." st)
61 (modify-syntax-entry ?& "." st)
62 (modify-syntax-entry ?| "." st)
63 (modify-syntax-entry ?_ "_" st)
64 (modify-syntax-entry ?\' "." st)
65 (modify-syntax-entry ?\" "\"" st)
67 "Syntax table in use in VCL Mode buffers.")
69 (define-abbrev-table 'vcl-mode-abbrev-table
70 '(("else" "else" c-electric-continued-statement :system t))
71 "Abbreviation table used in vcl-mode buffers.")
74 (defconst vcl-font-lock-keywords-1
77 ;; Version declaration
78 '("^[ \t]*\\(vcl\\)\\>[ \t]*\\([[:digit:]]+\\.[[:digit:]]+\\)"
79 (1 font-lock-keyword-face) (2 font-lock-constant-face nil t))
97 "vcl_backend_response"
98 "vcl_backend_error") t)
100 'font-lock-builtin-face)
110 'font-lock-keyword-face)
112 "Subdued level highlighting for VCL buffers.")
114 (defconst vcl-font-lock-keywords-2
115 (append vcl-font-lock-keywords-1
133 'font-lock-keyword-face)
151 'font-lock-constant-face)
165 'font-lock-function-name-face)
167 ;; Objects and variables
168 ;; See https://www.varnish-cache.org/docs/4.0/reference/vcl.html#variables
177 "\\.\\(http\\)\\(\\.\\([a-zA-Z_-][a-zA-Z_0-9-]*\\)\\)?")
178 '(1 font-lock-builtin-face)
179 '(2 font-lock-builtin-face)
180 '(4 font-lock-string-face nil t))
181 (list (concat "\\<\\(bereq\\)\\."
184 "between_bytes_timeout"
194 '(1 font-lock-builtin-face)
195 '(2 font-lock-builtin-face))
196 (list (concat "\\<\\(beresp\\)\\.\\(backend\\)\\."
201 '(1 font-lock-builtin-face)
202 '(2 font-lock-builtin-face)
203 '(3 font-lock-builtin-face))
204 (list (concat "\\<\\(beresp\\)\\."
219 '(1 font-lock-builtin-face)
220 '(2 font-lock-builtin-face))
221 (list (concat "\\<\\(client\\)\\."
226 '(1 font-lock-builtin-face)
227 '(2 font-lock-builtin-face))
228 (list (concat "\\<\\(obj\\)\\."
239 '(1 font-lock-builtin-face)
240 '(2 font-lock-builtin-face))
241 (list (concat "\\<\\(req\\)\\."
256 '(1 font-lock-builtin-face)
257 '(2 font-lock-builtin-face))
258 (list (concat "\\<\\(resp\\)\\."
264 '(1 font-lock-builtin-face)
265 '(2 font-lock-builtin-face))
266 (list (concat "\\<\\(server\\)\\."
272 '(1 font-lock-builtin-face)
273 '(2 font-lock-builtin-face))
274 (list (concat "\\<\\(storage\\)\\.\\(\\sw+\\)\\."
280 '(1 font-lock-builtin-face)
281 '(2 font-lock-variahle-name-face)
282 '(3 font-lock-builtin-face))
296 'font-lock-builtin-face)
299 '("\\<\\(\\(\\sw+\\)\\.\\)*\\(\\sw+\\)[ \t]*("
300 (2 font-lock-variable-name-face nil t)
301 (3 font-lock-function-name-face))
304 '("\\<\\([[:digit:]]+\\(\\.[[:digit:]]+\\)?\\)[ \t]*\\(ms\\|[smhdwy]\\)?\\>"
305 (1 font-lock-constant-face) (3 font-lock-builtin-face nil t)))))
306 "Medium level highlighting for VCL buffers.")
308 (defconst vcl-font-lock-keywords-3
309 (append vcl-font-lock-keywords-2
312 ;; User function names.
313 '("^[ \t]*\\(sub\\)\\>[ \t]*\\(\\sw+\\)?"
314 (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)))))
315 "Gaudy level highlighting for VCL buffers.")
317 (defvar vcl-font-lock-keywords vcl-font-lock-keywords-3)
319 (put 'vcl-mode 'c-mode-prefix "vcl-")
321 (defconst vcl-syntax-propertize-function
322 (syntax-propertize-rules
324 (1 (when (null (nth 8 (save-excursion
325 (syntax-ppss (match-beginning 0)))))
326 (string-to-syntax "|"))))
328 (1 (when (eq t (nth 3 (save-excursion
329 (syntax-ppss (match-beginning 0)))))
330 (string-to-syntax "|"))))))
332 (defun vcl-match-paren (&optional arg)
333 ;; FIXME: Assuming syntax-propertize works correctly, forward-sexp and
334 ;; backward-sexp should do the trick!
335 "If point is on a parenthesis (including VCL multi-line string delimiter),
336 find the matching one and move point to it.
337 With ARG, do it that many times."
343 (let ((s (match-string 0)))
352 (let ((s (match-string 0)))
354 (search-backward s)))
358 ((or (looking-at "{\"")
364 (search-forward "{\""))
366 (search-forward-regexp "\"}")
368 ((or (looking-at "\"}")
374 (search-backward "\"}"))
376 (search-backward-regexp "{\"")))))))
378 (message "Point not at parenthesis")
380 (let ((fx (car matcher))
391 (message "Not enough groups to satisfy the request")
392 (throw 'stop t)))))))
394 (scan-error (goto-char (nth 2 err))
395 (message "%s" (nth 1 err)))
396 (search-failed (message "Unbalanced %s" (cdr err)))))))
399 (add-to-list 'auto-mode-alist (cons (purecopy "\\.vcl\\'") 'vcl-mode))
402 (define-derived-mode vcl-mode prog-mode "VCL"
403 "Major mode for editing Varnish Configuration Language code.
407 :abbrev-table vcl-mode-abbrev-table
408 (set (make-local-variable 'syntax-propertize-function)
409 vcl-syntax-propertize-function)
410 (set (make-local-variable 'parse-sexp-lookup-properties) t)
412 (c-initialize-cc-mode t)
413 (c-lang-setvar comment-start "# ")
414 (setq c-opt-cpp-prefix nil)
416 (c-init-language-vars vcl-mode)
417 (c-common-init 'vcl-mode)
419 (run-mode-hooks 'c-mode-common-hook 'vcl-mode-hook)
424 ;; 2018-11-30 Stefan Monnier <monnier@iro.umontreal.ca>
426 ;; * vcl-mode/vcl-mode.el: Simplify syntax handling; plus cosmetics
428 ;; Use lexical-binding. Don't require `cl`.
429 ;; (vcl-mode-map): Move initialization into declaration. Don't rely on
430 ;; CC-mode's c-make-inherited-keymap.
431 ;; (vcl-mode-syntax-table): Use comment style b for `#` and mark `"` as a
433 ;; (vcl-mode-abbrev-table): Simplify definition.
434 ;; (vcl-font-lock-keywords-2): Don't request explicit subgroups if not
436 ;; (vcl-sharp-comment-syntax): Remove function.
437 ;; (vcl-syntax-propertize-function): Remove special cases for `#` and `"`.
438 ;; Refine `{"` and `"}` to filter out false positives.
439 ;; (vcl-match-paren): Use match-string.
440 ;; (vcl-mode): Let define-derived-mode set syntax-table, local-map, and
441 ;; abbrev-table. Use run-mode-hooks.
443 ;; 2018-11-29 Stefan Monnier <monnier@iro.umontreal.ca>
445 ;; * vcl-mode.el: Update header and fix last line; improve commentary
447 ;; 2018-11-29 Stefan Monnier <monnier@iro.umontreal.ca>
449 ;; Add 'packages/vcl-mode/' from commit
450 ;; 'd6bba7c13e0d72936001f5adea155256151339ac'
452 ;; git-subtree-dir: packages/vcl-mode git-subtree-mainline:
453 ;; c0c44c3c0ded215e5bc60da74e2aaa090a35617b git-subtree-split:
454 ;; d6bba7c13e0d72936001f5adea155256151339ac
459 ;;; vcl-mode.el ends here