From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Sergey Poznyakoff Newsgroups: gmane.emacs.devel Subject: [ELPA] New package: vcl-mode Date: Tue, 27 Nov 2018 14:45:44 +0200 Organization: GNU.org.ua Message-ID: <20181127144544.12338@ulysses.gnu.org.ua> NNTP-Posting-Host: blaine.gmane.org X-Trace: blaine.gmane.org 1543323271 23211 195.159.176.226 (27 Nov 2018 12:54:31 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 27 Nov 2018 12:54:31 +0000 (UTC) To: Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Nov 27 13:54:27 2018 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gRcsk-0005ww-5W for ged-emacs-devel@m.gmane.org; Tue, 27 Nov 2018 13:54:26 +0100 Original-Received: from localhost ([::1]:41973 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gRcuq-00058o-E7 for ged-emacs-devel@m.gmane.org; Tue, 27 Nov 2018 07:56:36 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:53933) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gRckV-0005za-8C for emacs-devel@gnu.org; Tue, 27 Nov 2018 07:45:57 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gRckP-0005WY-IM for emacs-devel@gnu.org; Tue, 27 Nov 2018 07:45:53 -0500 Original-Received: from ulysses.gnu.org.ua ([193.186.15.43]:49719 helo=gnu.org.ua) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gRckN-0005RU-G8 for emacs-devel@gnu.org; Tue, 27 Nov 2018 07:45:49 -0500 X-Mailutils-Message-Id: wARCjiSF012342 Original-Received: from gnu.org.ua (localhost [127.0.0.1]) by ulysses.gnu.org.ua with ESMTP id wARCjiSF012342 for ; Tue, 27 Nov 2018 14:45:44 +0200 X-Mailer: MH-E 8.3.1; GNU Mailutils 3.4.91; GNU Emacs 24.2.1 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 193.186.15.43 X-Mailman-Approved-At: Tue, 27 Nov 2018 07:56:31 -0500 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:231443 Archived-At: Hello, I would like to propose vcl-mode for inclusion to ELPA. Vcl-mode is a major mode for editing VCL code. VCL is a language used by Varnish HTTP Cache configuration files (http://varnish-cache.org). This is a single-file package. See the proposed patch. Regards, Sergey --- packages/vcl-mode/vcl-mode.el | 446 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 packages/vcl-mode/vcl-mode.el diff --git a/packages/vcl-mode/vcl-mode.el b/packages/vcl-mode/vcl-mode.el new file mode 100644 index 0000000..0f31178 --- /dev/null +++ b/packages/vcl-mode/vcl-mode.el @@ -0,0 +1,446 @@ +;;; vcl-mode.el --- major mode for editing VCL sources + +;; Authors: 2015-2018 Sergey Poznyakoff +;; Version: 1.0 +;; Keywords: Varnish, VCL + +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. + +;; This program 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 3 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 +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; 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, see . + +;; Installation: +;; You may wish to use precompiled version of the mode. To create it +;; run: +;; emacs -batch -f batch-byte-compile vcl-mode.el +;; Install the file vcl-mode.elc (and, optionally, vcl-mode.el) to +;; a directory in your Emacs load-path. + +;; Customization: +;; To your .emacs or site-start.el add: +;; (autoload 'vcl-mode "vcl-mode" "Major mode for Varnish VCL sources" t) +;; (add-to-list 'auto-mode-alist (cons (purecopy "\\.vcl\\'") 'vcl-mode)) + +(require 'cl) +(require 'cc-langs) + +(defvar vcl-mode-map () + "Keymap used in vcl-mode buffers.") +(if vcl-mode-map + nil + (setq vcl-mode-map (c-make-inherited-keymap)) + (define-key vcl-mode-map "\C-c%" 'vcl-match-paren)) + +(defvar vcl-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?\n "> b" st) + (modify-syntax-entry ?\r "> " st) + (modify-syntax-entry ?\f "> " st) +; (modify-syntax-entry ?\# "<" st) + (modify-syntax-entry ?/ ". 124b" st) + (modify-syntax-entry ?* ". 23" st) + (modify-syntax-entry ?+ "." st) + (modify-syntax-entry ?- "." st) + (modify-syntax-entry ?~ "." st) + (modify-syntax-entry ?= "." st) + (modify-syntax-entry ?% "." st) + (modify-syntax-entry ?< "." st) + (modify-syntax-entry ?> "." st) + (modify-syntax-entry ?& "." st) + (modify-syntax-entry ?| "." st) + (modify-syntax-entry ?_ "_" st) + (modify-syntax-entry ?\' "." st) + (modify-syntax-entry ?\" "." st) + st) + "Syntax table in use in VCL Mode buffers.") + +(defvar vcl-mode-abbrev-table nil + "Abbreviation table used in vcl-mode buffers.") +(c-define-abbrev-table 'vcl-mode-abbrev-table + '(("else" "else" c-electric-continued-statement 0))) + +;; Font locking +(defconst vcl-font-lock-keywords-1 + (eval-when-compile + (list + ;; Version declaration + '("^[ \t]*\\(vcl\\)\\>[ \t]*\\([[:digit:]]+\\.[[:digit:]]+\\)" + (1 font-lock-keyword-face) (2 font-lock-constant-face nil t)) + ;; Built-ins + (cons + (concat "\\<" + (regexp-opt + '("vcl_init" + "vcl_recv" + "vcl_pipe" + "vcl_pass" + "vcl_hash" + "vcl_hit" + "vcl_miss" + "vcl_fetch" + "vcl_deliver" + "vcl_error" + "vcl_fini" + "vcl_synth" + "vcl_backend_fetch" + "vcl_backend_response" + "vcl_backend_error") t) + "\\>") + 'font-lock-builtin-face) + ;; Keywords + (cons + (concat "\\<" + (regexp-opt + '("sub" + "import" + "include" + "backend")) + "\\>") + 'font-lock-keyword-face) + )) + "Subdued level highlighting for VCL buffers.") + +(defconst vcl-font-lock-keywords-2 + (append vcl-font-lock-keywords-1 + (eval-when-compile + (list + ;; Keywords + (cons + (concat "\\<" + (regexp-opt + '("acl" + "if" + "else" + "return" + "call" + "set" + "remove" + "unset" + "director" + "probe") t) + "\\>") + 'font-lock-keyword-face) + ;; Return values + (cons + (concat "\\<" + (regexp-opt + '("error" + "fetch" + "hash" + "hit_for_pass" + "lookup" + "ok" + "pass" + "pipe" + "deliver" + "restart" + "true" + "false") t) + "\\>") + 'font-lock-constant-face) + ;; Functions + (cons + (concat "\\<" + (regexp-opt + '("ban" + "call" + "hash_data" + "new" + "synth" + "synthetic" + "regsub" + "regsuball") t) + "\\>") + 'font-lock-function-name-face) + + ;; Objects and variables + ;; See https://www.varnish-cache.org/docs/4.0/reference/vcl.html#variables + (list (concat "\\<" + (regexp-opt + '("req" + "resp" + "bereq" + "beresp" + "obj") t) + "\\.\\(http\\)\\(\\.\\([a-zA-Z_-][a-zA-Z_0-9-]*\\)\\)?") + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face) + '(4 font-lock-string-face nil t)) + (list (concat "\\<\\(bereq\\)\\." + (regexp-opt + '("backend" + "between_bytes_timeout" + "connect_timeout" + "first_byte_timeout" + "method" + "proto" + "retries" + "uncacheable" + "url" + "xid") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face)) + (list (concat "\\<\\(beresp\\)\\.\\(backend\\)\\." + (regexp-opt + '("name" + "ip") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face) + '(3 font-lock-builtin-face)) + (list (concat "\\<\\(beresp\\)\\." + (regexp-opt + '("do_esi" + "do_gunzip" + "do_gzip" + "do_stream" + "grace" + "keep" + "proto" + "reason" + "status" + "storage_hint" + "ttl" + "uncacheable") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face)) + (list (concat "\\<\\(client\\)\\." + (regexp-opt + '("identity" + "ip") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face)) + (list (concat "\\<\\(obj\\)\\." + (regexp-opt + '("grace" + "hits" + "keep" + "proto" + "reason" + "status" + "ttl" + "uncacheable") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face)) + (list (concat "\\<\\(req\\)\\." + (regexp-opt + '("backend_hint" + "can_gzip" + "esi" + "esi_level" + "hash_always_miss" + "hash_ignore_busy" + "method" + "proto" + "restarts" + "ttl" + "url" + "xid") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face)) + (list (concat "\\<\\(resp\\)\\." + (regexp-opt + '("proto" + "reason" + "status") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face)) + (list (concat "\\<\\(server\\)\\." + (regexp-opt + '("hostname" + "identity" + "ip") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-builtin-face)) + (list (concat "\\<\\(storage\\)\\.\\(\\sw+\\)\\." + (regexp-opt + '("free_space" + "used_space" + "happy") t)) + '(1 font-lock-builtin-face) + '(2 font-lock-variahle-name-face) + '(3 font-lock-builtin-face)) + + (cons + (concat "\\<" + (regexp-opt + '("req" + "resp" + "bereq" + "beresp" + "client" + "server" + "obj" + "now") t) + "\\>") + 'font-lock-builtin-face) + + ;; Function calls + '("\\<\\(\\(\\sw+\\)\\.\\)*\\(\\sw+\\)[ \t]*(" + (2 font-lock-variable-name-face nil t) + (3 font-lock-function-name-face)) + ;; '("\\<\\(\\sw+\\)\\(\\.\\(\\sw+\\)\\)*[ \t]*(" + ;; (1 font-lock-function-name-face) + ;; (3 font-lock-function-name-face nil t)) + + ;; Constants + '("\\<\\([[:digit:]]+\\(\\.[[:digit:]]+\\)?\\)[ \t]*\\(ms\\|[smhdwy]\\)?\\>" + (1 font-lock-constant-face) (3 font-lock-builtin-face nil t))))) + "Medium level highlighting for VCL buffers.") + +(defconst vcl-font-lock-keywords-3 + (append vcl-font-lock-keywords-2 + (eval-when-compile + (list + ;; User function names. + '("^[ \t]*\\(sub\\)\\>[ \t]*\\(\\sw+\\)?" + (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t))))) + "Gaudy level highlighting for VCL buffers.") + +(defvar vcl-font-lock-keywords vcl-font-lock-keywords-3) + +(put 'vcl-mode 'c-mode-prefix "vcl-") + +(defun vcl-sharp-comment-syntax () + (save-excursion + (goto-char (match-beginning 0)) + (let ((syntax (save-match-data (syntax-ppss)))) + (cond + ((not (or (nth 3 syntax) (nth 4 syntax))) + (put-text-property (match-beginning 1) (match-end 1) + 'syntax-table (string-to-syntax "<")) + (end-of-line) + (put-text-property (point) (+ (point) 1) + 'syntax-table (string-to-syntax ">"))))))) + +(defconst vcl-syntax-propertize-function + (syntax-propertize-rules + ("\\({\\)\"" + (1 "|")) + ("\\({\\)[^\"]" + (1 "(}")) + ("\"\\(}\\)" + (1 "|")) + ("\\(^\\|[^\"]\\)\\(}\\)" + (2 "){")) + ("\\(\"\\)[^}]" + (1 (let ((syntax (save-match-data (syntax-ppss)))) + (string-to-syntax + (cond ((nth 4 syntax) + ".") + ((eq (nth 3 syntax) t) + (backward-char) + ".") + (t + (backward-char) + "\"")))))) + ("\\(#\\)" + (1 (ignore (vcl-sharp-comment-syntax)))) + )) + +(defun vcl-match-paren (&optional arg) + "If point is on a parenthesis (including VCL multi-line string delimiter), +find the matching one and move point to it. +With ARG, do it that many times. +" + (interactive "p") + (let ((n (or arg 1)) + (matcher (cond + ((looking-at "\\s\(") + (cons + (lexical-let ((s (buffer-substring + (match-beginning 0) + (match-end 0)))) + (lambda () + (search-forward s) + (backward-char))) + (lambda () + (forward-list) + (backward-char)))) + ((looking-at "\\s\)") + (cons + (lexical-let ((s (buffer-substring + (match-beginning 0) + (match-end 0)))) + (lambda () + (search-backward s))) + (lambda () + (forward-char) + (backward-list)))) + ((or (looking-at "{\"") + (save-excursion + (backward-char) + (looking-at "{\""))) + (cons + (lambda () + (search-forward "{\"")) + (lambda () + (search-forward-regexp "\"}") + (backward-char)))) + ((or (looking-at "\"}") + (save-excursion + (backward-char) + (looking-at "\"}"))) + (cons + (lambda () + (search-backward "\"}")) + (lambda () + (search-backward-regexp "{\""))))))) + (if (not matcher) + (message "Point not at parenthesis") + (condition-case err + (let ((fx (car matcher)) + (fn (cdr matcher))) + (catch 'stop + (while t + (funcall fn) + (setq n (1- n)) + (if (= n 0) + (throw 'stop t) + (condition-case e + (funcall fx) + (search-failed + (message "Not enough groups to satisfy the request") + (throw 'stop t))))))) + + (scan-error (goto-char (nth 2 err)) + (message "%s" (nth 1 err))) + (search-failed (message "Unbalanced %s" (cdr err))))))) + +;;;###autoload +(add-to-list 'auto-mode-alist (cons (purecopy "\\.vcl\\'") 'vcl-mode)) + +;;;###autoload +(define-derived-mode vcl-mode prog-mode "VCL" + "Major mode for editing VCL code. + +Key bindings: +\\{vcl-mode-map}" + + (set (make-local-variable 'syntax-propertize-function) + vcl-syntax-propertize-function) + (set (make-local-variable 'parse-sexp-lookup-properties) t) + + (c-initialize-cc-mode t) + (c-lang-setvar comment-start "# ") + (setq c-opt-cpp-prefix nil) + (set-syntax-table vcl-mode-syntax-table) + (setq local-abbrev-table vcl-mode-abbrev-table + abbrev-mode t) + (use-local-map vcl-mode-map) + (c-init-language-vars vcl-mode) + (c-common-init 'vcl-mode) + + (c-run-mode-hooks 'c-mode-common-hook 'vcl-mode-hook) + (c-update-modeline)) + +(provide 'vcl-mode) -- 1.8.4