unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#27881: New major mode: Less mode
@ 2017-07-30 17:53 Simen Heggestøyl
  2017-07-30 20:59 ` Stefan Monnier
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Simen Heggestøyl @ 2017-07-30 17:53 UTC (permalink / raw)
  To: 27881; +Cc: Steve Purcell, Tom Tromey, Stefan Monnier, Dmitry Gutov

[-- Attachment #1: Type: text/plain, Size: 1109 bytes --]

Hi.

Steve Purcell has developed a very nice major mode for editing Less
files (a minor variant of CSS). Seeing that Emacs' built-in CSS mode
got extended to support SCSS (another minor CSS variant) three years
ago, I contacted Steve and asked if he'd like Less mode to become a
part of Emacs as well, which he agreed to.

I've attached two patches which merge Steve's Less mode into
css-mode.el. The first patch is a verbatim copy of Steve's code, the
second one contains minor cleanups made by me.

The biggest change I've made is to rename the mode from "Less CSS
mode" to just "Less mode". I think the renaming has two advantages:
Aligning its name with "SCSS mode", and making the mode's commands,
variables and function names shorter. The downside is that existing
users of Less mode will have to update their config files. Maybe
aliasing `less-css-mode' to `less-mode' could help some in that
regard. What do you think of it?

If this looks okay, I'll complete the patches (fix the commit messages
and make a NEWS entry), install them, and start maintaining Less mode
as part of css-mode.el.

-- Simen

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.1: 0001-WIP-New-major-mode-Less-mode-a-minor-variant-of-CSS.patch --]
[-- Type: text/x-patch, Size: 9584 bytes --]

From 343b15f8c95ccbd935b5b715b8127d16bd4d7e84 Mon Sep 17 00:00:00 2001
From: Steve Purcell <steve@sanityinc.com>
Date: Mon, 12 Jun 2017 22:06:46 +0200
Subject: [PATCH 1/2] WIP: New major mode: Less mode (a minor variant of CSS)

---
 lisp/textmodes/css-mode.el | 228 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 227 insertions(+), 1 deletion(-)

diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 19cb7b4fea..c1ed44b984 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -32,9 +32,10 @@
 
 ;;; Code:
 
-(require 'eww)
 (require 'cl-lib)
 (require 'color)
+(require 'compile)
+(require 'eww)
 (require 'seq)
 (require 'sgml-mode)
 (require 'smie)
@@ -1558,6 +1559,231 @@ scss-mode
 
 \f
 
+;;; Less mode
+;;
+;; This mode provides syntax highlighting for LESS CSS files, plus
+;; optional support for compilation of .less files to .css files at
+;; the time they are saved: use `less-css-compile-at-save' to enable
+;; this.
+;;
+;; Command line utility "lessc" is required if setting
+;; `less-css-compile-at-save' to t.  To install "lessc" using the
+;; Node.js package manager, run "npm install less"
+;;
+;; Also make sure the "lessc" executable is in Emacs' PATH, example:
+;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin") exec-path))
+;; or customize `less-css-lessc-command' to point to your "lessc"
+;; executable.
+;;
+;; We target lessc >= 1.4.0, and thus use the `--no-color' flag by
+;; default.  You may want to adjust `less-css-lessc-options' for
+;; compatibility with older versions.
+;;
+;; `less-css-mode' is derived from `css-mode', and indentation of
+;; nested blocks may not work correctly with versions of `css-mode'
+;; other than that bundled with recent Emacs.
+;;
+;; You can specify per-file values for `less-css-compile-at-save',
+;; `less-css-output-file-name' or `less-css-output-directory' using a
+;; variables header at the top of your .less file, e.g.:
+;;
+;; // -*- less-css-compile-at-save: t; less-css-output-directory: "../css" -*-
+;;
+;; Alternatively, you can use directory local variables to set the
+;; default value of `less-css-output-directory' for your project.
+;;
+;; In the case of files which are included in other .less files, you
+;; may want to trigger the compilation of a "master" .less file on
+;; save: you can accomplish this with `less-css-input-file-name',
+;; which is probably best set using directory local variables.
+;;
+;; If you don't need CSS output but would like to be warned of any
+;; syntax errors in your .less source, consider using `flymake-less':
+;; https://github.com/purcell/flymake-less
+;;
+;;; Credits
+;;
+;; The original code for this mode was, in large part, written using
+;; Anton Johansson's scss-mode as a template -- thanks Anton!
+;; https://github.com/antonj
+
+(defgroup less-css nil
+  "Less-css mode"
+  :prefix "less-css-"
+  :group 'css)
+
+;;;###autoload
+(defcustom less-css-lessc-command "lessc"
+  "Command used to compile LESS files.
+Should be lessc or the complete path to your lessc executable,
+  e.g.: \"~/.gem/ruby/1.8/bin/lessc\""
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+
+;;;###autoload
+(defcustom less-css-compile-at-save nil
+  "If non-nil, the LESS buffers will be compiled to CSS after each save."
+  :type 'boolean
+  :group 'less-css
+  :safe 'booleanp)
+
+;;;###autoload
+(defcustom less-css-lessc-options '("--no-color")
+  "Command line options for less executable.
+
+Use \"-x\" to minify output."
+  :type '(repeat string)
+  :group 'less-css
+  :safe t)
+
+;;;###autoload
+(defcustom less-css-output-directory nil
+  "Directory in which to save CSS, or nil to use the LESS file's directory.
+
+This path is expanded relative to the directory of the LESS file
+using `expand-file-name', so both relative and absolute paths
+will work as expected."
+  :type 'directory
+  :group 'less-css
+  :safe 'stringp)
+
+;;;###autoload
+(defcustom less-css-output-file-name nil
+  "File name in which to save CSS, or nil to use <name>.css for <name>.less.
+
+This can be also be set to a full path, or a relative path.  If
+the path is relative, it will be relative to the value of
+`less-css-output-dir', if set, or the current directory by
+default."
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+(make-variable-buffer-local 'less-css-output-file-name)
+
+;;;###autoload
+(defcustom less-css-input-file-name nil
+  "File name which will be compiled to CSS.
+
+When the current buffer is saved `less-css-input-file-name' file
+will be compiled to css instead of the current file.
+
+Set this in order to trigger compilation of a \"master\" .less
+file which includes the current file.  The best way to set this
+variable in most cases is likely to be via directory local
+variables.
+
+This can be also be set to a full path, or a relative path. If
+the path is relative, it will be relative to the the current directory by
+default."
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+(make-variable-buffer-local 'less-css-input-file-name)
+
+(defconst less-css-default-error-regex
+  "^\\(?:\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\\)?")
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Compilation to CSS
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(add-to-list 'compilation-error-regexp-alist-alist
+             (list 'less-css less-css-default-error-regex 2 3 4 nil 1))
+(add-to-list 'compilation-error-regexp-alist 'less-css)
+
+
+(defun less-css-compile-maybe ()
+  "Run `less-css-compile' if `less-css-compile-at-save' is non-nil."
+  (if less-css-compile-at-save
+      (less-css-compile)))
+
+(defun less-css--output-path ()
+  "Calculate the path for the compiled CSS file created by `less-css-compile'."
+  (expand-file-name (or less-css-output-file-name
+                        (concat (file-name-nondirectory (file-name-sans-extension buffer-file-name)) ".css"))
+                    (or less-css-output-directory default-directory)))
+
+(defun less-css--maybe-shell-quote-command (command)
+  "Selectively shell-quote COMMAND appropriately for `system-type'."
+  (funcall (if (eq system-type 'windows-nt)
+               'identity
+             'shell-quote-argument) command))
+
+;;;###autoload
+(defun less-css-compile ()
+  "Compiles the current buffer to css using `less-css-lessc-command'."
+  (interactive)
+  (message "Compiling less to css")
+  (let ((compilation-buffer-name-function (lambda (mode-name) "*less-css-compilation*")))
+    (save-window-excursion
+      (with-current-buffer
+          (compile
+           (mapconcat 'identity
+                      (append (list (less-css--maybe-shell-quote-command less-css-lessc-command))
+                              (mapcar 'shell-quote-argument less-css-lessc-options)
+                              (list (shell-quote-argument
+                                     (or less-css-input-file-name buffer-file-name))
+                                    (shell-quote-argument (less-css--output-path))))
+                      " "))
+        (add-hook 'compilation-finish-functions
+                  (lambda (buf msg)
+                    (unless (string-match-p "^finished" msg)
+                      (display-buffer buf)))
+                  nil
+                  t)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Minor mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: interpolation ("@{val}"), escaped values (~"..."), JS eval (~`...`), custom faces
+(defconst less-css-font-lock-keywords
+  '(;; Variables
+    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
+    ("&" . font-lock-preprocessor-face)
+    ;; Mixins
+    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
+  )
+
+;;;###autoload
+(define-derived-mode less-css-mode css-mode "LESS"
+  "Major mode for editing LESS files, http://lesscss.org/
+Special commands:
+\\{less-css-mode-map}"
+  (font-lock-add-keywords nil less-css-font-lock-keywords)
+  ;; cpp-style comments
+  (modify-syntax-entry ?/ ". 124b" less-css-mode-syntax-table)
+  (modify-syntax-entry ?* ". 23" less-css-mode-syntax-table)
+  (modify-syntax-entry ?\n "> b" less-css-mode-syntax-table)
+  ;; Special chars that sometimes come at the beginning of words.
+  (modify-syntax-entry ?. "'" less-css-mode-syntax-table)
+
+  (set (make-local-variable 'comment-start) "//")
+  (set (make-local-variable 'comment-end) "")
+  (set (make-local-variable 'indent-line-function) 'less-css-indent-line)
+  (when (functionp 'css-smie-rules)
+    (smie-setup css-smie-grammar #'css-smie-rules
+                :forward-token #'css-smie--forward-token
+                :backward-token #'css-smie--backward-token))
+
+  (add-hook 'after-save-hook 'less-css-compile-maybe nil t))
+
+(define-key less-css-mode-map "\C-c\C-c" 'less-css-compile)
+
+(defun less-css-indent-line ()
+  "Indent current line according to LESS CSS indentation rules."
+  (let ((css-navigation-syntax-table less-css-mode-syntax-table))
+    (if (fboundp 'css-indent-line)
+        (css-indent-line)
+      (smie-indent-line))))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
+
+\f
+
 (defvar css--mdn-lookup-history nil)
 
 (defcustom css-lookup-url-format
-- 
2.13.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.2: 0002-WIP-Fixes-and-tweaks-for-the-new-Less-mode.patch --]
[-- Type: text/x-patch, Size: 13845 bytes --]

From 30d4e2a4b5bb80765e2efa0ef9eba547cc079f4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Mon, 12 Jun 2017 22:15:51 +0200
Subject: [PATCH 2/2] WIP: Fixes and tweaks for the new Less mode

---
 lisp/textmodes/css-mode.el | 232 +++++++++++++++++++++------------------------
 1 file changed, 109 insertions(+), 123 deletions(-)

diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index c1ed44b984..67b786622b 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -1561,41 +1561,42 @@ scss-mode
 
 ;;; Less mode
 ;;
+;; Originally authored by Steve Purcell.
+;;
 ;; This mode provides syntax highlighting for LESS CSS files, plus
 ;; optional support for compilation of .less files to .css files at
-;; the time they are saved: use `less-css-compile-at-save' to enable
-;; this.
+;; the time they are saved: use `less-compile-at-save' to enable this.
 ;;
 ;; Command line utility "lessc" is required if setting
-;; `less-css-compile-at-save' to t.  To install "lessc" using the
-;; Node.js package manager, run "npm install less"
+;; `less-compile-at-save' to t.  To install "lessc" using the Node.js
+;; package manager, run "npm install less".
 ;;
 ;; Also make sure the "lessc" executable is in Emacs' PATH, example:
-;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin") exec-path))
-;; or customize `less-css-lessc-command' to point to your "lessc"
-;; executable.
+;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin")
+;; exec-path)) or customize `less-lessc-command' to point to your
+;; "lessc" executable.
 ;;
 ;; We target lessc >= 1.4.0, and thus use the `--no-color' flag by
-;; default.  You may want to adjust `less-css-lessc-options' for
+;; default.  You may want to adjust `less-lessc-options' for
 ;; compatibility with older versions.
 ;;
-;; `less-css-mode' is derived from `css-mode', and indentation of
-;; nested blocks may not work correctly with versions of `css-mode'
-;; other than that bundled with recent Emacs.
+;; `less-mode' is derived from `css-mode', and indentation of nested
+;; blocks may not work correctly with versions of `css-mode' other
+;; than that bundled with recent Emacs.
 ;;
-;; You can specify per-file values for `less-css-compile-at-save',
-;; `less-css-output-file-name' or `less-css-output-directory' using a
+;; You can specify per-file values for `less-compile-at-save',
+;; `less-output-file-name' or `less-output-directory' using a
 ;; variables header at the top of your .less file, e.g.:
 ;;
-;; // -*- less-css-compile-at-save: t; less-css-output-directory: "../css" -*-
+;; // -*- less-compile-at-save: t; less-output-directory: "../css" -*-
 ;;
 ;; Alternatively, you can use directory local variables to set the
-;; default value of `less-css-output-directory' for your project.
+;; default value of `less-output-directory' for your project.
 ;;
 ;; In the case of files which are included in other .less files, you
 ;; may want to trigger the compilation of a "master" .less file on
-;; save: you can accomplish this with `less-css-input-file-name',
-;; which is probably best set using directory local variables.
+;; save: you can accomplish this with `less-input-file-name', which is
+;; probably best set using directory local variables.
 ;;
 ;; If you don't need CSS output but would like to be warned of any
 ;; syntax errors in your .less source, consider using `flymake-less':
@@ -1607,66 +1608,61 @@ scss-mode
 ;; Anton Johansson's scss-mode as a template -- thanks Anton!
 ;; https://github.com/antonj
 
-(defgroup less-css nil
-  "Less-css mode"
-  :prefix "less-css-"
+(defgroup less nil
+  "Less CSS mode"
+  :prefix "less-"
   :group 'css)
 
 ;;;###autoload
-(defcustom less-css-lessc-command "lessc"
-  "Command used to compile LESS files.
-Should be lessc or the complete path to your lessc executable,
-  e.g.: \"~/.gem/ruby/1.8/bin/lessc\""
+(defcustom less-lessc-command "lessc"
+  "Command used to compile Less files.
+Should be \"lessc\" or the complete path to your lessc executable,
+e.g.: \"~/.gem/ruby/1.8/bin/lessc\"."
   :type 'file
-  :group 'less-css
+  :group 'less
   :safe 'stringp)
 
 ;;;###autoload
-(defcustom less-css-compile-at-save nil
-  "If non-nil, the LESS buffers will be compiled to CSS after each save."
+(defcustom less-compile-at-save nil
+  "If non-nil, Less buffers are compiled to CSS after each save."
   :type 'boolean
-  :group 'less-css
+  :group 'less
   :safe 'booleanp)
 
 ;;;###autoload
-(defcustom less-css-lessc-options '("--no-color")
-  "Command line options for less executable.
-
+(defcustom less-lessc-options '("--no-color")
+  "Command line options for Less executable.
 Use \"-x\" to minify output."
   :type '(repeat string)
-  :group 'less-css
+  :group 'less
   :safe t)
 
 ;;;###autoload
-(defcustom less-css-output-directory nil
-  "Directory in which to save CSS, or nil to use the LESS file's directory.
-
-This path is expanded relative to the directory of the LESS file
+(defcustom less-output-directory nil
+  "Directory in which to save CSS, or nil to use the Less file's directory.
+This path is expanded relative to the directory of the Less file
 using `expand-file-name', so both relative and absolute paths
 will work as expected."
   :type 'directory
-  :group 'less-css
+  :group 'less
   :safe 'stringp)
 
 ;;;###autoload
-(defcustom less-css-output-file-name nil
+(defcustom less-output-file-name nil
   "File name in which to save CSS, or nil to use <name>.css for <name>.less.
-
 This can be also be set to a full path, or a relative path.  If
 the path is relative, it will be relative to the value of
-`less-css-output-dir', if set, or the current directory by
-default."
+`less-output-dir', if set, or the current directory by default."
   :type 'file
-  :group 'less-css
+  :group 'less
   :safe 'stringp)
-(make-variable-buffer-local 'less-css-output-file-name)
+(make-variable-buffer-local 'less-output-file-name)
 
 ;;;###autoload
-(defcustom less-css-input-file-name nil
+(defcustom less-input-file-name nil
   "File name which will be compiled to CSS.
-
-When the current buffer is saved `less-css-input-file-name' file
-will be compiled to css instead of the current file.
+When the current buffer is saved `less-input-file-name' file will
+be compiled to CSS instead of the current file.
 
 Set this in order to trigger compilation of a \"master\" .less
 file which includes the current file.  The best way to set this
@@ -1674,59 +1670,61 @@ less-css-input-file-name
 variables.
 
 This can be also be set to a full path, or a relative path. If
-the path is relative, it will be relative to the the current directory by
-default."
+the path is relative, it will be relative to the the current
+directory by default."
   :type 'file
-  :group 'less-css
+  :group 'less
   :safe 'stringp)
-(make-variable-buffer-local 'less-css-input-file-name)
+(make-variable-buffer-local 'less-input-file-name)
 
-(defconst less-css-default-error-regex
+(defconst less-default-error-regex
   "^\\(?:\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\\)?")
 
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Compilation to CSS
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (add-to-list 'compilation-error-regexp-alist-alist
-             (list 'less-css less-css-default-error-regex 2 3 4 nil 1))
-(add-to-list 'compilation-error-regexp-alist 'less-css)
-
-
-(defun less-css-compile-maybe ()
-  "Run `less-css-compile' if `less-css-compile-at-save' is non-nil."
-  (if less-css-compile-at-save
-      (less-css-compile)))
-
-(defun less-css--output-path ()
-  "Calculate the path for the compiled CSS file created by `less-css-compile'."
-  (expand-file-name (or less-css-output-file-name
-                        (concat (file-name-nondirectory (file-name-sans-extension buffer-file-name)) ".css"))
-                    (or less-css-output-directory default-directory)))
-
-(defun less-css--maybe-shell-quote-command (command)
+             (list 'less less-default-error-regex 2 3 4 nil 1))
+(add-to-list 'compilation-error-regexp-alist 'less)
+
+(defun less-compile-maybe ()
+  "Run `less-compile' if `less-compile-at-save' is non-nil."
+  (when less-compile-at-save
+    (less-compile)))
+
+(defun less--output-path ()
+  "Return the path to use for the compiled CSS file."
+  (expand-file-name
+   (or less-output-file-name
+       (concat
+        (file-name-nondirectory
+         (file-name-sans-extension buffer-file-name))
+        ".css"))
+   (or less-output-directory default-directory)))
+
+(defun less--maybe-shell-quote-command (command)
   "Selectively shell-quote COMMAND appropriately for `system-type'."
   (funcall (if (eq system-type 'windows-nt)
                'identity
-             'shell-quote-argument) command))
+             'shell-quote-argument)
+           command))
 
 ;;;###autoload
-(defun less-css-compile ()
-  "Compiles the current buffer to css using `less-css-lessc-command'."
+(defun less-compile ()
+  "Compile the current buffer to CSS using `less-lessc-command'."
   (interactive)
-  (message "Compiling less to css")
-  (let ((compilation-buffer-name-function (lambda (mode-name) "*less-css-compilation*")))
+  (message "Compiling Less to CSS")
+  (let ((compilation-buffer-name-function
+         (lambda (_) "*less-compilation*")))
     (save-window-excursion
       (with-current-buffer
           (compile
-           (mapconcat 'identity
-                      (append (list (less-css--maybe-shell-quote-command less-css-lessc-command))
-                              (mapcar 'shell-quote-argument less-css-lessc-options)
-                              (list (shell-quote-argument
-                                     (or less-css-input-file-name buffer-file-name))
-                                    (shell-quote-argument (less-css--output-path))))
-                      " "))
+           (string-join
+            (append (list (less--maybe-shell-quote-command less-lessc-command))
+                    (mapcar 'shell-quote-argument less-lessc-options)
+                    (list (shell-quote-argument
+                           (or less-input-file-name buffer-file-name))
+                          (shell-quote-argument (less--output-path))))
+            " "))
         (add-hook 'compilation-finish-functions
                   (lambda (buf msg)
                     (unless (string-match-p "^finished" msg)
@@ -1734,53 +1732,41 @@ less-css-compile
                   nil
                   t)))))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Minor mode
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; TODO: interpolation ("@{val}"), escaped values (~"..."), JS eval (~`...`), custom faces
-(defconst less-css-font-lock-keywords
+;; TODO:
+;; - interpolation ("@{val}")
+;; - escaped values (~"...")
+;; - JavaScript eval (~`...`)
+;; - custom faces
+(defconst less-font-lock-keywords
   '(;; Variables
-    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
+    ("@[a-z_-][a-z-_0-9]*" . font-lock-variable-name-face)
     ("&" . font-lock-preprocessor-face)
     ;; Mixins
-    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
-  )
+    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" .
+     (1 font-lock-keyword-face))))
+
+(defvar less-mode-syntax-table
+  (let ((st (make-syntax-table css-mode-syntax-table)))
+    ;; C++-style comments.
+    (modify-syntax-entry ?/ ". 124b" st)
+    (modify-syntax-entry ?* ". 23" st)
+    (modify-syntax-entry ?\n "> b" st)
+    ;; Special chars that sometimes come at the beginning of words.
+    (modify-syntax-entry ?. "'" st)
+    st))
 
+;;;###autoload (add-to-list 'auto-mode-alist '("\\.less\\'" . less-mode))
 ;;;###autoload
-(define-derived-mode less-css-mode css-mode "LESS"
-  "Major mode for editing LESS files, http://lesscss.org/
+(define-derived-mode less-mode css-mode "Less"
+  "Major mode for editing Less files, http://lesscss.org/
 Special commands:
-\\{less-css-mode-map}"
-  (font-lock-add-keywords nil less-css-font-lock-keywords)
-  ;; cpp-style comments
-  (modify-syntax-entry ?/ ". 124b" less-css-mode-syntax-table)
-  (modify-syntax-entry ?* ". 23" less-css-mode-syntax-table)
-  (modify-syntax-entry ?\n "> b" less-css-mode-syntax-table)
-  ;; Special chars that sometimes come at the beginning of words.
-  (modify-syntax-entry ?. "'" less-css-mode-syntax-table)
-
-  (set (make-local-variable 'comment-start) "//")
-  (set (make-local-variable 'comment-end) "")
-  (set (make-local-variable 'indent-line-function) 'less-css-indent-line)
-  (when (functionp 'css-smie-rules)
-    (smie-setup css-smie-grammar #'css-smie-rules
-                :forward-token #'css-smie--forward-token
-                :backward-token #'css-smie--backward-token))
-
-  (add-hook 'after-save-hook 'less-css-compile-maybe nil t))
-
-(define-key less-css-mode-map "\C-c\C-c" 'less-css-compile)
-
-(defun less-css-indent-line ()
-  "Indent current line according to LESS CSS indentation rules."
-  (let ((css-navigation-syntax-table less-css-mode-syntax-table))
-    (if (fboundp 'css-indent-line)
-        (css-indent-line)
-      (smie-indent-line))))
+\\{less-mode-map}"
+  (font-lock-add-keywords nil less-font-lock-keywords)
+  (setq-local comment-start "//")
+  (setq-local comment-end "")
+  (add-hook 'after-save-hook 'less-compile-maybe nil t))
 
-;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
+(define-key less-mode-map "\C-c\C-c" 'less-compile)
 
 \f
 
-- 
2.13.2


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-07-30 17:53 bug#27881: New major mode: Less mode Simen Heggestøyl
@ 2017-07-30 20:59 ` Stefan Monnier
  2017-07-31  6:39   ` Steve Purcell
  2017-07-31  2:02 ` Glenn Morris
  2017-08-02  2:16 ` Tom Tromey
  2 siblings, 1 reply; 14+ messages in thread
From: Stefan Monnier @ 2017-07-30 20:59 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: steve, tom, 27881, dgutov

Thanks.  Looks good.

See some comments below.  One thing, tho: an alternative would be to put
it into its own file, which would give it more visibility


        Stefan


> ;; This mode provides syntax highlighting for LESS CSS files, plus

I'd add a URL pointing to the "canonical" place for Less.

> -(defgroup less-css nil
> -  "Less-css mode"
> -  :prefix "less-css-"
> +(defgroup less nil
> +  "Less CSS mode"
> +  :prefix "less-"
>    :group 'css)
 
>  ;;;###autoload
> -(defcustom less-css-lessc-command "lessc"

Why autoload all these defcustom.  Sounds like a bug.

>    "File name in which to save CSS, or nil to use <name>.css for <name>.less.
> -
>  This can be also be set to a full path, or a relative path.  If
>  the path is relative, it will be relative to the value of
> -`less-css-output-dir', if set, or the current directory by
> -default."
> +`less-output-dir', if set, or the current directory by default."
>    :type 'file
> -  :group 'less-css
> +  :group 'less
>    :safe 'stringp)

Since these defcusotm come right after the (defgroup less ..), the

    :group 'less

is redundant.

>  (add-to-list 'compilation-error-regexp-alist-alist
> +             (list 'less less-default-error-regex 2 3 4 nil 1))

BTW, I recommend reporting the use of an ad-hoc error format as an error
to the Less developers.

>  ;;;###autoload
> +(defun less-compile ()

Why autoload?  This operates on the current buffer, so presumably this
buffer is already in less-mode.

> +  (message "Compiling Less to CSS")

I notice that "Less" is used pretty much everywhere except in the
define-derived-mode where Steve used "LESS".  Why not use "Less" in the
mode name as well?

> +            (append (list (less--maybe-shell-quote-command less-lessc-command))

I'd recommend to just never quote less-lessc-command rather than only
quote it on non-Windows systems.  It's actually more flexible this way
since less-lessc-command can then be set to something fancier (e.g. the
command with a few flags).

> +(defconst less-font-lock-keywords
>    '(;; Variables
> -    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
> +    ("@[a-z_-][a-z-_0-9]*" . font-lock-variable-name-face)
>      ("&" . font-lock-preprocessor-face)
>      ;; Mixins
> -    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
> -  )
> +    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" .
> +     (1 font-lock-keyword-face))))

Is it important to limit those to ASCII chars?  If not, then it's better
to use [[:alpha:]_-] and [[:alnum:]_-] in the above regexps.

> +(define-key less-mode-map "\C-c\C-c" 'less-compile)
 
The standard way is to do:

    (defvar less-mode-map
      (let ((map (make-sparse-keymap))))
        (define-key map "\C-c\C-c" 'less-compile)
        map)

before the `define-derived-mode'.






^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-07-30 17:53 bug#27881: New major mode: Less mode Simen Heggestøyl
  2017-07-30 20:59 ` Stefan Monnier
@ 2017-07-31  2:02 ` Glenn Morris
  2017-07-31  4:32   ` Marcin Borkowski
  2017-08-02  2:16 ` Tom Tromey
  2 siblings, 1 reply; 14+ messages in thread
From: Glenn Morris @ 2017-07-31  2:02 UTC (permalink / raw)
  To: Simen Heggestøyl
  Cc: Steve Purcell, Tom Tromey, 27881, Stefan Monnier, Dmitry Gutov

Simen Heggestøyl wrote:

> The biggest change I've made is to rename the mode from "Less CSS
> mode" to just "Less mode". I think the renaming has two advantages:
> Aligning its name with "SCSS mode", and making the mode's commands,
> variables and function names shorter. The downside is that existing
> users of Less mode will have to update their config files. Maybe
> aliasing `less-css-mode' to `less-mode' could help some in that
> regard. What do you think of it?

FWIW, I would assume that "less-mode" was some sort of file viewer mode
like the standalone less utility.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-07-31  2:02 ` Glenn Morris
@ 2017-07-31  4:32   ` Marcin Borkowski
  2017-08-03 17:47     ` Simen Heggestøyl
  0 siblings, 1 reply; 14+ messages in thread
From: Marcin Borkowski @ 2017-07-31  4:32 UTC (permalink / raw)
  To: Glenn Morris
  Cc: Simen Heggestøyl, Dmitry Gutov, Steve Purcell, Tom Tromey,
	27881, Stefan Monnier


On 2017-07-31, at 04:02, Glenn Morris <rgm@gnu.org> wrote:

> Simen Heggestøyl wrote:
>
>> The biggest change I've made is to rename the mode from "Less CSS
>> mode" to just "Less mode". I think the renaming has two advantages:
>> Aligning its name with "SCSS mode", and making the mode's commands,
>> variables and function names shorter. The downside is that existing
>> users of Less mode will have to update their config files. Maybe
>> aliasing `less-css-mode' to `less-mode' could help some in that
>> regard. What do you think of it?
>
> FWIW, I would assume that "less-mode" was some sort of file viewer mode
> like the standalone less utility.

Exactly my thought, I thought it is "view-mode on steroids"...

-- 
Marcin Borkowski





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-07-30 20:59 ` Stefan Monnier
@ 2017-07-31  6:39   ` Steve Purcell
  2017-07-31 12:44     ` Stefan Monnier
  0 siblings, 1 reply; 14+ messages in thread
From: Steve Purcell @ 2017-07-31  6:39 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: tom, dgutov, 27881, simenheg

Comments below:

>> ;;;###autoload
>> -(defcustom less-css-lessc-command "lessc"
> 
> Why autoload all these defcustom.  Sounds like a bug.


It may well be superfluous, but I found a note that this was added because non-autoloaded custom vars wouldn’t be considered safe as dir local variables otherwise.


> Since these defcusotm come right after the (defgroup less ..), the
> 
>    :group 'less
> 
> is redundant.


Agree.


>> ;;;###autoload
>> +(defun less-compile ()
> 
> Why autoload?  This operates on the current buffer, so presumably this
> buffer is already in less-mode.

Probably, yes.


>> +  (message "Compiling Less to CSS")
> 
> I notice that "Less" is used pretty much everywhere except in the
> define-derived-mode where Steve used "LESS".  Why not use "Less" in the
> mode name as well?


Agree, consistency would be good. Official upstream name is “Less”.


>> +            (append (list (less--maybe-shell-quote-command less-lessc-command))
> 
> I'd recommend to just never quote less-lessc-command rather than only
> quote it on non-Windows systems.  It's actually more flexible this way
> since less-lessc-command can then be set to something fancier (e.g. the
> command with a few flags).


This fixed a concrete bug, but yes, it’s probably misguided: it would have been better to always leave it unquoted.

However, I wouldn’t advocate adding arbitrary flags to a command like this, because it can’t then be passed to one of the “process” functions that expects the first of its args to be the path of the executable, right?


>> +(defconst less-font-lock-keywords
>>   '(;; Variables
>> -    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
>> +    ("@[a-z_-][a-z-_0-9]*" . font-lock-variable-name-face)
>>     ("&" . font-lock-preprocessor-face)
>>     ;; Mixins
>> -    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
>> -  )
>> +    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" .
>> +     (1 font-lock-keyword-face))))
> 
> Is it important to limit those to ASCII chars?  If not, then it's better
> to use [[:alpha:]_-] and [[:alnum:]_-] in the above regexps.


Unsure, but it’s likely safe to allow non-ASCII alphanumeric chars. 


>> +(define-key less-mode-map "\C-c\C-c" 'less-compile)
> 
> The standard way is to do:
> 
>    (defvar less-mode-map
>      (let ((map (make-sparse-keymap))))
>        (define-key map "\C-c\C-c" 'less-compile)
>        map)
> 
> before the `define-derived-mode’.


Is there an advantage to this? My reasoning is that “define-derived-mode” generates the mode map declaration, so why not fill it afterwards. If consistency is important, though, this would be a reasonable thing to change.

Thanks for the review!






^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-07-31  6:39   ` Steve Purcell
@ 2017-07-31 12:44     ` Stefan Monnier
  0 siblings, 0 replies; 14+ messages in thread
From: Stefan Monnier @ 2017-07-31 12:44 UTC (permalink / raw)
  To: Steve Purcell; +Cc: tom, dgutov, 27881, simenheg

>>> ;;;###autoload
>>> -(defcustom less-css-lessc-command "lessc"
>> Why autoload all these defcustom.  Sounds like a bug.
> It may well be superfluous, but I found a note that this was added because
> non-autoloaded custom vars wouldn’t be considered safe as dir local
> variables otherwise.

The safety info may need to be autoloaded, indeed (IIUC we have
a misfeature in there: we should enable the major mode (when possible)
before checking safety of the local vars).

But the vars themselves are better not autoloaded.

> However, I wouldn’t advocate adding arbitrary flags to a command like this,
> because it can’t then be passed to one of the “process” functions that
> expects the first of its args to be the path of the executable, right?

Indeed, if you don't quote it there, it means it's a "shell" string and
can hence only make sense when passed to a shell, rather than used
directly as a file name.

>> The standard way is to do:
>>
>> (defvar less-mode-map
>>   (let ((map (make-sparse-keymap))))
>>     (define-key map "\C-c\C-c" 'less-compile)
>>   map)
>>
>> before the `define-derived-mode’.
> Is there an advantage to this?

Mostly consistency (each style has its quirks).


        Stefan





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-07-30 17:53 bug#27881: New major mode: Less mode Simen Heggestøyl
  2017-07-30 20:59 ` Stefan Monnier
  2017-07-31  2:02 ` Glenn Morris
@ 2017-08-02  2:16 ` Tom Tromey
  2017-08-03 17:50   ` Simen Heggestøyl
  2 siblings, 1 reply; 14+ messages in thread
From: Tom Tromey @ 2017-08-02  2:16 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: steve, tom, dgutov, 27881, monnier

>>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:

Simen> +(defcustom less-css-lessc-command "lessc"
Simen> +  "Command used to compile LESS files.
Simen> +Should be lessc or the complete path to your lessc executable,
Simen> +  e.g.: \"~/.gem/ruby/1.8/bin/lessc\""
Simen> +  :type 'file
Simen> +  :group 'less-css
Simen> +  :safe 'stringp)
Simen> +
Simen> +;;;###autoload
Simen> +(defcustom less-css-compile-at-save nil
Simen> +  "If non-nil, the LESS buffers will be compiled to CSS after each save."
Simen> +  :type 'boolean
Simen> +  :group 'less-css
Simen> +  :safe 'booleanp)

It seems dangerous to me to mark these both as safe.  A file could
specify a dangerous command and then say it's ok to run it on save.

Probably the command should not be :safe.

Maybe the other variables defined after this should also not be safe.

Tom





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-07-31  4:32   ` Marcin Borkowski
@ 2017-08-03 17:47     ` Simen Heggestøyl
  2017-08-04 21:33       ` Richard Stallman
  0 siblings, 1 reply; 14+ messages in thread
From: Simen Heggestøyl @ 2017-08-03 17:47 UTC (permalink / raw)
  To: Marcin Borkowski
  Cc: Stefan Monnier, Dmitry Gutov, Steve Purcell, Tom Tromey, 27881

On Mon, Jul 31, 2017 at 6:32 AM, Marcin Borkowski <mbork@mbork.pl> 
wrote:
> 
> On 2017-07-31, at 04:02, Glenn Morris <rgm@gnu.org> wrote:
>>  FWIW, I would assume that "less-mode" was some sort of file viewer 
>> mode
>>  like the standalone less utility.
> 
> Exactly my thought, I thought it is "view-mode on steroids"...
> 
> --
> Marcin Borkowski

Hmm, given these comments, I think it might be better to keep Steve's
original name, "Less CSS mode".

-- Simen






^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-08-02  2:16 ` Tom Tromey
@ 2017-08-03 17:50   ` Simen Heggestøyl
  2017-08-03 20:08     ` Stefan Monnier
  0 siblings, 1 reply; 14+ messages in thread
From: Simen Heggestøyl @ 2017-08-03 17:50 UTC (permalink / raw)
  To: Tom Tromey; +Cc: steve, dgutov, 27881, monnier

[-- Attachment #1: Type: text/plain, Size: 2296 bytes --]

On Sun, Jul 30, 2017 at 10:59 PM, Stefan Monnier 
<monnier@IRO.UMontreal.CA> wrote:
 > See some comments below.  One thing, tho: an alternative would be to 
put
 > it into its own file, which would give it more visibility

Fine by me. Should we perhaps do the same for SCSS mode?


On Mon, Jul 31, 2017 at 8:39 AM, Steve Purcell <steve@sanityinc.com> 
wrote:
 >  +(defconst less-font-lock-keywords
 >    '(;; Variables
 >  -    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
 >  +    ("@[a-z_-][a-z-_0-9]*" . font-lock-variable-name-face)
 >      ("&" . font-lock-preprocessor-face)
 >      ;; Mixins
 >  -    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 
font-lock-keyword-face)))
 >  -  )
 >  +    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" .
 >  +     (1 font-lock-keyword-face))))
 >
 >  Is it important to limit those to ASCII chars?  If not, then it's 
better
 >  to use [[:alpha:]_-] and [[:alnum:]_-] in the above regexps.
 >
 >
 > Unsure, but it’s likely safe to allow non-ASCII alphanumeric chars.

I'm not sure either, but the Less compiler didn't like some non-ASCII
characters I fed to it, so I suspect it only allows ASCII characters
in variable names.


On Mon, Jul 31, 2017 at 2:44 PM, Stefan Monnier 
<monnier@iro.umontreal.ca> wrote:
 > The safety info may need to be autoloaded, indeed (IIUC we have
 > a misfeature in there: we should enable the major mode (when 
possible)
 > before checking safety of the local vars).

I've removed the autoloads from the defcustoms. How does one autoload
safety info?


On Wed, Aug 2, 2017 at 4:16 AM, Tom Tromey <tom@tromey.com> wrote:
 > It seems dangerous to me to mark these both as safe.  A file could
 > specify a dangerous command and then say it's ok to run it on save.
 >
 > Probably the command should not be :safe.
 >
 > Maybe the other variables defined after this should also not be safe.
 >
 > Tom

I think you're right, that seems particularly unsafe to me too. Maybe
`less-css-output-file-name' shouldn't be marked safe either, since it
may specify the name of an important file can get overwritten without
a confirmation.


I've updated the diffs based on your feedback. Thank you all for your
comments so far.

-- Simen

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.1: 0001-WIP-New-major-mode-Less-CSS-mode.patch --]
[-- Type: text/x-patch, Size: 10913 bytes --]

From 5f7cbac4d28cfc245c81b1527d8995d3310d5388 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Tue, 1 Aug 2017 20:15:45 +0200
Subject: [PATCH 1/2] WIP: New major mode: Less CSS mode

---
 lisp/textmodes/less-css-mode.el | 258 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 258 insertions(+)
 create mode 100644 lisp/textmodes/less-css-mode.el

diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el
new file mode 100644
index 0000000000..8a981d67b9
--- /dev/null
+++ b/lisp/textmodes/less-css-mode.el
@@ -0,0 +1,258 @@
+;;; less-css-mode.el --- Major mode for editing LESS CSS files (lesscss.org)
+;;
+;; Copyright (C) 2011-2014 Steve Purcell
+;;
+;; Author: Steve Purcell <steve@sanityinc.com>
+;; URL: https://github.com/purcell/less-css-mode
+;; Keywords: less css mode
+;; Version: DEV
+;;
+;; 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 2 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.
+;;
+;;; Commentary:
+;;
+;; This mode provides syntax highlighting for LESS CSS files, plus
+;; optional support for compilation of .less files to .css files at
+;; the time they are saved: use `less-css-compile-at-save' to enable
+;; this.
+;;
+;; Command line utility "lessc" is required if setting
+;; `less-css-compile-at-save' to t.  To install "lessc" using the
+;; Node.js package manager, run "npm install less"
+;;
+;; Also make sure the "lessc" executable is in Emacs' PATH, example:
+;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin") exec-path))
+;; or customize `less-css-lessc-command' to point to your "lessc" executable.
+;;
+;; We target lessc >= 1.4.0, and thus use the `--no-color' flag by
+;; default.  You may want to adjust `less-css-lessc-options' for
+;; compatibility with older versions.
+;;
+;; `less-css-mode' is derived from `css-mode', and indentation of
+;; nested blocks may not work correctly with versions of `css-mode'
+;; other than that bundled with recent Emacs.
+;;
+;; You can specify per-file values for `less-css-compile-at-save',
+;; `less-css-output-file-name' or `less-css-output-directory' using a
+;; variables header at the top of your .less file, e.g.:
+;;
+;; // -*- less-css-compile-at-save: t; less-css-output-directory: "../css" -*-
+;;
+;; Alternatively, you can use directory local variables to set the
+;; default value of `less-css-output-directory' for your project.
+;;
+;; In the case of files which are included in other .less files, you
+;; may want to trigger the compilation of a "master" .less file on
+;; save: you can accomplish this with `less-css-input-file-name',
+;; which is probably best set using directory local variables.
+;;
+;; If you don't need CSS output but would like to be warned of any
+;; syntax errors in your .less source, consider using `flymake-less':
+;; https://github.com/purcell/flymake-less
+;;
+;;; Credits
+;;
+;; The original code for this mode was, in large part, written using
+;; Anton Johansson's scss-mode as a template -- thanks Anton!
+;; https://github.com/antonj
+;;
+;;; Code:
+
+(require 'derived)
+(require 'compile)
+
+;; There are at least three css-mode.el implementations, but we need
+;; the right one in order to work as expected, not the versions by
+;; Landström or Garshol
+
+(require 'css-mode)
+(unless (or (boundp 'css-navigation-syntax-table)
+            (functionp 'css-smie-rules))
+  (error "Wrong css-mode.el: please use the version by Stefan Monnier, bundled with Emacs >= 23"))
+
+(defgroup less-css nil
+  "Less-css mode"
+  :prefix "less-css-"
+  :group 'css)
+
+;;;###autoload
+(defcustom less-css-lessc-command "lessc"
+  "Command used to compile LESS files.
+Should be lessc or the complete path to your lessc executable,
+  e.g.: \"~/.gem/ruby/1.8/bin/lessc\""
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+
+;;;###autoload
+(defcustom less-css-compile-at-save nil
+  "If non-nil, the LESS buffers will be compiled to CSS after each save."
+  :type 'boolean
+  :group 'less-css
+  :safe 'booleanp)
+
+;;;###autoload
+(defcustom less-css-lessc-options '("--no-color")
+  "Command line options for less executable.
+
+Use \"-x\" to minify output."
+  :type '(repeat string)
+  :group 'less-css
+  :safe t)
+
+;;;###autoload
+(defcustom less-css-output-directory nil
+  "Directory in which to save CSS, or nil to use the LESS file's directory.
+
+This path is expanded relative to the directory of the LESS file
+using `expand-file-name', so both relative and absolute paths
+will work as expected."
+  :type 'directory
+  :group 'less-css
+  :safe 'stringp)
+
+;;;###autoload
+(defcustom less-css-output-file-name nil
+  "File name in which to save CSS, or nil to use <name>.css for <name>.less.
+
+This can be also be set to a full path, or a relative path.  If
+the path is relative, it will be relative to the value of
+`less-css-output-dir', if set, or the current directory by
+default."
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+(make-variable-buffer-local 'less-css-output-file-name)
+
+;;;###autoload
+(defcustom less-css-input-file-name nil
+  "File name which will be compiled to CSS.
+
+When the current buffer is saved `less-css-input-file-name' file
+will be compiled to css instead of the current file.
+
+Set this in order to trigger compilation of a \"master\" .less
+file which includes the current file.  The best way to set this
+variable in most cases is likely to be via directory local
+variables.
+
+This can be also be set to a full path, or a relative path. If
+the path is relative, it will be relative to the the current directory by
+default."
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+(make-variable-buffer-local 'less-css-input-file-name)
+
+(defconst less-css-default-error-regex
+  "^\\(?:\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\\)?")
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Compilation to CSS
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(add-to-list 'compilation-error-regexp-alist-alist
+             (list 'less-css less-css-default-error-regex 2 3 4 nil 1))
+(add-to-list 'compilation-error-regexp-alist 'less-css)
+
+
+(defun less-css-compile-maybe ()
+  "Run `less-css-compile' if `less-css-compile-at-save' is non-nil."
+  (if less-css-compile-at-save
+      (less-css-compile)))
+
+(defun less-css--output-path ()
+  "Calculate the path for the compiled CSS file created by `less-css-compile'."
+  (expand-file-name (or less-css-output-file-name
+                        (concat (file-name-nondirectory (file-name-sans-extension buffer-file-name)) ".css"))
+                    (or less-css-output-directory default-directory)))
+
+(defun less-css--maybe-shell-quote-command (command)
+  "Selectively shell-quote COMMAND appropriately for `system-type'."
+  (funcall (if (eq system-type 'windows-nt)
+               'identity
+             'shell-quote-argument) command))
+
+;;;###autoload
+(defun less-css-compile ()
+  "Compiles the current buffer to css using `less-css-lessc-command'."
+  (interactive)
+  (message "Compiling less to css")
+  (let ((compilation-buffer-name-function (lambda (mode-name) "*less-css-compilation*")))
+    (save-window-excursion
+      (with-current-buffer
+          (compile
+           (mapconcat 'identity
+                      (append (list (less-css--maybe-shell-quote-command less-css-lessc-command))
+                              (mapcar 'shell-quote-argument less-css-lessc-options)
+                              (list (shell-quote-argument
+                                     (or less-css-input-file-name buffer-file-name))
+                                    (shell-quote-argument (less-css--output-path))))
+                      " "))
+        (add-hook 'compilation-finish-functions
+                  (lambda (buf msg)
+                    (unless (string-match-p "^finished" msg)
+                      (display-buffer buf)))
+                  nil
+                  t)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Minor mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: interpolation ("@{val}"), escaped values (~"..."), JS eval (~`...`), custom faces
+(defconst less-css-font-lock-keywords
+  '(;; Variables
+    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
+    ("&" . font-lock-preprocessor-face)
+    ;; Mixins
+    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
+  )
+
+;;;###autoload
+(define-derived-mode less-css-mode css-mode "LESS"
+  "Major mode for editing LESS files, http://lesscss.org/
+Special commands:
+\\{less-css-mode-map}"
+  (font-lock-add-keywords nil less-css-font-lock-keywords)
+  ;; cpp-style comments
+  (modify-syntax-entry ?/ ". 124b" less-css-mode-syntax-table)
+  (modify-syntax-entry ?* ". 23" less-css-mode-syntax-table)
+  (modify-syntax-entry ?\n "> b" less-css-mode-syntax-table)
+  ;; Special chars that sometimes come at the beginning of words.
+  (modify-syntax-entry ?. "'" less-css-mode-syntax-table)
+
+  (set (make-local-variable 'comment-start) "//")
+  (set (make-local-variable 'comment-end) "")
+  (set (make-local-variable 'indent-line-function) 'less-css-indent-line)
+  (when (functionp 'css-smie-rules)
+    (smie-setup css-smie-grammar #'css-smie-rules
+                :forward-token #'css-smie--forward-token
+                :backward-token #'css-smie--backward-token))
+
+  (add-hook 'after-save-hook 'less-css-compile-maybe nil t))
+
+(define-key less-css-mode-map "\C-c\C-c" 'less-css-compile)
+
+(defun less-css-indent-line ()
+  "Indent current line according to LESS CSS indentation rules."
+  (let ((css-navigation-syntax-table less-css-mode-syntax-table))
+    (if (fboundp 'css-indent-line)
+        (css-indent-line)
+      (smie-indent-line))))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
+
+
+(provide 'less-css-mode)
+;;; less-css-mode.el ends here
-- 
2.13.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.2: 0002-WIP-Fixes-and-tweaks-for-the-new-Less-CSS-mode.patch --]
[-- Type: text/x-patch, Size: 14096 bytes --]

From 030b430452b7a29f1231796c0f1bb65982eaf458 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Tue, 1 Aug 2017 20:23:21 +0200
Subject: [PATCH 2/2] WIP: Fixes and tweaks for the new Less CSS mode

---
 lisp/textmodes/less-css-mode.el | 239 +++++++++++++++++-----------------------
 1 file changed, 103 insertions(+), 136 deletions(-)

diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el
index 8a981d67b9..e54ae04364 100644
--- a/lisp/textmodes/less-css-mode.el
+++ b/lisp/textmodes/less-css-mode.el
@@ -1,36 +1,41 @@
-;;; less-css-mode.el --- Major mode for editing LESS CSS files (lesscss.org)
-;;
-;; Copyright (C) 2011-2014 Steve Purcell
-;;
+;;; less-css-mode.el --- Major mode for editing Less CSS files  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2011-2017 Free Software Foundation, Inc.
+
 ;; Author: Steve Purcell <steve@sanityinc.com>
-;; URL: https://github.com/purcell/less-css-mode
-;; Keywords: less css mode
-;; Version: DEV
-;;
-;; 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 2 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.
-;;
+;; Maintainer: Simen Heggestøyl <simenheg@gmail.com>
+;; Keywords: hypermedia
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
 ;;; Commentary:
-;;
-;; This mode provides syntax highlighting for LESS CSS files, plus
-;; optional support for compilation of .less files to .css files at
-;; the time they are saved: use `less-css-compile-at-save' to enable
-;; this.
+
+;; This mode provides syntax highlighting for Less CSS files
+;; (http://lesscss.org/), plus optional support for compilation of
+;; .less files to .css files at the time they are saved: use
+;; `less-css-compile-at-save' to enable this.
 ;;
 ;; Command line utility "lessc" is required if setting
 ;; `less-css-compile-at-save' to t.  To install "lessc" using the
-;; Node.js package manager, run "npm install less"
+;; Node.js package manager, run "npm install less".
 ;;
 ;; Also make sure the "lessc" executable is in Emacs' PATH, example:
-;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin") exec-path))
-;; or customize `less-css-lessc-command' to point to your "lessc" executable.
+;; (push (expand-file-name "~/.gem/ruby/1.8/bin") exec-path)
+;; or customize `less-css-lessc-command' to point to your "lessc"
+;; executable.
 ;;
 ;; We target lessc >= 1.4.0, and thus use the `--no-color' flag by
 ;; default.  You may want to adjust `less-css-lessc-options' for
@@ -56,88 +61,64 @@
 ;;
 ;; If you don't need CSS output but would like to be warned of any
 ;; syntax errors in your .less source, consider using `flymake-less':
-;; https://github.com/purcell/flymake-less
-;;
+;; https://github.com/purcell/flymake-less.
+
 ;;; Credits
-;;
+
 ;; The original code for this mode was, in large part, written using
 ;; Anton Johansson's scss-mode as a template -- thanks Anton!
 ;; https://github.com/antonj
-;;
+
 ;;; Code:
 
-(require 'derived)
 (require 'compile)
-
-;; There are at least three css-mode.el implementations, but we need
-;; the right one in order to work as expected, not the versions by
-;; Landström or Garshol
-
 (require 'css-mode)
-(unless (or (boundp 'css-navigation-syntax-table)
-            (functionp 'css-smie-rules))
-  (error "Wrong css-mode.el: please use the version by Stefan Monnier, bundled with Emacs >= 23"))
+(require 'derived)
+(eval-when-compile (require 'subr-x))
 
 (defgroup less-css nil
-  "Less-css mode"
+  "Less CSS mode."
   :prefix "less-css-"
   :group 'css)
 
-;;;###autoload
 (defcustom less-css-lessc-command "lessc"
-  "Command used to compile LESS files.
-Should be lessc or the complete path to your lessc executable,
-  e.g.: \"~/.gem/ruby/1.8/bin/lessc\""
-  :type 'file
-  :group 'less-css
-  :safe 'stringp)
+  "Command used to compile Less files.
+Should be \"lessc\" or the complete path to your lessc
+executable, e.g.: \"~/.gem/ruby/1.8/bin/lessc\"."
+  :type 'file)
 
-;;;###autoload
 (defcustom less-css-compile-at-save nil
-  "If non-nil, the LESS buffers will be compiled to CSS after each save."
+  "If non-nil, Less buffers are compiled to CSS after each save."
   :type 'boolean
-  :group 'less-css
   :safe 'booleanp)
 
-;;;###autoload
 (defcustom less-css-lessc-options '("--no-color")
-  "Command line options for less executable.
-
+  "Command line options for Less executable.
 Use \"-x\" to minify output."
   :type '(repeat string)
-  :group 'less-css
   :safe t)
 
-;;;###autoload
 (defcustom less-css-output-directory nil
-  "Directory in which to save CSS, or nil to use the LESS file's directory.
-
-This path is expanded relative to the directory of the LESS file
+  "Directory in which to save CSS, or nil to use the Less file's directory.
+This path is expanded relative to the directory of the Less file
 using `expand-file-name', so both relative and absolute paths
 will work as expected."
   :type 'directory
-  :group 'less-css
   :safe 'stringp)
 
-;;;###autoload
 (defcustom less-css-output-file-name nil
   "File name in which to save CSS, or nil to use <name>.css for <name>.less.
-
 This can be also be set to a full path, or a relative path.  If
 the path is relative, it will be relative to the value of
 `less-css-output-dir', if set, or the current directory by
 default."
-  :type 'file
-  :group 'less-css
-  :safe 'stringp)
+  :type 'file)
 (make-variable-buffer-local 'less-css-output-file-name)
 
-;;;###autoload
 (defcustom less-css-input-file-name nil
   "File name which will be compiled to CSS.
-
 When the current buffer is saved `less-css-input-file-name' file
-will be compiled to css instead of the current file.
+will be compiled to CSS instead of the current file.
 
 Set this in order to trigger compilation of a \"master\" .less
 file which includes the current file.  The best way to set this
@@ -145,59 +126,53 @@ less-css-input-file-name
 variables.
 
 This can be also be set to a full path, or a relative path. If
-the path is relative, it will be relative to the the current directory by
-default."
+the path is relative, it will be relative to the the current
+directory by default."
   :type 'file
-  :group 'less-css
   :safe 'stringp)
 (make-variable-buffer-local 'less-css-input-file-name)
 
 (defconst less-css-default-error-regex
   "^\\(?:\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\\)?")
 
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Compilation to CSS
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Compilation to CSS
 
 (add-to-list 'compilation-error-regexp-alist-alist
              (list 'less-css less-css-default-error-regex 2 3 4 nil 1))
 (add-to-list 'compilation-error-regexp-alist 'less-css)
 
-
 (defun less-css-compile-maybe ()
   "Run `less-css-compile' if `less-css-compile-at-save' is non-nil."
-  (if less-css-compile-at-save
-      (less-css-compile)))
+  (when less-css-compile-at-save
+    (less-css-compile)))
 
 (defun less-css--output-path ()
-  "Calculate the path for the compiled CSS file created by `less-css-compile'."
-  (expand-file-name (or less-css-output-file-name
-                        (concat (file-name-nondirectory (file-name-sans-extension buffer-file-name)) ".css"))
-                    (or less-css-output-directory default-directory)))
+  "Return the path to use for the compiled CSS file."
+  (expand-file-name
+   (or less-css-output-file-name
+       (concat
+        (file-name-nondirectory
+         (file-name-sans-extension buffer-file-name))
+        ".css"))
+   (or less-css-output-directory default-directory)))
 
-(defun less-css--maybe-shell-quote-command (command)
-  "Selectively shell-quote COMMAND appropriately for `system-type'."
-  (funcall (if (eq system-type 'windows-nt)
-               'identity
-             'shell-quote-argument) command))
-
-;;;###autoload
 (defun less-css-compile ()
-  "Compiles the current buffer to css using `less-css-lessc-command'."
+  "Compile the current buffer to CSS using `less-css-lessc-command'."
   (interactive)
-  (message "Compiling less to css")
-  (let ((compilation-buffer-name-function (lambda (mode-name) "*less-css-compilation*")))
+  (message "Compiling Less to CSS")
+  (let ((compilation-buffer-name-function
+         (lambda (_) "*less-css-compilation*")))
     (save-window-excursion
       (with-current-buffer
           (compile
-           (mapconcat 'identity
-                      (append (list (less-css--maybe-shell-quote-command less-css-lessc-command))
-                              (mapcar 'shell-quote-argument less-css-lessc-options)
-                              (list (shell-quote-argument
-                                     (or less-css-input-file-name buffer-file-name))
-                                    (shell-quote-argument (less-css--output-path))))
-                      " "))
+           (string-join
+            (append
+             (list less-css-lessc-command)
+             (mapcar #'shell-quote-argument less-css-lessc-options)
+             (list (shell-quote-argument
+                    (or less-css-input-file-name buffer-file-name))
+                   (shell-quote-argument (less-css--output-path))))
+            " "))
         (add-hook 'compilation-finish-functions
                   (lambda (buf msg)
                     (unless (string-match-p "^finished" msg)
@@ -205,54 +180,46 @@ less-css-compile
                   nil
                   t)))))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Minor mode
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Major mode
 
-;; TODO: interpolation ("@{val}"), escaped values (~"..."), JS eval (~`...`), custom faces
+;; TODO:
+;; - interpolation ("@{val}")
+;; - escaped values (~"...")
+;; - JS eval (~`...`)
+;; - custom faces.
 (defconst less-css-font-lock-keywords
   '(;; Variables
-    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
+    ("@[a-z_-][a-z-_0-9]*" . font-lock-variable-name-face)
     ("&" . font-lock-preprocessor-face)
     ;; Mixins
-    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
-  )
-
+    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" .
+     (1 font-lock-keyword-face))))
+
+(defvar less-css-mode-syntax-table
+  (let ((st (make-syntax-table css-mode-syntax-table)))
+    ;; C++-style comments.
+    (modify-syntax-entry ?/ ". 124b" st)
+    (modify-syntax-entry ?* ". 23" st)
+    (modify-syntax-entry ?\n "> b" st)
+    ;; Special chars that sometimes come at the beginning of words.
+    (modify-syntax-entry ?. "'" st)
+    st))
+
+(defvar less-css-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-c\C-c" 'less-css-compile)
+    map))
+
+;;;###autoload (add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
 ;;;###autoload
-(define-derived-mode less-css-mode css-mode "LESS"
-  "Major mode for editing LESS files, http://lesscss.org/
+(define-derived-mode less-css-mode css-mode "Less"
+  "Major mode for editing Less files, http://lesscss.org/
 Special commands:
 \\{less-css-mode-map}"
   (font-lock-add-keywords nil less-css-font-lock-keywords)
-  ;; cpp-style comments
-  (modify-syntax-entry ?/ ". 124b" less-css-mode-syntax-table)
-  (modify-syntax-entry ?* ". 23" less-css-mode-syntax-table)
-  (modify-syntax-entry ?\n "> b" less-css-mode-syntax-table)
-  ;; Special chars that sometimes come at the beginning of words.
-  (modify-syntax-entry ?. "'" less-css-mode-syntax-table)
-
-  (set (make-local-variable 'comment-start) "//")
-  (set (make-local-variable 'comment-end) "")
-  (set (make-local-variable 'indent-line-function) 'less-css-indent-line)
-  (when (functionp 'css-smie-rules)
-    (smie-setup css-smie-grammar #'css-smie-rules
-                :forward-token #'css-smie--forward-token
-                :backward-token #'css-smie--backward-token))
-
+  (setq-local comment-start "//")
+  (setq-local comment-end "")
   (add-hook 'after-save-hook 'less-css-compile-maybe nil t))
 
-(define-key less-css-mode-map "\C-c\C-c" 'less-css-compile)
-
-(defun less-css-indent-line ()
-  "Indent current line according to LESS CSS indentation rules."
-  (let ((css-navigation-syntax-table less-css-mode-syntax-table))
-    (if (fboundp 'css-indent-line)
-        (css-indent-line)
-      (smie-indent-line))))
-
-;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
-
-
 (provide 'less-css-mode)
 ;;; less-css-mode.el ends here
-- 
2.13.2


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-08-03 17:50   ` Simen Heggestøyl
@ 2017-08-03 20:08     ` Stefan Monnier
  2017-08-10 22:53       ` Simen Heggestøyl
  0 siblings, 1 reply; 14+ messages in thread
From: Stefan Monnier @ 2017-08-03 20:08 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: steve, Tom Tromey, 27881, dgutov

>> See some comments below.  One thing, tho: an alternative would be to
>> put it into its own file, which would give it more visibility
> Fine by me. Should we perhaps do the same for SCSS mode?

We could, but since scss-mode was already included in Emacs-25.1
(i.e. it's been distributed officially for a while now), it's not
as important.

Putting it in its own file makes it possible to distribute it via
GNU ELPA without having to wait for Emacs-26 to be released.

> I've removed the autoloads from the defcustoms. How does one autoload
> safety info?

    ;;;###autoload
    (put '<VAR> 'safe-local-variable '<PRED>)


-- Stefan





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-08-03 17:47     ` Simen Heggestøyl
@ 2017-08-04 21:33       ` Richard Stallman
  0 siblings, 0 replies; 14+ messages in thread
From: Richard Stallman @ 2017-08-04 21:33 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: mbork, monnier, dgutov, steve, tom, 27881

[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > Hmm, given these comments, I think it might be better to keep Steve's
  > original name, "Less CSS mode".

I agree.

-- 
Dr Richard Stallman
President, Free Software Foundation (gnu.org, fsf.org)
Internet Hall-of-Famer (internethalloffame.org)
Skype: No way! See stallman.org/skype.html.






^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-08-03 20:08     ` Stefan Monnier
@ 2017-08-10 22:53       ` Simen Heggestøyl
  2017-08-14 10:06         ` Stefan Monnier
  0 siblings, 1 reply; 14+ messages in thread
From: Simen Heggestøyl @ 2017-08-10 22:53 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: steve, Tom Tromey, 27881, dgutov

[-- Attachment #1: Type: text/plain, Size: 198 bytes --]

On Thu, Aug 3, 2017 at 10:08 PM, Stefan Monnier 
<monnier@IRO.UMontreal.CA> wrote:
>     ;;;###autoload
>     (put '<VAR> 'safe-local-variable '<PRED>)

OK, thanks.

How does it look now?

-- Simen

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.1: 0001-New-major-mode-Less-CSS-mode.patch --]
[-- Type: text/x-patch, Size: 10933 bytes --]

From 8d24b049ee1d051b975da6b1e8707f0db42a1ac7 Mon Sep 17 00:00:00 2001
From: Steve Purcell <steve@sanityinc.com>
Date: Tue, 1 Aug 2017 20:15:45 +0200
Subject: [PATCH 1/2] New major mode: Less CSS mode

* lisp/textmodes/less-css-mode.el: New file.
---
 lisp/textmodes/less-css-mode.el | 258 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 258 insertions(+)
 create mode 100644 lisp/textmodes/less-css-mode.el

diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el
new file mode 100644
index 0000000000..8a981d67b9
--- /dev/null
+++ b/lisp/textmodes/less-css-mode.el
@@ -0,0 +1,258 @@
+;;; less-css-mode.el --- Major mode for editing LESS CSS files (lesscss.org)
+;;
+;; Copyright (C) 2011-2014 Steve Purcell
+;;
+;; Author: Steve Purcell <steve@sanityinc.com>
+;; URL: https://github.com/purcell/less-css-mode
+;; Keywords: less css mode
+;; Version: DEV
+;;
+;; 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 2 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.
+;;
+;;; Commentary:
+;;
+;; This mode provides syntax highlighting for LESS CSS files, plus
+;; optional support for compilation of .less files to .css files at
+;; the time they are saved: use `less-css-compile-at-save' to enable
+;; this.
+;;
+;; Command line utility "lessc" is required if setting
+;; `less-css-compile-at-save' to t.  To install "lessc" using the
+;; Node.js package manager, run "npm install less"
+;;
+;; Also make sure the "lessc" executable is in Emacs' PATH, example:
+;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin") exec-path))
+;; or customize `less-css-lessc-command' to point to your "lessc" executable.
+;;
+;; We target lessc >= 1.4.0, and thus use the `--no-color' flag by
+;; default.  You may want to adjust `less-css-lessc-options' for
+;; compatibility with older versions.
+;;
+;; `less-css-mode' is derived from `css-mode', and indentation of
+;; nested blocks may not work correctly with versions of `css-mode'
+;; other than that bundled with recent Emacs.
+;;
+;; You can specify per-file values for `less-css-compile-at-save',
+;; `less-css-output-file-name' or `less-css-output-directory' using a
+;; variables header at the top of your .less file, e.g.:
+;;
+;; // -*- less-css-compile-at-save: t; less-css-output-directory: "../css" -*-
+;;
+;; Alternatively, you can use directory local variables to set the
+;; default value of `less-css-output-directory' for your project.
+;;
+;; In the case of files which are included in other .less files, you
+;; may want to trigger the compilation of a "master" .less file on
+;; save: you can accomplish this with `less-css-input-file-name',
+;; which is probably best set using directory local variables.
+;;
+;; If you don't need CSS output but would like to be warned of any
+;; syntax errors in your .less source, consider using `flymake-less':
+;; https://github.com/purcell/flymake-less
+;;
+;;; Credits
+;;
+;; The original code for this mode was, in large part, written using
+;; Anton Johansson's scss-mode as a template -- thanks Anton!
+;; https://github.com/antonj
+;;
+;;; Code:
+
+(require 'derived)
+(require 'compile)
+
+;; There are at least three css-mode.el implementations, but we need
+;; the right one in order to work as expected, not the versions by
+;; Landström or Garshol
+
+(require 'css-mode)
+(unless (or (boundp 'css-navigation-syntax-table)
+            (functionp 'css-smie-rules))
+  (error "Wrong css-mode.el: please use the version by Stefan Monnier, bundled with Emacs >= 23"))
+
+(defgroup less-css nil
+  "Less-css mode"
+  :prefix "less-css-"
+  :group 'css)
+
+;;;###autoload
+(defcustom less-css-lessc-command "lessc"
+  "Command used to compile LESS files.
+Should be lessc or the complete path to your lessc executable,
+  e.g.: \"~/.gem/ruby/1.8/bin/lessc\""
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+
+;;;###autoload
+(defcustom less-css-compile-at-save nil
+  "If non-nil, the LESS buffers will be compiled to CSS after each save."
+  :type 'boolean
+  :group 'less-css
+  :safe 'booleanp)
+
+;;;###autoload
+(defcustom less-css-lessc-options '("--no-color")
+  "Command line options for less executable.
+
+Use \"-x\" to minify output."
+  :type '(repeat string)
+  :group 'less-css
+  :safe t)
+
+;;;###autoload
+(defcustom less-css-output-directory nil
+  "Directory in which to save CSS, or nil to use the LESS file's directory.
+
+This path is expanded relative to the directory of the LESS file
+using `expand-file-name', so both relative and absolute paths
+will work as expected."
+  :type 'directory
+  :group 'less-css
+  :safe 'stringp)
+
+;;;###autoload
+(defcustom less-css-output-file-name nil
+  "File name in which to save CSS, or nil to use <name>.css for <name>.less.
+
+This can be also be set to a full path, or a relative path.  If
+the path is relative, it will be relative to the value of
+`less-css-output-dir', if set, or the current directory by
+default."
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+(make-variable-buffer-local 'less-css-output-file-name)
+
+;;;###autoload
+(defcustom less-css-input-file-name nil
+  "File name which will be compiled to CSS.
+
+When the current buffer is saved `less-css-input-file-name' file
+will be compiled to css instead of the current file.
+
+Set this in order to trigger compilation of a \"master\" .less
+file which includes the current file.  The best way to set this
+variable in most cases is likely to be via directory local
+variables.
+
+This can be also be set to a full path, or a relative path. If
+the path is relative, it will be relative to the the current directory by
+default."
+  :type 'file
+  :group 'less-css
+  :safe 'stringp)
+(make-variable-buffer-local 'less-css-input-file-name)
+
+(defconst less-css-default-error-regex
+  "^\\(?:\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\\)?")
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Compilation to CSS
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(add-to-list 'compilation-error-regexp-alist-alist
+             (list 'less-css less-css-default-error-regex 2 3 4 nil 1))
+(add-to-list 'compilation-error-regexp-alist 'less-css)
+
+
+(defun less-css-compile-maybe ()
+  "Run `less-css-compile' if `less-css-compile-at-save' is non-nil."
+  (if less-css-compile-at-save
+      (less-css-compile)))
+
+(defun less-css--output-path ()
+  "Calculate the path for the compiled CSS file created by `less-css-compile'."
+  (expand-file-name (or less-css-output-file-name
+                        (concat (file-name-nondirectory (file-name-sans-extension buffer-file-name)) ".css"))
+                    (or less-css-output-directory default-directory)))
+
+(defun less-css--maybe-shell-quote-command (command)
+  "Selectively shell-quote COMMAND appropriately for `system-type'."
+  (funcall (if (eq system-type 'windows-nt)
+               'identity
+             'shell-quote-argument) command))
+
+;;;###autoload
+(defun less-css-compile ()
+  "Compiles the current buffer to css using `less-css-lessc-command'."
+  (interactive)
+  (message "Compiling less to css")
+  (let ((compilation-buffer-name-function (lambda (mode-name) "*less-css-compilation*")))
+    (save-window-excursion
+      (with-current-buffer
+          (compile
+           (mapconcat 'identity
+                      (append (list (less-css--maybe-shell-quote-command less-css-lessc-command))
+                              (mapcar 'shell-quote-argument less-css-lessc-options)
+                              (list (shell-quote-argument
+                                     (or less-css-input-file-name buffer-file-name))
+                                    (shell-quote-argument (less-css--output-path))))
+                      " "))
+        (add-hook 'compilation-finish-functions
+                  (lambda (buf msg)
+                    (unless (string-match-p "^finished" msg)
+                      (display-buffer buf)))
+                  nil
+                  t)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Minor mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: interpolation ("@{val}"), escaped values (~"..."), JS eval (~`...`), custom faces
+(defconst less-css-font-lock-keywords
+  '(;; Variables
+    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
+    ("&" . font-lock-preprocessor-face)
+    ;; Mixins
+    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
+  )
+
+;;;###autoload
+(define-derived-mode less-css-mode css-mode "LESS"
+  "Major mode for editing LESS files, http://lesscss.org/
+Special commands:
+\\{less-css-mode-map}"
+  (font-lock-add-keywords nil less-css-font-lock-keywords)
+  ;; cpp-style comments
+  (modify-syntax-entry ?/ ". 124b" less-css-mode-syntax-table)
+  (modify-syntax-entry ?* ". 23" less-css-mode-syntax-table)
+  (modify-syntax-entry ?\n "> b" less-css-mode-syntax-table)
+  ;; Special chars that sometimes come at the beginning of words.
+  (modify-syntax-entry ?. "'" less-css-mode-syntax-table)
+
+  (set (make-local-variable 'comment-start) "//")
+  (set (make-local-variable 'comment-end) "")
+  (set (make-local-variable 'indent-line-function) 'less-css-indent-line)
+  (when (functionp 'css-smie-rules)
+    (smie-setup css-smie-grammar #'css-smie-rules
+                :forward-token #'css-smie--forward-token
+                :backward-token #'css-smie--backward-token))
+
+  (add-hook 'after-save-hook 'less-css-compile-maybe nil t))
+
+(define-key less-css-mode-map "\C-c\C-c" 'less-css-compile)
+
+(defun less-css-indent-line ()
+  "Indent current line according to LESS CSS indentation rules."
+  (let ((css-navigation-syntax-table less-css-mode-syntax-table))
+    (if (fboundp 'css-indent-line)
+        (css-indent-line)
+      (smie-indent-line))))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
+
+
+(provide 'less-css-mode)
+;;; less-css-mode.el ends here
-- 
2.13.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.2: 0002-Fixes-and-tweaks-for-the-new-Less-CSS-mode.patch --]
[-- Type: text/x-patch, Size: 16163 bytes --]

From cdf3582216b9f0a5a031dbad5784d87dbabb8a36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Tue, 1 Aug 2017 20:23:21 +0200
Subject: [PATCH 2/2] Fixes and tweaks for the new Less CSS mode

* etc/NEWS: Add an entry for the new mode.

* lisp/textmodes/less-css-mode.el (less-css): Tweak docstring.
(less-css-lessc-command): Tweak docstring. Don't mark it as
safe. Don't autoload.
(less-css-compile-at-save, less-css-lessc-options)
(less-css-output-directory): Tweak docstrings. Don't autoload.
(less-css-output-file-name): Tweak docstring. Don't mark it as safe.
(less-css-input-file-name): Tweak docstring. Don't autoload.
(less-css-compile-maybe): Use `when' for one-armed `if'.
(less-css--output-path): Tweak docstring.
(less-css--maybe-shell-quote-command): Remove function.
(less-css-compile): Don't autoload. Tweak docstring and message. Fix
compiler warning. Use `string-join' instead of `mapconcat'.
(less-css-font-lock-keywords): Use `font-lock-variable-name-face' for
variables.
(less-css-mode-syntax-table, less-css-mode-map): New variables.
(less-css-mode): Change status line mode name from "LESS" to
"Less". Tweak docstring. Move syntax table definitions to
`less-css-mode-syntax-table'.
(less-css-indent-line): Remove function.
---
 etc/NEWS                        |   3 +
 lisp/textmodes/less-css-mode.el | 261 ++++++++++++++++++----------------------
 2 files changed, 119 insertions(+), 145 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 2b789be3c8..7c2172947e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1100,6 +1100,9 @@ fontification, and commenting for embedded JavaScript and CSS.
 
 ** New minor mode 'pixel-scroll-mode' provides smooth pixel-level scrolling.
 
+** New major mode 'less-css-mode' (a minor variant of 'css-mode') for
+editing Less files.
+
 \f
 * Incompatible Lisp Changes in Emacs 26.1
 
diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el
index 8a981d67b9..1d3f2c6b8d 100644
--- a/lisp/textmodes/less-css-mode.el
+++ b/lisp/textmodes/less-css-mode.el
@@ -1,36 +1,41 @@
-;;; less-css-mode.el --- Major mode for editing LESS CSS files (lesscss.org)
-;;
-;; Copyright (C) 2011-2014 Steve Purcell
-;;
+;;; less-css-mode.el --- Major mode for editing Less CSS files  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2011-2017 Free Software Foundation, Inc.
+
 ;; Author: Steve Purcell <steve@sanityinc.com>
-;; URL: https://github.com/purcell/less-css-mode
-;; Keywords: less css mode
-;; Version: DEV
-;;
-;; 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 2 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.
-;;
+;; Maintainer: Simen Heggestøyl <simenheg@gmail.com>
+;; Keywords: hypermedia
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
 ;;; Commentary:
-;;
-;; This mode provides syntax highlighting for LESS CSS files, plus
-;; optional support for compilation of .less files to .css files at
-;; the time they are saved: use `less-css-compile-at-save' to enable
-;; this.
+
+;; This mode provides syntax highlighting for Less CSS files
+;; (http://lesscss.org/), plus optional support for compilation of
+;; .less files to .css files at the time they are saved: use
+;; `less-css-compile-at-save' to enable this.
 ;;
 ;; Command line utility "lessc" is required if setting
 ;; `less-css-compile-at-save' to t.  To install "lessc" using the
-;; Node.js package manager, run "npm install less"
+;; Node.js package manager, run "npm install less".
 ;;
 ;; Also make sure the "lessc" executable is in Emacs' PATH, example:
-;; (setq exec-path (cons (expand-file-name "~/.gem/ruby/1.8/bin") exec-path))
-;; or customize `less-css-lessc-command' to point to your "lessc" executable.
+;; (push (expand-file-name "~/.gem/ruby/1.8/bin") exec-path)
+;; or customize `less-css-lessc-command' to point to your "lessc"
+;; executable.
 ;;
 ;; We target lessc >= 1.4.0, and thus use the `--no-color' flag by
 ;; default.  You may want to adjust `less-css-lessc-options' for
@@ -56,148 +61,122 @@
 ;;
 ;; If you don't need CSS output but would like to be warned of any
 ;; syntax errors in your .less source, consider using `flymake-less':
-;; https://github.com/purcell/flymake-less
-;;
+;; https://github.com/purcell/flymake-less.
+
 ;;; Credits
-;;
+
 ;; The original code for this mode was, in large part, written using
 ;; Anton Johansson's scss-mode as a template -- thanks Anton!
 ;; https://github.com/antonj
-;;
+
 ;;; Code:
 
-(require 'derived)
 (require 'compile)
-
-;; There are at least three css-mode.el implementations, but we need
-;; the right one in order to work as expected, not the versions by
-;; Landström or Garshol
-
 (require 'css-mode)
-(unless (or (boundp 'css-navigation-syntax-table)
-            (functionp 'css-smie-rules))
-  (error "Wrong css-mode.el: please use the version by Stefan Monnier, bundled with Emacs >= 23"))
+(require 'derived)
+(eval-when-compile (require 'subr-x))
 
 (defgroup less-css nil
-  "Less-css mode"
+  "Less CSS mode."
   :prefix "less-css-"
   :group 'css)
 
-;;;###autoload
 (defcustom less-css-lessc-command "lessc"
-  "Command used to compile LESS files.
-Should be lessc or the complete path to your lessc executable,
-  e.g.: \"~/.gem/ruby/1.8/bin/lessc\""
-  :type 'file
-  :group 'less-css
-  :safe 'stringp)
+  "Command used to compile Less files.
+Should be \"lessc\" or the complete path to your lessc
+executable, e.g.: \"~/.gem/ruby/1.8/bin/lessc\"."
+  :type 'file)
 
-;;;###autoload
 (defcustom less-css-compile-at-save nil
-  "If non-nil, the LESS buffers will be compiled to CSS after each save."
-  :type 'boolean
-  :group 'less-css
-  :safe 'booleanp)
-
+  "If non-nil, Less buffers are compiled to CSS after each save."
+  :type 'boolean)
 ;;;###autoload
-(defcustom less-css-lessc-options '("--no-color")
-  "Command line options for less executable.
+(put 'less-css-compile-at-save 'safe-local-variable 'booleanp)
 
+(defcustom less-css-lessc-options '("--no-color")
+  "Command line options for Less executable.
 Use \"-x\" to minify output."
-  :type '(repeat string)
-  :group 'less-css
-  :safe t)
-
+  :type '(repeat string))
 ;;;###autoload
-(defcustom less-css-output-directory nil
-  "Directory in which to save CSS, or nil to use the LESS file's directory.
+(put 'less-css-lessc-options 'safe-local-variable t)
 
-This path is expanded relative to the directory of the LESS file
+(defcustom less-css-output-directory nil
+  "Directory in which to save CSS, or nil to use the Less file's directory.
+This path is expanded relative to the directory of the Less file
 using `expand-file-name', so both relative and absolute paths
 will work as expected."
-  :type 'directory
-  :group 'less-css
-  :safe 'stringp)
-
+  :type 'directory)
 ;;;###autoload
+(put 'less-css-output-directory 'safe-local-variable 'stringp)
+
 (defcustom less-css-output-file-name nil
   "File name in which to save CSS, or nil to use <name>.css for <name>.less.
-
 This can be also be set to a full path, or a relative path.  If
 the path is relative, it will be relative to the value of
 `less-css-output-dir', if set, or the current directory by
 default."
-  :type 'file
-  :group 'less-css
-  :safe 'stringp)
+  :type 'file)
 (make-variable-buffer-local 'less-css-output-file-name)
 
-;;;###autoload
 (defcustom less-css-input-file-name nil
   "File name which will be compiled to CSS.
-
 When the current buffer is saved `less-css-input-file-name' file
-will be compiled to css instead of the current file.
+will be compiled to CSS instead of the current file.
 
 Set this in order to trigger compilation of a \"master\" .less
 file which includes the current file.  The best way to set this
 variable in most cases is likely to be via directory local
 variables.
 
-This can be also be set to a full path, or a relative path. If
-the path is relative, it will be relative to the the current directory by
-default."
-  :type 'file
-  :group 'less-css
-  :safe 'stringp)
+This can be also be set to a full path, or a relative path.  If
+the path is relative, it will be relative to the the current
+directory by default."
+  :type 'file)
+;;;###autoload
+(put 'less-css-input-file-name 'safe-local-variable 'stringp)
 (make-variable-buffer-local 'less-css-input-file-name)
 
 (defconst less-css-default-error-regex
   "^\\(?:\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\\)?")
 
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Compilation to CSS
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Compilation to CSS
 
 (add-to-list 'compilation-error-regexp-alist-alist
              (list 'less-css less-css-default-error-regex 2 3 4 nil 1))
 (add-to-list 'compilation-error-regexp-alist 'less-css)
 
-
 (defun less-css-compile-maybe ()
   "Run `less-css-compile' if `less-css-compile-at-save' is non-nil."
-  (if less-css-compile-at-save
-      (less-css-compile)))
+  (when less-css-compile-at-save
+    (less-css-compile)))
 
 (defun less-css--output-path ()
-  "Calculate the path for the compiled CSS file created by `less-css-compile'."
-  (expand-file-name (or less-css-output-file-name
-                        (concat (file-name-nondirectory (file-name-sans-extension buffer-file-name)) ".css"))
-                    (or less-css-output-directory default-directory)))
+  "Return the path to use for the compiled CSS file."
+  (expand-file-name
+   (or less-css-output-file-name
+       (concat
+        (file-name-nondirectory
+         (file-name-sans-extension buffer-file-name))
+        ".css"))
+   (or less-css-output-directory default-directory)))
 
-(defun less-css--maybe-shell-quote-command (command)
-  "Selectively shell-quote COMMAND appropriately for `system-type'."
-  (funcall (if (eq system-type 'windows-nt)
-               'identity
-             'shell-quote-argument) command))
-
-;;;###autoload
 (defun less-css-compile ()
-  "Compiles the current buffer to css using `less-css-lessc-command'."
+  "Compile the current buffer to CSS using `less-css-lessc-command'."
   (interactive)
-  (message "Compiling less to css")
-  (let ((compilation-buffer-name-function (lambda (mode-name) "*less-css-compilation*")))
+  (message "Compiling Less to CSS")
+  (let ((compilation-buffer-name-function
+         (lambda (_) "*less-css-compilation*")))
     (save-window-excursion
       (with-current-buffer
           (compile
-           (mapconcat 'identity
-                      (append (list (less-css--maybe-shell-quote-command less-css-lessc-command))
-                              (mapcar 'shell-quote-argument less-css-lessc-options)
-                              (list (shell-quote-argument
-                                     (or less-css-input-file-name buffer-file-name))
-                                    (shell-quote-argument (less-css--output-path))))
-                      " "))
+           (string-join
+            (append
+             (list less-css-lessc-command)
+             (mapcar #'shell-quote-argument less-css-lessc-options)
+             (list (shell-quote-argument
+                    (or less-css-input-file-name buffer-file-name))
+                   (shell-quote-argument (less-css--output-path))))
+            " "))
         (add-hook 'compilation-finish-functions
                   (lambda (buf msg)
                     (unless (string-match-p "^finished" msg)
@@ -205,54 +184,46 @@ less-css-compile
                   nil
                   t)))))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Minor mode
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Major mode
 
-;; TODO: interpolation ("@{val}"), escaped values (~"..."), JS eval (~`...`), custom faces
+;; TODO:
+;; - interpolation ("@{val}")
+;; - escaped values (~"...")
+;; - JS eval (~`...`)
+;; - custom faces.
 (defconst less-css-font-lock-keywords
   '(;; Variables
-    ("@[a-z_-][a-z-_0-9]*" . font-lock-constant-face)
+    ("@[a-z_-][a-z-_0-9]*" . font-lock-variable-name-face)
     ("&" . font-lock-preprocessor-face)
     ;; Mixins
-    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" . (1 font-lock-keyword-face)))
-  )
-
+    ("\\(?:[ \t{;]\\|^\\)\\(\\.[a-z_-][a-z-_0-9]*\\)[ \t]*;" .
+     (1 font-lock-keyword-face))))
+
+(defvar less-css-mode-syntax-table
+  (let ((st (make-syntax-table css-mode-syntax-table)))
+    ;; C++-style comments.
+    (modify-syntax-entry ?/ ". 124b" st)
+    (modify-syntax-entry ?* ". 23" st)
+    (modify-syntax-entry ?\n "> b" st)
+    ;; Special chars that sometimes come at the beginning of words.
+    (modify-syntax-entry ?. "'" st)
+    st))
+
+(defvar less-css-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-c\C-c" 'less-css-compile)
+    map))
+
+;;;###autoload (add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
 ;;;###autoload
-(define-derived-mode less-css-mode css-mode "LESS"
-  "Major mode for editing LESS files, http://lesscss.org/
+(define-derived-mode less-css-mode css-mode "Less"
+  "Major mode for editing Less files (http://lesscss.org/).
 Special commands:
 \\{less-css-mode-map}"
   (font-lock-add-keywords nil less-css-font-lock-keywords)
-  ;; cpp-style comments
-  (modify-syntax-entry ?/ ". 124b" less-css-mode-syntax-table)
-  (modify-syntax-entry ?* ". 23" less-css-mode-syntax-table)
-  (modify-syntax-entry ?\n "> b" less-css-mode-syntax-table)
-  ;; Special chars that sometimes come at the beginning of words.
-  (modify-syntax-entry ?. "'" less-css-mode-syntax-table)
-
-  (set (make-local-variable 'comment-start) "//")
-  (set (make-local-variable 'comment-end) "")
-  (set (make-local-variable 'indent-line-function) 'less-css-indent-line)
-  (when (functionp 'css-smie-rules)
-    (smie-setup css-smie-grammar #'css-smie-rules
-                :forward-token #'css-smie--forward-token
-                :backward-token #'css-smie--backward-token))
-
+  (setq-local comment-start "//")
+  (setq-local comment-end "")
   (add-hook 'after-save-hook 'less-css-compile-maybe nil t))
 
-(define-key less-css-mode-map "\C-c\C-c" 'less-css-compile)
-
-(defun less-css-indent-line ()
-  "Indent current line according to LESS CSS indentation rules."
-  (let ((css-navigation-syntax-table less-css-mode-syntax-table))
-    (if (fboundp 'css-indent-line)
-        (css-indent-line)
-      (smie-indent-line))))
-
-;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
-
-
 (provide 'less-css-mode)
 ;;; less-css-mode.el ends here
-- 
2.13.2


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-08-10 22:53       ` Simen Heggestøyl
@ 2017-08-14 10:06         ` Stefan Monnier
  2017-08-15 10:33           ` Simen Heggestøyl
  0 siblings, 1 reply; 14+ messages in thread
From: Stefan Monnier @ 2017-08-14 10:06 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: steve, Tom Tromey, 27881, dgutov

> How does it look now?

Fine by me,


        Stefan





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27881: New major mode: Less mode
  2017-08-14 10:06         ` Stefan Monnier
@ 2017-08-15 10:33           ` Simen Heggestøyl
  0 siblings, 0 replies; 14+ messages in thread
From: Simen Heggestøyl @ 2017-08-15 10:33 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: steve, Tom Tromey, dgutov, 27881-done

Installed. Thanks again!

-- Simen






^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2017-08-15 10:33 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-07-30 17:53 bug#27881: New major mode: Less mode Simen Heggestøyl
2017-07-30 20:59 ` Stefan Monnier
2017-07-31  6:39   ` Steve Purcell
2017-07-31 12:44     ` Stefan Monnier
2017-07-31  2:02 ` Glenn Morris
2017-07-31  4:32   ` Marcin Borkowski
2017-08-03 17:47     ` Simen Heggestøyl
2017-08-04 21:33       ` Richard Stallman
2017-08-02  2:16 ` Tom Tromey
2017-08-03 17:50   ` Simen Heggestøyl
2017-08-03 20:08     ` Stefan Monnier
2017-08-10 22:53       ` Simen Heggestøyl
2017-08-14 10:06         ` Stefan Monnier
2017-08-15 10:33           ` Simen Heggestøyl

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).