1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-30 12:21:25 -08:00

Add missing colors to erc-irccontrols-mode

* lisp/erc/erc-goodies.el (erc-spoiler-face): Add new face.
(erc--controls-additional-colors): Add remaining 16-99 colors.
(erc-get-bg-color-face, erc-get-fg-color-face): Look up extended
colors in table.
(erc-controls-remove-regexp, erc-controls-highlight-regexp): Convert
to `rx' forms and move above first use to eliminate intra-file forward
declarations.
(erc-controls-propertize): Support spoilers.
* test/lisp/erc/erc-goodies-tests.el: New file.  (Bug#60954.)
This commit is contained in:
F. Jason Park 2021-07-09 20:03:51 -07:00
parent 9aa2806fdc
commit 22104de5da
2 changed files with 312 additions and 18 deletions

View file

@ -30,8 +30,6 @@
;;; Code:
(eval-when-compile (require 'cl-lib))
(defvar erc-controls-highlight-regexp)
(defvar erc-controls-remove-regexp)
(require 'erc)
(defun erc-imenu-setup ()
@ -243,6 +241,12 @@ The value `erc-interpret-controls-p' must also be t for this to work."
"ERC inverse face."
:group 'erc-faces)
(defface erc-spoiler-face
'((((background light)) :foreground "DimGray" :background "DimGray")
(((background dark)) :foreground "LightGray" :background "LightGray"))
"ERC spoiler face."
:group 'erc-faces)
(defface erc-underline-face '((t :underline t))
"ERC underline face."
:group 'erc-faces)
@ -345,19 +349,38 @@ The value `erc-interpret-controls-p' must also be t for this to work."
"ERC face."
:group 'erc-faces)
;; https://lists.gnu.org/archive/html/emacs-erc/2021-07/msg00005.html
(defvar erc--controls-additional-colors
["#470000" "#472100" "#474700" "#324700" "#004700" "#00472c"
"#004747" "#002747" "#000047" "#2e0047" "#470047" "#47002a"
"#740000" "#743a00" "#747400" "#517400" "#007400" "#007449"
"#007474" "#004074" "#000074" "#4b0074" "#740074" "#740045"
"#b50000" "#b56300" "#b5b500" "#7db500" "#00b500" "#00b571"
"#00b5b5" "#0063b5" "#0000b5" "#7500b5" "#b500b5" "#b5006b"
"#ff0000" "#ff8c00" "#ffff00" "#b2ff00" "#00ff00" "#00ffa0"
"#00ffff" "#008cff" "#0000ff" "#a500ff" "#ff00ff" "#ff0098"
"#ff5959" "#ffb459" "#ffff71" "#cfff60" "#6fff6f" "#65ffc9"
"#6dffff" "#59b4ff" "#5959ff" "#c459ff" "#ff66ff" "#ff59bc"
"#ff9c9c" "#ffd39c" "#ffff9c" "#e2ff9c" "#9cff9c" "#9cffdb"
"#9cffff" "#9cd3ff" "#9c9cff" "#dc9cff" "#ff9cff" "#ff94d3"
"#000000" "#131313" "#282828" "#363636" "#4d4d4d" "#656565"
"#818181" "#9f9f9f" "#bcbcbc" "#e2e2e2" "#ffffff"])
(defun erc-get-bg-color-face (n)
"Fetches the right face for background color N (0-15)."
(if (stringp n) (setq n (string-to-number n)))
(if (not (numberp n))
(prog1 'default
(erc-error "erc-get-bg-color-face: n is NaN: %S" n))
(when (> n 16)
(when (> n 99)
(erc-log (format " Wrong color: %s" n))
(setq n (mod n 16)))
(cond
((and (>= n 0) (< n 16))
(intern (concat "bg:erc-color-face" (number-to-string n))))
(t (erc-log (format " Wrong color: %s" n)) 'default))))
((< 15 n 99)
(list :background (aref erc--controls-additional-colors (- n 16))))
(t (erc-log (format " Wrong color: %s" n)) '(default)))))
(defun erc-get-fg-color-face (n)
"Fetches the right face for foreground color N (0-15)."
@ -365,13 +388,15 @@ The value `erc-interpret-controls-p' must also be t for this to work."
(if (not (numberp n))
(prog1 'default
(erc-error "erc-get-fg-color-face: n is NaN: %S" n))
(when (> n 16)
(when (> n 99)
(erc-log (format " Wrong color: %s" n))
(setq n (mod n 16)))
(cond
((and (>= n 0) (< n 16))
(intern (concat "fg:erc-color-face" (number-to-string n))))
(t (erc-log (format " Wrong color: %s" n)) 'default))))
((< 15 n 99)
(list :foreground (aref erc--controls-additional-colors (- n 16))))
(t (erc-log (format " Wrong color: %s" n)) '(default)))))
;;;###autoload(autoload 'erc-irccontrols-mode "erc-goodies" nil t)
(define-erc-module irccontrols nil
@ -383,6 +408,25 @@ The value `erc-interpret-controls-p' must also be t for this to work."
(remove-hook 'erc-send-modify-hook #'erc-controls-highlight)
(erc--modify-local-map nil "C-c C-c" #'erc-toggle-interpret-controls)))
;; These patterns were moved here to circumvent compiler warnings but
;; otherwise translated verbatim from their original string-literal
;; definitions (minus a small bug fix to satisfy newly added tests).
(defvar erc-controls-remove-regexp
(rx (or ?\C-b ?\C-\] ?\C-_ ?\C-v ?\C-g ?\C-o
(: ?\C-c (? (any "0-9")) (? (any "0-9"))
(? (group ?, (any "0-9") (? (any "0-9")))))))
"Regular expression matching control characters to remove.")
;; Before the change to `rx', group 3 used to be a sibling of group 2.
;; This was assumed to be a bug. A few minor simplifications were
;; also performed. If incorrect, please admonish.
(defvar erc-controls-highlight-regexp
(rx (group (or ?\C-b ?\C-\] ?\C-v ?\C-_ ?\C-g ?\C-o
(: ?\C-c (? (group (** 1 2 (any "0-9")))
(? (group ?, (group (** 1 2 (any "0-9")))))))))
(group (* (not (any ?\C-b ?\C-c ?\C-g ?\n ?\C-o ?\C-v ?\C-\] ?\C-_)))))
"Regular expression matching control chars to highlight.")
(defun erc-controls-interpret (str)
"Return a copy of STR after dealing with IRC control characters.
See `erc-interpret-controls-p' and `erc-interpret-mirc-color' for options."
@ -444,16 +488,6 @@ See `erc-interpret-controls-p' and `erc-interpret-mirc-color' for options."
(setq s (replace-match "" nil nil s)))
s)))
(defvar erc-controls-remove-regexp
"\C-b\\|\C-]\\|\C-_\\|\C-v\\|\C-g\\|\C-o\\|\C-c[0-9]?[0-9]?\\(,[0-9][0-9]?\\)?"
"Regular expression which matches control characters to remove.")
(defvar erc-controls-highlight-regexp
(concat "\\(\C-b\\|\C-]\\|\C-v\\|\C-_\\|\C-g\\|\C-o\\|"
"\C-c\\([0-9][0-9]?\\)?\\(,\\([0-9][0-9]?\\)\\)?\\)"
"\\([^\C-b\C-]\C-v\C-_\C-c\C-g\C-o\n]*\\)")
"Regular expression which matches control chars and the text to highlight.")
(defun erc-controls-highlight ()
"Highlight IRC control chars in the buffer.
This is useful for `erc-insert-modify-hook' and `erc-send-modify-hook'.
@ -510,6 +544,13 @@ Also see `erc-interpret-controls-p' and `erc-interpret-mirc-color'."
"Prepend properties from IRC control characters between FROM and TO.
If optional argument STR is provided, apply to STR, otherwise prepend properties
to a region in the current buffer."
(if (and fg bg (equal fg bg))
(progn
(setq fg 'erc-spoiler-face
bg nil)
(put-text-property from to 'mouse-face 'erc-inverse-face str))
(when fg (setq fg (erc-get-fg-color-face fg)))
(when bg (setq bg (erc-get-bg-color-face bg))))
(font-lock-prepend-text-property
from
to
@ -527,10 +568,10 @@ to a region in the current buffer."
'(erc-underline-face)
nil)
(if fg
(list (erc-get-fg-color-face fg))
(list fg)
nil)
(if bg
(list (erc-get-bg-color-face bg))
(list bg)
nil))
str)
str)

View file

@ -0,0 +1,253 @@
;;; erc-goodies-tests.el --- Tests for erc-goodies -*- lexical-binding:t -*-
;; Copyright (C) 2023 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
;;
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published
;; by the Free Software Foundation, either version 3 of the License,
;; or (at your option) any later version.
;;
;; GNU Emacs is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;; Code:
(require 'ert-x)
(require 'erc-goodies)
(declare-function erc--initialize-markers "erc" (old-point continued) t)
(defun erc-goodies-tests--assert-face (beg end-str present &optional absent)
(setq beg (+ beg (point-min)))
(let ((end (+ beg (1- (length end-str)))))
(while (and beg (< beg end))
(let* ((val (get-text-property beg 'font-lock-face))
(ft (flatten-tree (ensure-list val))))
(dolist (p (ensure-list present))
(if (consp p)
(should (member p val))
(should (memq p ft))))
(dolist (a (ensure-list absent))
(if (consp a)
(should-not (member a val))
(should-not (memq a ft))))
(setq beg (text-property-not-all beg (point-max)
'font-lock-face val))))))
;; These are from the "Examples" section of
;; https://modern.ircdocs.horse/formatting.html
(ert-deftest erc-controls-highlight--examples ()
;; FIXME remove after adding
(unless (fboundp 'erc--initialize-markers)
(ert-skip "Missing required function"))
(should (eq t erc-interpret-controls-p))
(let ((erc-insert-modify-hook '(erc-controls-highlight))
erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
(with-current-buffer (get-buffer-create "#chan")
(erc-mode)
(setq-local erc-interpret-mirc-color t)
(erc--initialize-markers (point) nil)
(let* ((m "I love \C-c3IRC!\C-c It is the \C-c7best protocol ever!")
(msg (erc-format-privmessage "bob" m nil t)))
(erc-display-message nil nil (current-buffer) msg))
(forward-line -1)
(should (search-forward "<bob> " nil t))
(save-restriction
(narrow-to-region (point) (pos-eol))
(erc-goodies-tests--assert-face
0 "I love" 'erc-default-face 'fg:erc-color-face3)
(erc-goodies-tests--assert-face
7 " IRC!" 'fg:erc-color-face3)
(erc-goodies-tests--assert-face
11 " It is the " 'erc-default-face 'fg:erc-color-face7)
(erc-goodies-tests--assert-face
22 "best protocol ever!" 'fg:erc-color-face7))
(let* ((m "This is a \C-]\C-c13,9cool \C-cmessage")
(msg (erc-format-privmessage "alice" m nil t)))
(erc-display-message nil nil (current-buffer) msg))
(should (search-forward "<alice> " nil t))
(save-restriction
(narrow-to-region (point) (pos-eol))
(erc-goodies-tests--assert-face
0 "this is a " 'erc-default-face 'erc-italic-face)
(erc-goodies-tests--assert-face
10 "cool " '(erc-italic-face fg:erc-color-face13 bg:erc-color-face9))
(erc-goodies-tests--assert-face
15 "message" 'erc-italic-face
'(fg:erc-color-face13 bg:erc-color-face9)))
(let* ((m "IRC \C-bis \C-c4,12so \C-cgreat\C-o!")
(msg (erc-format-privmessage "bob" m nil t)))
(erc-display-message nil nil (current-buffer) msg))
(should (search-forward "<bob> " nil t))
(save-restriction
(narrow-to-region (point) (pos-eol))
(erc-goodies-tests--assert-face
0 "IRC " 'erc-default-face 'erc-bold-face)
(erc-goodies-tests--assert-face
4 "is " 'erc-bold-face '(fg:erc-color-face4 bg:erc-color-face12))
(erc-goodies-tests--assert-face
7 "so " '(erc-bold-face fg:erc-color-face4 bg:erc-color-face12))
(erc-goodies-tests--assert-face
10 "great" 'erc-bold-face '(fg:erc-color-face4 bg:erc-color-face12))
(erc-goodies-tests--assert-face
15 "!" 'erc-default-face 'erc-bold-face))
(let* ((m (concat "Rules: Don't spam 5\C-c13,8,6\C-c,7,8, "
"and especially not \C-b9\C-b\C-]!"))
(msg (erc-format-privmessage "alice" m nil t)))
(erc-display-message nil nil (current-buffer) msg))
(should (search-forward "<alice> " nil t))
(save-restriction
(narrow-to-region (point) (pos-eol))
(erc-goodies-tests--assert-face
0 "Rules: Don't spam 5" 'erc-default-face
'(fg:erc-color-face13 bg:erc-color-face8))
(erc-goodies-tests--assert-face
19 ",6" '(fg:erc-color-face13 bg:erc-color-face8))
(erc-goodies-tests--assert-face
21 ",7,8, and especially not " 'erc-default-face
'(fg:erc-color-face13 bg:erc-color-face8 erc-bold-face))
(erc-goodies-tests--assert-face
44 "9" 'erc-bold-face 'erc-italic-face)
(erc-goodies-tests--assert-face
45 "!" 'erc-italic-face 'erc-bold-face))
(when noninteractive
(kill-buffer)))))
;; Like the test above, this is most intuitive when run interactively.
;; Hovering over the redacted area should reveal its underlying text
;; in a high-contrast face.
(ert-deftest erc-controls-highlight--inverse ()
;; FIXME remove after adding
(unless (fboundp 'erc--initialize-markers)
(ert-skip "Missing required function"))
(should (eq t erc-interpret-controls-p))
(let ((erc-insert-modify-hook '(erc-controls-highlight))
erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
(with-current-buffer (get-buffer-create "#chan")
(erc-mode)
(setq-local erc-interpret-mirc-color t)
(erc--initialize-markers (point) nil)
(let* ((m "Spoiler: \C-c0,0Hello\C-c1,1World!")
(msg (erc-format-privmessage "bob" m nil t)))
(erc-display-message nil nil (current-buffer) msg))
(forward-line -1)
(should (search-forward "<bob> " nil t))
(save-restriction
(narrow-to-region (point) (pos-eol))
(should (eq (get-text-property (+ 9 (point)) 'mouse-face)
'erc-inverse-face))
(should (eq (get-text-property (1- (pos-eol)) 'mouse-face)
'erc-inverse-face))
(erc-goodies-tests--assert-face
0 "Spoiler: " 'erc-default-face
'(fg:erc-color-face0 bg:erc-color-face0))
(erc-goodies-tests--assert-face
9 "Hello" '(erc-spoiler-face)
'( fg:erc-color-face0 bg:erc-color-face0
fg:erc-color-face1 bg:erc-color-face1))
(erc-goodies-tests--assert-face
18 " World" '(erc-spoiler-face)
'( fg:erc-color-face0 bg:erc-color-face0
fg:erc-color-face1 bg:erc-color-face1 )))
(when noninteractive
(kill-buffer)))))
(defvar erc-goodies-tests--motd
;; This is from ergo's MOTD
'((":- - this is \2bold text\17.")
(":- - this is \35italics text\17.")
(":- - this is \0034red\3 and \0032blue\3 text.")
(":- - this is \0034,12red text with a light blue background\3.")
(":- - this is a normal escaped dollarsign: $")
(":- ")
(":- "
"\0031,0 00 \0030,1 01 \0030,2 02 \0030,3 03 "
"\0031,4 04 \0030,5 05 \0030,6 06 \0031,7 07 ")
(":- "
"\0031,8 08 \0031,9 09 \0030,10 10 \0031,11 11 "
"\0030,12 12 \0031,13 13 \0031,14 14 \0031,15 15 ")
(":- ")
(":- "
"\0030,16 16 \0030,17 17 \0030,18 18 \0030,19 19 "
"\0030,20 20 \0030,21 21 \0030,22 22 \0030,23 23 "
"\0030,24 24 \0030,25 25 \0030,26 26 \0030,27 27 ")
(":- "
"\0030,28 28 \0030,29 29 \0030,30 30 \0030,31 31 "
"\0030,32 32 \0030,33 33 \0030,34 34 \0030,35 35 "
"\0030,36 36 \0030,37 37 \0030,38 38 \0030,39 39 ")
(":- "
"\0030,40 40 \0030,41 41 \0030,42 42 \0030,43 43 "
"\0030,44 44 \0030,45 45 \0030,46 46 \0030,47 47 "
"\0030,48 48 \0030,49 49 \0030,50 50 \0030,51 51 ")
(":- "
"\0030,52 52 \0030,53 53 \0031,54 54 \0031,55 55 "
"\0031,56 56 \0031,57 57 \0031,58 58 \0030,59 59 "
"\0030,60 60 \0030,61 61 \0030,62 62 \0030,63 63 ")
(":- "
"\0030,64 64 \0031,65 65 \0031,66 66 \0031,67 67 "
"\0031,68 68 \0031,69 69 \0031,70 70 \0031,71 71 "
"\0030,72 72 \0030,73 73 \0030,74 74 \0030,75 75 ")
(":- "
"\0031,76 76 \0031,77 77 \0031,78 78 \0031,79 79 "
"\0031,80 80 \0031,81 81 \0031,82 82 \0031,83 83 "
"\0031,84 84 \0031,85 85 \0031,86 86 \0031,87 87 ")
(":- "
"\0030,88 88 \0030,89 89 \0030,90 90 \0030,91 91 "
"\0030,92 92 \0030,93 93 \0030,94 94 \0030,95 95 "
"\0031,96 96 \0031,97 97 \0031,98 98 \399,99 99 ")
(":- ")))
(ert-deftest erc-controls-highlight--motd ()
;; FIXME remove after adding
(unless (fboundp 'erc--initialize-markers)
(ert-skip "Missing required function"))
(should (eq t erc-interpret-controls-p))
(let ((erc-insert-modify-hook '(erc-controls-highlight))
erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
(with-current-buffer (get-buffer-create "#chan")
(erc-mode)
(setq-local erc-interpret-mirc-color t)
(erc--initialize-markers (point) nil)
(dolist (parts erc-goodies-tests--motd)
(erc-display-message nil 'notice (current-buffer) (string-join parts)))
;; Spot check
(goto-char (point-min))
(should (search-forward " 16 " nil t))
(save-restriction
(narrow-to-region (point) (pos-eol))
(erc-goodies-tests--assert-face
0 " 17 " '(fg:erc-color-face0 (:background "#472100")))
(erc-goodies-tests--assert-face
4 " 18 " '(fg:erc-color-face0 (:background "#474700"))
'((:background "#472100"))))
(should (search-forward " 71 " nil t))
(save-restriction
(narrow-to-region (point) (pos-eol))
(erc-goodies-tests--assert-face
0 " 72 " '(fg:erc-color-face0 (:background "#5959ff")))
(erc-goodies-tests--assert-face
4 " 73 " '(fg:erc-color-face0 (:background "#c459ff"))
'((:background "#5959ff"))))
(goto-char (point-min))
(when noninteractive
(kill-buffer)))))
;;; erc-goodies-tests.el ends here