* bug#25525: 25.1.90; add color highlighting to css mode
@ 2017-01-25 8:06 Tom Tromey
2017-01-25 15:51 ` Eli Zaretskii
` (3 more replies)
0 siblings, 4 replies; 39+ messages in thread
From: Tom Tromey @ 2017-01-25 8:06 UTC (permalink / raw)
To: 25525
[-- Attachment #1: Type: text/plain, Size: 718 bytes --]
This patch adds color highlighting to CSS mode.
In particular, this provides a new jit-lock function that recognizes the
various forms of CSS color syntax, including some css-color-4 additions.
When such a color is seen, the background color of the text is set to
the color itself. In order to remain readable, the foreground is set to
a contrasting color, and a box is put around the text (this helps
distinguish colors that are close to the buffer's background color).
While doing this I noticed that css-mode was missing a few named colors
specified by CSS, in particular:
("darkgrey" "darkslategrey" "dimgrey" "grey" "lightgrey"
"lightslategrey" "slategrey")
This patch fixes this problem as well.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: the patch --]
[-- Type: text/x-patch, Size: 17636 bytes --]
commit 2b873579a2b821c073826eed5bc199216fbc14ae
Author: Tom Tromey <tom@tromey.com>
Date: Wed Jan 25 00:53:49 2017 -0700
add color highlighting to css-mode
* lisp/textmodes/css-mode.el (css--color-map): New constant.
(css-value-class-alist): Use css--color-map.
(css--number-regexp, css--percent-regexp)
(css--number-or-percent-regexp, css--angle-regexp): New constants.
(css--color-skip-blanks, css--rgb-color, css--hsl-color): New
functions.
(css--colors-regexp): New constant.
(css--hex-color, css--compute-color, css--contrasty-color)
(css--fontify-colors): New functions.
(css-mode): Register css--fontify-colors with jit-lock.
* test/lisp/textmodes/css-mode-tests.el (css-test-property-values):
Update.
(css-test-rgb-parser, css-test-hsl-parser): New tests.
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index c81c3f6..c4d86f5 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -32,6 +32,9 @@
;;; Code:
+(require 'cl-lib)
+(require 'cl-macs)
+(require 'color)
(require 'seq)
(require 'sgml-mode)
(require 'smie)
@@ -453,8 +456,157 @@ css-property-ids
(mapcar #'car css-property-alist)
"Identifiers for properties.")
+(defconst css--color-map
+ '(("black" . "#000000")
+ ("silver" . "#c0c0c0")
+ ("gray" . "#808080")
+ ("white" . "#ffffff")
+ ("maroon" . "#800000")
+ ("red" . "#ff0000")
+ ("purple" . "#800080")
+ ("fuchsia" . "#ff00ff")
+ ("green" . "#008000")
+ ("lime" . "#00ff00")
+ ("olive" . "#808000")
+ ("yellow" . "#ffff00")
+ ("navy" . "#000080")
+ ("blue" . "#0000ff")
+ ("teal" . "#008080")
+ ("aqua" . "#00ffff")
+ ("orange" . "#ffa500")
+ ("aliceblue" . "#f0f8ff")
+ ("antiquewhite" . "#faebd7")
+ ("aquamarine" . "#7fffd4")
+ ("azure" . "#f0ffff")
+ ("beige" . "#f5f5dc")
+ ("bisque" . "#ffe4c4")
+ ("blanchedalmond" . "#ffebcd")
+ ("blueviolet" . "#8a2be2")
+ ("brown" . "#a52a2a")
+ ("burlywood" . "#deb887")
+ ("cadetblue" . "#5f9ea0")
+ ("chartreuse" . "#7fff00")
+ ("chocolate" . "#d2691e")
+ ("coral" . "#ff7f50")
+ ("cornflowerblue" . "#6495ed")
+ ("cornsilk" . "#fff8dc")
+ ("crimson" . "#dc143c")
+ ("darkblue" . "#00008b")
+ ("darkcyan" . "#008b8b")
+ ("darkgoldenrod" . "#b8860b")
+ ("darkgray" . "#a9a9a9")
+ ("darkgreen" . "#006400")
+ ("darkgrey" . "#a9a9a9")
+ ("darkkhaki" . "#bdb76b")
+ ("darkmagenta" . "#8b008b")
+ ("darkolivegreen" . "#556b2f")
+ ("darkorange" . "#ff8c00")
+ ("darkorchid" . "#9932cc")
+ ("darkred" . "#8b0000")
+ ("darksalmon" . "#e9967a")
+ ("darkseagreen" . "#8fbc8f")
+ ("darkslateblue" . "#483d8b")
+ ("darkslategray" . "#2f4f4f")
+ ("darkslategrey" . "#2f4f4f")
+ ("darkturquoise" . "#00ced1")
+ ("darkviolet" . "#9400d3")
+ ("deeppink" . "#ff1493")
+ ("deepskyblue" . "#00bfff")
+ ("dimgray" . "#696969")
+ ("dimgrey" . "#696969")
+ ("dodgerblue" . "#1e90ff")
+ ("firebrick" . "#b22222")
+ ("floralwhite" . "#fffaf0")
+ ("forestgreen" . "#228b22")
+ ("gainsboro" . "#dcdcdc")
+ ("ghostwhite" . "#f8f8ff")
+ ("gold" . "#ffd700")
+ ("goldenrod" . "#daa520")
+ ("greenyellow" . "#adff2f")
+ ("grey" . "#808080")
+ ("honeydew" . "#f0fff0")
+ ("hotpink" . "#ff69b4")
+ ("indianred" . "#cd5c5c")
+ ("indigo" . "#4b0082")
+ ("ivory" . "#fffff0")
+ ("khaki" . "#f0e68c")
+ ("lavender" . "#e6e6fa")
+ ("lavenderblush" . "#fff0f5")
+ ("lawngreen" . "#7cfc00")
+ ("lemonchiffon" . "#fffacd")
+ ("lightblue" . "#add8e6")
+ ("lightcoral" . "#f08080")
+ ("lightcyan" . "#e0ffff")
+ ("lightgoldenrodyellow" . "#fafad2")
+ ("lightgray" . "#d3d3d3")
+ ("lightgreen" . "#90ee90")
+ ("lightgrey" . "#d3d3d3")
+ ("lightpink" . "#ffb6c1")
+ ("lightsalmon" . "#ffa07a")
+ ("lightseagreen" . "#20b2aa")
+ ("lightskyblue" . "#87cefa")
+ ("lightslategray" . "#778899")
+ ("lightslategrey" . "#778899")
+ ("lightsteelblue" . "#b0c4de")
+ ("lightyellow" . "#ffffe0")
+ ("limegreen" . "#32cd32")
+ ("linen" . "#faf0e6")
+ ("mediumaquamarine" . "#66cdaa")
+ ("mediumblue" . "#0000cd")
+ ("mediumorchid" . "#ba55d3")
+ ("mediumpurple" . "#9370db")
+ ("mediumseagreen" . "#3cb371")
+ ("mediumslateblue" . "#7b68ee")
+ ("mediumspringgreen" . "#00fa9a")
+ ("mediumturquoise" . "#48d1cc")
+ ("mediumvioletred" . "#c71585")
+ ("midnightblue" . "#191970")
+ ("mintcream" . "#f5fffa")
+ ("mistyrose" . "#ffe4e1")
+ ("moccasin" . "#ffe4b5")
+ ("navajowhite" . "#ffdead")
+ ("oldlace" . "#fdf5e6")
+ ("olivedrab" . "#6b8e23")
+ ("orangered" . "#ff4500")
+ ("orchid" . "#da70d6")
+ ("palegoldenrod" . "#eee8aa")
+ ("palegreen" . "#98fb98")
+ ("paleturquoise" . "#afeeee")
+ ("palevioletred" . "#db7093")
+ ("papayawhip" . "#ffefd5")
+ ("peachpuff" . "#ffdab9")
+ ("peru" . "#cd853f")
+ ("pink" . "#ffc0cb")
+ ("plum" . "#dda0dd")
+ ("powderblue" . "#b0e0e6")
+ ("rosybrown" . "#bc8f8f")
+ ("royalblue" . "#4169e1")
+ ("saddlebrown" . "#8b4513")
+ ("salmon" . "#fa8072")
+ ("sandybrown" . "#f4a460")
+ ("seagreen" . "#2e8b57")
+ ("seashell" . "#fff5ee")
+ ("sienna" . "#a0522d")
+ ("skyblue" . "#87ceeb")
+ ("slateblue" . "#6a5acd")
+ ("slategray" . "#708090")
+ ("slategrey" . "#708090")
+ ("snow" . "#fffafa")
+ ("springgreen" . "#00ff7f")
+ ("steelblue" . "#4682b4")
+ ("tan" . "#d2b48c")
+ ("thistle" . "#d8bfd8")
+ ("tomato" . "#ff6347")
+ ("turquoise" . "#40e0d0")
+ ("violet" . "#ee82ee")
+ ("wheat" . "#f5deb3")
+ ("whitesmoke" . "#f5f5f5")
+ ("yellowgreen" . "#9acd32")
+ ("rebeccapurple" . "#663399"))
+ "Map CSS named color to their hex RGB value.")
+
(defconst css-value-class-alist
- '((absolute-size
+ `((absolute-size
"xx-small" "x-small" "small" "medium" "large" "x-large"
"xx-large")
(alphavalue number)
@@ -506,36 +658,7 @@ css-value-class-alist
(line-width length "thin" "medium" "thick")
(linear-gradient "linear-gradient()")
(margin-width "auto" length percentage)
- (named-color
- "aliceblue" "antiquewhite" "aqua" "aquamarine" "azure" "beige"
- "bisque" "black" "blanchedalmond" "blue" "blueviolet" "brown"
- "burlywood" "cadetblue" "chartreuse" "chocolate" "coral"
- "cornflowerblue" "cornsilk" "crimson" "cyan" "darkblue"
- "darkcyan" "darkgoldenrod" "darkgray" "darkgreen" "darkkhaki"
- "darkmagenta" "darkolivegreen" "darkorange" "darkorchid"
- "darkred" "darksalmon" "darkseagreen" "darkslateblue"
- "darkslategray" "darkturquoise" "darkviolet" "deeppink"
- "deepskyblue" "dimgray" "dodgerblue" "firebrick" "floralwhite"
- "forestgreen" "fuchsia" "gainsboro" "ghostwhite" "gold"
- "goldenrod" "gray" "green" "greenyellow" "honeydew" "hotpink"
- "indianred" "indigo" "ivory" "khaki" "lavender" "lavenderblush"
- "lawngreen" "lemonchiffon" "lightblue" "lightcoral" "lightcyan"
- "lightgoldenrodyellow" "lightgray" "lightgreen" "lightpink"
- "lightsalmon" "lightseagreen" "lightskyblue" "lightslategray"
- "lightsteelblue" "lightyellow" "lime" "limegreen" "linen"
- "magenta" "maroon" "mediumaquamarine" "mediumblue" "mediumorchid"
- "mediumpurple" "mediumseagreen" "mediumslateblue"
- "mediumspringgreen" "mediumturquoise" "mediumvioletred"
- "midnightblue" "mintcream" "mistyrose" "moccasin" "navajowhite"
- "navy" "oldlace" "olive" "olivedrab" "orange" "orangered"
- "orchid" "palegoldenrod" "palegreen" "paleturquoise"
- "palevioletred" "papayawhip" "peachpuff" "peru" "pink" "plum"
- "powderblue" "purple" "rebeccapurple" "red" "rosybrown"
- "royalblue" "saddlebrown" "salmon" "sandybrown" "seagreen"
- "seashell" "sienna" "silver" "skyblue" "slateblue" "slategray"
- "snow" "springgreen" "steelblue" "tan" "teal" "thistle" "tomato"
- "turquoise" "violet" "wheat" "white" "whitesmoke" "yellow"
- "yellowgreen")
+ (named-color . ,(mapcar #'car css--color-map))
(number "calc()")
(numeric-figure-values "lining-nums" "oldstyle-nums")
(numeric-fraction-values "diagonal-fractions" "stacked-fractions")
@@ -726,6 +849,196 @@ css-font-lock-keywords
(defvar css-font-lock-defaults
'(css-font-lock-keywords nil t))
+(defconst css--number-regexp
+ "\\(\\(?:[0-9]*\\.[0-9]+\\(?:[eE][0-9]+\\)?\\)\\|[0-9]+\\)"
+ "A regular expression matching a CSS number.")
+
+(defconst css--percent-regexp "\\([0-9]+\\)%"
+ "A regular expression matching a CSS percentage.")
+
+(defconst css--number-or-percent-regexp
+ (concat "\\(?:" css--percent-regexp "\\)\\|\\(?:" css--number-regexp "\\)")
+ "A regular expression matching a CSS number or a CSS percentage.")
+
+(defconst css--angle-regexp
+ (concat css--number-regexp
+ (regexp-opt '("deg" "grad" "rad" "turn") t)
+ "?")
+ "A regular expression matching a CSS angle.")
+
+(defun css--color-skip-blanks ()
+ "Skip blanks and comments."
+ (while (forward-comment 1)))
+
+(cl-defun css--rgb-color ()
+ "Parse a CSS rgb() or rgba() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '())
+ (iter 0))
+ (while (< iter 4)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--css-4-rgb nil))
+ (let* ((is-percent (match-beginning 1))
+ (str (match-string (if is-percent 1 2)))
+ (number (string-to-number str)))
+ (when is-percent
+ (setq number (* 255 (/ number 100.0))))
+ ;; Don't push the alpha.
+ (when (< iter 3)
+ (push (min (max 0 (truncate number)) 255) result))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)
+ (cl-incf iter)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (and (= (skip-chars-forward ",/") 0)
+ (= iter 3))
+ ;; The alpha is optional.
+ (cl-incf iter))
+ (css--color-skip-blanks)))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'format "#%02x%02x%02x" (nreverse result)))))
+
+(cl-defun css--hsl-color ()
+ "Parse a CSS hsl() or hsla() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '()))
+ ;; First parse the hue.
+ (css--color-skip-blanks)
+ (unless (looking-at css--angle-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((hue (string-to-number (match-string 1)))
+ (unit (match-string 2)))
+ (goto-char (match-end 0))
+ ;; Note that here "turn" is just passed through.
+ (cond
+ ((or (not unit) (equal unit "deg"))
+ ;; Degrees.
+ (setq hue (/ hue 360.0)))
+ ((equal unit "grad")
+ (setq hue (/ hue 400.0)))
+ ((equal unit "rad")
+ (setq hue (/ hue (* 2 float-pi)))))
+ (push (mod hue 1.0) result))
+ (dotimes (_ 2)
+ (skip-chars-forward ",")
+ (css--color-skip-blanks)
+ (unless (looking-at css--percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((number (string-to-number (match-string 1))))
+ (setq number (/ number 100.0))
+ (push (min (max number 0.0) 1.0) result)
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)))
+ (css--color-skip-blanks)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (> (skip-chars-forward ",/") 0)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'color-rgb-to-hex
+ (apply #'color-hsl-to-rgb (nreverse result))))))
+
+(defconst css--colors-regexp
+ (concat
+ ;; Named colors.
+ (regexp-opt (mapcar #'car css--color-map) t)
+ "\\|"
+ ;; Short hex. css-color-4 adds alpha.
+ "\\(#[0-9a-fA-F]\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; Long hex. css-color-4 adds alpha.
+ "\\(#\\(?:[0-9a-fA-F][0-9a-fA-F]\\)\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; RGB.
+ "\\(rgba?(\\)"
+ "\\|"
+ ;; HSL.
+ "\\(hsla?(\\)")
+ "A regular expression that matches the start of a CSS color.")
+
+(defun css--hex-color (str)
+ "Convert a CSS hex color to an Emacs hex color.
+STR is the incoming CSS hex color.
+This function simply drops any transparency."
+ ;; Either #RGB or #RRGGBB, drop the "A" or "AA".
+ (if (> (length str) 4)
+ (substring str 0 7)
+ (substring str 0 4)))
+
+(defun css--compute-color ()
+ "Return the CSS color at point.
+Point should be just after the start of a CSS color, as recognized
+by `css--colors-regexp'. This function will either return the color,
+as a hex RGB string; or `nil' if no color could be recognized. When
+this function returns, point will be at the end of the recognized
+color."
+ (let ((match (downcase (match-string 0))))
+ (cond
+ ((eq (aref match 0) ?#)
+ (css--hex-color match))
+ ((member match '("rgb(" "rgba("))
+ (css--rgb-color))
+ ((member match '("hsl(" "hsla("))
+ (css--hsl-color))
+ ;; Evaluate to the color if the name is found.
+ ((cdr (assoc match css--color-map)))
+ (t
+ (error "invalid case in css--compute-color")))))
+
+(defun css--contrasty-color (name)
+ "Return a color that contrasts with NAME.
+NAME is of any form accepted by `color-name-to-rgb'.
+The returned color will be usable by Emacs and will contrast
+with NAME; in particular so that if NAME is used as a background
+color, the returned color can be used as the foreground and still
+be readable."
+ (let* ((color (color-name-to-rgb name))
+ (red (car color))
+ (green (cadr color))
+ (blue (cl-caddr color))
+ (luma (+ (* 0.299 red) (* 0.587 green) (* 0.114 blue))))
+ (if (> luma 0.5)
+ "black"
+ "white")))
+
+(defun css--fontify-colors (start end)
+ "Fontify CSS colors between START and END.
+START and END are buffer positions.
+This function is used via `jit-lock-register'."
+ (message "FONTIFY %S %S" start end)
+ (save-excursion
+ (let ((case-fold-search t))
+ (goto-char start)
+ (while (re-search-forward css--colors-regexp end t)
+ ;; Skip comments and strings.
+ (unless (nth 8 (syntax-ppss))
+ (let ((start (match-beginning 0))
+ (color (css--compute-color)))
+ (when color
+ (with-silent-modifications
+ ;; Use the color as the background, to make it more
+ ;; clear. Use a contrasting color as the foreground,
+ ;; to make it readable. Finally, have a small box
+ ;; using the existing foreground color, to make sure
+ ;; it stands out a bit from any other text; in
+ ;; particular this is nice when the color matches the
+ ;; buffer's background color.
+ (add-text-properties
+ start (point)
+ (list 'face (list :background color
+ :foreground (css--contrasty-color color)
+ :box '(:line-width -1))))))))))))
+
(defcustom css-indent-offset 4
"Basic size of one indentation step."
:version "22.2"
@@ -945,6 +1258,7 @@ css-mode
:backward-token #'css-smie--backward-token)
(setq-local electric-indent-chars
(append css-electric-keys electric-indent-chars))
+ (jit-lock-register #'css--fontify-colors)
(add-hook 'completion-at-point-functions
#'css-completion-at-point nil 'local))
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index 6eb32ea..f058bcf 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -58,7 +58,7 @@
;; Check that the `color' property doesn't cause infinite recursion
;; because it refers to the value class of the same name.
- (should (= (length (css--property-values "color")) 147)))
+ (should (= (length (css--property-values "color")) 152)))
(ert-deftest css-test-property-value-cache ()
"Test that `css--property-value-cache' is in use."
@@ -218,5 +218,40 @@ css-mode-tests--completions
(should (member "body" completions))
(should-not (member "article" completions)))))
+(ert-deftest css-test-rgb-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("255, 0, 127"
+ "255, /* comment */ 0, 127"
+ "255 0 127"
+ "255, 0, 127, 0.75"
+ "255 0 127 / 0.75"
+ "100%, 0%, 50%"
+ "100%, 0%, 50%, 0.115"
+ "100% 0% 50%"
+ "100% 0% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--rgb-color) "#ff007f")))))
+
+(ert-deftest css-test-hsl-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("0, 100%, 50%"
+ "0 100% 50%"
+ "0 /* two */ /* comments */100% 50%"
+ "0, 100%, 50%, 0.75"
+ "0 100% 50% / 0.75"
+ "0deg 100% 50%"
+ "360deg 100% 50%"
+ "0rad, 100%, 50%, 0.115"
+ "0grad, 100%, 50%, 0.115"
+ "1turn 100% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--hsl-color) "#ff0000")))))
+
(provide 'css-mode-tests)
;;; css-mode-tests.el ends here
[-- Attachment #3: Type: text/plain, Size: 6234 bytes --]
Tom
In GNU Emacs 25.1.90.1 (x86_64-unknown-linux-gnu, GTK+ Version 3.20.9)
of 2016-12-20 built on bapiya
Repository revision: 88cdf14b37a7344bb266e94512485e3cc738c23d
Windowing system distributor 'Fedora Project', version 11.0.11901000
System Description: Fedora release 25 (Twenty Five)
Configured using:
'configure --prefix=/home/tromey/Emacs/install/ --with-modules'
Configured features:
XPM JPEG TIFF GIF PNG RSVG IMAGEMAGICK SOUND GPM DBUS GCONF GSETTINGS
NOTIFY LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT LIBOTF XFT ZLIB
TOOLKIT_SCROLL_BARS GTK3 X11 MODULES
Important settings:
value of $LANG: en_US.utf8
value of $XMODIFIERS: @im=ibus
locale-coding-system: utf-8-unix
Major mode: VC dir
Minor modes in effect:
diff-auto-refine-mode: t
which-function-mode: t
erc-services-mode: t
erc-list-mode: t
erc-menu-mode: t
erc-autojoin-mode: t
erc-ring-mode: t
erc-networks-mode: t
erc-pcomplete-mode: t
erc-track-mode: t
erc-match-mode: t
erc-netsplit-mode: t
erc-hl-nicks-mode: t
erc-button-mode: t
erc-fill-mode: t
erc-stamp-mode: t
erc-irccontrols-mode: t
erc-noncommands-mode: t
erc-move-to-prompt-mode: t
erc-readonly-mode: t
savehist-mode: t
tooltip-mode: t
global-eldoc-mode: t
electric-indent-mode: t
mouse-wheel-mode: t
file-name-shadow-mode: t
global-font-lock-mode: t
font-lock-mode: t
auto-composition-mode: t
auto-encryption-mode: t
auto-compression-mode: t
buffer-read-only: t
column-number-mode: t
line-number-mode: t
transient-mark-mode: t
Recent messages:
Saving file /tmp/r.css...
Wrote /tmp/r.css
FONTIFY 30 530
FONTIFY 530 1030
Mark set [2 times]
Saving file /home/tromey/Emacs/trunk/test/lisp/textmodes/css-mode-tests.el...
Wrote /home/tromey/Emacs/trunk/test/lisp/textmodes/css-mode-tests.el
When done with a buffer, type C-x #
Hiding up-to-date and ignored items
When done with a buffer, type C-x #
Load-path shadows:
/home/tromey/.emacs.d/elpa/erc-hl-nicks-1.3.2/erc-hl-nicks hides /home/tromey/.emacs.d/elpa/erc-hl-nicks-1.3.1/erc-hl-nicks
/home/tromey/.emacs.d/elpa/erc-hl-nicks-1.3.2/erc-hl-nicks-autoloads hides /home/tromey/.emacs.d/elpa/erc-hl-nicks-1.3.1/erc-hl-nicks-autoloads
/home/tromey/.emacs.d/elpa/erc-hl-nicks-1.3.2/erc-hl-nicks-pkg hides /home/tromey/.emacs.d/elpa/erc-hl-nicks-1.3.1/erc-hl-nicks-pkg
/home/tromey/.emacs.d/elpa/bubbles-0.5/bubbles hides /home/tromey/Emacs/install/share/emacs/25.1.90/lisp/play/bubbles
Features:
(autoload cus-edit descr-text vc-annotate edebug xref project gnus-draft
bug-reference view tcl web-mode lisp-mnt url-handlers log-view url-http
url-gw url-auth eww url-queue gnus-html xml url-cache mm-url url
url-proxy url-privacy url-expand url-methods url-history url-cookie
url-domsuf smerge-mode shadow emacsbug log-edit pcvs-util whitespace
idutils derived noutline outline find-dired bbdb-sc supercite regi
css-mode smie js sgml-mode json map cc-mode cc-fonts cc-guess cc-menus
cc-cmds apropos debug dabbrev eieio-opt speedbar sb-image ezimage dframe
find-func copyright misearch multi-isearch add-log jka-compr
bbdb-message vc-mtn vc-hg mailalias mail-hist nnir vc-bzr vc-src vc-sccs
vc-svn vc-cvs vc-rcs vc-git diff-mode easy-mmode term/xterm xterm
flow-fill shr-color url-util url-parse url-vars shr dom subr-x
browse-url mm-archive sort smiley gnus-cite gnus-async gnus-bcklg
mail-extr qp gnus-ml disp-table gnus-topic nndraft nnmh nnfolder utf-7
bbdb-gnus bbdb-mua bbdb-com crm network-stream nsm starttls gnus-agent
gnus-srvr gnus-score score-mode nnvirtual gnus-msg nntp gnus-cache
gnus-registry registry eieio-compat eieio-base gnus-art mm-uu mml2015
mm-view mml-smime smime dig mailcap gnus-sum gnus-group gnus-undo
smtpmail sendmail gnus-start gnus-cloud nnimap nnmail mail-source tls
gnutls utf7 netrc nnoo parse-time gnus-spec gnus-int gnus-range message
idna dired rfc822 mml mml-sec epg mm-decode mm-bodies mm-encode
mail-parse rfc2231 rfc2047 rfc2045 ietf-drums mailabbrev gmm-utils
mailheader gnus-win gnus gnus-ems nnheader mail-utils flyspell ispell
diminish edmacro kmacro projectile grep compile ibuf-ext ibuffer dash
appt diary-lib diary-loaddefs cal-menu calendar cal-loaddefs which-func
imenu minimap autorevert filenotify cus-start cus-load status
erc-services erc-list erc-menu erc-join erc-ring erc-networks
erc-pcomplete pcomplete erc-track erc-match erc-netsplit erc-hl-nicks
color erc-button erc-fill erc-stamp wid-edit erc-goodies erc erc-backend
erc-compat format-spec auth-source eieio gnus-util mm-util help-fns
mail-prsvr password-cache thingatpt pp warnings advice vc-dir ewoc vc
vc-dispatcher cc-styles cc-align cc-engine cc-vars cc-defs bbdb
bbdb-site timezone ange-ftp comint ansi-color ring server savehist
finder-inf dwarf-mode-autoloads gdb-shell-autoloads eieio-core
lisppaste-autoloads pydoc-info-autoloads info-look cl-seq cl-macs cl
weblogger-autoloads info package epg-config seq byte-opt gv bytecomp
byte-compile cl-extra help-mode easymenu cconv cl-loaddefs pcase cl-lib
bbdb-loaddefs time-date mule-util tooltip eldoc electric uniquify
ediff-hook vc-hooks lisp-float-type mwheel x-win term/common-win x-dnd
tool-bar dnd fontset image regexp-opt fringe tabulated-list newcomment
elisp-mode lisp-mode prog-mode register page menu-bar rfn-eshadow timer
select scroll-bar mouse jit-lock font-lock syntax facemenu font-core
frame cl-generic cham georgian utf-8-lang misc-lang vietnamese tibetan
thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian
slovak czech european ethiopic indian cyrillic chinese charscript
case-table epa-hook jka-cmpr-hook help simple abbrev minibuffer
cl-preloaded nadvice loaddefs button faces cus-face macroexp files
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget hashtable-print-readable backquote dbusbind inotify
dynamic-setting system-font-setting font-render-setting move-toolbar gtk
x-toolkit x multi-tty make-network-process emacs)
Memory information:
((conses 16 1974680 547097)
(symbols 48 127411 61)
(miscs 40 27623 7735)
(strings 32 525621 148753)
(string-bytes 1 13196826)
(vectors 16 107759)
(vector-slots 8 2410562 185496)
(floats 8 874 1587)
(intervals 56 200301 17468)
(buffers 976 180)
(heap 1024 305851 47738))
^ permalink raw reply related [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 8:06 bug#25525: 25.1.90; add color highlighting to css mode Tom Tromey
@ 2017-01-25 15:51 ` Eli Zaretskii
2017-01-25 17:25 ` Tom Tromey
2017-01-25 16:52 ` Glenn Morris
` (2 subsequent siblings)
3 siblings, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2017-01-25 15:51 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
> From: Tom Tromey <tom@tromey.com>
> Date: Wed, 25 Jan 2017 01:06:48 -0700
>
> This patch adds color highlighting to CSS mode.
>
> In particular, this provides a new jit-lock function that recognizes the
> various forms of CSS color syntax, including some css-color-4 additions.
>
> When such a color is seen, the background color of the text is set to
> the color itself. In order to remain readable, the foreground is set to
> a contrasting color, and a box is put around the text (this helps
> distinguish colors that are close to the buffer's background color).
>
> While doing this I noticed that css-mode was missing a few named colors
> specified by CSS, in particular:
>
> ("darkgrey" "darkslategrey" "dimgrey" "grey" "lightgrey"
> "lightslategrey" "slategrey")
>
> This patch fixes this problem as well.
Thanks.
I wonder whether it would make sense to reuse some existing data and
code here?
For example, tty-colors.el already has a (longer) list of colors with
suitable RGB values, so perhaps we should simply add the few missing
ones to that list, and then use that for CSS?
As another example, tty-colors.el also includes code for parsing and
converting color values (although perhaps not all of the formats you
support in your code).
As yet another example of existing functionality that you could
perhaps reuse, there's the :distant-foreground attribute of a face
that might help you with the issue of color contrast.
If you already considered all those and decided not to use them, could
you tell why?
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 8:06 bug#25525: 25.1.90; add color highlighting to css mode Tom Tromey
2017-01-25 15:51 ` Eli Zaretskii
@ 2017-01-25 16:52 ` Glenn Morris
2017-01-25 20:23 ` Simen Heggestøyl
2017-05-03 22:04 ` bug#25525: done Tom Tromey
3 siblings, 0 replies; 39+ messages in thread
From: Glenn Morris @ 2017-01-25 16:52 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
Tom Tromey wrote:
> +(require 'cl-lib)
> +(require 'cl-macs)
> +(require 'color)
Don't explicitly load cl-macs. It's an internal part of cl-lib, and
loading cl-lib is all you need.
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 15:51 ` Eli Zaretskii
@ 2017-01-25 17:25 ` Tom Tromey
2017-01-25 23:34 ` Tom Tromey
0 siblings, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-01-25 17:25 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 25525, Tom Tromey
>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
Eli> For example, tty-colors.el already has a (longer) list of colors with
Eli> suitable RGB values, so perhaps we should simply add the few missing
Eli> ones to that list, and then use that for CSS?
CSS specifies the named colors that are available -- so the list in
tty-colors.el would not be correct.
Eli> As another example, tty-colors.el also includes code for parsing and
Eli> converting color values (although perhaps not all of the formats you
Eli> support in your code).
Here too the new code supports what CSS specifies. It's important to
the CSS developer that the Emacs mode follow CSS as precisely as
possible; using other formats supplied by tty-colors.el would result in
code that is invalid CSS.
I think the only area of overlap is the #RRGGBB hex syntax, but that is
so trivial as not to need any sharing.
Eli> As yet another example of existing functionality that you could
Eli> perhaps reuse, there's the :distant-foreground attribute of a face
Eli> that might help you with the issue of color contrast.
It seemed simpler to always set the :foreground, but I can experiment
with :distant-foreground instead.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 8:06 bug#25525: 25.1.90; add color highlighting to css mode Tom Tromey
2017-01-25 15:51 ` Eli Zaretskii
2017-01-25 16:52 ` Glenn Morris
@ 2017-01-25 20:23 ` Simen Heggestøyl
2017-01-25 20:41 ` Tom Tromey
2017-01-25 23:24 ` Tom Tromey
2017-05-03 22:04 ` bug#25525: done Tom Tromey
3 siblings, 2 replies; 39+ messages in thread
From: Simen Heggestøyl @ 2017-01-25 20:23 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
Thanks Tom, this looks to be a very nice addition to CSS mode!
Some comments:
- It should be possible to disable it. I'm not sure whether it should
be turned on by default or not, though I'd think that most CSS
developers will find it useful.
- Selectors that happen to contain the name of a color are
highlighted. The 'red' part of '.centered-button' is highlighted in
red, for instance.
- The same goes for variable names in both CSS and SCSS.
- Property names are also highlighted, for instance in the case of the
'white-space' property.
- There's a stray call to 'message' in the beginning of
'css--fontify-colors'.
- The error message in 'css--compute-color' should be capitalized.
- I think it deserves a NEWS entry.
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 20:23 ` Simen Heggestøyl
@ 2017-01-25 20:41 ` Tom Tromey
2017-01-25 23:24 ` Tom Tromey
1 sibling, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-01-25 20:41 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
Simen> - It should be possible to disable it. I'm not sure whether it should
Simen> be turned on by default or not, though I'd think that most CSS
Simen> developers will find it useful.
I can add a defcustom.
Simen> - Selectors that happen to contain the name of a color are
Simen> highlighted. The 'red' part of '.centered-button' is highlighted in
Simen> red, for instance.
Thanks for noticing this.
I am sure I had \b or \< or something in the regexp earlier but I must
have dropped it. I'll fix this up.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 20:23 ` Simen Heggestøyl
2017-01-25 20:41 ` Tom Tromey
@ 2017-01-25 23:24 ` Tom Tromey
2017-01-26 18:25 ` Simen Heggestøyl
1 sibling, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-01-25 23:24 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
[-- Attachment #1: Type: text/plain, Size: 311 bytes --]
Simen> Thanks Tom, this looks to be a very nice addition to CSS mode!
Simen> Some comments:
[...]
I think this version addresses all the review comments.
This necessitated changing the syntax table a bit. Now things like
"#red" appear as a symbol, but "<red" appears as punctuation followed by
a word.
Tom
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: patch v2 --]
[-- Type: text/x-patch, Size: 19816 bytes --]
commit c544f57e5102fb19ab06bea6eb2cfeda5b839890
Author: Tom Tromey <tom@tromey.com>
Date: Wed Jan 25 00:53:49 2017 -0700
add color highlighting to css-mode
* lisp/textmodes/css-mode.el (css--color-map): New constant.
(css-value-class-alist): Use css--color-map.
(css--number-regexp, css--percent-regexp)
(css--number-or-percent-regexp, css--angle-regexp): New constants.
(css--color-skip-blanks, css--rgb-color, css--hsl-color): New
functions.
(css--colors-regexp): New constant.
(css--hex-color, css--compute-color, css--contrasty-color)
(css--fontify-colors): New functions.
(css-mode): Register css--fontify-colors with jit-lock.
(css-mode-syntax-table): Set syntax on more characters.
(css-fontify-colors): New defcustom.
* test/lisp/textmodes/css-mode-tests.el (css-test-property-values):
Update.
(css-test-rgb-parser, css-test-hsl-parser): New tests.
* etc/NEWS: Add entry.
diff --git a/etc/NEWS b/etc/NEWS
index ca66df6..73ef1de 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -603,6 +603,11 @@ HTML tags, classes and IDs using the 'completion-at-point' command.
Completion candidates for HTML classes and IDs are retrieved from open
HTML mode buffers.
+---
+*** CSS colors are fontified using the color they represent as the
+background. For instance, #ff0000 would be fontified with a red
+background.
+
+++
** Emacs now supports character name escape sequences in character and
string literals. The syntax variants \N{character name} and
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index c81c3f6..0f6c996 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -32,6 +32,8 @@
;;; Code:
+(require 'cl-lib)
+(require 'color)
(require 'seq)
(require 'sgml-mode)
(require 'smie)
@@ -453,8 +455,157 @@ css-property-ids
(mapcar #'car css-property-alist)
"Identifiers for properties.")
+(defconst css--color-map
+ '(("black" . "#000000")
+ ("silver" . "#c0c0c0")
+ ("gray" . "#808080")
+ ("white" . "#ffffff")
+ ("maroon" . "#800000")
+ ("red" . "#ff0000")
+ ("purple" . "#800080")
+ ("fuchsia" . "#ff00ff")
+ ("green" . "#008000")
+ ("lime" . "#00ff00")
+ ("olive" . "#808000")
+ ("yellow" . "#ffff00")
+ ("navy" . "#000080")
+ ("blue" . "#0000ff")
+ ("teal" . "#008080")
+ ("aqua" . "#00ffff")
+ ("orange" . "#ffa500")
+ ("aliceblue" . "#f0f8ff")
+ ("antiquewhite" . "#faebd7")
+ ("aquamarine" . "#7fffd4")
+ ("azure" . "#f0ffff")
+ ("beige" . "#f5f5dc")
+ ("bisque" . "#ffe4c4")
+ ("blanchedalmond" . "#ffebcd")
+ ("blueviolet" . "#8a2be2")
+ ("brown" . "#a52a2a")
+ ("burlywood" . "#deb887")
+ ("cadetblue" . "#5f9ea0")
+ ("chartreuse" . "#7fff00")
+ ("chocolate" . "#d2691e")
+ ("coral" . "#ff7f50")
+ ("cornflowerblue" . "#6495ed")
+ ("cornsilk" . "#fff8dc")
+ ("crimson" . "#dc143c")
+ ("darkblue" . "#00008b")
+ ("darkcyan" . "#008b8b")
+ ("darkgoldenrod" . "#b8860b")
+ ("darkgray" . "#a9a9a9")
+ ("darkgreen" . "#006400")
+ ("darkgrey" . "#a9a9a9")
+ ("darkkhaki" . "#bdb76b")
+ ("darkmagenta" . "#8b008b")
+ ("darkolivegreen" . "#556b2f")
+ ("darkorange" . "#ff8c00")
+ ("darkorchid" . "#9932cc")
+ ("darkred" . "#8b0000")
+ ("darksalmon" . "#e9967a")
+ ("darkseagreen" . "#8fbc8f")
+ ("darkslateblue" . "#483d8b")
+ ("darkslategray" . "#2f4f4f")
+ ("darkslategrey" . "#2f4f4f")
+ ("darkturquoise" . "#00ced1")
+ ("darkviolet" . "#9400d3")
+ ("deeppink" . "#ff1493")
+ ("deepskyblue" . "#00bfff")
+ ("dimgray" . "#696969")
+ ("dimgrey" . "#696969")
+ ("dodgerblue" . "#1e90ff")
+ ("firebrick" . "#b22222")
+ ("floralwhite" . "#fffaf0")
+ ("forestgreen" . "#228b22")
+ ("gainsboro" . "#dcdcdc")
+ ("ghostwhite" . "#f8f8ff")
+ ("gold" . "#ffd700")
+ ("goldenrod" . "#daa520")
+ ("greenyellow" . "#adff2f")
+ ("grey" . "#808080")
+ ("honeydew" . "#f0fff0")
+ ("hotpink" . "#ff69b4")
+ ("indianred" . "#cd5c5c")
+ ("indigo" . "#4b0082")
+ ("ivory" . "#fffff0")
+ ("khaki" . "#f0e68c")
+ ("lavender" . "#e6e6fa")
+ ("lavenderblush" . "#fff0f5")
+ ("lawngreen" . "#7cfc00")
+ ("lemonchiffon" . "#fffacd")
+ ("lightblue" . "#add8e6")
+ ("lightcoral" . "#f08080")
+ ("lightcyan" . "#e0ffff")
+ ("lightgoldenrodyellow" . "#fafad2")
+ ("lightgray" . "#d3d3d3")
+ ("lightgreen" . "#90ee90")
+ ("lightgrey" . "#d3d3d3")
+ ("lightpink" . "#ffb6c1")
+ ("lightsalmon" . "#ffa07a")
+ ("lightseagreen" . "#20b2aa")
+ ("lightskyblue" . "#87cefa")
+ ("lightslategray" . "#778899")
+ ("lightslategrey" . "#778899")
+ ("lightsteelblue" . "#b0c4de")
+ ("lightyellow" . "#ffffe0")
+ ("limegreen" . "#32cd32")
+ ("linen" . "#faf0e6")
+ ("mediumaquamarine" . "#66cdaa")
+ ("mediumblue" . "#0000cd")
+ ("mediumorchid" . "#ba55d3")
+ ("mediumpurple" . "#9370db")
+ ("mediumseagreen" . "#3cb371")
+ ("mediumslateblue" . "#7b68ee")
+ ("mediumspringgreen" . "#00fa9a")
+ ("mediumturquoise" . "#48d1cc")
+ ("mediumvioletred" . "#c71585")
+ ("midnightblue" . "#191970")
+ ("mintcream" . "#f5fffa")
+ ("mistyrose" . "#ffe4e1")
+ ("moccasin" . "#ffe4b5")
+ ("navajowhite" . "#ffdead")
+ ("oldlace" . "#fdf5e6")
+ ("olivedrab" . "#6b8e23")
+ ("orangered" . "#ff4500")
+ ("orchid" . "#da70d6")
+ ("palegoldenrod" . "#eee8aa")
+ ("palegreen" . "#98fb98")
+ ("paleturquoise" . "#afeeee")
+ ("palevioletred" . "#db7093")
+ ("papayawhip" . "#ffefd5")
+ ("peachpuff" . "#ffdab9")
+ ("peru" . "#cd853f")
+ ("pink" . "#ffc0cb")
+ ("plum" . "#dda0dd")
+ ("powderblue" . "#b0e0e6")
+ ("rosybrown" . "#bc8f8f")
+ ("royalblue" . "#4169e1")
+ ("saddlebrown" . "#8b4513")
+ ("salmon" . "#fa8072")
+ ("sandybrown" . "#f4a460")
+ ("seagreen" . "#2e8b57")
+ ("seashell" . "#fff5ee")
+ ("sienna" . "#a0522d")
+ ("skyblue" . "#87ceeb")
+ ("slateblue" . "#6a5acd")
+ ("slategray" . "#708090")
+ ("slategrey" . "#708090")
+ ("snow" . "#fffafa")
+ ("springgreen" . "#00ff7f")
+ ("steelblue" . "#4682b4")
+ ("tan" . "#d2b48c")
+ ("thistle" . "#d8bfd8")
+ ("tomato" . "#ff6347")
+ ("turquoise" . "#40e0d0")
+ ("violet" . "#ee82ee")
+ ("wheat" . "#f5deb3")
+ ("whitesmoke" . "#f5f5f5")
+ ("yellowgreen" . "#9acd32")
+ ("rebeccapurple" . "#663399"))
+ "Map CSS named color to their hex RGB value.")
+
(defconst css-value-class-alist
- '((absolute-size
+ `((absolute-size
"xx-small" "x-small" "small" "medium" "large" "x-large"
"xx-large")
(alphavalue number)
@@ -506,36 +657,7 @@ css-value-class-alist
(line-width length "thin" "medium" "thick")
(linear-gradient "linear-gradient()")
(margin-width "auto" length percentage)
- (named-color
- "aliceblue" "antiquewhite" "aqua" "aquamarine" "azure" "beige"
- "bisque" "black" "blanchedalmond" "blue" "blueviolet" "brown"
- "burlywood" "cadetblue" "chartreuse" "chocolate" "coral"
- "cornflowerblue" "cornsilk" "crimson" "cyan" "darkblue"
- "darkcyan" "darkgoldenrod" "darkgray" "darkgreen" "darkkhaki"
- "darkmagenta" "darkolivegreen" "darkorange" "darkorchid"
- "darkred" "darksalmon" "darkseagreen" "darkslateblue"
- "darkslategray" "darkturquoise" "darkviolet" "deeppink"
- "deepskyblue" "dimgray" "dodgerblue" "firebrick" "floralwhite"
- "forestgreen" "fuchsia" "gainsboro" "ghostwhite" "gold"
- "goldenrod" "gray" "green" "greenyellow" "honeydew" "hotpink"
- "indianred" "indigo" "ivory" "khaki" "lavender" "lavenderblush"
- "lawngreen" "lemonchiffon" "lightblue" "lightcoral" "lightcyan"
- "lightgoldenrodyellow" "lightgray" "lightgreen" "lightpink"
- "lightsalmon" "lightseagreen" "lightskyblue" "lightslategray"
- "lightsteelblue" "lightyellow" "lime" "limegreen" "linen"
- "magenta" "maroon" "mediumaquamarine" "mediumblue" "mediumorchid"
- "mediumpurple" "mediumseagreen" "mediumslateblue"
- "mediumspringgreen" "mediumturquoise" "mediumvioletred"
- "midnightblue" "mintcream" "mistyrose" "moccasin" "navajowhite"
- "navy" "oldlace" "olive" "olivedrab" "orange" "orangered"
- "orchid" "palegoldenrod" "palegreen" "paleturquoise"
- "palevioletred" "papayawhip" "peachpuff" "peru" "pink" "plum"
- "powderblue" "purple" "rebeccapurple" "red" "rosybrown"
- "royalblue" "saddlebrown" "salmon" "sandybrown" "seagreen"
- "seashell" "sienna" "silver" "skyblue" "slateblue" "slategray"
- "snow" "springgreen" "steelblue" "tan" "teal" "thistle" "tomato"
- "turquoise" "violet" "wheat" "white" "whitesmoke" "yellow"
- "yellowgreen")
+ (named-color . ,(mapcar #'car css--color-map))
(number "calc()")
(numeric-figure-values "lining-nums" "oldstyle-nums")
(numeric-fraction-values "diagonal-fractions" "stacked-fractions")
@@ -614,11 +736,23 @@ css-mode-syntax-table
(modify-syntax-entry ?\[ "(]" st)
(modify-syntax-entry ?\] ")[" st)
;; Special chars that sometimes come at the beginning of words.
- (modify-syntax-entry ?@ "'" st)
- ;; (modify-syntax-entry ?: "'" st)
- (modify-syntax-entry ?# "'" st)
+ ;; We'll treat them as symbol constituents.
+ (modify-syntax-entry ?@ "_" st)
+ (modify-syntax-entry ?# "_" st)
+ (modify-syntax-entry ?. "_" st)
;; Distinction between words and symbols.
(modify-syntax-entry ?- "_" st)
+
+ (modify-syntax-entry ?! "." st)
+ (modify-syntax-entry ?$ "." st)
+ (modify-syntax-entry ?% "." st)
+ (modify-syntax-entry ?& "." st)
+ (modify-syntax-entry ?+ "." st)
+ (modify-syntax-entry ?, "." st)
+ (modify-syntax-entry ?< "." st)
+ (modify-syntax-entry ?> "." st)
+ (modify-syntax-entry ?= "." st)
+ (modify-syntax-entry ?? "." st)
st))
(eval-and-compile
@@ -726,6 +860,206 @@ css-font-lock-keywords
(defvar css-font-lock-defaults
'(css-font-lock-keywords nil t))
+(defconst css--number-regexp
+ "\\(\\(?:[0-9]*\\.[0-9]+\\(?:[eE][0-9]+\\)?\\)\\|[0-9]+\\)"
+ "A regular expression matching a CSS number.")
+
+(defconst css--percent-regexp "\\([0-9]+\\)%"
+ "A regular expression matching a CSS percentage.")
+
+(defconst css--number-or-percent-regexp
+ (concat "\\(?:" css--percent-regexp "\\)\\|\\(?:" css--number-regexp "\\)")
+ "A regular expression matching a CSS number or a CSS percentage.")
+
+(defconst css--angle-regexp
+ (concat css--number-regexp
+ (regexp-opt '("deg" "grad" "rad" "turn") t)
+ "?")
+ "A regular expression matching a CSS angle.")
+
+(defun css--color-skip-blanks ()
+ "Skip blanks and comments."
+ (while (forward-comment 1)))
+
+(cl-defun css--rgb-color ()
+ "Parse a CSS rgb() or rgba() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '())
+ (iter 0))
+ (while (< iter 4)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--css-4-rgb nil))
+ (let* ((is-percent (match-beginning 1))
+ (str (match-string (if is-percent 1 2)))
+ (number (string-to-number str)))
+ (when is-percent
+ (setq number (* 255 (/ number 100.0))))
+ ;; Don't push the alpha.
+ (when (< iter 3)
+ (push (min (max 0 (truncate number)) 255) result))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)
+ (cl-incf iter)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (and (= (skip-chars-forward ",/") 0)
+ (= iter 3))
+ ;; The alpha is optional.
+ (cl-incf iter))
+ (css--color-skip-blanks)))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'format "#%02x%02x%02x" (nreverse result)))))
+
+(cl-defun css--hsl-color ()
+ "Parse a CSS hsl() or hsla() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '()))
+ ;; First parse the hue.
+ (css--color-skip-blanks)
+ (unless (looking-at css--angle-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((hue (string-to-number (match-string 1)))
+ (unit (match-string 2)))
+ (goto-char (match-end 0))
+ ;; Note that here "turn" is just passed through.
+ (cond
+ ((or (not unit) (equal unit "deg"))
+ ;; Degrees.
+ (setq hue (/ hue 360.0)))
+ ((equal unit "grad")
+ (setq hue (/ hue 400.0)))
+ ((equal unit "rad")
+ (setq hue (/ hue (* 2 float-pi)))))
+ (push (mod hue 1.0) result))
+ (dotimes (_ 2)
+ (skip-chars-forward ",")
+ (css--color-skip-blanks)
+ (unless (looking-at css--percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((number (string-to-number (match-string 1))))
+ (setq number (/ number 100.0))
+ (push (min (max number 0.0) 1.0) result)
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)))
+ (css--color-skip-blanks)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (> (skip-chars-forward ",/") 0)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'color-rgb-to-hex
+ (apply #'color-hsl-to-rgb (nreverse result))))))
+
+(defconst css--colors-regexp
+ (concat
+ ;; Named colors.
+ (regexp-opt (mapcar #'car css--color-map) 'symbols)
+ "\\|"
+ ;; Short hex. css-color-4 adds alpha.
+ "\\(#[0-9a-fA-F]\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; Long hex. css-color-4 adds alpha.
+ "\\(#\\(?:[0-9a-fA-F][0-9a-fA-F]\\)\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; RGB.
+ "\\(\\_<rgba?(\\)"
+ "\\|"
+ ;; HSL.
+ "\\(\\_<hsla?(\\)")
+ "A regular expression that matches the start of a CSS color.")
+
+(defun css--hex-color (str)
+ "Convert a CSS hex color to an Emacs hex color.
+STR is the incoming CSS hex color.
+This function simply drops any transparency."
+ ;; Either #RGB or #RRGGBB, drop the "A" or "AA".
+ (if (> (length str) 4)
+ (substring str 0 7)
+ (substring str 0 4)))
+
+(defun css--compute-color ()
+ "Return the CSS color at point.
+Point should be just after the start of a CSS color, as recognized
+by `css--colors-regexp'. This function will either return the color,
+as a hex RGB string; or `nil' if no color could be recognized. When
+this function returns, point will be at the end of the recognized
+color."
+ (let ((match (downcase (match-string 0))))
+ (cond
+ ((eq (aref match 0) ?#)
+ (css--hex-color match))
+ ((member match '("rgb(" "rgba("))
+ (css--rgb-color))
+ ((member match '("hsl(" "hsla("))
+ (css--hsl-color))
+ ;; Evaluate to the color if the name is found.
+ ((cdr (assoc match css--color-map)))
+ (t
+ (error "Invalid case in css--compute-color")))))
+
+(defun css--contrasty-color (name)
+ "Return a color that contrasts with NAME.
+NAME is of any form accepted by `color-name-to-rgb'.
+The returned color will be usable by Emacs and will contrast
+with NAME; in particular so that if NAME is used as a background
+color, the returned color can be used as the foreground and still
+be readable."
+ (let* ((color (color-name-to-rgb name))
+ (red (car color))
+ (green (cadr color))
+ (blue (cl-caddr color))
+ (luma (+ (* 0.299 red) (* 0.587 green) (* 0.114 blue))))
+ (if (> luma 0.5)
+ "black"
+ "white")))
+
+(defcustom css-fontify-colors t
+ "Whether CSS colors should be fontified using the color as the background.
+When non-`nil', a text representing CSS color will be fontified
+such that its background is the color itself. E.g., #ff0000 will
+be fontified with a red background."
+ :version "26.1"
+ :group 'css
+ :type 'boolean
+ :safe 'booleanp)
+
+(defun css--fontify-colors (start end)
+ "Fontify CSS colors between START and END.
+START and END are buffer positions.
+This function is used via `jit-lock-register'."
+ (when css-fontify-colors
+ (save-excursion
+ (let ((case-fold-search t))
+ (goto-char start)
+ (while (re-search-forward css--colors-regexp end t)
+ ;; Skip comments and strings.
+ (unless (nth 8 (syntax-ppss))
+ (let ((start (match-beginning 0))
+ (color (css--compute-color)))
+ (when color
+ (with-silent-modifications
+ ;; Use the color as the background, to make it more
+ ;; clear. Use a contrasting color as the foreground,
+ ;; to make it readable. Finally, have a small box
+ ;; using the existing foreground color, to make sure
+ ;; it stands out a bit from any other text; in
+ ;; particular this is nice when the color matches the
+ ;; buffer's background color.
+ (add-text-properties
+ start (point)
+ (list 'face (list :background color
+ :foreground (css--contrasty-color color)
+ :box '(:line-width -1)))))))))))))
+
(defcustom css-indent-offset 4
"Basic size of one indentation step."
:version "22.2"
@@ -945,6 +1279,7 @@ css-mode
:backward-token #'css-smie--backward-token)
(setq-local electric-indent-chars
(append css-electric-keys electric-indent-chars))
+ (jit-lock-register #'css--fontify-colors)
(add-hook 'completion-at-point-functions
#'css-completion-at-point nil 'local))
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index 6eb32ea..f058bcf 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -58,7 +58,7 @@
;; Check that the `color' property doesn't cause infinite recursion
;; because it refers to the value class of the same name.
- (should (= (length (css--property-values "color")) 147)))
+ (should (= (length (css--property-values "color")) 152)))
(ert-deftest css-test-property-value-cache ()
"Test that `css--property-value-cache' is in use."
@@ -218,5 +218,40 @@ css-mode-tests--completions
(should (member "body" completions))
(should-not (member "article" completions)))))
+(ert-deftest css-test-rgb-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("255, 0, 127"
+ "255, /* comment */ 0, 127"
+ "255 0 127"
+ "255, 0, 127, 0.75"
+ "255 0 127 / 0.75"
+ "100%, 0%, 50%"
+ "100%, 0%, 50%, 0.115"
+ "100% 0% 50%"
+ "100% 0% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--rgb-color) "#ff007f")))))
+
+(ert-deftest css-test-hsl-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("0, 100%, 50%"
+ "0 100% 50%"
+ "0 /* two */ /* comments */100% 50%"
+ "0, 100%, 50%, 0.75"
+ "0 100% 50% / 0.75"
+ "0deg 100% 50%"
+ "360deg 100% 50%"
+ "0rad, 100%, 50%, 0.115"
+ "0grad, 100%, 50%, 0.115"
+ "1turn 100% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--hsl-color) "#ff0000")))))
+
(provide 'css-mode-tests)
;;; css-mode-tests.el ends here
^ permalink raw reply related [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 17:25 ` Tom Tromey
@ 2017-01-25 23:34 ` Tom Tromey
2017-01-26 16:05 ` Eli Zaretskii
0 siblings, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-01-25 23:34 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
Tom> It seemed simpler to always set the :foreground, but I can experiment
Tom> with :distant-foreground instead.
I gave this a try. I think the code in this patch works better than
:distant-foreground.
One case where it is better is the CSS color "purple", aka #800080.
In my theme the foreground color is black. My patch picks white as the
foreground, but the Emacs chooses not to use a distant-foreground, but
rather keep a black foreground.
You can compare these two cases by evalling:
(progn
(insert (propertize
"hello\n"
'font-lock-face '(:background "#800080" :foreground "black"
:distant-foreground "white")))
(insert (propertize
"hello\n"
'font-lock-face '(:background "#800080" :foreground "white"))))
I find the latter much more readable.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 23:34 ` Tom Tromey
@ 2017-01-26 16:05 ` Eli Zaretskii
2017-01-26 17:13 ` Tom Tromey
2017-01-26 17:17 ` Tom Tromey
0 siblings, 2 replies; 39+ messages in thread
From: Eli Zaretskii @ 2017-01-26 16:05 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
> From: Tom Tromey <tom@tromey.com>
> Cc: Eli Zaretskii <eliz@gnu.org>, 25525@debbugs.gnu.org
> Date: Wed, 25 Jan 2017 16:34:00 -0700
>
> >>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
>
> Tom> It seemed simpler to always set the :foreground, but I can experiment
> Tom> with :distant-foreground instead.
>
> I gave this a try. I think the code in this patch works better than
> :distant-foreground.
>
> One case where it is better is the CSS color "purple", aka #800080.
>
> In my theme the foreground color is black. My patch picks white as the
> foreground, but the Emacs chooses not to use a distant-foreground, but
> rather keep a black foreground.
>
> You can compare these two cases by evalling:
>
> (progn
> (insert (propertize
> "hello\n"
> 'font-lock-face '(:background "#800080" :foreground "black"
> :distant-foreground "white")))
> (insert (propertize
> "hello\n"
> 'font-lock-face '(:background "#800080" :foreground "white"))))
>
> I find the latter much more readable.
If all you need is to choose either black or white as the foreground
color, then :distant-foreground is indeed not for you. Still, I'd
suggest to use color-distance rather than to invent a new metric. Or
maybe just always use the color that is complementary to the
background color, as black and white seem arbitrary to me.
Thanks.
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-26 16:05 ` Eli Zaretskii
@ 2017-01-26 17:13 ` Tom Tromey
2017-01-26 19:32 ` Eli Zaretskii
2017-01-26 17:17 ` Tom Tromey
1 sibling, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-01-26 17:13 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 25525, Tom Tromey
Eli> If all you need is to choose either black or white as the foreground
Eli> color, then :distant-foreground is indeed not for you. Still, I'd
Eli> suggest to use color-distance rather than to invent a new metric. Or
Eli> maybe just always use the color that is complementary to the
Eli> background color, as black and white seem arbitrary to me.
I will test with complementary colors, but I am not sure those are
always as visible. Black and white are arbitrary, but at least they
work ok. I'm not sure there's a good way to use color distance to get
the desired effect; if there were, surely Emacs core would be doing that
already.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-26 16:05 ` Eli Zaretskii
2017-01-26 17:13 ` Tom Tromey
@ 2017-01-26 17:17 ` Tom Tromey
1 sibling, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-01-26 17:17 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 25525, Tom Tromey
Eli> Still, I'd suggest to use color-distance rather than to invent a
Eli> new metric.
BTW I wanted to mention, I didn't invent this metric, it's well-known:
https://en.wikipedia.org/wiki/Luma_(video)
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-25 23:24 ` Tom Tromey
@ 2017-01-26 18:25 ` Simen Heggestøyl
2017-02-11 4:16 ` Tom Tromey
2017-02-11 15:17 ` Tom Tromey
0 siblings, 2 replies; 39+ messages in thread
From: Simen Heggestøyl @ 2017-01-26 18:25 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
On Thu, Jan 26, 2017 at 12:24 AM, Tom Tromey <tom@tromey.com> wrote:
> Simen> Thanks Tom, this looks to be a very nice addition to CSS mode!
> Simen> Some comments:
> [...]
>
> I think this version addresses all the review comments.
Thanks Tom, that seems to fix all the issues I mentioned.
I've come across one new problem when testing with the newest patch:
Sometimes, in buffers that are long enough to scroll in, some colors
aren't fontified until the line that contains the color is edited.
I'm able to reproduce it in
https://www.gnu.org/software/emacs/layout.css for instance, by opening
the file and searching for "#".
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-26 17:13 ` Tom Tromey
@ 2017-01-26 19:32 ` Eli Zaretskii
2017-02-11 4:11 ` Tom Tromey
0 siblings, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2017-01-26 19:32 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
> From: Tom Tromey <tom@tromey.com>
> Cc: Tom Tromey <tom@tromey.com>, 25525@debbugs.gnu.org
> Date: Thu, 26 Jan 2017 10:13:33 -0700
>
> I'm not sure there's a good way to use color distance to get the
> desired effect; if there were, surely Emacs core would be doing that
> already.
I thought about something like
(if (> (color-distance color "black") 292485) "black" "white")
I think this is the equivalent of your test.
(One reason for color-distance being unused could be that it isn't
mentioned in the ELisp manual.)
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-26 19:32 ` Eli Zaretskii
@ 2017-02-11 4:11 ` Tom Tromey
0 siblings, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-02-11 4:11 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 25525, Tom Tromey
>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
Eli> I thought about something like
Eli> (if (> (color-distance color "black") 292485) "black" "white")
Eli> I think this is the equivalent of your test.
I tried this. In particular with the patch applied, I did this in a
temporary (fundamental-mode) buffer:
(mapcar
(lambda (c)
(insert
(propertize (car c) 'font-lock-face
(list :background (cdr c)
:foreground (css--contrasty-color (cdr c))))
" "
(propertize (car c) 'font-lock-face
(list :background (cdr c)
:foreground
(if (> (color-distance (cdr c) "black") 292485)
"black" "white")))
"\n"))
css--color-map)
... then I looked at all the pairs.
For the most part I think they are all ok. Occasionally color-distance
picks a different color, sometimes a mildly worse one IMO, though at
least in one case ("grey") a mildly better one.
Anyway, I think it's good enough, and if someone trips across a
difficult case and files a bug, we can revisit it at that time.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-26 18:25 ` Simen Heggestøyl
@ 2017-02-11 4:16 ` Tom Tromey
2017-02-11 15:17 ` Tom Tromey
1 sibling, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-02-11 4:16 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
>>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:
Simen> I've come across one new problem when testing with the newest patch:
Simen> Sometimes, in buffers that are long enough to scroll in, some colors
Simen> aren't fontified until the line that contains the color is edited.
I've seen weird behavior too but haven't debugged it yet. Maybe
css-mode needs to set font-lock-fontify-region-function instead. I am
not sure.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-01-26 18:25 ` Simen Heggestøyl
2017-02-11 4:16 ` Tom Tromey
@ 2017-02-11 15:17 ` Tom Tromey
2017-02-17 19:29 ` Simen Heggestøyl
1 sibling, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-02-11 15:17 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
>>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:
Simen> I've come across one new problem when testing with the newest patch:
Simen> Sometimes, in buffers that are long enough to scroll in, some colors
Simen> aren't fontified until the line that contains the color is edited.
Could you try this version?
Tom
diff --git a/etc/NEWS b/etc/NEWS
index da0b538..4287387 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -638,6 +638,11 @@ pseudo-element, with the default being guessed from context). By
default the information is looked up on the Mozilla Developer Network,
but this can be customized using 'css-lookup-url-format'.
+---
+*** CSS colors are fontified using the color they represent as the
+background. For instance, #ff0000 would be fontified with a red
+background.
+
+++
** Emacs now supports character name escape sequences in character and
string literals. The syntax variants \N{character name} and
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 19746c6..a1e17b4 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -33,6 +33,8 @@
;;; Code:
(require 'eww)
+(require 'cl-lib)
+(require 'color)
(require 'seq)
(require 'sgml-mode)
(require 'smie)
@@ -455,8 +457,157 @@ css-property-ids
(mapcar #'car css-property-alist)
"Identifiers for properties.")
+(defconst css--color-map
+ '(("black" . "#000000")
+ ("silver" . "#c0c0c0")
+ ("gray" . "#808080")
+ ("white" . "#ffffff")
+ ("maroon" . "#800000")
+ ("red" . "#ff0000")
+ ("purple" . "#800080")
+ ("fuchsia" . "#ff00ff")
+ ("green" . "#008000")
+ ("lime" . "#00ff00")
+ ("olive" . "#808000")
+ ("yellow" . "#ffff00")
+ ("navy" . "#000080")
+ ("blue" . "#0000ff")
+ ("teal" . "#008080")
+ ("aqua" . "#00ffff")
+ ("orange" . "#ffa500")
+ ("aliceblue" . "#f0f8ff")
+ ("antiquewhite" . "#faebd7")
+ ("aquamarine" . "#7fffd4")
+ ("azure" . "#f0ffff")
+ ("beige" . "#f5f5dc")
+ ("bisque" . "#ffe4c4")
+ ("blanchedalmond" . "#ffebcd")
+ ("blueviolet" . "#8a2be2")
+ ("brown" . "#a52a2a")
+ ("burlywood" . "#deb887")
+ ("cadetblue" . "#5f9ea0")
+ ("chartreuse" . "#7fff00")
+ ("chocolate" . "#d2691e")
+ ("coral" . "#ff7f50")
+ ("cornflowerblue" . "#6495ed")
+ ("cornsilk" . "#fff8dc")
+ ("crimson" . "#dc143c")
+ ("darkblue" . "#00008b")
+ ("darkcyan" . "#008b8b")
+ ("darkgoldenrod" . "#b8860b")
+ ("darkgray" . "#a9a9a9")
+ ("darkgreen" . "#006400")
+ ("darkgrey" . "#a9a9a9")
+ ("darkkhaki" . "#bdb76b")
+ ("darkmagenta" . "#8b008b")
+ ("darkolivegreen" . "#556b2f")
+ ("darkorange" . "#ff8c00")
+ ("darkorchid" . "#9932cc")
+ ("darkred" . "#8b0000")
+ ("darksalmon" . "#e9967a")
+ ("darkseagreen" . "#8fbc8f")
+ ("darkslateblue" . "#483d8b")
+ ("darkslategray" . "#2f4f4f")
+ ("darkslategrey" . "#2f4f4f")
+ ("darkturquoise" . "#00ced1")
+ ("darkviolet" . "#9400d3")
+ ("deeppink" . "#ff1493")
+ ("deepskyblue" . "#00bfff")
+ ("dimgray" . "#696969")
+ ("dimgrey" . "#696969")
+ ("dodgerblue" . "#1e90ff")
+ ("firebrick" . "#b22222")
+ ("floralwhite" . "#fffaf0")
+ ("forestgreen" . "#228b22")
+ ("gainsboro" . "#dcdcdc")
+ ("ghostwhite" . "#f8f8ff")
+ ("gold" . "#ffd700")
+ ("goldenrod" . "#daa520")
+ ("greenyellow" . "#adff2f")
+ ("grey" . "#808080")
+ ("honeydew" . "#f0fff0")
+ ("hotpink" . "#ff69b4")
+ ("indianred" . "#cd5c5c")
+ ("indigo" . "#4b0082")
+ ("ivory" . "#fffff0")
+ ("khaki" . "#f0e68c")
+ ("lavender" . "#e6e6fa")
+ ("lavenderblush" . "#fff0f5")
+ ("lawngreen" . "#7cfc00")
+ ("lemonchiffon" . "#fffacd")
+ ("lightblue" . "#add8e6")
+ ("lightcoral" . "#f08080")
+ ("lightcyan" . "#e0ffff")
+ ("lightgoldenrodyellow" . "#fafad2")
+ ("lightgray" . "#d3d3d3")
+ ("lightgreen" . "#90ee90")
+ ("lightgrey" . "#d3d3d3")
+ ("lightpink" . "#ffb6c1")
+ ("lightsalmon" . "#ffa07a")
+ ("lightseagreen" . "#20b2aa")
+ ("lightskyblue" . "#87cefa")
+ ("lightslategray" . "#778899")
+ ("lightslategrey" . "#778899")
+ ("lightsteelblue" . "#b0c4de")
+ ("lightyellow" . "#ffffe0")
+ ("limegreen" . "#32cd32")
+ ("linen" . "#faf0e6")
+ ("mediumaquamarine" . "#66cdaa")
+ ("mediumblue" . "#0000cd")
+ ("mediumorchid" . "#ba55d3")
+ ("mediumpurple" . "#9370db")
+ ("mediumseagreen" . "#3cb371")
+ ("mediumslateblue" . "#7b68ee")
+ ("mediumspringgreen" . "#00fa9a")
+ ("mediumturquoise" . "#48d1cc")
+ ("mediumvioletred" . "#c71585")
+ ("midnightblue" . "#191970")
+ ("mintcream" . "#f5fffa")
+ ("mistyrose" . "#ffe4e1")
+ ("moccasin" . "#ffe4b5")
+ ("navajowhite" . "#ffdead")
+ ("oldlace" . "#fdf5e6")
+ ("olivedrab" . "#6b8e23")
+ ("orangered" . "#ff4500")
+ ("orchid" . "#da70d6")
+ ("palegoldenrod" . "#eee8aa")
+ ("palegreen" . "#98fb98")
+ ("paleturquoise" . "#afeeee")
+ ("palevioletred" . "#db7093")
+ ("papayawhip" . "#ffefd5")
+ ("peachpuff" . "#ffdab9")
+ ("peru" . "#cd853f")
+ ("pink" . "#ffc0cb")
+ ("plum" . "#dda0dd")
+ ("powderblue" . "#b0e0e6")
+ ("rosybrown" . "#bc8f8f")
+ ("royalblue" . "#4169e1")
+ ("saddlebrown" . "#8b4513")
+ ("salmon" . "#fa8072")
+ ("sandybrown" . "#f4a460")
+ ("seagreen" . "#2e8b57")
+ ("seashell" . "#fff5ee")
+ ("sienna" . "#a0522d")
+ ("skyblue" . "#87ceeb")
+ ("slateblue" . "#6a5acd")
+ ("slategray" . "#708090")
+ ("slategrey" . "#708090")
+ ("snow" . "#fffafa")
+ ("springgreen" . "#00ff7f")
+ ("steelblue" . "#4682b4")
+ ("tan" . "#d2b48c")
+ ("thistle" . "#d8bfd8")
+ ("tomato" . "#ff6347")
+ ("turquoise" . "#40e0d0")
+ ("violet" . "#ee82ee")
+ ("wheat" . "#f5deb3")
+ ("whitesmoke" . "#f5f5f5")
+ ("yellowgreen" . "#9acd32")
+ ("rebeccapurple" . "#663399"))
+ "Map CSS named color to their hex RGB value.")
+
(defconst css-value-class-alist
- '((absolute-size
+ `((absolute-size
"xx-small" "x-small" "small" "medium" "large" "x-large"
"xx-large")
(alphavalue number)
@@ -508,36 +659,7 @@ css-value-class-alist
(line-width length "thin" "medium" "thick")
(linear-gradient "linear-gradient()")
(margin-width "auto" length percentage)
- (named-color
- "aliceblue" "antiquewhite" "aqua" "aquamarine" "azure" "beige"
- "bisque" "black" "blanchedalmond" "blue" "blueviolet" "brown"
- "burlywood" "cadetblue" "chartreuse" "chocolate" "coral"
- "cornflowerblue" "cornsilk" "crimson" "cyan" "darkblue"
- "darkcyan" "darkgoldenrod" "darkgray" "darkgreen" "darkkhaki"
- "darkmagenta" "darkolivegreen" "darkorange" "darkorchid"
- "darkred" "darksalmon" "darkseagreen" "darkslateblue"
- "darkslategray" "darkturquoise" "darkviolet" "deeppink"
- "deepskyblue" "dimgray" "dodgerblue" "firebrick" "floralwhite"
- "forestgreen" "fuchsia" "gainsboro" "ghostwhite" "gold"
- "goldenrod" "gray" "green" "greenyellow" "honeydew" "hotpink"
- "indianred" "indigo" "ivory" "khaki" "lavender" "lavenderblush"
- "lawngreen" "lemonchiffon" "lightblue" "lightcoral" "lightcyan"
- "lightgoldenrodyellow" "lightgray" "lightgreen" "lightpink"
- "lightsalmon" "lightseagreen" "lightskyblue" "lightslategray"
- "lightsteelblue" "lightyellow" "lime" "limegreen" "linen"
- "magenta" "maroon" "mediumaquamarine" "mediumblue" "mediumorchid"
- "mediumpurple" "mediumseagreen" "mediumslateblue"
- "mediumspringgreen" "mediumturquoise" "mediumvioletred"
- "midnightblue" "mintcream" "mistyrose" "moccasin" "navajowhite"
- "navy" "oldlace" "olive" "olivedrab" "orange" "orangered"
- "orchid" "palegoldenrod" "palegreen" "paleturquoise"
- "palevioletred" "papayawhip" "peachpuff" "peru" "pink" "plum"
- "powderblue" "purple" "rebeccapurple" "red" "rosybrown"
- "royalblue" "saddlebrown" "salmon" "sandybrown" "seagreen"
- "seashell" "sienna" "silver" "skyblue" "slateblue" "slategray"
- "snow" "springgreen" "steelblue" "tan" "teal" "thistle" "tomato"
- "turquoise" "violet" "wheat" "white" "whitesmoke" "yellow"
- "yellowgreen")
+ (named-color . ,(mapcar #'car css--color-map))
(number "calc()")
(numeric-figure-values "lining-nums" "oldstyle-nums")
(numeric-fraction-values "diagonal-fractions" "stacked-fractions")
@@ -616,11 +738,23 @@ css-mode-syntax-table
(modify-syntax-entry ?\[ "(]" st)
(modify-syntax-entry ?\] ")[" st)
;; Special chars that sometimes come at the beginning of words.
- (modify-syntax-entry ?@ "'" st)
- ;; (modify-syntax-entry ?: "'" st)
- (modify-syntax-entry ?# "'" st)
+ ;; We'll treat them as symbol constituents.
+ (modify-syntax-entry ?@ "_" st)
+ (modify-syntax-entry ?# "_" st)
+ (modify-syntax-entry ?. "_" st)
;; Distinction between words and symbols.
(modify-syntax-entry ?- "_" st)
+
+ (modify-syntax-entry ?! "." st)
+ (modify-syntax-entry ?$ "." st)
+ (modify-syntax-entry ?% "." st)
+ (modify-syntax-entry ?& "." st)
+ (modify-syntax-entry ?+ "." st)
+ (modify-syntax-entry ?, "." st)
+ (modify-syntax-entry ?< "." st)
+ (modify-syntax-entry ?> "." st)
+ (modify-syntax-entry ?= "." st)
+ (modify-syntax-entry ?? "." st)
st))
(defvar css-mode-map
@@ -734,6 +868,201 @@ css-font-lock-keywords
(defvar css-font-lock-defaults
'(css-font-lock-keywords nil t))
+(defconst css--number-regexp
+ "\\(\\(?:[0-9]*\\.[0-9]+\\(?:[eE][0-9]+\\)?\\)\\|[0-9]+\\)"
+ "A regular expression matching a CSS number.")
+
+(defconst css--percent-regexp "\\([0-9]+\\)%"
+ "A regular expression matching a CSS percentage.")
+
+(defconst css--number-or-percent-regexp
+ (concat "\\(?:" css--percent-regexp "\\)\\|\\(?:" css--number-regexp "\\)")
+ "A regular expression matching a CSS number or a CSS percentage.")
+
+(defconst css--angle-regexp
+ (concat css--number-regexp
+ (regexp-opt '("deg" "grad" "rad" "turn") t)
+ "?")
+ "A regular expression matching a CSS angle.")
+
+(defun css--color-skip-blanks ()
+ "Skip blanks and comments."
+ (while (forward-comment 1)))
+
+(cl-defun css--rgb-color ()
+ "Parse a CSS rgb() or rgba() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '())
+ (iter 0))
+ (while (< iter 4)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--css-4-rgb nil))
+ (let* ((is-percent (match-beginning 1))
+ (str (match-string (if is-percent 1 2)))
+ (number (string-to-number str)))
+ (when is-percent
+ (setq number (* 255 (/ number 100.0))))
+ ;; Don't push the alpha.
+ (when (< iter 3)
+ (push (min (max 0 (truncate number)) 255) result))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)
+ (cl-incf iter)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (and (= (skip-chars-forward ",/") 0)
+ (= iter 3))
+ ;; The alpha is optional.
+ (cl-incf iter))
+ (css--color-skip-blanks)))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'format "#%02x%02x%02x" (nreverse result)))))
+
+(cl-defun css--hsl-color ()
+ "Parse a CSS hsl() or hsla() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '()))
+ ;; First parse the hue.
+ (css--color-skip-blanks)
+ (unless (looking-at css--angle-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((hue (string-to-number (match-string 1)))
+ (unit (match-string 2)))
+ (goto-char (match-end 0))
+ ;; Note that here "turn" is just passed through.
+ (cond
+ ((or (not unit) (equal unit "deg"))
+ ;; Degrees.
+ (setq hue (/ hue 360.0)))
+ ((equal unit "grad")
+ (setq hue (/ hue 400.0)))
+ ((equal unit "rad")
+ (setq hue (/ hue (* 2 float-pi)))))
+ (push (mod hue 1.0) result))
+ (dotimes (_ 2)
+ (skip-chars-forward ",")
+ (css--color-skip-blanks)
+ (unless (looking-at css--percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((number (string-to-number (match-string 1))))
+ (setq number (/ number 100.0))
+ (push (min (max number 0.0) 1.0) result)
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)))
+ (css--color-skip-blanks)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (> (skip-chars-forward ",/") 0)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'color-rgb-to-hex
+ (apply #'color-hsl-to-rgb (nreverse result))))))
+
+(defconst css--colors-regexp
+ (concat
+ ;; Named colors.
+ (regexp-opt (mapcar #'car css--color-map) 'symbols)
+ "\\|"
+ ;; Short hex. css-color-4 adds alpha.
+ "\\(#[0-9a-fA-F]\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; Long hex. css-color-4 adds alpha.
+ "\\(#\\(?:[0-9a-fA-F][0-9a-fA-F]\\)\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; RGB.
+ "\\(\\_<rgba?(\\)"
+ "\\|"
+ ;; HSL.
+ "\\(\\_<hsla?(\\)")
+ "A regular expression that matches the start of a CSS color.")
+
+(defun css--hex-color (str)
+ "Convert a CSS hex color to an Emacs hex color.
+STR is the incoming CSS hex color.
+This function simply drops any transparency."
+ ;; Either #RGB or #RRGGBB, drop the "A" or "AA".
+ (if (> (length str) 4)
+ (substring str 0 7)
+ (substring str 0 4)))
+
+(defun css--compute-color ()
+ "Return the CSS color at point.
+Point should be just after the start of a CSS color, as recognized
+by `css--colors-regexp'. This function will either return the color,
+as a hex RGB string; or `nil' if no color could be recognized. When
+this function returns, point will be at the end of the recognized
+color."
+ (let ((match (downcase (match-string 0))))
+ (cond
+ ((eq (aref match 0) ?#)
+ (css--hex-color match))
+ ((member match '("rgb(" "rgba("))
+ (css--rgb-color))
+ ((member match '("hsl(" "hsla("))
+ (css--hsl-color))
+ ;; Evaluate to the color if the name is found.
+ ((cdr (assoc match css--color-map)))
+ (t
+ (error "Invalid case in css--compute-color")))))
+
+(defun css--contrasty-color (name)
+ "Return a color that contrasts with NAME.
+NAME is of any form accepted by `color-distance'.
+The returned color will be usable by Emacs and will contrast
+with NAME; in particular so that if NAME is used as a background
+color, the returned color can be used as the foreground and still
+be readable."
+ ;; See bug#25525 for a discussion of this.
+ (if (> (color-distance name "black") 292485)
+ "black" "white"))
+
+(defcustom css-fontify-colors t
+ "Whether CSS colors should be fontified using the color as the background.
+When non-`nil', a text representing CSS color will be fontified
+such that its background is the color itself. E.g., #ff0000 will
+be fontified with a red background."
+ :version "26.1"
+ :group 'css
+ :type 'boolean
+ :safe 'booleanp)
+
+(defun css--fontify-region (start end &optional loudly)
+ "Fontify a CSS buffer between START and END.
+START and END are buffer positions."
+ (font-lock-default-fontify-region start end loudly)
+ (when css-fontify-colors
+ (save-excursion
+ (let ((case-fold-search t))
+ (goto-char start)
+ (while (re-search-forward css--colors-regexp end t)
+ ;; Skip comments and strings.
+ (unless (nth 8 (syntax-ppss))
+ (let ((start (match-beginning 0))
+ (color (css--compute-color)))
+ (when color
+ (with-silent-modifications
+ ;; Use the color as the background, to make it more
+ ;; clear. Use a contrasting color as the foreground,
+ ;; to make it readable. Finally, have a small box
+ ;; using the existing foreground color, to make sure
+ ;; it stands out a bit from any other text; in
+ ;; particular this is nice when the color matches the
+ ;; buffer's background color.
+ (add-text-properties
+ start (point)
+ (list 'face (list :background color
+ :foreground (css--contrasty-color color)
+ :box '(:line-width -1)))))))))))))
+
(defcustom css-indent-offset 4
"Basic size of one indentation step."
:version "22.2"
@@ -988,6 +1317,7 @@ css-mode
:backward-token #'css-smie--backward-token)
(setq-local electric-indent-chars
(append css-electric-keys electric-indent-chars))
+ (setq-local font-lock-fontify-region-function #'css--fontify-region)
(add-hook 'completion-at-point-functions
#'css-completion-at-point nil 'local))
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index 5372c37..a741484 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -58,7 +58,7 @@
;; Check that the `color' property doesn't cause infinite recursion
;; because it refers to the value class of the same name.
- (should (= (length (css--property-values "color")) 147)))
+ (should (= (length (css--property-values "color")) 152)))
(ert-deftest css-test-property-value-cache ()
"Test that `css--property-value-cache' is in use."
@@ -233,5 +233,40 @@ css-mode-tests--completions
(save-excursion (insert (nth 1 item)))
(should (equal (nth 2 item) (css--mdn-find-symbol))))))
+(ert-deftest css-test-rgb-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("255, 0, 127"
+ "255, /* comment */ 0, 127"
+ "255 0 127"
+ "255, 0, 127, 0.75"
+ "255 0 127 / 0.75"
+ "100%, 0%, 50%"
+ "100%, 0%, 50%, 0.115"
+ "100% 0% 50%"
+ "100% 0% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--rgb-color) "#ff007f")))))
+
+(ert-deftest css-test-hsl-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("0, 100%, 50%"
+ "0 100% 50%"
+ "0 /* two */ /* comments */100% 50%"
+ "0, 100%, 50%, 0.75"
+ "0 100% 50% / 0.75"
+ "0deg 100% 50%"
+ "360deg 100% 50%"
+ "0rad, 100%, 50%, 0.115"
+ "0grad, 100%, 50%, 0.115"
+ "1turn 100% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--hsl-color) "#ff0000")))))
+
(provide 'css-mode-tests)
;;; css-mode-tests.el ends here
^ permalink raw reply related [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-02-11 15:17 ` Tom Tromey
@ 2017-02-17 19:29 ` Simen Heggestøyl
2017-03-04 17:55 ` Tom Tromey
0 siblings, 1 reply; 39+ messages in thread
From: Simen Heggestøyl @ 2017-02-17 19:29 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
On Sat, Feb 11, 2017 at 4:17 PM, Tom Tromey <tom@tromey.com> wrote:
>>>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:
>
> Simen> I've come across one new problem when testing with the newest
> patch:
> Simen> Sometimes, in buffers that are long enough to scroll in, some
> colors
> Simen> aren't fontified until the line that contains the color is
> edited.
>
> Could you try this version?
>
> Tom
Hi, Tom.
Unfortunately I'm still experiencing the bug with that version too.
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-02-17 19:29 ` Simen Heggestøyl
@ 2017-03-04 17:55 ` Tom Tromey
2017-03-04 18:46 ` Simen Heggestøyl
0 siblings, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-03-04 17:55 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
[-- Attachment #1: Type: text/plain, Size: 381 bytes --]
Simen> Unfortunately I'm still experiencing the bug with that version
Simen> too.
I was able to reproduce the problem and I think I found the bug:
css--fontify-region was not respecting any region extension done by
font-lock-default-fontify-region, but this is what extended
fontification to include the whole line containing point when searching.
Please try this version.
Tom
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: newest patch --]
[-- Type: text/x-patch, Size: 18848 bytes --]
diff --git a/etc/NEWS b/etc/NEWS
index a8db54c..ae2ac3e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -668,6 +668,11 @@ pseudo-element, with the default being guessed from context). By
default the information is looked up on the Mozilla Developer Network,
but this can be customized using 'css-lookup-url-format'.
+---
+*** CSS colors are fontified using the color they represent as the
+background. For instance, #ff0000 would be fontified with a red
+background.
+
+++
** Emacs now supports character name escape sequences in character and
string literals. The syntax variants \N{character name} and
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 7a9454f..8cec7ca 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -33,6 +33,8 @@
;;; Code:
(require 'eww)
+(require 'cl-lib)
+(require 'color)
(require 'seq)
(require 'sgml-mode)
(require 'smie)
@@ -455,8 +457,157 @@ css-property-ids
(mapcar #'car css-property-alist)
"Identifiers for properties.")
+(defconst css--color-map
+ '(("black" . "#000000")
+ ("silver" . "#c0c0c0")
+ ("gray" . "#808080")
+ ("white" . "#ffffff")
+ ("maroon" . "#800000")
+ ("red" . "#ff0000")
+ ("purple" . "#800080")
+ ("fuchsia" . "#ff00ff")
+ ("green" . "#008000")
+ ("lime" . "#00ff00")
+ ("olive" . "#808000")
+ ("yellow" . "#ffff00")
+ ("navy" . "#000080")
+ ("blue" . "#0000ff")
+ ("teal" . "#008080")
+ ("aqua" . "#00ffff")
+ ("orange" . "#ffa500")
+ ("aliceblue" . "#f0f8ff")
+ ("antiquewhite" . "#faebd7")
+ ("aquamarine" . "#7fffd4")
+ ("azure" . "#f0ffff")
+ ("beige" . "#f5f5dc")
+ ("bisque" . "#ffe4c4")
+ ("blanchedalmond" . "#ffebcd")
+ ("blueviolet" . "#8a2be2")
+ ("brown" . "#a52a2a")
+ ("burlywood" . "#deb887")
+ ("cadetblue" . "#5f9ea0")
+ ("chartreuse" . "#7fff00")
+ ("chocolate" . "#d2691e")
+ ("coral" . "#ff7f50")
+ ("cornflowerblue" . "#6495ed")
+ ("cornsilk" . "#fff8dc")
+ ("crimson" . "#dc143c")
+ ("darkblue" . "#00008b")
+ ("darkcyan" . "#008b8b")
+ ("darkgoldenrod" . "#b8860b")
+ ("darkgray" . "#a9a9a9")
+ ("darkgreen" . "#006400")
+ ("darkgrey" . "#a9a9a9")
+ ("darkkhaki" . "#bdb76b")
+ ("darkmagenta" . "#8b008b")
+ ("darkolivegreen" . "#556b2f")
+ ("darkorange" . "#ff8c00")
+ ("darkorchid" . "#9932cc")
+ ("darkred" . "#8b0000")
+ ("darksalmon" . "#e9967a")
+ ("darkseagreen" . "#8fbc8f")
+ ("darkslateblue" . "#483d8b")
+ ("darkslategray" . "#2f4f4f")
+ ("darkslategrey" . "#2f4f4f")
+ ("darkturquoise" . "#00ced1")
+ ("darkviolet" . "#9400d3")
+ ("deeppink" . "#ff1493")
+ ("deepskyblue" . "#00bfff")
+ ("dimgray" . "#696969")
+ ("dimgrey" . "#696969")
+ ("dodgerblue" . "#1e90ff")
+ ("firebrick" . "#b22222")
+ ("floralwhite" . "#fffaf0")
+ ("forestgreen" . "#228b22")
+ ("gainsboro" . "#dcdcdc")
+ ("ghostwhite" . "#f8f8ff")
+ ("gold" . "#ffd700")
+ ("goldenrod" . "#daa520")
+ ("greenyellow" . "#adff2f")
+ ("grey" . "#808080")
+ ("honeydew" . "#f0fff0")
+ ("hotpink" . "#ff69b4")
+ ("indianred" . "#cd5c5c")
+ ("indigo" . "#4b0082")
+ ("ivory" . "#fffff0")
+ ("khaki" . "#f0e68c")
+ ("lavender" . "#e6e6fa")
+ ("lavenderblush" . "#fff0f5")
+ ("lawngreen" . "#7cfc00")
+ ("lemonchiffon" . "#fffacd")
+ ("lightblue" . "#add8e6")
+ ("lightcoral" . "#f08080")
+ ("lightcyan" . "#e0ffff")
+ ("lightgoldenrodyellow" . "#fafad2")
+ ("lightgray" . "#d3d3d3")
+ ("lightgreen" . "#90ee90")
+ ("lightgrey" . "#d3d3d3")
+ ("lightpink" . "#ffb6c1")
+ ("lightsalmon" . "#ffa07a")
+ ("lightseagreen" . "#20b2aa")
+ ("lightskyblue" . "#87cefa")
+ ("lightslategray" . "#778899")
+ ("lightslategrey" . "#778899")
+ ("lightsteelblue" . "#b0c4de")
+ ("lightyellow" . "#ffffe0")
+ ("limegreen" . "#32cd32")
+ ("linen" . "#faf0e6")
+ ("mediumaquamarine" . "#66cdaa")
+ ("mediumblue" . "#0000cd")
+ ("mediumorchid" . "#ba55d3")
+ ("mediumpurple" . "#9370db")
+ ("mediumseagreen" . "#3cb371")
+ ("mediumslateblue" . "#7b68ee")
+ ("mediumspringgreen" . "#00fa9a")
+ ("mediumturquoise" . "#48d1cc")
+ ("mediumvioletred" . "#c71585")
+ ("midnightblue" . "#191970")
+ ("mintcream" . "#f5fffa")
+ ("mistyrose" . "#ffe4e1")
+ ("moccasin" . "#ffe4b5")
+ ("navajowhite" . "#ffdead")
+ ("oldlace" . "#fdf5e6")
+ ("olivedrab" . "#6b8e23")
+ ("orangered" . "#ff4500")
+ ("orchid" . "#da70d6")
+ ("palegoldenrod" . "#eee8aa")
+ ("palegreen" . "#98fb98")
+ ("paleturquoise" . "#afeeee")
+ ("palevioletred" . "#db7093")
+ ("papayawhip" . "#ffefd5")
+ ("peachpuff" . "#ffdab9")
+ ("peru" . "#cd853f")
+ ("pink" . "#ffc0cb")
+ ("plum" . "#dda0dd")
+ ("powderblue" . "#b0e0e6")
+ ("rosybrown" . "#bc8f8f")
+ ("royalblue" . "#4169e1")
+ ("saddlebrown" . "#8b4513")
+ ("salmon" . "#fa8072")
+ ("sandybrown" . "#f4a460")
+ ("seagreen" . "#2e8b57")
+ ("seashell" . "#fff5ee")
+ ("sienna" . "#a0522d")
+ ("skyblue" . "#87ceeb")
+ ("slateblue" . "#6a5acd")
+ ("slategray" . "#708090")
+ ("slategrey" . "#708090")
+ ("snow" . "#fffafa")
+ ("springgreen" . "#00ff7f")
+ ("steelblue" . "#4682b4")
+ ("tan" . "#d2b48c")
+ ("thistle" . "#d8bfd8")
+ ("tomato" . "#ff6347")
+ ("turquoise" . "#40e0d0")
+ ("violet" . "#ee82ee")
+ ("wheat" . "#f5deb3")
+ ("whitesmoke" . "#f5f5f5")
+ ("yellowgreen" . "#9acd32")
+ ("rebeccapurple" . "#663399"))
+ "Map CSS named color to their hex RGB value.")
+
(defconst css-value-class-alist
- '((absolute-size
+ `((absolute-size
"xx-small" "x-small" "small" "medium" "large" "x-large"
"xx-large")
(alphavalue number)
@@ -508,36 +659,7 @@ css-value-class-alist
(line-width length "thin" "medium" "thick")
(linear-gradient "linear-gradient()")
(margin-width "auto" length percentage)
- (named-color
- "aliceblue" "antiquewhite" "aqua" "aquamarine" "azure" "beige"
- "bisque" "black" "blanchedalmond" "blue" "blueviolet" "brown"
- "burlywood" "cadetblue" "chartreuse" "chocolate" "coral"
- "cornflowerblue" "cornsilk" "crimson" "cyan" "darkblue"
- "darkcyan" "darkgoldenrod" "darkgray" "darkgreen" "darkkhaki"
- "darkmagenta" "darkolivegreen" "darkorange" "darkorchid"
- "darkred" "darksalmon" "darkseagreen" "darkslateblue"
- "darkslategray" "darkturquoise" "darkviolet" "deeppink"
- "deepskyblue" "dimgray" "dodgerblue" "firebrick" "floralwhite"
- "forestgreen" "fuchsia" "gainsboro" "ghostwhite" "gold"
- "goldenrod" "gray" "green" "greenyellow" "honeydew" "hotpink"
- "indianred" "indigo" "ivory" "khaki" "lavender" "lavenderblush"
- "lawngreen" "lemonchiffon" "lightblue" "lightcoral" "lightcyan"
- "lightgoldenrodyellow" "lightgray" "lightgreen" "lightpink"
- "lightsalmon" "lightseagreen" "lightskyblue" "lightslategray"
- "lightsteelblue" "lightyellow" "lime" "limegreen" "linen"
- "magenta" "maroon" "mediumaquamarine" "mediumblue" "mediumorchid"
- "mediumpurple" "mediumseagreen" "mediumslateblue"
- "mediumspringgreen" "mediumturquoise" "mediumvioletred"
- "midnightblue" "mintcream" "mistyrose" "moccasin" "navajowhite"
- "navy" "oldlace" "olive" "olivedrab" "orange" "orangered"
- "orchid" "palegoldenrod" "palegreen" "paleturquoise"
- "palevioletred" "papayawhip" "peachpuff" "peru" "pink" "plum"
- "powderblue" "purple" "rebeccapurple" "red" "rosybrown"
- "royalblue" "saddlebrown" "salmon" "sandybrown" "seagreen"
- "seashell" "sienna" "silver" "skyblue" "slateblue" "slategray"
- "snow" "springgreen" "steelblue" "tan" "teal" "thistle" "tomato"
- "turquoise" "violet" "wheat" "white" "whitesmoke" "yellow"
- "yellowgreen")
+ (named-color . ,(mapcar #'car css--color-map))
(number "calc()")
(numeric-figure-values "lining-nums" "oldstyle-nums")
(numeric-fraction-values "diagonal-fractions" "stacked-fractions")
@@ -616,11 +738,23 @@ css-mode-syntax-table
(modify-syntax-entry ?\[ "(]" st)
(modify-syntax-entry ?\] ")[" st)
;; Special chars that sometimes come at the beginning of words.
- (modify-syntax-entry ?@ "'" st)
- ;; (modify-syntax-entry ?: "'" st)
- (modify-syntax-entry ?# "'" st)
+ ;; We'll treat them as symbol constituents.
+ (modify-syntax-entry ?@ "_" st)
+ (modify-syntax-entry ?# "_" st)
+ (modify-syntax-entry ?. "_" st)
;; Distinction between words and symbols.
(modify-syntax-entry ?- "_" st)
+
+ (modify-syntax-entry ?! "." st)
+ (modify-syntax-entry ?$ "." st)
+ (modify-syntax-entry ?% "." st)
+ (modify-syntax-entry ?& "." st)
+ (modify-syntax-entry ?+ "." st)
+ (modify-syntax-entry ?, "." st)
+ (modify-syntax-entry ?< "." st)
+ (modify-syntax-entry ?> "." st)
+ (modify-syntax-entry ?= "." st)
+ (modify-syntax-entry ?? "." st)
st))
(defvar css-mode-map
@@ -735,6 +869,206 @@ css-font-lock-keywords
(defvar css-font-lock-defaults
'(css-font-lock-keywords nil t))
+(defconst css--number-regexp
+ "\\(\\(?:[0-9]*\\.[0-9]+\\(?:[eE][0-9]+\\)?\\)\\|[0-9]+\\)"
+ "A regular expression matching a CSS number.")
+
+(defconst css--percent-regexp "\\([0-9]+\\)%"
+ "A regular expression matching a CSS percentage.")
+
+(defconst css--number-or-percent-regexp
+ (concat "\\(?:" css--percent-regexp "\\)\\|\\(?:" css--number-regexp "\\)")
+ "A regular expression matching a CSS number or a CSS percentage.")
+
+(defconst css--angle-regexp
+ (concat css--number-regexp
+ (regexp-opt '("deg" "grad" "rad" "turn") t)
+ "?")
+ "A regular expression matching a CSS angle.")
+
+(defun css--color-skip-blanks ()
+ "Skip blanks and comments."
+ (while (forward-comment 1)))
+
+(cl-defun css--rgb-color ()
+ "Parse a CSS rgb() or rgba() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '())
+ (iter 0))
+ (while (< iter 4)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--css-4-rgb nil))
+ (let* ((is-percent (match-beginning 1))
+ (str (match-string (if is-percent 1 2)))
+ (number (string-to-number str)))
+ (when is-percent
+ (setq number (* 255 (/ number 100.0))))
+ ;; Don't push the alpha.
+ (when (< iter 3)
+ (push (min (max 0 (truncate number)) 255) result))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)
+ (cl-incf iter)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (and (= (skip-chars-forward ",/") 0)
+ (= iter 3))
+ ;; The alpha is optional.
+ (cl-incf iter))
+ (css--color-skip-blanks)))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'format "#%02x%02x%02x" (nreverse result)))))
+
+(cl-defun css--hsl-color ()
+ "Parse a CSS hsl() or hsla() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '()))
+ ;; First parse the hue.
+ (css--color-skip-blanks)
+ (unless (looking-at css--angle-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((hue (string-to-number (match-string 1)))
+ (unit (match-string 2)))
+ (goto-char (match-end 0))
+ ;; Note that here "turn" is just passed through.
+ (cond
+ ((or (not unit) (equal unit "deg"))
+ ;; Degrees.
+ (setq hue (/ hue 360.0)))
+ ((equal unit "grad")
+ (setq hue (/ hue 400.0)))
+ ((equal unit "rad")
+ (setq hue (/ hue (* 2 float-pi)))))
+ (push (mod hue 1.0) result))
+ (dotimes (_ 2)
+ (skip-chars-forward ",")
+ (css--color-skip-blanks)
+ (unless (looking-at css--percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((number (string-to-number (match-string 1))))
+ (setq number (/ number 100.0))
+ (push (min (max number 0.0) 1.0) result)
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)))
+ (css--color-skip-blanks)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (> (skip-chars-forward ",/") 0)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'color-rgb-to-hex
+ (apply #'color-hsl-to-rgb (nreverse result))))))
+
+(defconst css--colors-regexp
+ (concat
+ ;; Named colors.
+ (regexp-opt (mapcar #'car css--color-map) 'symbols)
+ "\\|"
+ ;; Short hex. css-color-4 adds alpha.
+ "\\(#[0-9a-fA-F]\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; Long hex. css-color-4 adds alpha.
+ "\\(#\\(?:[0-9a-fA-F][0-9a-fA-F]\\)\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; RGB.
+ "\\(\\_<rgba?(\\)"
+ "\\|"
+ ;; HSL.
+ "\\(\\_<hsla?(\\)")
+ "A regular expression that matches the start of a CSS color.")
+
+(defun css--hex-color (str)
+ "Convert a CSS hex color to an Emacs hex color.
+STR is the incoming CSS hex color.
+This function simply drops any transparency."
+ ;; Either #RGB or #RRGGBB, drop the "A" or "AA".
+ (if (> (length str) 4)
+ (substring str 0 7)
+ (substring str 0 4)))
+
+(defun css--compute-color ()
+ "Return the CSS color at point.
+Point should be just after the start of a CSS color, as recognized
+by `css--colors-regexp'. This function will either return the color,
+as a hex RGB string; or `nil' if no color could be recognized. When
+this function returns, point will be at the end of the recognized
+color."
+ (let ((match (downcase (match-string 0))))
+ (cond
+ ((eq (aref match 0) ?#)
+ (css--hex-color match))
+ ((member match '("rgb(" "rgba("))
+ (css--rgb-color))
+ ((member match '("hsl(" "hsla("))
+ (css--hsl-color))
+ ;; Evaluate to the color if the name is found.
+ ((cdr (assoc match css--color-map)))
+ (t
+ (error "Invalid case in css--compute-color")))))
+
+(defun css--contrasty-color (name)
+ "Return a color that contrasts with NAME.
+NAME is of any form accepted by `color-distance'.
+The returned color will be usable by Emacs and will contrast
+with NAME; in particular so that if NAME is used as a background
+color, the returned color can be used as the foreground and still
+be readable."
+ ;; See bug#25525 for a discussion of this.
+ (if (> (color-distance name "black") 292485)
+ "black" "white"))
+
+(defcustom css-fontify-colors t
+ "Whether CSS colors should be fontified using the color as the background.
+When non-`nil', a text representing CSS color will be fontified
+such that its background is the color itself. E.g., #ff0000 will
+be fontified with a red background."
+ :version "26.1"
+ :group 'css
+ :type 'boolean
+ :safe 'booleanp)
+
+(defun css--fontify-region (start end &optional loudly)
+ "Fontify a CSS buffer between START and END.
+START and END are buffer positions."
+ (let ((extended-region (font-lock-default-fontify-region start end loudly)))
+ (when css-fontify-colors
+ (when (and (consp extended-region)
+ (eq (car extended-region) 'jit-lock-bounds))
+ (setq start (cadr extended-region))
+ (setq end (cddr extended-region)))
+ (save-excursion
+ (let ((case-fold-search t))
+ (goto-char start)
+ (while (re-search-forward css--colors-regexp end t)
+ ;; Skip comments and strings.
+ (unless (nth 8 (syntax-ppss))
+ (let ((start (match-beginning 0))
+ (color (css--compute-color)))
+ (when color
+ (with-silent-modifications
+ ;; Use the color as the background, to make it more
+ ;; clear. Use a contrasting color as the foreground,
+ ;; to make it readable. Finally, have a small box
+ ;; using the existing foreground color, to make sure
+ ;; it stands out a bit from any other text; in
+ ;; particular this is nice when the color matches the
+ ;; buffer's background color.
+ (add-text-properties
+ start (point)
+ (list 'face (list :background color
+ :foreground (css--contrasty-color color)
+ :box '(:line-width -1))))))))))))
+ extended-region))
+
(defcustom css-indent-offset 4
"Basic size of one indentation step."
:version "22.2"
@@ -1001,6 +1335,7 @@ css-mode
:backward-token #'css-smie--backward-token)
(setq-local electric-indent-chars
(append css-electric-keys electric-indent-chars))
+ (setq-local font-lock-fontify-region-function #'css--fontify-region)
(add-hook 'completion-at-point-functions
#'css-completion-at-point nil 'local))
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index d601f43..5fa4ae2 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -58,7 +58,7 @@
;; Check that the `color' property doesn't cause infinite recursion
;; because it refers to the value class of the same name.
- (should (= (length (css--property-values "color")) 147)))
+ (should (= (length (css--property-values "color")) 152)))
(ert-deftest css-test-property-value-cache ()
"Test that `css--property-value-cache' is in use."
@@ -234,5 +234,40 @@ css-mode-tests--completions
(save-excursion (insert (nth 1 item)))
(should (equal (nth 2 item) (css--mdn-find-symbol))))))
+(ert-deftest css-test-rgb-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("255, 0, 127"
+ "255, /* comment */ 0, 127"
+ "255 0 127"
+ "255, 0, 127, 0.75"
+ "255 0 127 / 0.75"
+ "100%, 0%, 50%"
+ "100%, 0%, 50%, 0.115"
+ "100% 0% 50%"
+ "100% 0% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--rgb-color) "#ff007f")))))
+
+(ert-deftest css-test-hsl-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("0, 100%, 50%"
+ "0 100% 50%"
+ "0 /* two */ /* comments */100% 50%"
+ "0, 100%, 50%, 0.75"
+ "0 100% 50% / 0.75"
+ "0deg 100% 50%"
+ "360deg 100% 50%"
+ "0rad, 100%, 50%, 0.115"
+ "0grad, 100%, 50%, 0.115"
+ "1turn 100% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--hsl-color) "#ff0000")))))
+
(provide 'css-mode-tests)
;;; css-mode-tests.el ends here
^ permalink raw reply related [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-03-04 17:55 ` Tom Tromey
@ 2017-03-04 18:46 ` Simen Heggestøyl
2017-03-04 19:58 ` Tom Tromey
2017-03-05 2:42 ` Tom Tromey
0 siblings, 2 replies; 39+ messages in thread
From: Simen Heggestøyl @ 2017-03-04 18:46 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
On Sat, Mar 4, 2017 at 6:55 PM, Tom Tromey <tom@tromey.com> wrote:
> I was able to reproduce the problem and I think I found the bug:
> css--fontify-region was not respecting any region extension done by
> font-lock-default-fontify-region, but this is what extended
> fontification to include the whole line containing point when
> searching.
>
> Please try this version.
Nice, that seems to have fixed the bug!
I found another one: in SCSS mode, when using the `rgba` function
where one argument is a variable, subsequent colors aren't
highlighted. An example:
body {
color: #ddd;
color: rgba($color-var, 1);
color: #abc; // Not highlighted
}
There is also a test failure when you rebase the patch on the latest
master due to the changes in 7b00e956b4. It can be fixed by passing 2
as the last argument to `color-rgb-to-hex' in `css--hsl-color'.
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-03-04 18:46 ` Simen Heggestøyl
@ 2017-03-04 19:58 ` Tom Tromey
2017-03-05 18:28 ` Simen Heggestøyl
2017-03-05 2:42 ` Tom Tromey
1 sibling, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-03-04 19:58 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
>>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:
Simen> I found another one: in SCSS mode, when using the `rgba` function
Simen> where one argument is a variable, subsequent colors aren't
Simen> highlighted. An example:
Thank you. This was due to using the wrong block name in a call to
cl-return-from. I must have changed the function name and forgotten
about this bit. Anyway, apply the appended patch on top of what you've
got and please try to find more bugs :)
Simen> There is also a test failure when you rebase the patch on the latest
Simen> master due to the changes in 7b00e956b4. It can be fixed by passing 2
Simen> as the last argument to `color-rgb-to-hex' in `css--hsl-color'.
Thanks, I'll apply this to my patch.
Tom
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 8cec7ca..7af9fdd 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -900,7 +900,7 @@ css--color-skip-blanks
(while (< iter 4)
(css--color-skip-blanks)
(unless (looking-at css--number-or-percent-regexp)
- (cl-return-from css--css-4-rgb nil))
+ (cl-return-from css--rgb-color nil))
(let* ((is-percent (match-beginning 1))
(str (match-string (if is-percent 1 2)))
(number (string-to-number str)))
^ permalink raw reply related [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-03-04 18:46 ` Simen Heggestøyl
2017-03-04 19:58 ` Tom Tromey
@ 2017-03-05 2:42 ` Tom Tromey
2017-03-05 15:34 ` Eli Zaretskii
1 sibling, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-03-05 2:42 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
>>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:
Simen> There is also a test failure when you rebase the patch on the latest
Simen> master due to the changes in 7b00e956b4. It can be fixed by passing 2
Simen> as the last argument to `color-rgb-to-hex' in `css--hsl-color'.
This happens due to the change in 7b00e956b485d8ade03c870cbdd0ae086348737b,
which changed color-name-to-rgb in a backward-incompatible way.
Eli, you made this change -- it seems to me that it would be safer if it
defaulted to 2 digits, which was how it worked previously. This would
be backward-compatible and allow the new functionality as well.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-03-05 2:42 ` Tom Tromey
@ 2017-03-05 15:34 ` Eli Zaretskii
0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2017-03-05 15:34 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525, simenheg
> From: Tom Tromey <tom@tromey.com>
> Cc: Tom Tromey <tom@tromey.com>, 25525@debbugs.gnu.org, Eli Zaretskii <eliz@gnu.org>
> Date: Sat, 04 Mar 2017 19:42:34 -0700
>
> >>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:
>
> Simen> There is also a test failure when you rebase the patch on the latest
> Simen> master due to the changes in 7b00e956b4. It can be fixed by passing 2
> Simen> as the last argument to `color-rgb-to-hex' in `css--hsl-color'.
>
> This happens due to the change in 7b00e956b485d8ade03c870cbdd0ae086348737b,
> which changed color-name-to-rgb in a backward-incompatible way.
> Eli, you made this change -- it seems to me that it would be safer if it
> defaulted to 2 digits, which was how it worked previously. This would
> be backward-compatible and allow the new functionality as well.
If we leave the default at 2, people will never discover they need to
use 4 there, and the original problem which prompted those changes
will be still with us years from now. The reasons for using 4-digit
(i.e. 16 bits per component) hex specifications are subtle and buried
deep in the Emacs internals related to color calculations. I don't
expect Lisp programmers to understand those reasons. I actually
forgot about this subtlety myself, until that bug surfaced.
When I worked on the change, it seemed harmless: the function has only
one caller outside of color.el, and I changed that single caller to
use the optional argument. css-mode is the second such caller, and if
the code was there when I made the changes, I'd have fixed that as
well. Both shr-color and css-mode manipulate Web-related color specs,
where 2 hex digits per component are the rule. That's a niche
application, as far as color management in Emacs is concerned, so,
unfortunate as it is, it's up to the people who develop such
applications to know that they need 2 digits rather than the default
4.
If the change I made is nevertheless deemed too drastic, then what are
our alternatives? The only one I could think of is to define a new
function and deprecate color-name-to-rgb in favor of that new
function, which will then display warnings when code using it is
compiled, and eventually cause them to make changes in their code
anyway. Is that better? Or are there any better ideas?
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-03-04 19:58 ` Tom Tromey
@ 2017-03-05 18:28 ` Simen Heggestøyl
[not found] ` <871str3b48.fsf@tromey.com>
0 siblings, 1 reply; 39+ messages in thread
From: Simen Heggestøyl @ 2017-03-05 18:28 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
On Sat, Mar 4, 2017 at 8:58 PM, Tom Tromey <tom@tromey.com> wrote:
> Thank you. This was due to using the wrong block name in a call to
> cl-return-from. I must have changed the function name and forgotten
> about this bit. Anyway, apply the appended patch on top of what
> you've
> got and please try to find more bugs :)
Yes, that fixed it, thanks.
Here's another one: SCSS variables and mixins with the exact same name
as a color are highlighted. So in the following, both instances of
"black" are highlighted in black:
@mixin black {
$black: #000;
}
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
[not found] ` <871str3b48.fsf@tromey.com>
@ 2017-03-20 21:28 ` Tom Tromey
2017-03-22 22:07 ` Tom Tromey
1 sibling, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-03-20 21:28 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525, Simen Heggestøyl
Simen> Here's another one: SCSS variables and mixins with the exact same name
Simen> as a color are highlighted. So in the following, both instances of
Simen> "black" are highlighted in black:
Simen> @mixin black {
Simen> $black: #000;
Simen> }
Tom> Fixing "$black" seems possible. I'll take a look.
I just tried my patch and "$black" is not highlighted here in scss mode.
I think that's expected, now that I look at it, because
css--colors-regexp starts with a "\_<", so it only matches at symbol
boundaries.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
[not found] ` <871str3b48.fsf@tromey.com>
2017-03-20 21:28 ` Tom Tromey
@ 2017-03-22 22:07 ` Tom Tromey
2017-03-29 18:27 ` Simen Heggestøyl
1 sibling, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-03-22 22:07 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525, Simen Heggestøyl
Tom> I'm not sure the @mixin problem can be fixed without doing more parsing.
On second thought, I suppose checking backward one word isn't such a big
deal. I don't know SCSS though. Is @mixin the only problem case or are
there others?
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-03-22 22:07 ` Tom Tromey
@ 2017-03-29 18:27 ` Simen Heggestøyl
2017-04-21 3:40 ` Tom Tromey
0 siblings, 1 reply; 39+ messages in thread
From: Simen Heggestøyl @ 2017-03-29 18:27 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
> I just tried my patch and "$black" is not highlighted here in scss
mode.
> I think that's expected, now that I look at it, because
> css--colors-regexp starts with a "\_<", so it only matches at symbol
> boundaries.
Hm, I'm still seeing it here with the patch from March 4. Maybe that one
is outdated?
> On second thought, I suppose checking backward one word isn't such a
big
> deal. I don't know SCSS though. Is @mixin the only problem case or
are
> there others?
I think the names of mixins and placeholders are most important, because
it's not unlikely for them to have colors for names. Examples of usage:
@mixin black {}
.foo { @include black; }
%black {}
.foo { @extend %black; }
There's also maps, but I think they're less important if it's hard to
solve:
$foo: (bar: 1, black: 2);
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-03-29 18:27 ` Simen Heggestøyl
@ 2017-04-21 3:40 ` Tom Tromey
2017-04-21 9:33 ` Andreas Schwab
2017-04-22 15:07 ` Simen Heggestøyl
0 siblings, 2 replies; 39+ messages in thread
From: Tom Tromey @ 2017-04-21 3:40 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
[-- Attachment #1: Type: text/plain, Size: 928 bytes --]
>>>>> "Simen" == Simen Heggestøyl <simenheg@gmail.com> writes:
Sorry about the delay on this. I've been busy with other things.
Simen> Hm, I'm still seeing it here with the patch from March 4. Maybe
Simen> that one is outdated?
I wouldn't think so, but I'm attaching a rolled-up patch here.
Simen> I think the names of mixins and placeholders are most important, because
Simen> it's not unlikely for them to have colors for names. Examples of usage:
Ok, I can easily exclude @mixin and @extend.
Simen> There's also maps, but I think they're less important if it's hard to
Simen> solve:
Simen> $foo: (bar: 1, black: 2);
I'm not sure there's a good way to distinguish these.
Let me know what you think. I appreciate you taking the time to try
this out.
FWIW I've been using this in my work. It works with mhtml-mode, so I've
been seeing colors show up in Firefox mochitests... fun!
Tom
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: latest patch --]
[-- Type: text/x-patch, Size: 20609 bytes --]
commit 44c67a067d7759fb15af032416bce51714bb12eb
Author: Tom Tromey <tom@tromey.com>
Date: Wed Jan 25 00:53:49 2017 -0700
Add color highlighting to css-mode
* lisp/textmodes/css-mode.el (css--color-map): New constant.
(css-value-class-alist): Use css--color-map.
(css--number-regexp, css--percent-regexp)
(css--number-or-percent-regexp, css--angle-regexp): New constants.
(css--color-skip-blanks, css--rgb-color, css--hsl-color): New
functions.
(css--colors-regexp): New constant.
(css--hex-color, css--named-color, css--compute-color)
(css--contrasty-color, css--fontify-colors)
(css--fontify-region): New functions.
(css-mode): Set font-lock-fontify-region-function.
(css-mode-syntax-table): Set syntax on more characters.
(css-fontify-colors): New defcustom.
* test/lisp/textmodes/css-mode-tests.el (css-test-property-values):
Update.
(css-test-rgb-parser, css-test-hsl-parser)
(css-test-named-color): New tests.
* etc/NEWS: Add entry.
diff --git a/etc/NEWS b/etc/NEWS
index 7281827..2b0841d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -719,6 +719,11 @@ pseudo-element, with the default being guessed from context). By
default the information is looked up on the Mozilla Developer Network,
but this can be customized using 'css-lookup-url-format'.
+---
+*** CSS colors are fontified using the color they represent as the
+background. For instance, #ff0000 would be fontified with a red
+background.
+
+++
** Emacs now supports character name escape sequences in character and
string literals. The syntax variants \N{character name} and
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index d4a5cfe..89c7cb9 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -33,6 +33,8 @@
;;; Code:
(require 'eww)
+(require 'cl-lib)
+(require 'color)
(require 'seq)
(require 'sgml-mode)
(require 'smie)
@@ -487,8 +489,157 @@ css-property-ids
(mapcar #'car css-property-alist)
"Identifiers for properties.")
+(defconst css--color-map
+ '(("black" . "#000000")
+ ("silver" . "#c0c0c0")
+ ("gray" . "#808080")
+ ("white" . "#ffffff")
+ ("maroon" . "#800000")
+ ("red" . "#ff0000")
+ ("purple" . "#800080")
+ ("fuchsia" . "#ff00ff")
+ ("green" . "#008000")
+ ("lime" . "#00ff00")
+ ("olive" . "#808000")
+ ("yellow" . "#ffff00")
+ ("navy" . "#000080")
+ ("blue" . "#0000ff")
+ ("teal" . "#008080")
+ ("aqua" . "#00ffff")
+ ("orange" . "#ffa500")
+ ("aliceblue" . "#f0f8ff")
+ ("antiquewhite" . "#faebd7")
+ ("aquamarine" . "#7fffd4")
+ ("azure" . "#f0ffff")
+ ("beige" . "#f5f5dc")
+ ("bisque" . "#ffe4c4")
+ ("blanchedalmond" . "#ffebcd")
+ ("blueviolet" . "#8a2be2")
+ ("brown" . "#a52a2a")
+ ("burlywood" . "#deb887")
+ ("cadetblue" . "#5f9ea0")
+ ("chartreuse" . "#7fff00")
+ ("chocolate" . "#d2691e")
+ ("coral" . "#ff7f50")
+ ("cornflowerblue" . "#6495ed")
+ ("cornsilk" . "#fff8dc")
+ ("crimson" . "#dc143c")
+ ("darkblue" . "#00008b")
+ ("darkcyan" . "#008b8b")
+ ("darkgoldenrod" . "#b8860b")
+ ("darkgray" . "#a9a9a9")
+ ("darkgreen" . "#006400")
+ ("darkgrey" . "#a9a9a9")
+ ("darkkhaki" . "#bdb76b")
+ ("darkmagenta" . "#8b008b")
+ ("darkolivegreen" . "#556b2f")
+ ("darkorange" . "#ff8c00")
+ ("darkorchid" . "#9932cc")
+ ("darkred" . "#8b0000")
+ ("darksalmon" . "#e9967a")
+ ("darkseagreen" . "#8fbc8f")
+ ("darkslateblue" . "#483d8b")
+ ("darkslategray" . "#2f4f4f")
+ ("darkslategrey" . "#2f4f4f")
+ ("darkturquoise" . "#00ced1")
+ ("darkviolet" . "#9400d3")
+ ("deeppink" . "#ff1493")
+ ("deepskyblue" . "#00bfff")
+ ("dimgray" . "#696969")
+ ("dimgrey" . "#696969")
+ ("dodgerblue" . "#1e90ff")
+ ("firebrick" . "#b22222")
+ ("floralwhite" . "#fffaf0")
+ ("forestgreen" . "#228b22")
+ ("gainsboro" . "#dcdcdc")
+ ("ghostwhite" . "#f8f8ff")
+ ("gold" . "#ffd700")
+ ("goldenrod" . "#daa520")
+ ("greenyellow" . "#adff2f")
+ ("grey" . "#808080")
+ ("honeydew" . "#f0fff0")
+ ("hotpink" . "#ff69b4")
+ ("indianred" . "#cd5c5c")
+ ("indigo" . "#4b0082")
+ ("ivory" . "#fffff0")
+ ("khaki" . "#f0e68c")
+ ("lavender" . "#e6e6fa")
+ ("lavenderblush" . "#fff0f5")
+ ("lawngreen" . "#7cfc00")
+ ("lemonchiffon" . "#fffacd")
+ ("lightblue" . "#add8e6")
+ ("lightcoral" . "#f08080")
+ ("lightcyan" . "#e0ffff")
+ ("lightgoldenrodyellow" . "#fafad2")
+ ("lightgray" . "#d3d3d3")
+ ("lightgreen" . "#90ee90")
+ ("lightgrey" . "#d3d3d3")
+ ("lightpink" . "#ffb6c1")
+ ("lightsalmon" . "#ffa07a")
+ ("lightseagreen" . "#20b2aa")
+ ("lightskyblue" . "#87cefa")
+ ("lightslategray" . "#778899")
+ ("lightslategrey" . "#778899")
+ ("lightsteelblue" . "#b0c4de")
+ ("lightyellow" . "#ffffe0")
+ ("limegreen" . "#32cd32")
+ ("linen" . "#faf0e6")
+ ("mediumaquamarine" . "#66cdaa")
+ ("mediumblue" . "#0000cd")
+ ("mediumorchid" . "#ba55d3")
+ ("mediumpurple" . "#9370db")
+ ("mediumseagreen" . "#3cb371")
+ ("mediumslateblue" . "#7b68ee")
+ ("mediumspringgreen" . "#00fa9a")
+ ("mediumturquoise" . "#48d1cc")
+ ("mediumvioletred" . "#c71585")
+ ("midnightblue" . "#191970")
+ ("mintcream" . "#f5fffa")
+ ("mistyrose" . "#ffe4e1")
+ ("moccasin" . "#ffe4b5")
+ ("navajowhite" . "#ffdead")
+ ("oldlace" . "#fdf5e6")
+ ("olivedrab" . "#6b8e23")
+ ("orangered" . "#ff4500")
+ ("orchid" . "#da70d6")
+ ("palegoldenrod" . "#eee8aa")
+ ("palegreen" . "#98fb98")
+ ("paleturquoise" . "#afeeee")
+ ("palevioletred" . "#db7093")
+ ("papayawhip" . "#ffefd5")
+ ("peachpuff" . "#ffdab9")
+ ("peru" . "#cd853f")
+ ("pink" . "#ffc0cb")
+ ("plum" . "#dda0dd")
+ ("powderblue" . "#b0e0e6")
+ ("rosybrown" . "#bc8f8f")
+ ("royalblue" . "#4169e1")
+ ("saddlebrown" . "#8b4513")
+ ("salmon" . "#fa8072")
+ ("sandybrown" . "#f4a460")
+ ("seagreen" . "#2e8b57")
+ ("seashell" . "#fff5ee")
+ ("sienna" . "#a0522d")
+ ("skyblue" . "#87ceeb")
+ ("slateblue" . "#6a5acd")
+ ("slategray" . "#708090")
+ ("slategrey" . "#708090")
+ ("snow" . "#fffafa")
+ ("springgreen" . "#00ff7f")
+ ("steelblue" . "#4682b4")
+ ("tan" . "#d2b48c")
+ ("thistle" . "#d8bfd8")
+ ("tomato" . "#ff6347")
+ ("turquoise" . "#40e0d0")
+ ("violet" . "#ee82ee")
+ ("wheat" . "#f5deb3")
+ ("whitesmoke" . "#f5f5f5")
+ ("yellowgreen" . "#9acd32")
+ ("rebeccapurple" . "#663399"))
+ "Map CSS named colors to their hex RGB value.")
+
(defconst css-value-class-alist
- '((absolute-size
+ `((absolute-size
"xx-small" "x-small" "small" "medium" "large" "x-large"
"xx-large")
(alphavalue number)
@@ -550,36 +701,7 @@ css-value-class-alist
(line-width length "thin" "medium" "thick")
(linear-gradient "linear-gradient()")
(margin-width "auto" length percentage)
- (named-color
- "aliceblue" "antiquewhite" "aqua" "aquamarine" "azure" "beige"
- "bisque" "black" "blanchedalmond" "blue" "blueviolet" "brown"
- "burlywood" "cadetblue" "chartreuse" "chocolate" "coral"
- "cornflowerblue" "cornsilk" "crimson" "cyan" "darkblue"
- "darkcyan" "darkgoldenrod" "darkgray" "darkgreen" "darkkhaki"
- "darkmagenta" "darkolivegreen" "darkorange" "darkorchid"
- "darkred" "darksalmon" "darkseagreen" "darkslateblue"
- "darkslategray" "darkturquoise" "darkviolet" "deeppink"
- "deepskyblue" "dimgray" "dodgerblue" "firebrick" "floralwhite"
- "forestgreen" "fuchsia" "gainsboro" "ghostwhite" "gold"
- "goldenrod" "gray" "green" "greenyellow" "honeydew" "hotpink"
- "indianred" "indigo" "ivory" "khaki" "lavender" "lavenderblush"
- "lawngreen" "lemonchiffon" "lightblue" "lightcoral" "lightcyan"
- "lightgoldenrodyellow" "lightgray" "lightgreen" "lightpink"
- "lightsalmon" "lightseagreen" "lightskyblue" "lightslategray"
- "lightsteelblue" "lightyellow" "lime" "limegreen" "linen"
- "magenta" "maroon" "mediumaquamarine" "mediumblue" "mediumorchid"
- "mediumpurple" "mediumseagreen" "mediumslateblue"
- "mediumspringgreen" "mediumturquoise" "mediumvioletred"
- "midnightblue" "mintcream" "mistyrose" "moccasin" "navajowhite"
- "navy" "oldlace" "olive" "olivedrab" "orange" "orangered"
- "orchid" "palegoldenrod" "palegreen" "paleturquoise"
- "palevioletred" "papayawhip" "peachpuff" "peru" "pink" "plum"
- "powderblue" "purple" "rebeccapurple" "red" "rosybrown"
- "royalblue" "saddlebrown" "salmon" "sandybrown" "seagreen"
- "seashell" "sienna" "silver" "skyblue" "slateblue" "slategray"
- "snow" "springgreen" "steelblue" "tan" "teal" "thistle" "tomato"
- "turquoise" "violet" "wheat" "white" "whitesmoke" "yellow"
- "yellowgreen")
+ (named-color . ,(mapcar #'car css--color-map))
(number "calc()")
(numeric-figure-values "lining-nums" "oldstyle-nums")
(numeric-fraction-values "diagonal-fractions" "stacked-fractions")
@@ -663,11 +785,23 @@ css-mode-syntax-table
(modify-syntax-entry ?\[ "(]" st)
(modify-syntax-entry ?\] ")[" st)
;; Special chars that sometimes come at the beginning of words.
- (modify-syntax-entry ?@ "'" st)
- ;; (modify-syntax-entry ?: "'" st)
- (modify-syntax-entry ?# "'" st)
+ ;; We'll treat them as symbol constituents.
+ (modify-syntax-entry ?@ "_" st)
+ (modify-syntax-entry ?# "_" st)
+ (modify-syntax-entry ?. "_" st)
;; Distinction between words and symbols.
(modify-syntax-entry ?- "_" st)
+
+ (modify-syntax-entry ?! "." st)
+ (modify-syntax-entry ?$ "." st)
+ (modify-syntax-entry ?% "." st)
+ (modify-syntax-entry ?& "." st)
+ (modify-syntax-entry ?+ "." st)
+ (modify-syntax-entry ?, "." st)
+ (modify-syntax-entry ?< "." st)
+ (modify-syntax-entry ?> "." st)
+ (modify-syntax-entry ?= "." st)
+ (modify-syntax-entry ?? "." st)
st))
(defvar css-mode-map
@@ -782,6 +916,218 @@ css-font-lock-keywords
(defvar css-font-lock-defaults
'(css-font-lock-keywords nil t))
+(defconst css--number-regexp
+ "\\(\\(?:[0-9]*\\.[0-9]+\\(?:[eE][0-9]+\\)?\\)\\|[0-9]+\\)"
+ "A regular expression matching a CSS number.")
+
+(defconst css--percent-regexp "\\([0-9]+\\)%"
+ "A regular expression matching a CSS percentage.")
+
+(defconst css--number-or-percent-regexp
+ (concat "\\(?:" css--percent-regexp "\\)\\|\\(?:" css--number-regexp "\\)")
+ "A regular expression matching a CSS number or a CSS percentage.")
+
+(defconst css--angle-regexp
+ (concat css--number-regexp
+ (regexp-opt '("deg" "grad" "rad" "turn") t)
+ "?")
+ "A regular expression matching a CSS angle.")
+
+(defun css--color-skip-blanks ()
+ "Skip blanks and comments."
+ (while (forward-comment 1)))
+
+(cl-defun css--rgb-color ()
+ "Parse a CSS rgb() or rgba() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '())
+ (iter 0))
+ (while (< iter 4)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--rgb-color nil))
+ (let* ((is-percent (match-beginning 1))
+ (str (match-string (if is-percent 1 2)))
+ (number (string-to-number str)))
+ (when is-percent
+ (setq number (* 255 (/ number 100.0))))
+ ;; Don't push the alpha.
+ (when (< iter 3)
+ (push (min (max 0 (truncate number)) 255) result))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)
+ (cl-incf iter)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (and (= (skip-chars-forward ",/") 0)
+ (= iter 3))
+ ;; The alpha is optional.
+ (cl-incf iter))
+ (css--color-skip-blanks)))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'format "#%02x%02x%02x" (nreverse result)))))
+
+(cl-defun css--hsl-color ()
+ "Parse a CSS hsl() or hsla() color.
+Point should be just after the open paren.
+Returns a hex RGB color, or nil if the color could not be recognized.
+This recognizes CSS-color-4 extensions."
+ (let ((result '()))
+ ;; First parse the hue.
+ (css--color-skip-blanks)
+ (unless (looking-at css--angle-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((hue (string-to-number (match-string 1)))
+ (unit (match-string 2)))
+ (goto-char (match-end 0))
+ ;; Note that here "turn" is just passed through.
+ (cond
+ ((or (not unit) (equal unit "deg"))
+ ;; Degrees.
+ (setq hue (/ hue 360.0)))
+ ((equal unit "grad")
+ (setq hue (/ hue 400.0)))
+ ((equal unit "rad")
+ (setq hue (/ hue (* 2 float-pi)))))
+ (push (mod hue 1.0) result))
+ (dotimes (_ 2)
+ (skip-chars-forward ",")
+ (css--color-skip-blanks)
+ (unless (looking-at css--percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (let ((number (string-to-number (match-string 1))))
+ (setq number (/ number 100.0))
+ (push (min (max number 0.0) 1.0) result)
+ (goto-char (match-end 0))
+ (css--color-skip-blanks)))
+ (css--color-skip-blanks)
+ ;; Accept a superset of the CSS syntax since I'm feeling lazy.
+ (when (> (skip-chars-forward ",/") 0)
+ (css--color-skip-blanks)
+ (unless (looking-at css--number-or-percent-regexp)
+ (cl-return-from css--hsl-color nil))
+ (goto-char (match-end 0))
+ (css--color-skip-blanks))
+ (when (looking-at ")")
+ (forward-char)
+ (apply #'color-rgb-to-hex
+ (nconc (apply #'color-hsl-to-rgb (nreverse result)) '(2))))))
+
+(defconst css--colors-regexp
+ (concat
+ ;; Named colors.
+ (regexp-opt (mapcar #'car css--color-map) 'symbols)
+ "\\|"
+ ;; Short hex. css-color-4 adds alpha.
+ "\\(#[0-9a-fA-F]\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; Long hex. css-color-4 adds alpha.
+ "\\(#\\(?:[0-9a-fA-F][0-9a-fA-F]\\)\\{3,4\\}\\b\\)"
+ "\\|"
+ ;; RGB.
+ "\\(\\_<rgba?(\\)"
+ "\\|"
+ ;; HSL.
+ "\\(\\_<hsla?(\\)")
+ "A regular expression that matches the start of a CSS color.")
+
+(defun css--hex-color (str)
+ "Convert a CSS hex color to an Emacs hex color.
+STR is the incoming CSS hex color.
+This function simply drops any transparency."
+ ;; Either #RGB or #RRGGBB, drop the "A" or "AA".
+ (if (> (length str) 4)
+ (substring str 0 7)
+ (substring str 0 4)))
+
+(defun css--named-color (str)
+ "Check whether STR, seen at point, is CSS named color.
+Returns STR if it is a valid color. Special care is taken
+to exclude some SCSS contructs."
+ (when-let ((color (assoc str css--color-map)))
+ (save-excursion
+ ;; We still have the match from the caller of
+ ;; css--compute-color.
+ (goto-char (match-beginning 0))
+ (forward-comment (- (point)))
+ (skip-chars-backward "@[:alpha:]")
+ (unless (looking-at-p "@\\(mixin\\|include\\)")
+ (cdr color)))))
+
+(defun css--compute-color ()
+ "Return the CSS color at point.
+Point should be just after the start of a CSS color, as recognized
+by `css--colors-regexp'. This function will either return the color,
+as a hex RGB string; or `nil' if no color could be recognized. When
+this function returns, point will be at the end of the recognized
+color."
+ (let ((match (downcase (match-string 0))))
+ (cond
+ ((eq (aref match 0) ?#)
+ (css--hex-color match))
+ ((member match '("rgb(" "rgba("))
+ (css--rgb-color))
+ ((member match '("hsl(" "hsla("))
+ (css--hsl-color))
+ ;; Evaluate to the color if the name is found.
+ ((css--named-color match)))))
+
+(defun css--contrasty-color (name)
+ "Return a color that contrasts with NAME.
+NAME is of any form accepted by `color-distance'.
+The returned color will be usable by Emacs and will contrast
+with NAME; in particular so that if NAME is used as a background
+color, the returned color can be used as the foreground and still
+be readable."
+ ;; See bug#25525 for a discussion of this.
+ (if (> (color-distance name "black") 292485)
+ "black" "white"))
+
+(defcustom css-fontify-colors t
+ "Whether CSS colors should be fontified using the color as the background.
+When non-`nil', a text representing CSS color will be fontified
+such that its background is the color itself. E.g., #ff0000 will
+be fontified with a red background."
+ :version "26.1"
+ :group 'css
+ :type 'boolean
+ :safe 'booleanp)
+
+(defun css--fontify-region (start end &optional loudly)
+ "Fontify a CSS buffer between START and END.
+START and END are buffer positions."
+ (let ((extended-region (font-lock-default-fontify-region start end loudly)))
+ (when css-fontify-colors
+ (when (and (consp extended-region)
+ (eq (car extended-region) 'jit-lock-bounds))
+ (setq start (cadr extended-region))
+ (setq end (cddr extended-region)))
+ (save-excursion
+ (let ((case-fold-search t))
+ (goto-char start)
+ (while (re-search-forward css--colors-regexp end t)
+ ;; Skip comments and strings.
+ (unless (nth 8 (syntax-ppss))
+ (let ((start (match-beginning 0))
+ (color (css--compute-color)))
+ (when color
+ (with-silent-modifications
+ ;; Use the color as the background, to make it more
+ ;; clear. Use a contrasting color as the foreground,
+ ;; to make it readable. Finally, have a small box
+ ;; using the existing foreground color, to make sure
+ ;; it stands out a bit from any other text; in
+ ;; particular this is nice when the color matches the
+ ;; buffer's background color.
+ (add-text-properties
+ start (point)
+ (list 'face (list :background color
+ :foreground (css--contrasty-color color)
+ :box '(:line-width -1))))))))))))
+ extended-region))
+
(defcustom css-indent-offset 4
"Basic size of one indentation step."
:version "22.2"
@@ -1048,6 +1394,7 @@ css-mode
:backward-token #'css-smie--backward-token)
(setq-local electric-indent-chars
(append css-electric-keys electric-indent-chars))
+ (setq-local font-lock-fontify-region-function #'css--fontify-region)
(add-hook 'completion-at-point-functions
#'css-completion-at-point nil 'local))
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index d601f43..0e7e945 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -58,7 +58,7 @@
;; Check that the `color' property doesn't cause infinite recursion
;; because it refers to the value class of the same name.
- (should (= (length (css--property-values "color")) 147)))
+ (should (= (length (css--property-values "color")) 152)))
(ert-deftest css-test-property-value-cache ()
"Test that `css--property-value-cache' is in use."
@@ -234,5 +234,48 @@ css-mode-tests--completions
(save-excursion (insert (nth 1 item)))
(should (equal (nth 2 item) (css--mdn-find-symbol))))))
+(ert-deftest css-test-rgb-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("255, 0, 127"
+ "255, /* comment */ 0, 127"
+ "255 0 127"
+ "255, 0, 127, 0.75"
+ "255 0 127 / 0.75"
+ "100%, 0%, 50%"
+ "100%, 0%, 50%, 0.115"
+ "100% 0% 50%"
+ "100% 0% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--rgb-color) "#ff007f")))))
+
+(ert-deftest css-test-hsl-parser ()
+ (with-temp-buffer
+ (css-mode)
+ (dolist (input '("0, 100%, 50%"
+ "0 100% 50%"
+ "0 /* two */ /* comments */100% 50%"
+ "0, 100%, 50%, 0.75"
+ "0 100% 50% / 0.75"
+ "0deg 100% 50%"
+ "360deg 100% 50%"
+ "0rad, 100%, 50%, 0.115"
+ "0grad, 100%, 50%, 0.115"
+ "1turn 100% 50% / 0.115"))
+ (erase-buffer)
+ (save-excursion
+ (insert input ")"))
+ (should (equal (css--hsl-color) "#ff0000")))))
+
+(ert-deftest css-test-named-color ()
+ (dolist (text '("@mixin black" "@include black"))
+ (with-temp-buffer
+ (insert text)
+ (backward-word)
+ (should (and (looking-at "black")
+ (not (css--named-color "black")))))))
+
(provide 'css-mode-tests)
;;; css-mode-tests.el ends here
^ permalink raw reply related [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-04-21 3:40 ` Tom Tromey
@ 2017-04-21 9:33 ` Andreas Schwab
2017-04-22 0:44 ` Tom Tromey
2017-04-22 15:07 ` Simen Heggestøyl
1 sibling, 1 reply; 39+ messages in thread
From: Andreas Schwab @ 2017-04-21 9:33 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525, Simen Heggestøyl
On Apr 20 2017, Tom Tromey <tom@tromey.com> wrote:
> +(defun css--named-color (str)
> + "Check whether STR, seen at point, is CSS named color.
> +Returns STR if it is a valid color. Special care is taken
> +to exclude some SCSS contructs."
> + (when-let ((color (assoc str css--color-map)))
> + (save-excursion
> + ;; We still have the match from the caller of
> + ;; css--compute-color.
> + (goto-char (match-beginning 0))
> + (forward-comment (- (point)))
> + (skip-chars-backward "@[:alpha:]")
> + (unless (looking-at-p "@\\(mixin\\|include\\)")
> + (cdr color)))))
> +
> +(defun css--compute-color ()
> + "Return the CSS color at point.
> +Point should be just after the start of a CSS color, as recognized
> +by `css--colors-regexp'. This function will either return the color,
> +as a hex RGB string; or `nil' if no color could be recognized. When
> +this function returns, point will be at the end of the recognized
> +color."
> + (let ((match (downcase (match-string 0))))
> + (cond
> + ((eq (aref match 0) ?#)
> + (css--hex-color match))
> + ((member match '("rgb(" "rgba("))
> + (css--rgb-color))
> + ((member match '("hsl(" "hsla("))
> + (css--hsl-color))
> + ;; Evaluate to the color if the name is found.
> + ((css--named-color match)))))
It would be better to pass the match string as an argument instead of
depending on the caller to establish a match.
Andreas.
--
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-04-21 9:33 ` Andreas Schwab
@ 2017-04-22 0:44 ` Tom Tromey
0 siblings, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-04-22 0:44 UTC (permalink / raw)
To: Andreas Schwab; +Cc: 25525, Tom Tromey, Simen Heggestøyl
Andreas> It would be better to pass the match string as an argument instead of
Andreas> depending on the caller to establish a match.
Yes, thanks. I made this change.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-04-21 3:40 ` Tom Tromey
2017-04-21 9:33 ` Andreas Schwab
@ 2017-04-22 15:07 ` Simen Heggestøyl
2017-04-24 13:44 ` Tom Tromey
1 sibling, 1 reply; 39+ messages in thread
From: Simen Heggestøyl @ 2017-04-22 15:07 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
Hi Tom, thanks for this update.
On Fri, Apr 21, 2017 at 5:40 AM, Tom Tromey <tom@tromey.com> wrote:
> Sorry about the delay on this. I've been busy with other things.
No problem.
> Let me know what you think. I appreciate you taking the time to try
> this out.
I think it's starting to look very good (I hope you're not getting
discouraged by my nitpickery).
The problem with SCSS variable names remains for me, even with the
newest patch. See the following image:
http://folk.uio.no/simenheg/tmp/screenshot-2017-04-22-16-53-25.png.
Are you able to reproduce it? I think that's the last important problem
to fix before we land this.
> FWIW I've been using this in my work. It works with mhtml-mode, so
> I've
> been seeing colors show up in Firefox mochitests... fun!
Cool! I'm looking forward to getting this in.
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-04-22 15:07 ` Simen Heggestøyl
@ 2017-04-24 13:44 ` Tom Tromey
2017-04-29 4:17 ` Tom Tromey
0 siblings, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-04-24 13:44 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
Simen> Are you able to reproduce it? I think that's the last important
Simen> problem to fix before we land this.
I finally was, I will look into it soon. Thanks again.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-04-24 13:44 ` Tom Tromey
@ 2017-04-29 4:17 ` Tom Tromey
2017-05-01 19:15 ` Simen Heggestøyl
2017-05-01 21:25 ` Etienne Prud’homme
0 siblings, 2 replies; 39+ messages in thread
From: Tom Tromey @ 2017-04-29 4:17 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525, Simen Heggestøyl
Tom> I finally was, I will look into it soon. Thanks again.
This patch on top of my current patch was enough. However I'm not sure
it is the best way. It does pass the css mode tests, but I'm also not
sure that is sufficient. Could you try it out?
Tom
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 9584bd2..2c81710 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -1506,7 +1506,8 @@ scss-mode-syntax-table
(modify-syntax-entry ?/ ". 124" st)
(modify-syntax-entry ?\n ">" st)
;; Variable names are prefixed by $.
- (modify-syntax-entry ?$ "'" st)
+ (modify-syntax-entry ?$ "_" st)
+ (modify-syntax-entry ?% "_" st)
st))
(defun scss-font-lock-keywords ()
^ permalink raw reply related [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-04-29 4:17 ` Tom Tromey
@ 2017-05-01 19:15 ` Simen Heggestøyl
2017-05-03 22:04 ` Tom Tromey
2017-05-01 21:25 ` Etienne Prud’homme
1 sibling, 1 reply; 39+ messages in thread
From: Simen Heggestøyl @ 2017-05-01 19:15 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525
On Sat, Apr 29, 2017 at 6:17 AM, Tom Tromey <tom@tromey.com> wrote:
> Tom> I finally was, I will look into it soon. Thanks again.
>
> This patch on top of my current patch was enough. However I'm not
> sure
> it is the best way. It does pass the css mode tests, but I'm also not
> sure that is sufficient. Could you try it out?
I'm not sure either, but it does seem to fix the issue.
I haven't discovered any more issues since last time, so the feature
seems good to go as far as I'm concerned!
-- Simen
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-04-29 4:17 ` Tom Tromey
2017-05-01 19:15 ` Simen Heggestøyl
@ 2017-05-01 21:25 ` Etienne Prud’homme
2017-05-03 22:03 ` Tom Tromey
1 sibling, 1 reply; 39+ messages in thread
From: Etienne Prud’homme @ 2017-05-01 21:25 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525, Simen Heggestøyl
Tom Tromey <tom@tromey.com> writes:
> This patch on top of my current patch was enough. However I'm not sure
> it is the best way. It does pass the css mode tests, but I'm also not
> sure that is sufficient. Could you try it out?
I’ve thought in the pass it would be nicer if we had a real (S)CSS
parser. I don’t mean by that a renderer (it would be a completely
different project). Instead I mean something that tells us the generated
“cascade”.
This would give us the ability to fontify (S)CSS color variables with
what color they got. We could also fontify color transformations.
What do you think?
--
Etienne
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-05-01 21:25 ` Etienne Prud’homme
@ 2017-05-03 22:03 ` Tom Tromey
2017-05-03 23:24 ` Etienne Prud’homme
0 siblings, 1 reply; 39+ messages in thread
From: Tom Tromey @ 2017-05-03 22:03 UTC (permalink / raw)
To: Etienne Prud’homme; +Cc: 25525, Tom Tromey, Simen Heggestøyl
Etienne> I’ve thought in the pass it would be nicer if we had a real (S)CSS
Etienne> parser. I don’t mean by that a renderer (it would be a completely
Etienne> different project). Instead I mean something that tells us the generated
Etienne> “cascade”.
Etienne> This would give us the ability to fontify (S)CSS color variables with
Etienne> what color they got. We could also fontify color transformations.
Etienne> What do you think?
I think it would be good, but it would require more than just a CSS
parser - you also need the DOM, so you'd need a way for Emacs to
associate CSS and HTML files.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-05-01 19:15 ` Simen Heggestøyl
@ 2017-05-03 22:04 ` Tom Tromey
0 siblings, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-05-03 22:04 UTC (permalink / raw)
To: Simen Heggestøyl; +Cc: 25525, Tom Tromey
Simen> I haven't discovered any more issues since last time, so the feature
Simen> seems good to go as far as I'm concerned!
I'm going to check it in now. Thanks once again.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: done
2017-01-25 8:06 bug#25525: 25.1.90; add color highlighting to css mode Tom Tromey
` (2 preceding siblings ...)
2017-01-25 20:23 ` Simen Heggestøyl
@ 2017-05-03 22:04 ` Tom Tromey
3 siblings, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-05-03 22:04 UTC (permalink / raw)
To: 25525-done
Fix checked in.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-05-03 22:03 ` Tom Tromey
@ 2017-05-03 23:24 ` Etienne Prud’homme
2017-05-04 3:51 ` Tom Tromey
0 siblings, 1 reply; 39+ messages in thread
From: Etienne Prud’homme @ 2017-05-03 23:24 UTC (permalink / raw)
To: Tom Tromey; +Cc: 25525, Simen Heggestøyl
Tom Tromey <tom@tromey.com> writes:
> I think it would be good, but it would require more than just a CSS
> parser - you also need the DOM, so you'd need a way for Emacs to
> associate CSS and HTML files.
>
> Tom
Of course, I wasn’t talking about HTML. CSS is not confined only to
HTML. Sure HTML (the DOM) can set styles, but the styles are coming from
either an external style sheet or the browser default style sheet. CSS in
itself is a set of cascading style rules that apply according to a tree
structure.
What we could do with the parser is to tell us which rules are applied
in a given node path.
We could even apply CSS to the Emacs widget system (although it would
require lots of work).
--
Etienne
^ permalink raw reply [flat|nested] 39+ messages in thread
* bug#25525: 25.1.90; add color highlighting to css mode
2017-05-03 23:24 ` Etienne Prud’homme
@ 2017-05-04 3:51 ` Tom Tromey
0 siblings, 0 replies; 39+ messages in thread
From: Tom Tromey @ 2017-05-04 3:51 UTC (permalink / raw)
To: Etienne Prud’homme; +Cc: 25525, Tom Tromey, Simen Heggestøyl
Etienne> What we could do with the parser is to tell us which rules are applied
Etienne> in a given node path.
I'm not sure I understand what you are proposing, but I'm generally in
favor of anything making Emacs more useful. Perhaps we should discuss
it not in this bug.
Tom
^ permalink raw reply [flat|nested] 39+ messages in thread
end of thread, other threads:[~2017-05-04 3:51 UTC | newest]
Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-01-25 8:06 bug#25525: 25.1.90; add color highlighting to css mode Tom Tromey
2017-01-25 15:51 ` Eli Zaretskii
2017-01-25 17:25 ` Tom Tromey
2017-01-25 23:34 ` Tom Tromey
2017-01-26 16:05 ` Eli Zaretskii
2017-01-26 17:13 ` Tom Tromey
2017-01-26 19:32 ` Eli Zaretskii
2017-02-11 4:11 ` Tom Tromey
2017-01-26 17:17 ` Tom Tromey
2017-01-25 16:52 ` Glenn Morris
2017-01-25 20:23 ` Simen Heggestøyl
2017-01-25 20:41 ` Tom Tromey
2017-01-25 23:24 ` Tom Tromey
2017-01-26 18:25 ` Simen Heggestøyl
2017-02-11 4:16 ` Tom Tromey
2017-02-11 15:17 ` Tom Tromey
2017-02-17 19:29 ` Simen Heggestøyl
2017-03-04 17:55 ` Tom Tromey
2017-03-04 18:46 ` Simen Heggestøyl
2017-03-04 19:58 ` Tom Tromey
2017-03-05 18:28 ` Simen Heggestøyl
[not found] ` <871str3b48.fsf@tromey.com>
2017-03-20 21:28 ` Tom Tromey
2017-03-22 22:07 ` Tom Tromey
2017-03-29 18:27 ` Simen Heggestøyl
2017-04-21 3:40 ` Tom Tromey
2017-04-21 9:33 ` Andreas Schwab
2017-04-22 0:44 ` Tom Tromey
2017-04-22 15:07 ` Simen Heggestøyl
2017-04-24 13:44 ` Tom Tromey
2017-04-29 4:17 ` Tom Tromey
2017-05-01 19:15 ` Simen Heggestøyl
2017-05-03 22:04 ` Tom Tromey
2017-05-01 21:25 ` Etienne Prud’homme
2017-05-03 22:03 ` Tom Tromey
2017-05-03 23:24 ` Etienne Prud’homme
2017-05-04 3:51 ` Tom Tromey
2017-03-05 2:42 ` Tom Tromey
2017-03-05 15:34 ` Eli Zaretskii
2017-05-03 22:04 ` bug#25525: done Tom Tromey
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).