unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#29456: [PATCH] Add command for cycling between CSS color formats
@ 2017-11-26 15:21 Simen Heggestøyl
  2017-11-26 17:53 ` Tom Tromey
  2017-11-27 17:07 ` Eli Zaretskii
  0 siblings, 2 replies; 12+ messages in thread
From: Simen Heggestøyl @ 2017-11-26 15:21 UTC (permalink / raw)
  To: 29456; +Cc: Tom Tromey, Stefan Monnier


[-- Attachment #1.1: Type: text/plain, Size: 267 bytes --]

The attached patch adds a new command 'css-cycle-color-format' to CSS
mode, for cycling between color formats (e.g. "black" => "#000000" =>
"rgb(0, 0, 0)" => "black"), bound to 'C-c C-f'.

I'll install the patch after a while unless there are any comments.

-- Simen

[-- Attachment #1.2: Type: text/html, Size: 392 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-command-for-cycling-between-CSS-color-formats.patch --]
[-- Type: text/x-patch, Size: 7951 bytes --]

From 54f258b39822d7042c240fe6bbde63ccfb49e59d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Sun, 17 Sep 2017 20:08:18 +0200
Subject: [PATCH] Add command for cycling between CSS color formats

* lisp/textmodes/css-mode.el (css-mode-map): Add keybinding for
`css-cycle-color-format'.
(css--web-color-to-4-dpc, css-named-color-to-hex)
(css-hex-to-rgb, css-rgb-to-named-color-or-hex): New functions.
(css-cycle-color-format): New command for cycling between color
formats.

* test/lisp/textmodes/css-mode-tests.el (css-test-web-color-to-4-dpc):
(css-test-named-color-to-hex, css-test-hex-to-rgb)
(css-test-rgb-to-named-color-or-hex, css-test-cycle-color-format): New
tests for the functions mentioned above.

* etc/NEWS: Mention the new command.
---
 etc/NEWS                              |  7 ++++
 lisp/textmodes/css-mode.el            | 79 ++++++++++++++++++++++++++++++++++-
 test/lisp/textmodes/css-mode-tests.el | 53 +++++++++++++++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/etc/NEWS b/etc/NEWS
index c47ca42d27..ab9f285740 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -63,6 +63,13 @@ whether '"' is also replaced in 'electric-quote-mode'.  If non-nil,
 \f
 * Changes in Specialized Modes and Packages in Emacs 27.1
 
+** CSS mode
+
+---
+*** A new command 'css-cycle-color-format' for cycling between color
+formats (e.g. "black" => "#000000" => "rgb(0, 0, 0)") has been added,
+bound to 'C-c C-f'.
+
 ** Dired
 
 +++
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 93ca36b08a..aafcf8ade1 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -32,12 +32,13 @@
 
 ;;; Code:
 
-(require 'eww)
 (require 'cl-lib)
 (require 'color)
+(require 'eww)
 (require 'seq)
 (require 'sgml-mode)
 (require 'smie)
+(require 'thingatpt)
 (eval-when-compile (require 'subr-x))
 
 (defgroup css nil
@@ -806,6 +807,7 @@ css-mode-syntax-table
 (defvar css-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map [remap info-lookup-symbol] 'css-lookup-symbol)
+    (define-key map "\C-c\C-f" 'css-cycle-color-format)
     map)
   "Keymap used in `css-mode'.")
 
@@ -1383,6 +1385,81 @@ css-completion-at-point
                       (progn (insert ": ;")
                              (forward-char -1))))))))))
 
+(defun css--web-color-to-4-dpc (hex)
+  "Convert HEX web color to four digits per component.
+Web colors use one or two digits per component for RGB hex
+values.  Convert the given color to four digits per component."
+  (let ((six-digits (= (length hex) 7)))
+    (apply
+     #'concat
+     `("#"
+       ,@(seq-mapcat
+          (apply-partially #'make-list (if six-digits 2 4))
+          (seq-partition (seq-drop hex 1) (if six-digits 2 1)))))))
+
+(defun css-named-color-to-hex ()
+  "Convert named color at point to hex format.
+Return non-nil if a conversion was made."
+  (save-excursion
+    (unless (or (looking-at css--colors-regexp)
+                (eq (char-before) ?#))
+      (backward-word))
+    (when (member (word-at-point) (mapcar #'car css--color-map))
+      (looking-at css--colors-regexp)
+      (let ((color (css--compute-color (point) (match-string 0))))
+        (replace-match color))
+      t)))
+
+(defun css-hex-to-rgb ()
+  "Convert hex color at point to RGB format.
+Return non-nil if a conversion was made."
+  (save-excursion
+    (unless (eq (char-after) ?#)
+      (backward-sexp))
+    (when-let* ((hex (when (looking-at css--colors-regexp)
+                       (css--compute-color (point) (match-string 0)))))
+      (seq-let (r g b)
+          (mapcar (lambda (x) (round (* x 255)))
+                  (color-name-to-rgb (css--web-color-to-4-dpc hex)))
+        (replace-match (format "rgb(%d, %d, %d)" r g b)))
+      t)))
+
+(defun css-rgb-to-named-color-or-hex ()
+  "Convert RGB color at point to a named color or hex format.
+Convert to a named color if the color at point has a name, else
+convert to hex format.  Return non-nil if a conversion was made."
+  (save-excursion
+    (when-let* ((open-paren-pos (nth 1 (syntax-ppss))))
+      (when (save-excursion
+              (goto-char open-paren-pos)
+              (looking-back "rgb" 3))
+        (goto-char (nth 1 (syntax-ppss)))))
+    (when (eq (char-before) ?\))
+      (backward-sexp))
+    (skip-chars-backward "rgb")
+    (when (looking-at css--colors-regexp)
+      (let* ((start (match-end 0))
+             (color (save-excursion
+                      (goto-char start)
+                      (css--compute-color start (match-string 0)))))
+        (when color
+          (kill-sexp)
+          (kill-sexp)
+          (let ((named-color (seq-find (lambda (x) (equal (cdr x) color))
+                                       css--color-map)))
+            (insert (if named-color (car named-color) color)))
+          t)))))
+
+(defun css-cycle-color-format ()
+  "Cycle the color at point between different formats.
+Supported formats are by name (if possible), hexadecimal, and
+RGB."
+  (interactive)
+  (or (css-named-color-to-hex)
+      (css-hex-to-rgb)
+      (css-rgb-to-named-color-or-hex)
+      (message "It doesn't look like a color at point")))
+
 ;;;###autoload
 (define-derived-mode css-mode prog-mode "CSS"
   "Major mode to edit Cascading Style Sheets (CSS).
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index 47cf5f9244..be1ed55e42 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -244,6 +244,59 @@ css-mode-tests--completions
       (should (member "body" completions))
       (should-not (member "article" completions)))))
 
+(ert-deftest css-test-web-color-to-4-dpc ()
+  (should (equal (css--web-color-to-4-dpc "#ffffff")
+                 (css--web-color-to-4-dpc "#fff")))
+  (should (equal (css--web-color-to-4-dpc "#aabbcc")
+                 (css--web-color-to-4-dpc "#abc")))
+  (should (equal (css--web-color-to-4-dpc "#fab")
+                 "#ffffaaaabbbb"))
+  (should (equal (css--web-color-to-4-dpc "#fafbfc")
+                 "#fafafbfbfcfc")))
+
+(ert-deftest css-test-named-color-to-hex ()
+  (dolist (item '(("black" "#000000")
+                  ("white" "#ffffff")
+                  ("salmon" "#fa8072")))
+    (with-temp-buffer
+      (css-mode)
+      (insert (nth 0 item))
+      (css-named-color-to-hex)
+      (should (equal (buffer-string) (nth 1 item))))))
+
+(ert-deftest css-test-hex-to-rgb ()
+  (dolist (item '(("#000" "rgb(0, 0, 0)")
+                  ("#000000" "rgb(0, 0, 0)")
+                  ("#fff" "rgb(255, 255, 255)")
+                  ("#ffffff" "rgb(255, 255, 255)")))
+    (with-temp-buffer
+      (css-mode)
+      (insert (nth 0 item))
+      (css-hex-to-rgb)
+      (should (equal (buffer-string) (nth 1 item))))))
+
+(ert-deftest css-test-rgb-to-named-color-or-hex ()
+  (dolist (item '(("rgb(0, 0, 0)" "black")
+                  ("rgb(255, 255, 255)" "white")
+                  ("rgb(255, 255, 240)" "ivory")
+                  ("rgb(18, 52, 86)" "#123456")))
+    (with-temp-buffer
+      (css-mode)
+      (insert (nth 0 item))
+      (css-rgb-to-named-color-or-hex)
+      (should (equal (buffer-string) (nth 1 item))))))
+
+(ert-deftest css-test-cycle-color-format ()
+  (with-temp-buffer
+    (css-mode)
+    (insert "black")
+    (css-cycle-color-format)
+    (should (equal (buffer-string) "#000000"))
+    (css-cycle-color-format)
+    (should (equal (buffer-string) "rgb(0, 0, 0)"))
+    (css-cycle-color-format)
+    (should (equal (buffer-string) "black"))))
+
 (ert-deftest css-mdn-symbol-guessing ()
   (dolist (item '(("@med" "ia" "@media")
                   ("@keyframes " "{" "@keyframes")
-- 
2.15.0


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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-26 15:21 bug#29456: [PATCH] Add command for cycling between CSS color formats Simen Heggestøyl
@ 2017-11-26 17:53 ` Tom Tromey
  2017-11-27 17:07 ` Eli Zaretskii
  1 sibling, 0 replies; 12+ messages in thread
From: Tom Tromey @ 2017-11-26 17:53 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: 29456, tom, monnier

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

Simen> I'll install the patch after a while unless there are any comments.

Nice feature!

It seems to me that none of the functions handle alpha components.
css-color-4 added optional alpha to hex colors (and made rgb and rgba
synonyms), and there's been rgba for a while...

Tom





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-26 15:21 bug#29456: [PATCH] Add command for cycling between CSS color formats Simen Heggestøyl
  2017-11-26 17:53 ` Tom Tromey
@ 2017-11-27 17:07 ` Eli Zaretskii
  2017-11-27 17:41   ` Tom Tromey
  1 sibling, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2017-11-27 17:07 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: 29456, tom, monnier

> Date: Sun, 26 Nov 2017 16:21:51 +0100
> From: Simen Heggestøyl <simenheg@gmail.com>
> Cc: Tom Tromey <tom@tromey.com>, Stefan Monnier <monnier@iro.umontreal.ca>
> 
> The attached patch adds a new command 'css-cycle-color-format' to CSS
> mode, for cycling between color formats (e.g. "black" => "#000000" =>
> "rgb(0, 0, 0)" => "black"), bound to 'C-c C-f'.

Thanks.

These functions are confusingly similar to, but subtly different from,
the similar functions in color.el, and in general the whole system of
color conversions used by Emacs for its display needs.  Here's a
tell-tale example:

    (css--web-color-to-4-dpc "#fff")
      => "#ffffffffffff"
but
    (apply 'color-rgb-to-hex (color-name-to-rgb "#fff"))
      => "#f0f0f0f0f0f0"

The latter follows the X color definitions, so why does css-mode.el
need a different interpretation of the hex-RGB notation?

My fear is that someone will mix css-mode functions with those from
elsewhere in Emacs, and will bump into confusing and contradicting
results.  Is it possible to avoid that?





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-27 17:07 ` Eli Zaretskii
@ 2017-11-27 17:41   ` Tom Tromey
  2017-11-27 18:11     ` Eli Zaretskii
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2017-11-27 17:41 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 29456, tom, Simen Heggestøyl, monnier

>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:

Eli> The latter follows the X color definitions, so why does css-mode.el
Eli> need a different interpretation of the hex-RGB notation?

CSS color names came from X a long time ago, but their mapping to rgb is
now part of the CSS specification.  And, apparently there were multiple
versions of this file coming from X.  The Emacs one would be ok provided
the lists are identical and will always remain so.  Otherwise, css-mode
needs its own list.

Eli> My fear is that someone will mix css-mode functions with those from
Eli> elsewhere in Emacs, and will bump into confusing and contradicting
Eli> results.  Is it possible to avoid that?

Using "css-" in the name seems sufficient to me.  Maybe a comment
explaining this would help as well.

Tom





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-27 17:41   ` Tom Tromey
@ 2017-11-27 18:11     ` Eli Zaretskii
  2017-11-27 18:35       ` Tom Tromey
  0 siblings, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2017-11-27 18:11 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 29456, simenheg, monnier

> From: Tom Tromey <tom@tromey.com>
> Cc: Simen Heggestøyl <simenheg@gmail.com>,
>   29456@debbugs.gnu.org,  tom@tromey.com,  monnier@iro.umontreal.ca
> Date: Mon, 27 Nov 2017 10:41:29 -0700
> 
> >>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
> 
> Eli> The latter follows the X color definitions, so why does css-mode.el
> Eli> need a different interpretation of the hex-RGB notation?
> 
> CSS color names came from X a long time ago, but their mapping to rgb is
> now part of the CSS specification.  And, apparently there were multiple
> versions of this file coming from X.  The Emacs one would be ok provided
> the lists are identical and will always remain so.  Otherwise, css-mode
> needs its own list.

Sorry, I don't understand: what list are you talking about?

> Eli> My fear is that someone will mix css-mode functions with those from
> Eli> elsewhere in Emacs, and will bump into confusing and contradicting
> Eli> results.  Is it possible to avoid that?
> 
> Using "css-" in the name seems sufficient to me.  Maybe a comment
> explaining this would help as well.

A comment will help, but I'm actually wondering why does css-mode need
to use 12-bit or 16-bit per component RGB notation?  Why not use only
8-bit, as in #RRGGBB?  The problems only happen in converting 8-bit
RGB notations to higher number of bits.





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-27 18:11     ` Eli Zaretskii
@ 2017-11-27 18:35       ` Tom Tromey
  2017-11-27 18:43         ` Eli Zaretskii
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2017-11-27 18:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 29456, Tom Tromey, simenheg, monnier

>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:

Eli> Sorry, I don't understand: what list are you talking about?

Sorry, I misunderstood.

CSS also specifies hex and rgb syntax itself.

Eli> A comment will help, but I'm actually wondering why does css-mode need
Eli> to use 12-bit or 16-bit per component RGB notation?  Why not use only
Eli> 8-bit, as in #RRGGBB?  The problems only happen in converting 8-bit
Eli> RGB notations to higher number of bits.

#RGB is specified by CSS, it is equivalent to #RRGGBB, and is often
seen.  With css-color-4, #RGBA is also allowed.

Basically css-mode needs to understand the peculiarities of CSS syntax,
which differs somewhat from other systems AFAIK.

Tom





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-27 18:35       ` Tom Tromey
@ 2017-11-27 18:43         ` Eli Zaretskii
  2017-11-28 18:33           ` Simen Heggestøyl
  0 siblings, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2017-11-27 18:43 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 29456, simenheg, monnier

> From: Tom Tromey <tom@tromey.com>
> Cc: Tom Tromey <tom@tromey.com>,  simenheg@gmail.com,  29456@debbugs.gnu.org,  monnier@iro.umontreal.ca
> Date: Mon, 27 Nov 2017 11:35:04 -0700
> 
> Eli> A comment will help, but I'm actually wondering why does css-mode need
> Eli> to use 12-bit or 16-bit per component RGB notation?  Why not use only
> Eli> 8-bit, as in #RRGGBB?  The problems only happen in converting 8-bit
> Eli> RGB notations to higher number of bits.
> 
> #RGB is specified by CSS, it is equivalent to #RRGGBB, and is often
> seen.  With css-color-4, #RGBA is also allowed.
> 
> Basically css-mode needs to understand the peculiarities of CSS syntax,
> which differs somewhat from other systems AFAIK.

Then I think there should be a prominent remark in the doc strings of
css-mode not to mix these functions with those from color.el, due to
this incompatibility.





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-27 18:43         ` Eli Zaretskii
@ 2017-11-28 18:33           ` Simen Heggestøyl
  2017-11-28 20:57             ` Simen Heggestøyl
  0 siblings, 1 reply; 12+ messages in thread
From: Simen Heggestøyl @ 2017-11-28 18:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 29456, tom, monnier

Tom Tromey <tom@tromey.com> writes:
> Nice feature!

Glad you like it!

> It seems to me that none of the functions handle alpha components.
> css-color-4 added optional alpha to hex colors (and made rgb and rgba
> synonyms), and there's been rgba for a while...

Right, it makes sense to support that too. I haven't used alpha formats
other than rgba(...) myself. Would it be useful for rgba(255, 0, 0, 0.5)
to turn into #ff000088, for instance?

Eli Zaretskii <eliz@gnu.org> writes:
> Then I think there should be a prominent remark in the doc strings of
> css-mode not to mix these functions with those from color.el, due to
> this incompatibility.

That sounds like a good idea.

I'll address the comments from both of you in an update to the patch,
though it might be a while until I have the time to update it.

Thanks for your comments so far.

-- Simen





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-28 18:33           ` Simen Heggestøyl
@ 2017-11-28 20:57             ` Simen Heggestøyl
  2017-12-10 12:46               ` Simen Heggestøyl
  0 siblings, 1 reply; 12+ messages in thread
From: Simen Heggestøyl @ 2017-11-28 20:57 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: 29456, tom, monnier

Simen Heggestøyl <simenheg@gmail.com> writes:
> Right, it makes sense to support that too. I haven't used alpha formats
> other than rgba(...) myself. Would it be useful for rgba(255, 0, 0, 0.5)
> to turn into #ff000088, for instance?
               ^^^^^^^^^ #ff000080, sorry.

-- Simen





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-11-28 20:57             ` Simen Heggestøyl
@ 2017-12-10 12:46               ` Simen Heggestøyl
  2017-12-10 18:09                 ` Eli Zaretskii
  0 siblings, 1 reply; 12+ messages in thread
From: Simen Heggestøyl @ 2017-12-10 12:46 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: 29456, tom, monnier


[-- Attachment #1.1: Type: text/plain, Size: 431 bytes --]

I've updated the patch to support switching between #RGBA/#RRGGBBAA and
rgba() too, fixing a bug in `css--hex-color' along the way for handling
the #RGBA case. If you agree, I think that fix should go into the
emacs-26 branch as well.

I also prefixed the function names with `css--' to more strongly
indicate that they're intended for internal usage, and mentioned in the
docstrings their incompatibility with color.el.

-- Simen

[-- Attachment #1.2: Type: text/html, Size: 577 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-command-for-cycling-between-CSS-color-formats.patch --]
[-- Type: text/x-patch, Size: 12890 bytes --]

From 70d8834de32e31862dc0f14ce80f7e608e4151db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Sun, 17 Sep 2017 20:08:18 +0200
Subject: [PATCH] Add command for cycling between CSS color formats

* lisp/textmodes/css-mode.el (css-mode-map): Add keybinding for
'css-cycle-color-format'.
(css--rgb-color): Add support for extracting alpha component.
(css--hex-color): Correct function when the hex string is in
the #RGBA format.
(css--hex-alpha, css--color-to-4-dpc, css--named-color-to-hex)
(css--format-rgba-alpha, css--hex-to-rgb)
(css--rgb-to-named-color-or-hex): New functions.
(css-cycle-color-format): New command for cycling between color
formats.

* test/lisp/textmodes/css-mode-tests.el (css-test-color-to-4-dpc):
(css-test-named-color-to-hex, css-test-format-rgba-alpha)
(css-test-hex-to-rgb, css-test-rgb-to-named-color-or-hex)
(css-test-cycle-color-format, css-test-hex-color)
(css-test-hex-alpha): New tests for the changes mentioned above.

* etc/NEWS: Mention the new command.
---
 etc/NEWS                              |   7 ++
 lisp/textmodes/css-mode.el            | 133 ++++++++++++++++++++++++++++++++--
 test/lisp/textmodes/css-mode-tests.el |  77 ++++++++++++++++++++
 3 files changed, 210 insertions(+), 7 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index dd7d983970..33eaf257df 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -63,6 +63,13 @@ whether '"' is also replaced in 'electric-quote-mode'.  If non-nil,
 \f
 * Changes in Specialized Modes and Packages in Emacs 27.1
 
+** CSS mode
+
+---
+*** A new command 'css-cycle-color-format' for cycling between color
+formats (e.g. "black" => "#000000" => "rgb(0, 0, 0)") has been added,
+bound to 'C-c C-f'.
+
 ** Dired
 
 +++
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 93ca36b08a..6bf365ecb3 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -32,12 +32,13 @@
 
 ;;; Code:
 
-(require 'eww)
 (require 'cl-lib)
 (require 'color)
+(require 'eww)
 (require 'seq)
 (require 'sgml-mode)
 (require 'smie)
+(require 'thingatpt)
 (eval-when-compile (require 'subr-x))
 
 (defgroup css nil
@@ -806,6 +807,7 @@ css-mode-syntax-table
 (defvar css-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map [remap info-lookup-symbol] 'css-lookup-symbol)
+    (define-key map "\C-c\C-f" 'css-cycle-color-format)
     map)
   "Keymap used in `css-mode'.")
 
@@ -936,11 +938,13 @@ css--color-skip-blanks
   "Skip blanks and comments."
   (while (forward-comment 1)))
 
-(cl-defun css--rgb-color ()
+(cl-defun css--rgb-color (&optional include-alpha)
   "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."
+This recognizes CSS-color-4 extensions.
+When INCLUDE-ALPHA is non-nil, the alpha component is included in
+the returned hex string."
   (let ((result '())
 	(iter 0))
     (while (< iter 4)
@@ -952,8 +956,8 @@ css--color-skip-blanks
 	     (number (string-to-number str)))
 	(when is-percent
 	  (setq number (* 255 (/ number 100.0))))
-        ;; Don't push the alpha.
-        (when (< iter 3)
+        (if (and include-alpha (= iter 3))
+            (push (round (* number 255)) result)
           (push (min (max 0 (truncate number)) 255) result))
 	(goto-char (match-end 0))
 	(css--color-skip-blanks)
@@ -966,7 +970,11 @@ css--color-skip-blanks
 	(css--color-skip-blanks)))
     (when (looking-at ")")
       (forward-char)
-      (apply #'format "#%02x%02x%02x" (nreverse result)))))
+      (apply #'format
+             (if (and include-alpha (= (length result) 4))
+                 "#%02x%02x%02x%02x"
+               "#%02x%02x%02x")
+             (nreverse result)))))
 
 (cl-defun css--hsl-color ()
   "Parse a CSS hsl() or hsla() color.
@@ -1037,10 +1045,18 @@ css--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)
+  (if (> (length str) 5)
       (substring str 0 7)
     (substring str 0 4)))
 
+(defun css--hex-alpha (hex)
+  "Return the alpha component of CSS color HEX.
+HEX can either be in the #RGBA or #RRGGBBAA format.  If the color
+doesn't have an alpha component, nil is returned."
+  (cl-case (length hex)
+    (5 (string (elt hex 4)))
+    (9 (substring hex 7 9))))
+
 (defun css--named-color (start-point str)
   "Check whether STR, seen at point, is CSS named color.
 Returns STR if it is a valid color.  Special care is taken
@@ -1383,6 +1399,109 @@ css-completion-at-point
                       (progn (insert ": ;")
                              (forward-char -1))))))))))
 
+(defun css--color-to-4-dpc (hex)
+  "Convert the CSS color HEX to four digits per component.
+CSS colors use one or two digits per component for RGB hex
+values.  Convert the given color to four digits per component.
+
+Note that this function handles CSS colors specifically, and
+should not be mixed with those in color.el."
+  (let ((six-digits (= (length hex) 7)))
+    (apply
+     #'concat
+     `("#"
+       ,@(seq-mapcat
+          (apply-partially #'make-list (if six-digits 2 4))
+          (seq-partition (seq-drop hex 1) (if six-digits 2 1)))))))
+
+(defun css--named-color-to-hex ()
+  "Convert named CSS color at point to hex format.
+Return non-nil if a conversion was made.
+
+Note that this function handles CSS colors specifically, and
+should not be mixed with those in color.el."
+  (save-excursion
+    (unless (or (looking-at css--colors-regexp)
+                (eq (char-before) ?#))
+      (backward-word))
+    (when (member (word-at-point) (mapcar #'car css--color-map))
+      (looking-at css--colors-regexp)
+      (let ((color (css--compute-color (point) (match-string 0))))
+        (replace-match color))
+      t)))
+
+(defun css--format-rgba-alpha (alpha)
+  "Return ALPHA component formatted for use in rgba()."
+  (if (or (= alpha 0)
+          (= alpha 1))
+      (format "%d" alpha)
+    (string-remove-suffix "0" (format "%.2f" alpha))))
+
+(defun css--hex-to-rgb ()
+  "Convert CSS hex color at point to RGB format.
+Return non-nil if a conversion was made.
+
+Note that this function handles CSS colors specifically, and
+should not be mixed with those in color.el."
+  (save-excursion
+    (unless (or (eq (char-after) ?#)
+                (eq (char-before) ?\())
+      (backward-sexp))
+    (when-let* ((hex (when (looking-at css--colors-regexp)
+                       (and (eq (elt (match-string 0) 0) ?#)
+                            (match-string 0))))
+                (rgb (css--hex-color hex)))
+      (seq-let (r g b)
+          (mapcar (lambda (x) (round (* x 255)))
+                  (color-name-to-rgb (css--color-to-4-dpc rgb)))
+        (replace-match
+         (if-let* ((alpha (css--hex-alpha hex))
+                   (a (css--format-rgba-alpha
+                       (/ (string-to-number alpha 16)
+                          (float (expt 16 (length alpha)))))))
+             (format "rgba(%d, %d, %d, %s)" r g b a)
+           (format "rgb(%d, %d, %d)" r g b))))
+      t)))
+
+(defun css--rgb-to-named-color-or-hex ()
+  "Convert CSS RGB color at point to a named color or hex format.
+Convert to a named color if the color at point has a name, else
+convert to hex format.  Return non-nil if a conversion was made.
+
+Note that this function handles CSS colors specifically, and
+should not be mixed with those in color.el."
+  (save-excursion
+    (when-let* ((open-paren-pos (nth 1 (syntax-ppss))))
+      (when (save-excursion
+              (goto-char open-paren-pos)
+              (looking-back "rgba?" (- (point) 4)))
+        (goto-char (nth 1 (syntax-ppss)))))
+    (when (eq (char-before) ?\))
+      (backward-sexp))
+    (skip-chars-backward "rgba")
+    (when (looking-at css--colors-regexp)
+      (let* ((start (match-end 0))
+             (color (save-excursion
+                      (goto-char start)
+                      (css--rgb-color t))))
+        (when color
+          (kill-sexp)
+          (kill-sexp)
+          (let ((named-color (seq-find (lambda (x) (equal (cdr x) color))
+                                       css--color-map)))
+            (insert (if named-color (car named-color) color)))
+          t)))))
+
+(defun css-cycle-color-format ()
+  "Cycle the color at point between different CSS color formats.
+Supported formats are by name (if possible), hexadecimal, and
+RGB."
+  (interactive)
+  (or (css--named-color-to-hex)
+      (css--hex-to-rgb)
+      (css--rgb-to-named-color-or-hex)
+      (message "It doesn't look like a color at point")))
+
 ;;;###autoload
 (define-derived-mode css-mode prog-mode "CSS"
   "Major mode to edit Cascading Style Sheets (CSS).
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index 47cf5f9244..f3d4243721 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -244,6 +244,71 @@ css-mode-tests--completions
       (should (member "body" completions))
       (should-not (member "article" completions)))))
 
+(ert-deftest css-test-color-to-4-dpc ()
+  (should (equal (css--color-to-4-dpc "#ffffff")
+                 (css--color-to-4-dpc "#fff")))
+  (should (equal (css--color-to-4-dpc "#aabbcc")
+                 (css--color-to-4-dpc "#abc")))
+  (should (equal (css--color-to-4-dpc "#fab")
+                 "#ffffaaaabbbb"))
+  (should (equal (css--color-to-4-dpc "#fafbfc")
+                 "#fafafbfbfcfc")))
+
+(ert-deftest css-test-named-color-to-hex ()
+  (dolist (item '(("black" "#000000")
+                  ("white" "#ffffff")
+                  ("salmon" "#fa8072")))
+    (with-temp-buffer
+      (css-mode)
+      (insert (nth 0 item))
+      (css--named-color-to-hex)
+      (should (equal (buffer-string) (nth 1 item))))))
+
+(ert-deftest css-test-format-rgba-alpha ()
+  (should (equal (css--format-rgba-alpha 0) "0"))
+  (should (equal (css--format-rgba-alpha 0.0) "0"))
+  (should (equal (css--format-rgba-alpha 1) "1"))
+  (should (equal (css--format-rgba-alpha 1.0) "1"))
+  (should (equal (css--format-rgba-alpha 0.10000) "0.1"))
+  (should (equal (css--format-rgba-alpha 0.100001) "0.1"))
+  (should (equal (css--format-rgba-alpha 0.2524334) "0.25")))
+
+(ert-deftest css-test-hex-to-rgb ()
+  (dolist (item '(("#000" "rgb(0, 0, 0)")
+                  ("#000000" "rgb(0, 0, 0)")
+                  ("#fff" "rgb(255, 255, 255)")
+                  ("#ffffff" "rgb(255, 255, 255)")
+                  ("#ffffff80" "rgba(255, 255, 255, 0.5)")
+                  ("#fff8" "rgba(255, 255, 255, 0.5)")))
+    (with-temp-buffer
+      (css-mode)
+      (insert (nth 0 item))
+      (css--hex-to-rgb)
+      (should (equal (buffer-string) (nth 1 item))))))
+
+(ert-deftest css-test-rgb-to-named-color-or-hex ()
+  (dolist (item '(("rgb(0, 0, 0)" "black")
+                  ("rgb(255, 255, 255)" "white")
+                  ("rgb(255, 255, 240)" "ivory")
+                  ("rgb(18, 52, 86)" "#123456")
+                  ("rgba(18, 52, 86, 0.5)" "#12345680")))
+    (with-temp-buffer
+      (css-mode)
+      (insert (nth 0 item))
+      (css--rgb-to-named-color-or-hex)
+      (should (equal (buffer-string) (nth 1 item))))))
+
+(ert-deftest css-test-cycle-color-format ()
+  (with-temp-buffer
+    (css-mode)
+    (insert "black")
+    (css-cycle-color-format)
+    (should (equal (buffer-string) "#000000"))
+    (css-cycle-color-format)
+    (should (equal (buffer-string) "rgb(0, 0, 0)"))
+    (css-cycle-color-format)
+    (should (equal (buffer-string) "black"))))
+
 (ert-deftest css-mdn-symbol-guessing ()
   (dolist (item '(("@med" "ia" "@media")
                   ("@keyframes " "{" "@keyframes")
@@ -295,6 +360,18 @@ css-mode-tests--completions
         (insert input ")"))
       (should (equal (css--hsl-color) "#ff0000")))))
 
+(ert-deftest css-test-hex-color ()
+  (should (equal (css--hex-color "#abc") "#abc"))
+  (should (equal (css--hex-color "#abcd") "#abc"))
+  (should (equal (css--hex-color "#aabbcc") "#aabbcc"))
+  (should (equal (css--hex-color "#aabbccdd") "#aabbcc")))
+
+(ert-deftest css-test-hex-alpha ()
+  (should (equal (css--hex-alpha "#abcd") "d"))
+  (should-not (css--hex-alpha "#abc"))
+  (should (equal (css--hex-alpha "#aabbccdd") "dd"))
+  (should-not (css--hex-alpha "#aabbcc")))
+
 (ert-deftest css-test-named-color ()
   (dolist (text '("@mixin black" "@include black"))
     (with-temp-buffer
-- 
2.15.1


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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-12-10 12:46               ` Simen Heggestøyl
@ 2017-12-10 18:09                 ` Eli Zaretskii
  2017-12-17  9:32                   ` Simen Heggestøyl
  0 siblings, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2017-12-10 18:09 UTC (permalink / raw)
  To: Simen Heggestøyl; +Cc: 29456, tom, monnier

> Date: Sun, 10 Dec 2017 13:46:14 +0100
> From: Simen Heggestøyl <simenheg@gmail.com>
> Cc: eliz@gnu.org, 29456@debbugs.gnu.org, tom@tromey.com,
> 	monnier@iro.umontreal.ca
> 
> I've updated the patch to support switching between #RGBA/#RRGGBBAA and
> rgba() too, fixing a bug in `css--hex-color' along the way for handling
> the #RGBA case. If you agree, I think that fix should go into the
> emacs-26 branch as well.

It's OK to put the css--hex-color bugfix on emacs-26, but then please
remove it from the changeset you push to master, so that it could be
merged from emacs-26.

Thanks.





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

* bug#29456: [PATCH] Add command for cycling between CSS color formats
  2017-12-10 18:09                 ` Eli Zaretskii
@ 2017-12-17  9:32                   ` Simen Heggestøyl
  0 siblings, 0 replies; 12+ messages in thread
From: Simen Heggestøyl @ 2017-12-17  9:32 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: tom, 29456-done, monnier

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

Good, thanks. I've installed the two patches.

-- Simen

On Sun, Dec 10, 2017 at 7:09 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> It's OK to put the css--hex-color bugfix on emacs-26, but then please
> remove it from the changeset you push to master, so that it could be
> merged from emacs-26.
> 
> Thanks.

[-- Attachment #2: Type: text/html, Size: 494 bytes --]

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

end of thread, other threads:[~2017-12-17  9:32 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-11-26 15:21 bug#29456: [PATCH] Add command for cycling between CSS color formats Simen Heggestøyl
2017-11-26 17:53 ` Tom Tromey
2017-11-27 17:07 ` Eli Zaretskii
2017-11-27 17:41   ` Tom Tromey
2017-11-27 18:11     ` Eli Zaretskii
2017-11-27 18:35       ` Tom Tromey
2017-11-27 18:43         ` Eli Zaretskii
2017-11-28 18:33           ` Simen Heggestøyl
2017-11-28 20:57             ` Simen Heggestøyl
2017-12-10 12:46               ` Simen Heggestøyl
2017-12-10 18:09                 ` Eli Zaretskii
2017-12-17  9:32                   ` Simen Heggestøyl

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

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

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