;;; nf-configparser --- minor more and syntax highlighting for NF::ConfigParser config files ;; Author: Noah Friedman ;; Created: 2011-01-18 ;; Public domain ;; $Id: nf-configparser.el,v 1.6 2011/11/29 00:45:09 friedman Exp $ ;; Commentary: ;; Code: (require 'conf-mode) (defvar nf-configparser-mode-syntax-table (let ((table (make-syntax-table conf-mode-syntax-table))) (modify-syntax-entry ?# "<" table) (modify-syntax-entry ?\; "<" table) table) "Syntax table in use in NF::ConfigParser config files.") (defvar nf-configparser-mode-font-lock-keywords '( ;; [section] (do this first because it may look like a parameter) ("^[ \t]*\\[\\(.+\\)\\]" 1 'font-lock-preprocessor-face) ;; var=val or var: val ("^\\([^ \t\n]+?\\)[ \t]*\\(?:=\\|:\\(?:[ \t]\\|$\\)\\)" (1 'font-lock-variable-name-face)) ;; {a,b,c}, ${env}, or $(var) without \-escape ;; This torturous regexp discounts even numbers of preceding escape ;; chars, which just escape each other. It would be nice if emacs had ;; pcre's zero-width lookahead/behind assertions someday. ("\\(?:\\(?:^\\|[^\\\\\n]\\)\\(?:\\\\\\\\\\)*\\)\\(\\$[\{\(]\\|\{\\)" (1 'default t t) ((lambda (bound) (setq nf-configparser-font-lock-state (match-end 1)) (condition-case nil (cond ((string= (buffer-substring (match-beginning 1) (match-end 1)) "{") ;; {a,b,c} case (let* ((beg (match-end 1)) (end (min bound (progn (backward-char 1) (forward-sexp 1) (point)))) (re "\\(?:\\=\\|[^\\\\\n]\\)\\(?:\\\\\\\\\\)*,") (re2 "\\(?:\\=\\|[^\\\\\n]\\)\\(?:\\\\\\\\\\)*[,}]")) (when end (goto-char beg) (while (re-search-forward re end t) (put-text-property beg (1- (match-end 0)) 'face 'font-lock-doc-face) ;;(put-text-property (match-beginning 0) (match-end 0) 'face 'default) (put-text-property beg (match-end 0) 'fontified t) (setq re re2) ;; accept close brace after first comma (setq beg (point)))))) (t ;; ${env} or $(var) case (let* ((beg (point)) (end (min bound (progn (backward-char 1) (forward-sexp 1) (point)))) (face (if (char-equal (char-after (1- beg)) ?{) 'font-lock-function-name-face 'font-lock-type-face))) (goto-char beg) (when (re-search-forward ".*\\(.\\)" end t) (put-text-property (match-beginning 0) (match-beginning 1) 'face face) (put-text-property (match-beginning 1) (match-end 1) 'face 'default) (put-text-property (match-beginning 0) (match-end 0) 'fontified t))))) (error nil))) nil (when nf-configparser-font-lock-state (goto-char nf-configparser-font-lock-state) (setq nf-configparser-font-lock-state nil)) )) ;; comments ;; `;' is a comment if only whitespace precedes it. ;; `#' is a comment anywhere, unless escaped. ("^\\s *;+.*" (0 'font-lock-comment-face t t)) ("\\(?:\\(?:^\\|[^\\\\\n]\\)\\(?:\\\\\\\\\\)*\\)\\(#.*\\)" (1 'font-lock-comment-face t t)) )) ;;;###autoload (define-derived-mode nf-configparser-mode conf-mode "Conf[NF]" :syntax-table nf-configparser-mode-syntax-table (conf-mode-initialize "#") ;; We set font-lock-keywords-only => t ;; make a new list, otherwise the default is changed for all other conf ;; minor modes. Oops! (setq font-lock-defaults '(nf-configparser-mode-font-lock-keywords t t nil nil)) (make-local-variable 'conf-assignment-regexp) (setq conf-assignment-regexp ".+?\\([ \t]*[=:][ \t]*\\)") (make-local-variable 'beginning-of-defun-function) (setq beginning-of-defun-function 'nf-configparser-beginning-of-defun) (make-local-variable 'end-of-defun-function) (setq end-of-defun-function 'nf-configparser-end-of-defun) (make-local-variable 'nf-configparser-font-lock-state)) (defun nf-configparser-beginning-of-defun (&optional arg) (save-match-data (re-search-backward "^\\[.*?\\]" nil t))) (defun nf-configparser-end-of-defun (&optional arg) (save-match-data ;; We're at the beginning of the defun; skip over it. (end-of-line) (re-search-forward "^\\[.*?\\]" nil t) (beginning-of-line) (backward-char 1))) (provide 'nf-configparser-mode) ;; eof