1 ;; emchat-emphasis.el --- Gnus-style text emphasis in EMchat
3 ;; Copyright (C) 2005 - 2010 Steve Youngs
5 ;; Author: Steve Youngs <steve@emchat.org>
6 ;; Maintainer: Steve Youngs <steve@emchat.org>
7 ;; Created: <2005-04-29>
8 ;; Homepage: http://www.emchat.org/
11 ;; This file is part of EMchat.
13 ;; Redistribution and use in source and binary forms, with or without
14 ;; modification, are permitted provided that the following conditions
17 ;; 1. Redistributions of source code must retain the above copyright
18 ;; notice, this list of conditions and the following disclaimer.
20 ;; 2. Redistributions in binary form must reproduce the above copyright
21 ;; notice, this list of conditions and the following disclaimer in the
22 ;; documentation and/or other materials provided with the distribution.
24 ;; 3. Neither the name of the author nor the names of any contributors
25 ;; may be used to endorse or promote products derived from this
26 ;; software without specific prior written permission.
28 ;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
29 ;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30 ;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31 ;; DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 ;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 ;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 ;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35 ;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
36 ;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
37 ;; OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
38 ;; IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 ;; "prettify" the log buffer, ala Gnus. Most of this is unashamedly
51 (autoload 'manual-entry "man" nil t))
53 (defmacro emchat-emphasis-custom-with-format (&rest body)
55 \\(\\s-\\|^\\|\\=\\|[-\"]\\|\\s(\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*[.,]?\\)%s\\)\
56 \\(\\([-,.;:!?\"]\\|\\s)\\)+\\s-\\|[?!.]\\s-\\|\\s)\\|\\s-\\)"))
59 (defun emchat-emphasis-custom-value-to-external (value)
60 (emchat-emphasis-custom-with-format
61 (if (consp (car value))
62 (list (format format (car (car value)) (cdr (car value)))
64 (if (nth 1 value) 2 3)
68 (defun emchat-emphasis-custom-value-to-internal (value)
69 (emchat-emphasis-custom-with-format
70 (let ((regexp (concat "\\`"
71 (format (regexp-quote format)
72 "\\([^()]+\\)" "\\([^()]+\\)")
75 (if (string-match regexp (setq pattern (car value)))
76 (list (cons (match-string 1 pattern) (match-string 2 pattern))
81 (defgroup emchat-emphasis nil
82 "Emphasise text in the log buffer."
83 :prefix "emchat-emphasis-"
86 (defcustom emchat-emphasis-enabled-flag nil
87 "*When non-nil, emphasise text in the log buffer."
89 :group 'emchat-emphasis)
91 (defcustom emchat-emphasis-alist
96 ("_/" "/_" underline-italic)
97 ("_\\*" "\\*_" underline-bold)
98 ("\\*/" "/\\*" bold-italic)
99 ("_\\*/" "/\\*_" underline-bold-italic))))
101 (emchat-emphasis-custom-with-format
102 (mapcar (lambda (spec)
103 (list (format format (car spec) (cadr spec))
106 (intern (format "emchat-emphasis-%s" (nth 2 spec)))))
108 '(("\\(\\s-\\|^\\)\\(_\\(\\(\\w\\|_[^_]\\)+\\)_\\)\\(\\s-\\|[?!.,;]\\)"
109 2 3 emchat-emphasis-underline))))
110 "*Alist that says how to fontify certain phrases.
111 Each item looks like this:
113 (\"_\\\\(\\\\w+\\\\)_\" 0 1 'underline)
115 The first element is a regular expression to be matched. The second
116 is a number that says what regular expression grouping used to find
117 the entire emphasised word. The third is a number that says what
118 regexp grouping should be displayed and highlighted. The fourth
119 is the face used for highlighting."
123 :format "%[Customizing Style%]\n%v"
125 (group :tag "Default"
126 :value ("" 0 0 default)
129 (let ((value (widget-get
130 (cadr (widget-get (widget-get widget :parent)
133 (if (not (eq (nth 2 value) 'default))
137 (emchat-emphasis-custom-value-to-external value))))
138 (widget-group-value-create widget))
140 (integer :format "Match group: %v")
141 (integer :format "Emphasise group: %v")
144 :value (("_" . "_") nil default)
146 (regexp :format "Start regexp: %v")
147 (regexp :format "End regexp: %v"))
148 (boolean :format "Show start and end patterns: %[%v%]\n"
149 :on " On " :off " Off ")
151 :get #'(lambda (symbol)
152 (mapcar #'emchat-emphasis-custom-value-to-internal
153 (default-value symbol)))
154 :set #'(lambda (symbol value)
155 (set-default symbol (mapcar #'emchat-emphasis-custom-value-to-external
157 :group 'emchat-emphasis)
159 (defcustom emchat-emphasise-whitespace-regexp "^[ \t]+\\|[ \t]*\n"
160 "A regexp to describe whitespace which should not be emphasised.
161 Typical values are \"^[ \\t]+\\\\|[ \\t]*\\n\" and \"[ \\t]+\\\\|[ \\t]*\\n\".
162 The former avoids underlining of leading and trailing whitespace,
163 and the latter avoids underlining any whitespace at all."
164 :group 'emchat-emphasis
167 (defcustom emchat-emphasis-url-regexp
168 (concat "\\(https?://\\|s?ftp://\\|gopher://\\|telnet://"
169 "\\|wais://\\|file:/\\|s?news:\\)"
170 "[^]\t\n \"'()<>[^`{}]*[^]\t\n \"'()<>[^`{}.,;]+")
171 "A regular expression matching URLs."
173 :group 'emchat-emphasis)
175 (defcustom emchat-emphasis-email-regexp
176 "[-a-zA-Z0-9._]+@\\([-a-zA-z0-9_]+\\.\\)+[a-zA-Z0-9]+"
177 "A regular expression matching email addresses."
179 :group 'emchat-emphasis)
181 (defcustom emchat-emphasis-man-regexp "\\b\\w+([1-9n])"
182 "A regular expression matching unix manual pages.
184 For example, `xemacs\(1\)'."
186 :group 'emchat-emphasis)
188 (make-face 'emchat-emphasis-bold)
189 (set-face-parent 'emchat-emphasis-bold 'bold)
191 (defcustom emchat-emphasis-bold 'emchat-emphasis-bold
192 "Face used for displaying strong emphasised text (*word*)."
194 :group 'emchat-emphasis)
196 (make-face 'emchat-emphasis-italic)
197 (set-face-parent 'emchat-emphasis-italic 'italic)
199 (defcustom emchat-emphasis-italic 'emchat-emphasis-italic
200 "Face used for displaying italic emphasised text (/word/)."
202 :group 'emchat-emphasis)
204 (make-face 'emchat-emphasis-underline)
205 (set-face-parent 'emchat-emphasis-underline 'underline)
207 (defcustom emchat-emphasis-underline 'emchat-emphasis-underline
208 "Face used for displaying underlined emphasised text (_word_)."
210 :group 'emchat-emphasis)
212 (make-face 'emchat-emphasis-underline-bold)
213 (set-face-parent 'emchat-emphasis-underline-bold 'bold)
214 (set-face-property 'emchat-emphasis-underline-bold 'underline t)
216 (defcustom emchat-emphasis-underline-bold 'emchat-emphasis-underline-bold
217 "Face used for displaying underlined bold emphasised text (_*word*_)."
219 :group 'emchat-emphasis)
221 (make-face 'emchat-emphasis-underline-italic)
222 (set-face-parent 'emchat-emphasis-underline-italic 'italic)
223 (set-face-property 'emchat-emphasis-underline-italic 'underline t)
225 (defcustom emchat-emphasis-underline-italic 'emchat-emphasis-underline-italic
226 "Face used for displaying underlined italic emphasised text (_/word/_)."
228 :group 'emchat-emphasis)
230 (make-face 'emchat-emphasis-bold-italic)
231 (set-face-parent 'emchat-emphasis-bold-italic 'bold-italic)
233 (defcustom emchat-emphasis-bold-italic 'emchat-emphasis-bold-italic
234 "Face used for displaying bold italic emphasised text (/*word*/)."
236 :group 'emchat-emphasis)
238 (make-face 'emchat-emphasis-underline-bold-italic)
239 (set-face-parent 'emchat-emphasis-underline-bold-italic 'bold-italic)
240 (set-face-property 'emchat-emphasis-underline-bold-italic 'underline t)
242 (defcustom emchat-emphasis-underline-bold-italic
243 'emchat-emphasis-underline-bold-italic
244 "Face used for displaying underlined bold italic emphasised text.
245 Example: (_/*word*/_)."
247 :group 'emchat-emphasis)
249 (make-face 'emchat-emphasis-strikethru)
250 (set-face-property 'emchat-emphasis-strikethru 'strikethru t)
252 (defcustom emchat-emphasis-strikethru 'emchat-emphasis-strikethru
253 "Face used for displaying strike-through text (-word-)."
255 :group 'emchat-emphasis)
257 (defface emchat-emphasis-highlight-words
258 '((t (:background "black" :foreground "yellow")))
259 "Face used for displaying highlighted words."
260 :group 'emchat-emphasis)
262 ;;; Internal variables
264 (defun emchat-emphasis-treat-message (b e)
265 "Emphasise text in region B E according to `emchat-emphasis-alist'."
266 (let ((alist emchat-emphasis-alist)
267 regexp elem beg invisible visible face)
270 (narrow-to-region b e)
271 (goto-char (point-min))
273 (while (setq elem (pop alist))
275 (setq regexp (car elem)
276 invisible (nth 1 elem)
279 (while (re-search-forward regexp nil t)
280 (when (and (match-beginning visible) (match-beginning invisible))
282 (match-beginning invisible) (match-end invisible) 'invisible t)
283 (remove-text-properties
284 (match-beginning visible) (match-end visible) '(invisible t))
286 (match-beginning visible) (match-end visible) 'face face)
287 (goto-char (match-end invisible)))))))))
289 (defun emchat-emphasis-visit-hyperlink-at-point ()
290 "Follow the hyperlink at point in the EMchat log buffer.
292 This can either be a URL, in which case `browse-url' is called with
293 the string of the extent as an arg. Or it can be an email address, in
294 which case `compose-mail' is called. Or it can be a Unix manual page,
295 where `manual-entry' is called."
297 (when (extentp (extent-at (point)))
298 (let ((str (extent-string (extent-at (point)))))
299 (cond ((string-match emchat-emphasis-url-regexp str)
301 ((string-match emchat-emphasis-email-regexp str)
303 ((string-match emchat-emphasis-man-regexp str)
304 (if (fboundp 'manual-entry)
306 (error 'unimplemented "Unix manual pages")))
308 (error 'invalid-operation))))))
310 (defun emchat-emphasis-visit-hyperlink-at-mouse (event)
311 "Follow the hyperlink at EVENT in the EMchat log buffer.
313 This can either be a URL, in which case `browse-url' is called with
314 the string of the extent as an arg. Or it can be an email address, in
315 which case `compose-mail' is called. Or it can be a Unix manual page,
316 where `manual-entry' is called."
318 (when (extentp (extent-at-event event))
319 (let ((str (extent-string (extent-at-event event))))
320 (cond ((string-match emchat-emphasis-url-regexp str)
322 ((string-match emchat-emphasis-email-regexp str)
324 ((string-match emchat-emphasis-man-regexp str)
325 (if (fboundp 'manual-entry)
327 (error 'unimplemented "Unix manual pages")))
329 (error 'invalid-operation))))))
331 (defun emchat-emphasis-hyperlink-message (b e)
332 "Add hyperlinks to the message in region B E.
334 In other words, URLs, email addresses, and unix manual page names will
338 (narrow-to-region b e)
339 (goto-char (point-min))
340 (while (re-search-forward emchat-emphasis-url-regexp nil t)
341 (let ((extent (make-extent (match-beginning 0) (match-end 0)))
342 (echo "Mouse button2 -- Follow this link."))
343 (set-extent-property extent 'face 'widget-button-face)
344 (set-extent-property extent 'mouse-face 'highlight)
345 (set-extent-property extent 'keymap emchat-hyperlink-map)
346 (set-extent-property extent 'help-echo echo)
347 (set-extent-property extent 'balloon-help echo)
348 (set-extent-property extent 'duplicable t)))
349 (while (re-search-forward emchat-emphasis-email-regexp nil t)
350 (let ((extent (make-extent (match-beginning 0) (match-end 0)))
351 (echo "Mouse button2 -- Compose mail."))
352 (set-extent-property extent 'face 'emchat-emphasis-highlight-words)
353 (set-extent-property extent 'mouse-face 'highlight)
354 (set-extent-property extent 'keymap emchat-hyperlink-map)
355 (set-extent-property extent 'help-echo echo)
356 (set-extent-property extent 'balloon-help echo)
357 (set-extent-property extent 'duplicable t)))
358 (while (re-search-forward emchat-emphasis-man-regexp nil t)
359 (let ((extent (make-extent (match-beginning 0) (match-end 0)))
360 (echo "Mouse button2 -- Read this manual."))
361 (set-extent-property extent 'face 'man-xref)
362 (set-extent-property extent 'mouse-face 'highlight)
363 (set-extent-property extent 'keymap emchat-hyperlink-map)
364 (set-extent-property extent 'help-echo echo)
365 (set-extent-property extent 'balloon-help echo)
366 (set-extent-property extent 'duplicable t))))))
368 (provide 'emchat-emphasis)
369 ;;; emchat-emphasis.el ends here