From a252e892a9a266f780f13f943f4499488bbdcdc1 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Tue, 17 May 2011 00:45:41 +0200 Subject: EMACS: Added Rainbow delimiters mode --- emacs.d/10-modules.el | 2 - emacs.d/10-naquadah-theme.el | 16 ++ emacs.d/10-settings.el | 1 - emacs.d/20-rainbow-delimiters.el | 4 + emacs.d/elisp/rainbow-delimiters.el | 392 ++++++++++++++++++++++++++++++++++++ 5 files changed, 412 insertions(+), 3 deletions(-) create mode 100644 emacs.d/10-naquadah-theme.el create mode 100644 emacs.d/20-rainbow-delimiters.el create mode 100644 emacs.d/elisp/rainbow-delimiters.el diff --git a/emacs.d/10-modules.el b/emacs.d/10-modules.el index 18731d7..b6cd429 100644 --- a/emacs.d/10-modules.el +++ b/emacs.d/10-modules.el @@ -1,5 +1,3 @@ -(require 'naquadah-theme) - (autoload 'vala-mode "vala-mode" "A Major mode for editing Vala files" t) (autoload 'csharp-mode "csharp-mode" diff --git a/emacs.d/10-naquadah-theme.el b/emacs.d/10-naquadah-theme.el new file mode 100644 index 0000000..5507800 --- /dev/null +++ b/emacs.d/10-naquadah-theme.el @@ -0,0 +1,16 @@ +(require 'naquadah-theme) + +;; Extension to the naquadah theme +(naquadah-theme-set-faces + 'naquadah + '(rainbow-delimiters-depth-1-face (:foreground gradient-1)) + '(rainbow-delimiters-depth-2-face (:foreground gradient-2)) + '(rainbow-delimiters-depth-3-face (:foreground gradient-3)) + '(rainbow-delimiters-depth-4-face (:foreground gradient-4)) + '(rainbow-delimiters-depth-5-face (:foreground gradient-5)) + '(rainbow-delimiters-depth-6-face (:foreground gradient-6)) + '(rainbow-delimiters-depth-7-face (:foreground gradient-7)) + '(rainbow-delimiters-depth-8-face (:foreground gradient-8)) + '(rainbow-delimiters-depth-9-face (:foreground gradient-9)) + '(rainbow-delimiters-depth-10-face (:foreground gradient-10)) + '(rainbow-delimiters-depth-11-face (:foreground gradient-11))) diff --git a/emacs.d/10-settings.el b/emacs.d/10-settings.el index ac0db1c..42d9ed3 100644 --- a/emacs.d/10-settings.el +++ b/emacs.d/10-settings.el @@ -22,7 +22,6 @@ (global-linum-mode t) ; show them in the gutter (column-number-mode t) ; show column number in splitter (global-font-lock-mode t) ; show syntax highlighting, old -(show-paren-mode t) ; show matching parens (delete-selection-mode t) ; delete selection upon typing ;; Byte-compile elisp files on save diff --git a/emacs.d/20-rainbow-delimiters.el b/emacs.d/20-rainbow-delimiters.el new file mode 100644 index 0000000..dbf387f --- /dev/null +++ b/emacs.d/20-rainbow-delimiters.el @@ -0,0 +1,4 @@ +(require 'rainbow-delimiters) + +(add-hook 'c-mode-hook 'rainbow-delimiters-mode) +(add-hook 'emacs-lisp-mode-hook 'rainbow-delimiters-mode) diff --git a/emacs.d/elisp/rainbow-delimiters.el b/emacs.d/elisp/rainbow-delimiters.el new file mode 100644 index 0000000..19f1aa0 --- /dev/null +++ b/emacs.d/elisp/rainbow-delimiters.el @@ -0,0 +1,392 @@ +;;; rainbow-delimiters.el --- Color nested parentheses, brackets, and braces according to their depth. + +;; Copyright (C) 2010-2011 Jeremy L. Rayman. +;; Author: Jeremy L. Rayman +;; Maintainer: Jeremy L. Rayman +;; Created: 2010-09-02 +;; Version: 1.2.1 +;; Keywords: faces, convenience, lisp, matching, tools +;; EmacsWiki: RainbowDelimiters +;; URL: http://www.emacswiki.org/emacs/rainbow-delimiters.el + +;; 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 . + + +;;; Commentary: + +;; This is a "rainbow parentheses" mode which includes support for +;; parens "()", brackets "[]", and braces "{}". It conveys nesting +;; depth by using a different face for each level. It colors all +;; statements at a given level using the same color - if several +;; statements are all at the same nested depth, they will all be the +;; same color. +;; +;; Great care has been taken to make this mode FAST. You should see no +;; discernible change in scrolling or editing speed while using it, +;; even with delimiter-rich languages like Clojure, Lisp, and Scheme. +;; +;; The one exception is with extremely large nested data structures +;; having hundreds of delimiters; in that case there will be a brief +;; pause to colorize the structure the very first time it is displayed +;; on screen; from then on editing this structure will perform at full +;; speed. +;; +;; Default colors have been chosen with the philosophy that it's +;; better be less intrusive than to be more colorful. Color schemes +;; are always a matter of taste. If you do take the time to design a +;; new color scheme, please post it on the EmacsWiki page! +;; URL: http://www.emacswiki.org/emacs/RainbowDelimiters + +;;; Installation: + +;; 1. Place rainbow-delimiters.el on your emacs load-path. +;; +;; 2. Compile the file (necessary for speed): +;; M-x byte-compile-file +;; +;; 3. Add the following to your dot-emacs/init file: +;; (require 'rainbow-delimiters) +;; +;; 4. Add hooks for modes where you want it enabled, for example: +;; (add-hook 'clojure-mode-hook 'rainbow-delimiters-mode) +;; +;; - To activate rainbow-delimiters mode temporarily in a buffer: +;; M-x rainbow-delimiters-mode + +;;; Customization: + +;; To customize various options, including the color scheme: +;; M-x customize-group rainbow-delimiters +;; +;; color-theme.el users: +;; If you use the color-theme package, you can specify custom colors +;; by adding the appropriate faces to your theme. +;; - Faces take the form of: +;; 'rainbow-delimiters-depth-#-face' with # being the depth. +;; Depth begins at 1, the outermost color. +;; Faces exist for depths 1-12. +;; - The unmatched delimiter face is: +;; 'rainbow-delimiters-unmatched-delimiter-face' + +;;; Change Log: + +;; 1.0 - Initial release. +;; 1.1 - Stop tracking each delimiter's depth independently. +;; This had lead to confusing results when viewing clojure +;; code. Instead, just color based on current nesting inside +;; all delimiters combined. +;; - Added 'all-delimiters' faces to apply a color scheme to +;; all delimiters at once. Other faces inherit from this group. +;; 1.1.1 - Change color scheme to a lighter, more subtle style. +;; 1.1.2: (2011-03-25) +;; - Add an unmatched-delimiter face and correct problem with +;; coloring of text following unmatched closing delims. +;; 1.2: (2011-03-28) +;; - Unify delimiter faces: all delimiter types now use the same depth +;; faces, of form 'rainbow-delimiters-depth-#-face'. +;; 1.2.1: (2011-03-29) +;; - Conform to ELPA conventions. + +;;; TODO: + +;; - Add support for nested tags (XML, HTML) + +;;; Issues: + +;; - Rainbow-delimiters mode does not appear to change the color of +;; delimiters when Org-mode is enabled. + + +;;; Code: + +(eval-when-compile (require 'cl)) + + +;;; Customize interface: + +(defgroup rainbow-delimiters nil + "Color nested parentheses, brackets, and braces according to their depth." + :prefix "rainbow-delimiters-" + :link '(url-link :tag "Website for rainbow-delimiters (EmacsWiki)" + "http://www.emacswiki.org/emacs/RainbowDelimiters") + :group 'applications) + +(defgroup rainbow-delimiters-faces nil + "Faces for each nested depth. Used to color delimiter pairs. + +Depths 1-12 are defined. Depth 1 is the outermost delimiter pair." + :group 'rainbow-delimiters + :link '(custom-group-link "rainbow-delimiters") + :prefix 'rainbow-delimiters-faces-) + + +;;; Faces: + +;; Unmatched delimiter face: +(defface rainbow-delimiters-unmatched-face + '((t (:foreground "#88090B"))) + "Face to color unmatched closing delimiters with." + :group 'rainbow-delimiters + :group 'rainbow-delimiters-faces) + + +;; NOTE: The use of repetitious definitions for depth faces is temporary. +;; Once the emacs 24 color theme support comes in, this will be reevaluated. + +;; Faces for colorizing delimiters at each level: +(defface rainbow-delimiters-depth-1-face + '((t (:foreground "grey55"))) + "Rainbow-delimiters nested delimiter face, depth 1 - the outermost pair." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-2-face + '((t (:foreground "#93a8c6"))) + "Rainbow-delimiters nested delimiter face, depth 2." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-3-face + '((t (:foreground "#b0b1a3"))) + "Rainbow-delimiters nested delimiter face, depth 3." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-4-face + '((t (:foreground "#97b098"))) + "Rainbow-delimiters nested delimiter face, depth 4." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-5-face + '((t (:foreground "#aebed8"))) + "Rainbow-delimiters nested delimiter face, depth 5." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-6-face + '((t (:foreground "#b0b0b3"))) + "Rainbow-delimiters nested delimiter face, depth 6." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-7-face + '((t (:foreground "#90a890"))) + "Rainbow-delimiters nested delimiter face, depth 7." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-8-face + '((t (:foreground "#a2b6da"))) + "Rainbow-delimiters nested delimiter face, depth 8." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-9-face + '((t (:foreground "#9cb6ad"))) + "Rainbow-delimiters nested delimiter face, depth 9." + :group 'rainbow-delimiters-faces) + +;; Emacs doesn't sort face names by number correctly above 1-9; trick it into +;; proper sorting by prepending a _ before the faces with depths over 10. +(defface rainbow-delimiters-depth-_10-face + '((t (:foreground "#83787e"))) + "Rainbow-delimiters nested delimiter face, depth 10." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-_11-face + '((t (:foreground "#e1ddca"))) + "Rainbow-delimiters nested delimiter face, depth 11." + :group 'rainbow-delimiters-faces) + +(defface rainbow-delimiters-depth-_12-face + '((t (:foreground "#e0c7c7"))) + "Rainbow-delimiters nested delimiter face, depth 12." + :group 'rainbow-delimiters-faces) + +;; Variable aliases for faces 10+: +;; We prepend an underline to numbers 10+ to force customize to sort correctly. +;; Here we define aliases without the underline for use everywhere else. +(put 'rainbow-delimiters-depth-10-face + 'face-alias + 'rainbow-delimiters-depth-_10-face) +(put 'rainbow-delimiters-depth-11-face + 'face-alias + 'rainbow-delimiters-depth-_11-face) +(put 'rainbow-delimiters-depth-12-face + 'face-alias + 'rainbow-delimiters-depth-_12-face) + + +;;; Face utility functions + +;; inlining this function for speed: +;; see: http://www.gnu.org/s/emacs/manual/html_node/elisp/Compilation-Tips.html +;; this will cause problems with debugging. To debug, change defsubst -> defun. +(defsubst rainbow-delimiters-depth-face (depth) + "Return face-name 'rainbow-delimiters-depth-DEPTH-face' as a string. + +DEPTH is the number of nested levels deep for the delimiter being colorized. + +Returns a face namestring the of form 'rainbow-delimiters-depth-DEPTH-face', +e.g. 'rainbow-delimiters-depth-1-face'." + (concat "rainbow-delimiters-depth-" (number-to-string depth) "-face")) + + +;;; Nesting level + +;; syntax-table: used with parse-partial-sexp for determining current depth. +(defvar rainbow-delimiters-delim-syntax-table + (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table))) + (modify-syntax-entry ?\( "() " table) + (modify-syntax-entry ?\) ")( " table) + (modify-syntax-entry ?\[ "(]" table) + (modify-syntax-entry ?\] ")[" table) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + table) + "Syntax table for recognizing all supported delimiter types.") + +(defun rainbow-delimiters-depth (point) + "Return # of nested levels of parens, brackets, braces POINT is inside of." + (save-excursion + (beginning-of-defun) + (let ((depth + (with-syntax-table rainbow-delimiters-delim-syntax-table + (car (parse-partial-sexp (point) point))))) + (if (>= depth 0) + depth + 0)))) ; ignore negative depths created by unmatched closing parens. + + +;;; Text properties + +;; inlining this function for speed: +;; see: http://www.gnu.org/s/emacs/manual/html_node/elisp/Compilation-Tips.html +;; this will cause problems with debugging. To debug, change defsubst -> defun. +(defsubst rainbow-delimiters-propertize-delimiter (point depth) + "Colorize delimiter at POINT according to DEPTH. + +POINT is the point of character to propertize. +DEPTH is the nested delimiter depth at POINT, which determines the face to use. + +Sets text properties: +`font-lock-face' to the corresponding delimiter face. +`rear-nonsticky' to prevent color from bleeding into subsequent characters typed by the user." + (with-silent-modifications + (let ((delim-face (if (<= depth 0) + "rainbow-delimiters-unmatched-face" + (rainbow-delimiters-depth-face depth)))) + ;; (when (eq depth -1) (message "Unmatched delimiter at char %s." point)) + (add-text-properties point (1+ point) + `(font-lock-face ,delim-face + rear-nonsticky t))))) + + +(defun rainbow-delimiters-unpropertize-delimiter (point) + "Remove text properties set by rainbow-delimiters mode from char at POINT." + (remove-text-properties point (1+ point) + '(font-lock-face nil + rear-nonsticky nil))) + + +(defun rainbow-delimiters-char-ineligible-p (point) + "Return t if char at POINT should be skipped, e.g. if inside a comment. + +Returns t if char at point meets one of the following conditions: +- Inside a string. +- Inside a comment. +- Is an escaped char, e.g. ?\)" + (let ((parse-state (save-excursion + (beginning-of-defun) + ;; (point) is at beg-of-defun; point is the char location + (parse-partial-sexp (point) point)))) + (or + (nth 3 parse-state) ; inside string? + (nth 4 parse-state) ; inside comment? + (and (eq (char-before point) ?\\) ; escaped char, e.g. ?\) - not counted + (and (not (eq (char-before (1- point)) ?\\)) ; special-case: ignore ?\\ + (eq (char-before (1- point)) ?\?)))))) +;; standard char read syntax '?)' is not tested for because emacs manual states +;; that punctuation such as delimiters should _always_ use escaped '?\)' form. + + +;;; JIT-Lock functionality + +;; Used to skip delimiter-by-delimiter `rainbow-delimiters-propertize-region'. +(defvar rainbow-delimiters-delim-regex "\\(\(\\|\)\\|\\[\\|\\]\\|\{\\|\}\\)" + "Regex matching all opening and closing delimiters we intend to colorize.") + +;; main function called by jit-lock: +(defun rainbow-delimiters-propertize-region (start end) + "Colorize delimiters in region between START and END. + +Used by jit-lock for dynamic highlighting." + (save-excursion + (goto-char start) + ;; START can be anywhere in buffer; begin depth counts from values at START. + (let ((depth (rainbow-delimiters-depth start))) + (while (and (< (point) end) + (re-search-forward rainbow-delimiters-delim-regex end t)) + (backward-char) ; re-search-forward places point after delim; go back. + (unless (rainbow-delimiters-char-ineligible-p (point)) + (let ((delim (char-after (point)))) + (cond ((eq ?\( delim) ; ( + (setq depth (1+ depth)) + (rainbow-delimiters-propertize-delimiter (point) + depth)) + ((eq ?\) delim) ; ) + (rainbow-delimiters-propertize-delimiter (point) + depth) + (setq depth (or (and (<= depth 0) 0) ; unmatched paren + (1- depth)))) + ((eq ?\[ delim) ; [ + (setq depth (1+ depth)) + (rainbow-delimiters-propertize-delimiter (point) + depth)) + ((eq ?\] delim) ; ] + (rainbow-delimiters-propertize-delimiter (point) + depth) + (setq depth (or (and (<= depth 0) 0) ; unmatched bracket + (1- depth)))) + ((eq ?\{ delim) ; { + (setq depth (1+ depth)) + (rainbow-delimiters-propertize-delimiter (point) + depth)) + ((eq ?\} delim) ; } + (rainbow-delimiters-propertize-delimiter (point) + depth) + (setq depth (or (and (<= depth 0) 0) ; unmatched brace + (1- depth))))))) + ;; move past delimiter so re-search-forward doesn't pick it up again + (forward-char))))) + +(defun rainbow-delimiters-unpropertize-region (start end) + "Remove mode faces from delimiters in region between START and END." + (save-excursion + (goto-char start) + (while (and (< (point) end) + (re-search-forward rainbow-delimiters-delim-regex end t)) + ;; re-search-forward places point 1 further than the delim matched: + (rainbow-delimiters-unpropertize-delimiter (1- (point)))))) + + +;;; Minor mode: + +;;;###autoload +(define-minor-mode rainbow-delimiters-mode + "Color nested parentheses, brackets, and braces according to their depth." + nil "" nil ; No modeline lighter - it's already obvious when the mode is on. + (if (not rainbow-delimiters-mode) + (progn + (jit-lock-unregister 'rainbow-delimiters-propertize-region) + (rainbow-delimiters-unpropertize-region (point-min) (1- (point-max)))) + (jit-lock-register 'rainbow-delimiters-propertize-region t))) + + +(provide 'rainbow-delimiters) + +;;; rainbow-delimiters.el ends here. -- cgit v1.2.3-54-g00ecf