aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/textmodes/less-css-mode.el258
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.
89Should 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
106Use \"-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
115This path is expanded relative to the directory of the LESS file
116using `expand-file-name', so both relative and absolute paths
117will 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
126This can be also be set to a full path, or a relative path. If
127the path is relative, it will be relative to the value of
128`less-css-output-dir', if set, or the current directory by
129default."
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
139When the current buffer is saved `less-css-input-file-name' file
140will be compiled to css instead of the current file.
141
142Set this in order to trigger compilation of a \"master\" .less
143file which includes the current file. The best way to set this
144variable in most cases is likely to be via directory local
145variables.
146
147This can be also be set to a full path, or a relative path. If
148the path is relative, it will be relative to the the current directory by
149default."
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/
224Special 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