diff options
| -rw-r--r-- | lisp/textmodes/less-css-mode.el | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el new file mode 100644 index 00000000000..8a981d67b93 --- /dev/null +++ b/lisp/textmodes/less-css-mode.el | |||
| @@ -0,0 +1,258 @@ | |||
| 1 | ;;; less-css-mode.el --- Major mode for editing LESS CSS files (lesscss.org) | ||
| 2 | ;; | ||
| 3 | ;; Copyright (C) 2011-2014 Steve Purcell | ||
| 4 | ;; | ||
| 5 | ;; Author: Steve Purcell <steve@sanityinc.com> | ||
| 6 | ;; URL: https://github.com/purcell/less-css-mode | ||
| 7 | ;; Keywords: less css mode | ||
| 8 | ;; Version: DEV | ||
| 9 | ;; | ||
| 10 | ;; This program is free software; you can redistribute it and/or | ||
| 11 | ;; modify it under the terms of the GNU General Public License as | ||
| 12 | ;; published by the Free Software Foundation; either version 2 of | ||
| 13 | ;; the License, or (at your option) any later version. | ||
| 14 | ;; | ||
| 15 | ;; This program is distributed in the hope that it will be | ||
| 16 | ;; useful, but WITHOUT ANY WARRANTY; without even the implied | ||
| 17 | ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | ||
| 18 | ;; PURPOSE. See the GNU General Public License for more details. | ||
| 19 | ;; | ||
| 20 | ;;; Commentary: | ||
| 21 | ;; | ||
| 22 | ;; This mode provides syntax highlighting for LESS CSS files, plus | ||
| 23 | ;; optional support for compilation of .less files to .css files at | ||
| 24 | ;; the time they are saved: use `less-css-compile-at-save' to enable | ||
| 25 | ;; this. | ||
| 26 | ;; | ||
| 27 | ;; Command line utility "lessc" is required if setting | ||
| 28 | ;; `less-css-compile-at-save' to t. To install "lessc" using the | ||
| 29 | ;; Node.js package manager, run "npm install less" | ||
| 30 | ;; | ||
| 31 | ;; Also make sure the "lessc" executable is in Emacs' PATH, example: | ||
| 32 | ;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin") exec-path)) | ||
| 33 | ;; or customize `less-css-lessc-command' to point to your "lessc" executable. | ||
| 34 | ;; | ||
| 35 | ;; We target lessc >= 1.4.0, and thus use the `--no-color' flag by | ||
| 36 | ;; default. You may want to adjust `less-css-lessc-options' for | ||
| 37 | ;; compatibility with older versions. | ||
| 38 | ;; | ||
| 39 | ;; `less-css-mode' is derived from `css-mode', and indentation of | ||
| 40 | ;; nested blocks may not work correctly with versions of `css-mode' | ||
| 41 | ;; other than that bundled with recent Emacs. | ||
| 42 | ;; | ||
| 43 | ;; You can specify per-file values for `less-css-compile-at-save', | ||
| 44 | ;; `less-css-output-file-name' or `less-css-output-directory' using a | ||
| 45 | ;; variables header at the top of your .less file, e.g.: | ||
| 46 | ;; | ||
| 47 | ;; // -*- less-css-compile-at-save: t; less-css-output-directory: "../css" -*- | ||
| 48 | ;; | ||
| 49 | ;; Alternatively, you can use directory local variables to set the | ||
| 50 | ;; default value of `less-css-output-directory' for your project. | ||
| 51 | ;; | ||
| 52 | ;; In the case of files which are included in other .less files, you | ||
| 53 | ;; may want to trigger the compilation of a "master" .less file on | ||
| 54 | ;; save: you can accomplish this with `less-css-input-file-name', | ||
| 55 | ;; which is probably best set using directory local variables. | ||
| 56 | ;; | ||
| 57 | ;; If you don't need CSS output but would like to be warned of any | ||
| 58 | ;; syntax errors in your .less source, consider using `flymake-less': | ||
| 59 | ;; https://github.com/purcell/flymake-less | ||
| 60 | ;; | ||
| 61 | ;;; Credits | ||
| 62 | ;; | ||
| 63 | ;; The original code for this mode was, in large part, written using | ||
| 64 | ;; Anton Johansson's scss-mode as a template -- thanks Anton! | ||
| 65 | ;; https://github.com/antonj | ||
| 66 | ;; | ||
| 67 | ;;; Code: | ||
| 68 | |||
| 69 | (require 'derived) | ||
| 70 | (require 'compile) | ||
| 71 | |||
| 72 | ;; There are at least three css-mode.el implementations, but we need | ||
| 73 | ;; the right one in order to work as expected, not the versions by | ||
| 74 | ;; Landström or Garshol | ||
| 75 | |||
| 76 | (require 'css-mode) | ||
| 77 | (unless (or (boundp 'css-navigation-syntax-table) | ||
| 78 | (functionp 'css-smie-rules)) | ||
| 79 | (error "Wrong css-mode.el: please use the version by Stefan Monnier, bundled with Emacs >= 23")) | ||
| 80 | |||
| 81 | (defgroup less-css nil | ||
| 82 | "Less-css mode" | ||
| 83 | :prefix "less-css-" | ||
| 84 | :group 'css) | ||
| 85 | |||
| 86 | ;;;###autoload | ||
| 87 | (defcustom less-css-lessc-command "lessc" | ||
| 88 | "Command used to compile LESS files. | ||
| 89 | Should be lessc or the complete path to your lessc executable, | ||
| 90 | e.g.: \"~/.gem/ruby/1.8/bin/lessc\"" | ||
| 91 | :type 'file | ||
| 92 | :group 'less-css | ||
| 93 | :safe 'stringp) | ||
| 94 | |||
| 95 | ;;;###autoload | ||
| 96 | (defcustom less-css-compile-at-save nil | ||
| 97 | "If non-nil, the LESS buffers will be compiled to CSS after each save." | ||
| 98 | :type 'boolean | ||
| 99 | :group 'less-css | ||
| 100 | :safe 'booleanp) | ||
| 101 | |||
| 102 | ;;;###autoload | ||
| 103 | (defcustom less-css-lessc-options '("--no-color") | ||
| 104 | "Command line options for less executable. | ||
| 105 | |||
| 106 | Use \"-x\" to minify output." | ||
| 107 | :type '(repeat string) | ||
| 108 | :group 'less-css | ||
| 109 | :safe t) | ||
| 110 | |||
| 111 | ;;;###autoload | ||
| 112 | (defcustom less-css-output-directory nil | ||
| 113 | "Directory in which to save CSS, or nil to use the LESS file's directory. | ||
| 114 | |||
| 115 | This path is expanded relative to the directory of the LESS file | ||
| 116 | using `expand-file-name', so both relative and absolute paths | ||
| 117 | will work as expected." | ||
| 118 | :type 'directory | ||
| 119 | :group 'less-css | ||
| 120 | :safe 'stringp) | ||
| 121 | |||
| 122 | ;;;###autoload | ||
| 123 | (defcustom less-css-output-file-name nil | ||
| 124 | "File name in which to save CSS, or nil to use <name>.css for <name>.less. | ||
| 125 | |||
| 126 | This can be also be set to a full path, or a relative path. If | ||
| 127 | the path is relative, it will be relative to the value of | ||
| 128 | `less-css-output-dir', if set, or the current directory by | ||
| 129 | default." | ||
| 130 | :type 'file | ||
| 131 | :group 'less-css | ||
| 132 | :safe 'stringp) | ||
| 133 | (make-variable-buffer-local 'less-css-output-file-name) | ||
| 134 | |||
| 135 | ;;;###autoload | ||
| 136 | (defcustom less-css-input-file-name nil | ||
| 137 | "File name which will be compiled to CSS. | ||
| 138 | |||
| 139 | When the current buffer is saved `less-css-input-file-name' file | ||
| 140 | will be compiled to css instead of the current file. | ||
| 141 | |||
| 142 | Set this in order to trigger compilation of a \"master\" .less | ||
| 143 | file which includes the current file. The best way to set this | ||
| 144 | variable in most cases is likely to be via directory local | ||
| 145 | variables. | ||
| 146 | |||
| 147 | This can be also be set to a full path, or a relative path. If | ||
| 148 | the path is relative, it will be relative to the the current directory by | ||
| 149 | default." | ||
| 150 | :type 'file | ||
| 151 | :group 'less-css | ||
| 152 | :safe 'stringp) | ||
| 153 | (make-variable-buffer-local 'less-css-input-file-name) | ||
| 154 | |||
| 155 | (defconst less-css-default-error-regex | ||
| 156 | "^\\(?:\e\\[31m\\)?\\([^\e\n]*\\|FileError:.*\n\\)\\(?:\e\\[39m\e\\[31m\\)? in \\(?:\e\\[39m\\)?\\([^ \r\n\t\e]+\\)\\(?:\e\\[90m\\)?\\(?::\\| on line \\)\\([0-9]+\\)\\(?::\\|, column \\)\\([0-9]+\\):?\\(?:\e\\[39m\\)?") | ||
| 157 | |||
| 158 | |||
| 159 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 160 | ;; Compilation to CSS | ||
| 161 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 162 | |||
| 163 | (add-to-list 'compilation-error-regexp-alist-alist | ||
| 164 | (list 'less-css less-css-default-error-regex 2 3 4 nil 1)) | ||
| 165 | (add-to-list 'compilation-error-regexp-alist 'less-css) | ||
| 166 | |||
| 167 | |||
| 168 | (defun less-css-compile-maybe () | ||
| 169 | "Run `less-css-compile' if `less-css-compile-at-save' is non-nil." | ||
| 170 | (if less-css-compile-at-save | ||
| 171 | (less-css-compile))) | ||
| 172 | |||
| 173 | (defun less-css--output-path () | ||
| 174 | "Calculate the path for the compiled CSS file created by `less-css-compile'." | ||
| 175 | (expand-file-name (or less-css-output-file-name | ||
| 176 | (concat (file-name-nondirectory (file-name-sans-extension buffer-file-name)) ".css")) | ||
| 177 | (or less-css-output-directory default-directory))) | ||
| 178 | |||
| 179 | (defun less-css--maybe-shell-quote-command (command) | ||
| 180 | "Selectively shell-quote COMMAND appropriately for `system-type'." | ||
| 181 | (funcall (if (eq system-type 'windows-nt) | ||
| 182 | 'identity | ||
| 183 | 'shell-quote-argument) command)) | ||
| 184 | |||
| 185 | ;;;###autoload | ||
| 186 | (defun less-css-compile () | ||
| 187 | "Compiles the current buffer to css using `less-css-lessc-command'." | ||
| 188 | (interactive) | ||
| 189 | (message "Compiling less to css") | ||
| 190 | (let ((compilation-buffer-name-function (lambda (mode-name) "*less-css-compilation*"))) | ||
| 191 | (save-window-excursion | ||
| 192 | (with-current-buffer | ||
| 193 | (compile | ||
| 194 | (mapconcat 'identity | ||
| 195 | (append (list (less-css--maybe-shell-quote-command less-css-lessc-command)) | ||
| 196 | (mapcar 'shell-quote-argument less-css-lessc-options) | ||
| 197 | (list (shell-quote-argument | ||
| 198 | (or less-css-input-file-name buffer-file-name)) | ||
| 199 | (shell-quote-argument (less-css--output-path)))) | ||
| 200 | " ")) | ||
| 201 | (add-hook 'compilation-finish-functions | ||
| 202 | (lambda (buf msg) | ||
| 203 | (unless (string-match-p "^finished" msg) | ||
| 204 | (display-buffer buf))) | ||
| 205 | nil | ||
| 206 | t))))) | ||
| 207 | |||
| 208 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 209 | ;; Minor mode | ||
| 210 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 211 | |||
| 212 | ;; TODO: interpolation ("@{val}"), escaped values (~"..."), JS eval (~`...`), custom faces | ||
| 213 | (defconst less-css-font-lock-keywords | ||
| 214 | '(;; Variables | ||
| 215 | ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face) | ||
| 216 | ("&" . font-lock-preprocessor-face) | ||
| 217 | ;; Mixins | ||
| 218 | ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face))) | ||
| 219 | ) | ||
| 220 | |||
| 221 | ;;;###autoload | ||
| 222 | (define-derived-mode less-css-mode css-mode "LESS" | ||
| 223 | "Major mode for editing LESS files, http://lesscss.org/ | ||
| 224 | Special commands: | ||
| 225 | \\{less-css-mode-map}" | ||
| 226 | (font-lock-add-keywords nil less-css-font-lock-keywords) | ||
| 227 | ;; cpp-style comments | ||
| 228 | (modify-syntax-entry ?/ ". 124b" less-css-mode-syntax-table) | ||
| 229 | (modify-syntax-entry ?* ". 23" less-css-mode-syntax-table) | ||
| 230 | (modify-syntax-entry ?\n "> b" less-css-mode-syntax-table) | ||
| 231 | ;; Special chars that sometimes come at the beginning of words. | ||
| 232 | (modify-syntax-entry ?. "'" less-css-mode-syntax-table) | ||
| 233 | |||
| 234 | (set (make-local-variable 'comment-start) "//") | ||
| 235 | (set (make-local-variable 'comment-end) "") | ||
| 236 | (set (make-local-variable 'indent-line-function) 'less-css-indent-line) | ||
| 237 | (when (functionp 'css-smie-rules) | ||
| 238 | (smie-setup css-smie-grammar #'css-smie-rules | ||
| 239 | :forward-token #'css-smie--forward-token | ||
| 240 | :backward-token #'css-smie--backward-token)) | ||
| 241 | |||
| 242 | (add-hook 'after-save-hook 'less-css-compile-maybe nil t)) | ||
| 243 | |||
| 244 | (define-key less-css-mode-map "\C-c\C-c" 'less-css-compile) | ||
| 245 | |||
| 246 | (defun less-css-indent-line () | ||
| 247 | "Indent current line according to LESS CSS indentation rules." | ||
| 248 | (let ((css-navigation-syntax-table less-css-mode-syntax-table)) | ||
| 249 | (if (fboundp 'css-indent-line) | ||
| 250 | (css-indent-line) | ||
| 251 | (smie-indent-line)))) | ||
| 252 | |||
| 253 | ;;;###autoload | ||
| 254 | (add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode)) | ||
| 255 | |||
| 256 | |||
| 257 | (provide 'less-css-mode) | ||
| 258 | ;;; less-css-mode.el ends here | ||