From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Simen =?UTF-8?Q?Heggest=C3=B8yl?= Newsgroups: gmane.emacs.bugs Subject: bug#27881: New major mode: Less mode Date: Thu, 03 Aug 2017 19:50:58 +0200 Message-ID: <1501782658.6810.1@smtp.gmail.com> References: <1501437183.13205.0@smtp.gmail.com> <"Simen \=\?utf-8\?Q\?Heggest\?\= \=\?utf-8\?Q\?\=C3\=B8yl\=22's\?\= message of "Sun, 30 Jul 2017 19:53:03 +0200"> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-wU7UjnCtLc6993g+jM5r" X-Trace: blaine.gmane.org 1501782742 641 195.159.176.226 (3 Aug 2017 17:52:22 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 3 Aug 2017 17:52:22 +0000 (UTC) Cc: steve@sanityinc.com, dgutov@yandex.ru, 27881@debbugs.gnu.org, monnier@iro.umontreal.ca To: Tom Tromey Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Thu Aug 03 19:52:15 2017 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ddKIA-0008I3-UY for geb-bug-gnu-emacs@m.gmane.org; Thu, 03 Aug 2017 19:52:15 +0200 Original-Received: from localhost ([::1]:36840 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ddKIG-00061E-OB for geb-bug-gnu-emacs@m.gmane.org; Thu, 03 Aug 2017 13:52:20 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:36543) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ddKI2-0005zf-0n for bug-gnu-emacs@gnu.org; Thu, 03 Aug 2017 13:52:09 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ddKHy-00055u-AK for bug-gnu-emacs@gnu.org; Thu, 03 Aug 2017 13:52:06 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:37648) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ddKHy-00055d-2Q for bug-gnu-emacs@gnu.org; Thu, 03 Aug 2017 13:52:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1ddKHx-0005Jo-Q1 for bug-gnu-emacs@gnu.org; Thu, 03 Aug 2017 13:52:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Simen =?UTF-8?Q?Heggest=C3=B8yl?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 03 Aug 2017 17:52:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 27881 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 27881-submit@debbugs.gnu.org id=B27881.150178267820358 (code B ref 27881); Thu, 03 Aug 2017 17:52:01 +0000 Original-Received: (at 27881) by debbugs.gnu.org; 3 Aug 2017 17:51:18 +0000 Original-Received: from localhost ([127.0.0.1]:40325 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ddKH8-0005I2-G6 for submit@debbugs.gnu.org; Thu, 03 Aug 2017 13:51:16 -0400 Original-Received: from mail-lf0-f49.google.com ([209.85.215.49]:33764) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ddKH5-0005He-ON for 27881@debbugs.gnu.org; Thu, 03 Aug 2017 13:51:09 -0400 Original-Received: by mail-lf0-f49.google.com with SMTP id d17so9213225lfe.0 for <27881@debbugs.gnu.org>; Thu, 03 Aug 2017 10:51:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:subject:to:cc:message-id:in-reply-to:references :mime-version; bh=m63GTWloDGmLv5YJdCKku8tjnQoko7Z4i+rDqv2gTa0=; b=L4jNtitbE4TjeJSnP2QIvjSoWVGJhkqT6hNb6oDiMShLB8zI4uEPUEmoS9ZiU0KEYq g4SrCU1Ilg43Ngne0pKIb2AX0Oqgk8+HF826w8OlPUWpVOF+VMQrRG7WFHtDHM7ei0d9 GzMYnCq64hOVU5ZR93LPKXtQTs+2+qnH+nGry0YcFEXBhPjddMsdYlTqmzdP5RiBg6VD j1sOkmVdK1ylf2Lvq9+BcdkmO56fI0NuxkwwUE4lvpTW3QpsGlVnO8tfsVTBoEdzlkfH M72bUMTwKixmi+5ee5xwX4nzI74DGRbDeZz36h0yjLpPy3Juoomm8yJV1QFNQ2rFEDGJ 7+lA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:subject:to:cc:message-id:in-reply-to :references:mime-version; bh=m63GTWloDGmLv5YJdCKku8tjnQoko7Z4i+rDqv2gTa0=; b=jnEXn1qUkD/8B/IAW34ms97twDBQXliJTRcT8KfbsRfEaX2n24XRwTrSMM7j55basg cic2SxC5B25cnUTAkwXJfLBRJPgvsw10lhTkju4Ct+sAXlU9ZvRYu7eBhf8LmrqlTkBl 5fm995j82qD+RvSR/FZeHCGkPGuaf2LzZbU4ZpDMmnnP612qD7+uOn3uwh+F2NfkgPLJ orgfJhbH5TTfgd4cZeoHZQRAAQRODV9Ey68MOySOKfnL4Dgb4l5UZXp45hZaaR7Xv2cC lsTT7475WJ0IzlRkc6xGiCHNni0Y1J7jGWb951AMKeLgx4Si7norP1d4XHGHgiJ16CJo 20Kg== X-Gm-Message-State: AHYfb5jkV498eHaSpYtCZQmkV6C2fN1C595u6keDIidSg0+QxBAZ51vN grYHv3Mf6AAX+pwU X-Received: by 10.25.225.70 with SMTP id y67mr909669lfg.34.1501782662027; Thu, 03 Aug 2017 10:51:02 -0700 (PDT) Original-Received: from [192.168.100.10] (cm-84.210.143.4.getinternet.no. [84.210.143.4]) by smtp.gmail.com with ESMTPSA id f88sm148286lji.47.2017.08.03.10.51.00 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 03 Aug 2017 10:51:00 -0700 (PDT) In-Reply-To: <87k22m90is.fsf@tromey.com> X-Mailer: geary/0.11.3 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:135292 Archived-At: --=-wU7UjnCtLc6993g+jM5r Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: quoted-printable On Sun, Jul 30, 2017 at 10:59 PM, Stefan Monnier=20 wrote: > See some comments below. One thing, tho: an alternative would be to=20 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 =20 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=20 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=20 better > to use [[:alpha:]_-] and [[:alnum:]_-] in the above regexps. > > > Unsure, but it=E2=80=99s likely safe to allow non-ASCII alphanumeric cha= rs. 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=20 wrote: > The safety info may need to be autoloaded, indeed (IIUC we have > a misfeature in there: we should enable the major mode (when=20 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 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 = --=-wU7UjnCtLc6993g+jM5r Content-Type: multipart/mixed; boundary="=-ywzuS9897uoUaF6vLJRR" --=-ywzuS9897uoUaF6vLJRR Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-WIP-New-major-mode-Less-CSS-mode.patch Content-Transfer-Encoding: quoted-printable >From 5f7cbac4d28cfc245c81b1527d8995d3310d5388 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Simen=3D20Heggest=3DC3=3DB8yl?=3D 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.or= g) +;; +;; Copyright (C) 2011-2014 Steve Purcell +;; +;; Author: Steve Purcell +;; 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-pat= h)) +;; or customize `less-css-lessc-command' to point to your "lessc" executab= le. +;; +;; We target lessc >=3D 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=C3=B6m 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, bun= dled with Emacs >=3D 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 .css for .les= s. + +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 li= ne \\)\\([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-compi= le'." + (expand-file-name (or less-css-output-file-name + (concat (file-name-nondirectory (file-name-sans-ex= tension 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-c= ompilation*"))) + (save-window-excursion + (with-current-buffer + (compile + (mapconcat 'identity + (append (list (less-css--maybe-shell-quote-command l= ess-css-lessc-command)) + (mapcar 'shell-quote-argument less-css-lessc= -options) + (list (shell-quote-argument + (or less-css-input-file-name buffer-f= ile-name)) + (shell-quote-argument (less-css--outpu= t-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-loc= k-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 --=20 2.13.2 = --=-ywzuS9897uoUaF6vLJRR Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-WIP-Fixes-and-tweaks-for-the-new-Less-CSS-mode.patch Content-Transfer-Encoding: quoted-printable >From 030b430452b7a29f1231796c0f1bb65982eaf458 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Simen=3D20Heggest=3DC3=3DB8yl?=3D 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.or= g) -;; -;; Copyright (C) 2011-2014 Steve Purcell -;; +;;; less-css-mode.el --- Major mode for editing Less CSS files -*- lexica= l-binding: t; -*- + +;; Copyright (C) 2011-2017 Free Software Foundation, Inc. + ;; Author: Steve Purcell -;; 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=C3=B8yl +;; 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 . + ;;; 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-pat= h)) -;; or customize `less-css-lessc-command' to point to your "lessc" executab= le. +;; (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 >=3D 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: =20 -(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=C3=B6m 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, bun= dled with Emacs >=3D 23")) +(require 'derived) +(eval-when-compile (require 'subr-x)) =20 (defgroup less-css nil - "Less-css mode" + "Less CSS mode." :prefix "less-css-" :group 'css) =20 -;;;###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) =20 -;;;###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) =20 -;;;###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) =20 -;;;###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) =20 -;;;###autoload (defcustom less-css-output-file-name nil "File name in which to save CSS, or nil to use .css for .les= s. - 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) =20 -;;;###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. =20 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. =20 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) =20 (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 li= ne \\)\\([0-9]+\\)\\(?::\\|, column \\)\\([0-9]+\\):?\\(?:\e\\[39m\\)?") =20 - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;; -;; Compilation to CSS -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;; +;;; Compilation to CSS =20 (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) =20 - (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))) =20 (defun less-css--output-path () - "Calculate the path for the compiled CSS file created by `less-css-compi= le'." - (expand-file-name (or less-css-output-file-name - (concat (file-name-nondirectory (file-name-sans-ex= tension 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))) =20 -(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-c= ompilation*"))) + (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 l= ess-css-lessc-command)) - (mapcar 'shell-quote-argument less-css-lessc= -options) - (list (shell-quote-argument - (or less-css-input-file-name buffer-f= ile-name)) - (shell-quote-argument (less-css--outpu= t-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))))) =20 -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;; -;; Minor mode -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;; +;;; Major mode =20 -;; 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-loc= k-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-mod= e)) ;;;###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)) =20 -(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 --=20 2.13.2 = --=-ywzuS9897uoUaF6vLJRR-- --=-wU7UjnCtLc6993g+jM5r--