
This reverts almost all my recent changes to use curved quotes in docstrings and/or strings used for error diagnostics. There are a few exceptions, e.g., Bahá’í proper names. * admin/unidata/unidata-gen.el (unidata-gen-table): * lisp/abbrev.el (expand-region-abbrevs): * lisp/align.el (align-region): * lisp/allout.el (allout-mode, allout-solicit-alternate-bullet) (outlineify-sticky): * lisp/apropos.el (apropos-library): * lisp/bookmark.el (bookmark-default-annotation-text): * lisp/button.el (button-category-symbol, button-put) (make-text-button): * lisp/calc/calc-aent.el (math-read-if, math-read-factor): * lisp/calc/calc-embed.el (calc-do-embedded): * lisp/calc/calc-ext.el (calc-user-function-list): * lisp/calc/calc-graph.el (calc-graph-show-dumb): * lisp/calc/calc-help.el (calc-describe-key) (calc-describe-thing, calc-full-help): * lisp/calc/calc-lang.el (calc-c-language) (math-parse-fortran-vector-end, math-parse-tex-sum) (math-parse-eqn-matrix, math-parse-eqn-prime) (calc-yacas-language, calc-maxima-language, calc-giac-language) (math-read-giac-subscr, math-read-math-subscr) (math-read-big-rec, math-read-big-balance): * lisp/calc/calc-misc.el (calc-help, report-calc-bug): * lisp/calc/calc-mode.el (calc-auto-why, calc-save-modes) (calc-auto-recompute): * lisp/calc/calc-prog.el (calc-fix-token-name) (calc-read-parse-table-part, calc-user-define-invocation) (math-do-arg-check): * lisp/calc/calc-store.el (calc-edit-variable): * lisp/calc/calc-units.el (math-build-units-table-buffer): * lisp/calc/calc-vec.el (math-read-brackets): * lisp/calc/calc-yank.el (calc-edit-mode): * lisp/calc/calc.el (calc, calc-do, calc-user-invocation): * lisp/calendar/appt.el (appt-display-message): * lisp/calendar/diary-lib.el (diary-check-diary-file) (diary-mail-entries, diary-from-outlook): * lisp/calendar/icalendar.el (icalendar-export-region) (icalendar--convert-float-to-ical) (icalendar--convert-date-to-ical) (icalendar--convert-ical-to-diary) (icalendar--convert-recurring-to-diary) (icalendar--add-diary-entry): * lisp/calendar/time-date.el (format-seconds): * lisp/calendar/timeclock.el (timeclock-mode-line-display) (timeclock-make-hours-explicit, timeclock-log-data): * lisp/calendar/todo-mode.el (todo-prefix, todo-delete-category) (todo-item-mark, todo-check-format) (todo-insert-item--next-param, todo-edit-item--next-key) (todo-mode): * lisp/cedet/ede/pmake.el (ede-proj-makefile-insert-dist-rules): * lisp/cedet/mode-local.el (describe-mode-local-overload) (mode-local-print-binding, mode-local-describe-bindings-2): * lisp/cedet/semantic/complete.el (semantic-displayor-show-request): * lisp/cedet/srecode/srt-mode.el (srecode-macro-help): * lisp/cus-start.el (standard): * lisp/cus-theme.el (describe-theme-1): * lisp/custom.el (custom-add-dependencies, custom-check-theme) (custom--sort-vars-1, load-theme): * lisp/descr-text.el (describe-text-properties-1, describe-char): * lisp/dired-x.el (dired-do-run-mail): * lisp/dired.el (dired-log): * lisp/emacs-lisp/advice.el (ad-read-advised-function) (ad-read-advice-class, ad-read-advice-name, ad-enable-advice) (ad-disable-advice, ad-remove-advice, ad-set-argument) (ad-set-arguments, ad--defalias-fset, ad-activate) (ad-deactivate): * lisp/emacs-lisp/byte-opt.el (byte-compile-inline-expand) (byte-compile-unfold-lambda, byte-optimize-form-code-walker) (byte-optimize-while, byte-optimize-apply): * lisp/emacs-lisp/byte-run.el (defun, defsubst): * lisp/emacs-lisp/bytecomp.el (byte-compile-lapcode) (byte-compile-log-file, byte-compile-format-warn) (byte-compile-nogroup-warn, byte-compile-arglist-warn) (byte-compile-cl-warn) (byte-compile-warn-about-unresolved-functions) (byte-compile-file, byte-compile--declare-var) (byte-compile-file-form-defmumble, byte-compile-form) (byte-compile-normal-call, byte-compile-check-variable) (byte-compile-variable-ref, byte-compile-variable-set) (byte-compile-subr-wrong-args, byte-compile-setq-default) (byte-compile-negation-optimizer) (byte-compile-condition-case--old) (byte-compile-condition-case--new, byte-compile-save-excursion) (byte-compile-defvar, byte-compile-autoload) (byte-compile-lambda-form) (byte-compile-make-variable-buffer-local, display-call-tree) (batch-byte-compile): * lisp/emacs-lisp/cconv.el (cconv-convert, cconv--analyze-use): * lisp/emacs-lisp/chart.el (chart-space-usage): * lisp/emacs-lisp/check-declare.el (check-declare-scan) (check-declare-warn, check-declare-file) (check-declare-directory): * lisp/emacs-lisp/checkdoc.el (checkdoc-this-string-valid-engine) (checkdoc-message-text-engine): * lisp/emacs-lisp/cl-extra.el (cl-parse-integer) (cl--describe-class): * lisp/emacs-lisp/cl-generic.el (cl-defgeneric) (cl--generic-describe, cl-generic-generalizers): * lisp/emacs-lisp/cl-macs.el (cl--parse-loop-clause, cl-tagbody) (cl-symbol-macrolet): * lisp/emacs-lisp/cl.el (cl-unload-function, flet): * lisp/emacs-lisp/copyright.el (copyright) (copyright-update-directory): * lisp/emacs-lisp/edebug.el (edebug-read-list): * lisp/emacs-lisp/eieio-base.el (eieio-persistent-read): * lisp/emacs-lisp/eieio-core.el (eieio--slot-override) (eieio-oref): * lisp/emacs-lisp/eieio-opt.el (eieio-help-constructor): * lisp/emacs-lisp/eieio-speedbar.el: (eieio-speedbar-child-make-tag-lines) (eieio-speedbar-child-description): * lisp/emacs-lisp/eieio.el (defclass, change-class): * lisp/emacs-lisp/elint.el (elint-file, elint-get-top-forms) (elint-init-form, elint-check-defalias-form) (elint-check-let-form): * lisp/emacs-lisp/ert.el (ert-get-test, ert-results-mode-menu) (ert-results-pop-to-backtrace-for-test-at-point) (ert-results-pop-to-messages-for-test-at-point) (ert-results-pop-to-should-forms-for-test-at-point) (ert-describe-test): * lisp/emacs-lisp/find-func.el (find-function-search-for-symbol) (find-function-library): * lisp/emacs-lisp/generator.el (iter-yield): * lisp/emacs-lisp/gv.el (gv-define-simple-setter): * lisp/emacs-lisp/lisp-mnt.el (lm-verify): * lisp/emacs-lisp/macroexp.el (macroexp--obsolete-warning): * lisp/emacs-lisp/map-ynp.el (map-y-or-n-p): * lisp/emacs-lisp/nadvice.el (advice--make-docstring) (advice--make, define-advice): * lisp/emacs-lisp/package-x.el (package-upload-file): * lisp/emacs-lisp/package.el (package-version-join) (package-disabled-p, package-activate-1, package-activate) (package--download-one-archive) (package--download-and-read-archives) (package-compute-transaction, package-install-from-archive) (package-install, package-install-selected-packages) (package-delete, package-autoremove, describe-package-1) (package-install-button-action, package-delete-button-action) (package-menu-hide-package, package-menu--list-to-prompt) (package-menu--perform-transaction) (package-menu--find-and-notify-upgrades): * lisp/emacs-lisp/pcase.el (pcase-exhaustive, pcase--u1): * lisp/emacs-lisp/re-builder.el (reb-enter-subexp-mode): * lisp/emacs-lisp/ring.el (ring-previous, ring-next): * lisp/emacs-lisp/rx.el (rx-check, rx-anything) (rx-check-any-string, rx-check-any, rx-check-not, rx-=) (rx-repeat, rx-check-backref, rx-syntax, rx-check-category) (rx-form): * lisp/emacs-lisp/smie.el (smie-config-save): * lisp/emacs-lisp/subr-x.el (internal--check-binding): * lisp/emacs-lisp/tabulated-list.el (tabulated-list-put-tag): * lisp/emacs-lisp/testcover.el (testcover-1value): * lisp/emacs-lisp/timer.el (timer-event-handler): * lisp/emulation/viper-cmd.el (viper-toggle-parse-sexp-ignore-comments) (viper-toggle-search-style, viper-kill-buffer) (viper-brac-function): * lisp/emulation/viper-macs.el (viper-record-kbd-macro): * lisp/env.el (setenv): * lisp/erc/erc-button.el (erc-nick-popup): * lisp/erc/erc.el (erc-cmd-LOAD, erc-handle-login, english): * lisp/eshell/em-dirs.el (eshell/cd): * lisp/eshell/em-glob.el (eshell-glob-regexp) (eshell-glob-entries): * lisp/eshell/em-pred.el (eshell-parse-modifiers): * lisp/eshell/esh-opt.el (eshell-show-usage): * lisp/facemenu.el (facemenu-add-new-face) (facemenu-add-new-color): * lisp/faces.el (read-face-name, read-face-font, describe-face) (x-resolve-font-name): * lisp/files-x.el (modify-file-local-variable): * lisp/files.el (locate-user-emacs-file, find-alternate-file) (set-auto-mode, hack-one-local-variable--obsolete) (dir-locals-set-directory-class, write-file, basic-save-buffer) (delete-directory, copy-directory, recover-session) (recover-session-finish, insert-directory) (file-modes-char-to-who, file-modes-symbolic-to-number) (move-file-to-trash): * lisp/filesets.el (filesets-add-buffer, filesets-remove-buffer): * lisp/find-cmd.el (find-generic, find-to-string): * lisp/finder.el (finder-commentary): * lisp/font-lock.el (font-lock-fontify-buffer): * lisp/format.el (format-write-file, format-find-file) (format-insert-file): * lisp/frame.el (get-device-terminal, select-frame-by-name): * lisp/fringe.el (fringe--check-style): * lisp/gnus/nnmairix.el (nnmairix-widget-create-query): * lisp/help-fns.el (help-fns--key-bindings) (help-fns--compiler-macro, help-fns--parent-mode) (help-fns--obsolete, help-fns--interactive-only) (describe-function-1, describe-variable): * lisp/help.el (describe-mode) (describe-minor-mode-from-indicator): * lisp/image.el (image-type): * lisp/international/ccl.el (ccl-dump): * lisp/international/fontset.el (x-must-resolve-font-name): * lisp/international/mule-cmds.el (prefer-coding-system) (select-safe-coding-system-interactively) (select-safe-coding-system, activate-input-method) (toggle-input-method, describe-current-input-method) (describe-language-environment): * lisp/international/mule-conf.el (code-offset): * lisp/international/mule-diag.el (describe-character-set) (list-input-methods-1): * lisp/mail/feedmail.el (feedmail-run-the-queue): * lisp/mouse.el (minor-mode-menu-from-indicator): * lisp/mpc.el (mpc-playlist-rename): * lisp/msb.el (msb--choose-menu): * lisp/net/ange-ftp.el (ange-ftp-shell-command): * lisp/net/imap.el (imap-interactive-login): * lisp/net/mairix.el (mairix-widget-create-query): * lisp/net/newst-backend.el (newsticker--sentinel-work): * lisp/net/newst-treeview.el (newsticker--treeview-load): * lisp/net/rlogin.el (rlogin): * lisp/obsolete/iswitchb.el (iswitchb-possible-new-buffer): * lisp/obsolete/otodo-mode.el (todo-more-important-p): * lisp/obsolete/pgg-gpg.el (pgg-gpg-process-region): * lisp/obsolete/pgg-pgp.el (pgg-pgp-process-region): * lisp/obsolete/pgg-pgp5.el (pgg-pgp5-process-region): * lisp/org/ob-core.el (org-babel-goto-named-src-block) (org-babel-goto-named-result): * lisp/org/ob-fortran.el (org-babel-fortran-ensure-main-wrap): * lisp/org/ob-ref.el (org-babel-ref-resolve): * lisp/org/org-agenda.el (org-agenda-prepare): * lisp/org/org-clock.el (org-clock-notify-once-if-expired) (org-clock-resolve): * lisp/org/org-ctags.el (org-ctags-ask-rebuild-tags-file-then-find-tag): * lisp/org/org-feed.el (org-feed-parse-atom-entry): * lisp/org/org-habit.el (org-habit-parse-todo): * lisp/org/org-mouse.el (org-mouse-popup-global-menu) (org-mouse-context-menu): * lisp/org/org-table.el (org-table-edit-formulas): * lisp/org/ox.el (org-export-async-start): * lisp/proced.el (proced-log): * lisp/progmodes/ada-mode.el (ada-get-indent-case) (ada-check-matching-start, ada-goto-matching-start): * lisp/progmodes/ada-prj.el (ada-prj-display-page): * lisp/progmodes/ada-xref.el (ada-find-executable): * lisp/progmodes/ebrowse.el (ebrowse-tags-apropos): * lisp/progmodes/etags.el (etags-tags-apropos-additional): * lisp/progmodes/flymake.el (flymake-parse-err-lines) (flymake-start-syntax-check-process): * lisp/progmodes/python.el (python-shell-get-process-or-error) (python-define-auxiliary-skeleton): * lisp/progmodes/sql.el (sql-comint): * lisp/progmodes/verilog-mode.el (verilog-load-file-at-point): * lisp/progmodes/vhdl-mode.el (vhdl-widget-directory-validate): * lisp/recentf.el (recentf-open-files): * lisp/replace.el (query-replace-read-from) (occur-after-change-function, occur-1): * lisp/scroll-bar.el (scroll-bar-columns): * lisp/server.el (server-get-auth-key): * lisp/simple.el (execute-extended-command) (undo-outer-limit-truncate, list-processes--refresh) (compose-mail, set-variable, choose-completion-string) (define-alternatives): * lisp/startup.el (site-run-file, tty-handle-args, command-line) (command-line-1): * lisp/subr.el (noreturn, define-error, add-to-list) (read-char-choice, version-to-list): * lisp/term/common-win.el (x-handle-xrm-switch) (x-handle-name-switch, x-handle-args): * lisp/term/x-win.el (x-handle-parent-id, x-handle-smid): * lisp/textmodes/reftex-ref.el (reftex-label): * lisp/textmodes/reftex-toc.el (reftex-toc-rename-label): * lisp/textmodes/two-column.el (2C-split): * lisp/tutorial.el (tutorial--describe-nonstandard-key) (tutorial--find-changed-keys): * lisp/type-break.el (type-break-noninteractive-query): * lisp/wdired.el (wdired-do-renames, wdired-do-symlink-changes) (wdired-do-perm-changes): * lisp/whitespace.el (whitespace-report-region): Prefer grave quoting in source-code strings used to generate help and diagnostics. * lisp/faces.el (face-documentation): No need to convert quotes, since the result is a docstring. * lisp/info.el (Info-virtual-index-find-node) (Info-virtual-index, info-apropos): Simplify by generating only curved quotes, since info files are typically that ways nowadays anyway. * lisp/international/mule-diag.el (list-input-methods): Don’t assume text quoting style is curved. * lisp/org/org-bibtex.el (org-bibtex-fields): Revert my recent changes, going back to the old quoting style.
549 lines
21 KiB
EmacsLisp
549 lines
21 KiB
EmacsLisp
;; erc-button.el --- A way of buttonizing certain things in ERC buffers -*- lexical-binding:t -*-
|
||
|
||
;; Copyright (C) 1996-2004, 2006-2015 Free Software Foundation, Inc.
|
||
|
||
;; Author: Mario Lang <mlang@delysid.org>
|
||
;; Maintainer: emacs-devel@gnu.org
|
||
;; Keywords: irc, button, url, regexp
|
||
;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?ErcButton
|
||
|
||
;; 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 <http://www.gnu.org/licenses/>.
|
||
|
||
;;; Commentary:
|
||
|
||
;; Heavily borrowed from gnus-art.el. Thanks to the original authors.
|
||
;; This buttonizes nicks and other stuff to make it all clickable.
|
||
;; To enable, add to your init file:
|
||
;; (require 'erc-button)
|
||
;; (erc-button-mode 1)
|
||
;;
|
||
;; Todo:
|
||
;; * Rewrite all this to do the same, but use button.el from GNU Emacs
|
||
;; if it's available for xemacs too. Why? button.el is much faster,
|
||
;; and much more elegant, and solves the problem we get with large buffers
|
||
;; and a large erc-button-marker-list.
|
||
|
||
|
||
;;; Code:
|
||
|
||
(require 'erc)
|
||
(require 'wid-edit)
|
||
(require 'erc-fill)
|
||
|
||
;;; Minor Mode
|
||
|
||
(defgroup erc-button nil
|
||
"Define how text can be turned into clickable buttons."
|
||
:group 'erc)
|
||
|
||
;;;###autoload (autoload 'erc-button-mode "erc-button" nil t)
|
||
(define-erc-module button nil
|
||
"This mode buttonizes all messages according to `erc-button-alist'."
|
||
((add-hook 'erc-insert-modify-hook 'erc-button-add-buttons 'append)
|
||
(add-hook 'erc-send-modify-hook 'erc-button-add-buttons 'append)
|
||
(add-hook 'erc-complete-functions 'erc-button-next-function)
|
||
(add-hook 'erc-mode-hook 'erc-button-setup))
|
||
((remove-hook 'erc-insert-modify-hook 'erc-button-add-buttons)
|
||
(remove-hook 'erc-send-modify-hook 'erc-button-add-buttons)
|
||
(remove-hook 'erc-complete-functions 'erc-button-next-function)
|
||
(remove-hook 'erc-mode-hook 'erc-button-setup)
|
||
(when (featurep 'xemacs)
|
||
(dolist (buffer (erc-buffer-list))
|
||
(with-current-buffer buffer
|
||
(kill-local-variable 'widget-button-face))))))
|
||
|
||
;;; Variables
|
||
|
||
(defface erc-button '((t :weight bold))
|
||
"ERC button face."
|
||
:group 'erc-faces)
|
||
|
||
(defcustom erc-button-face 'erc-button
|
||
"Face used for highlighting buttons in ERC buffers.
|
||
|
||
A button is a piece of text that you can activate by pressing
|
||
`RET' or `mouse-2' above it. See also `erc-button-keymap'."
|
||
:type 'face
|
||
:group 'erc-faces)
|
||
|
||
(defcustom erc-button-nickname-face 'erc-nick-default-face
|
||
"Face used for ERC nickname buttons."
|
||
:type 'face
|
||
:group 'erc-faces)
|
||
|
||
(defcustom erc-button-mouse-face 'highlight
|
||
"Face used for mouse highlighting in ERC buffers.
|
||
|
||
Buttons will be displayed in this face when the mouse cursor is
|
||
above them."
|
||
:type 'face
|
||
:group 'erc-faces)
|
||
|
||
(defcustom erc-button-url-regexp
|
||
(concat "\\(www\\.\\|\\(s?https?\\|"
|
||
"ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\):\\)"
|
||
"\\(//[-a-zA-Z0-9_.]+:[0-9]*\\)?"
|
||
"[-a-zA-Z0-9_=!?#$@~`%&*+\\/:;.,()]+[-a-zA-Z0-9_=#$@~`%&*+\\/()]")
|
||
"Regular expression that matches URLs."
|
||
:group 'erc-button
|
||
:type 'regexp)
|
||
|
||
(defcustom erc-button-wrap-long-urls nil
|
||
"If non-nil, \"long\" URLs matching `erc-button-url-regexp' will be wrapped.
|
||
|
||
If this variable is a number, consider URLs longer than its value to
|
||
be \"long\". If t, URLs will be considered \"long\" if they are
|
||
longer than `erc-fill-column'."
|
||
:group 'erc-button
|
||
:type '(choice integer boolean))
|
||
|
||
(defcustom erc-button-buttonize-nicks t
|
||
"Flag indicating whether nicks should be buttonized or not."
|
||
:group 'erc-button
|
||
:type 'boolean)
|
||
|
||
(defcustom erc-button-rfc-url "http://www.faqs.org/rfcs/rfc%s.html"
|
||
"URL used to browse rfc references.
|
||
%s is replaced by the number."
|
||
:group 'erc-button
|
||
:type 'string)
|
||
|
||
(defcustom erc-button-google-url "http://www.google.com/search?q=%s"
|
||
"URL used to browse Google search references.
|
||
%s is replaced by the search string."
|
||
:group 'erc-button
|
||
:type 'string)
|
||
|
||
(defcustom erc-button-alist
|
||
;; Since the callback is only executed when the user is clicking on
|
||
;; a button, it makes no sense to optimize performance by
|
||
;; bytecompiling lambdas in this alist. On the other hand, it makes
|
||
;; things hard to maintain.
|
||
'(('nicknames 0 erc-button-buttonize-nicks erc-nick-popup 0)
|
||
(erc-button-url-regexp 0 t browse-url 0)
|
||
("<URL: *\\([^<> ]+\\) *>" 0 t browse-url 1)
|
||
;;; ("(\\(\\([^~\n \t@][^\n \t@]*\\)@\\([a-zA-Z0-9.:-]+\\)\\)" 1 t finger 2 3)
|
||
;; emacs internal
|
||
("[`]\\([a-zA-Z][-a-zA-Z_0-9]+\\)[']" 1 t erc-button-describe-symbol 1)
|
||
;; pseudo links
|
||
("\\bInfo:[\"]\\([^\"]+\\)[\"]" 0 t Info-goto-node 1)
|
||
("\\b\\(Ward\\|Wiki\\|WardsWiki\\|TheWiki\\):\\([A-Z][a-z]+\\([A-Z][a-z]+\\)+\\)"
|
||
0 t (lambda (page)
|
||
(browse-url (concat "http://c2.com/cgi-bin/wiki?" page)))
|
||
2)
|
||
("EmacsWiki:\\([A-Z][a-z]+\\([A-Z][a-z]+\\)+\\)" 0 t erc-browse-emacswiki 1)
|
||
("Lisp:\\([a-zA-Z.+-]+\\)" 0 t erc-browse-emacswiki-lisp 1)
|
||
("\\bGoogle:\\([^ \t\n\r\f]+\\)"
|
||
0 t (lambda (keywords)
|
||
(browse-url (format erc-button-google-url keywords)))
|
||
1)
|
||
("\\brfc[#: ]?\\([0-9]+\\)"
|
||
0 t (lambda (num)
|
||
(browse-url (format erc-button-rfc-url num)))
|
||
1)
|
||
;; other
|
||
("\\s-\\(@\\([0-9][0-9][0-9]\\)\\)" 1 t erc-button-beats-to-time 2))
|
||
"Alist of regexps matching buttons in ERC buffers.
|
||
Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where
|
||
|
||
REGEXP is the string matching text around the button or a symbol
|
||
indicating a variable holding that string, or a list of
|
||
strings, or an alist with the strings in the car. Note that
|
||
entries in lists or alists are considered to be nicks or other
|
||
complete words. Therefore they are enclosed in \\< and \\>
|
||
while searching. REGEXP can also be the quoted symbol
|
||
'nicknames, which matches the nickname of any user on the
|
||
current server.
|
||
|
||
BUTTON is the number of the regexp grouping actually matching the
|
||
button, This is ignored if REGEXP is 'nicknames.
|
||
|
||
FORM is a lisp expression which must eval to true for the button to
|
||
be added,
|
||
|
||
CALLBACK is the function to call when the user push this button.
|
||
CALLBACK can also be a symbol. Its variable value will be used
|
||
as the callback function.
|
||
|
||
PAR is a number of a regexp grouping whose text will be passed to
|
||
CALLBACK. There can be several PAR arguments. If REGEXP is
|
||
'nicknames, these are ignored, and CALLBACK will be called with
|
||
the nickname matched as the argument."
|
||
:group 'erc-button
|
||
:version "24.1" ; remove finger (bug#4443)
|
||
:type '(repeat
|
||
(list :tag "Button"
|
||
(choice :tag "Matches"
|
||
regexp
|
||
(variable :tag "Variable containing regexp")
|
||
;; FIXME It really does mean 'nicknames
|
||
;; rather than just nicknames.
|
||
(const :tag "Nicknames" 'nicknames))
|
||
(integer :tag "Number of the regexp section that matches")
|
||
(choice :tag "When to buttonize"
|
||
(const :tag "Always" t)
|
||
(sexp :tag "Only when this evaluates to non-nil"))
|
||
(function :tag "Function to call when button is pressed")
|
||
(repeat :tag "Sections of regexp to send to the function"
|
||
:inline t
|
||
(integer :tag "Regexp section number")))))
|
||
|
||
(defcustom erc-emacswiki-url "http://www.emacswiki.org/cgi-bin/wiki.pl?"
|
||
"URL of the EmacsWiki Homepage."
|
||
:group 'erc-button
|
||
:type 'string)
|
||
|
||
(defcustom erc-emacswiki-lisp-url "http://www.emacswiki.org/elisp/"
|
||
"URL of the EmacsWiki ELisp area."
|
||
:group 'erc-button
|
||
:type 'string)
|
||
|
||
(defvar erc-button-keymap
|
||
(let ((map (make-sparse-keymap)))
|
||
(define-key map (kbd "RET") 'erc-button-press-button)
|
||
(if (featurep 'xemacs)
|
||
(define-key map (kbd "<button2>") 'erc-button-click-button)
|
||
(define-key map (kbd "<mouse-2>") 'erc-button-click-button))
|
||
(define-key map (kbd "TAB") 'erc-button-next)
|
||
(define-key map (kbd "<backtab>") 'erc-button-previous)
|
||
(define-key map [follow-link] 'mouse-face)
|
||
(set-keymap-parent map erc-mode-map)
|
||
map)
|
||
"Local keymap for ERC buttons.")
|
||
|
||
(defvar erc-button-syntax-table
|
||
(let ((table (make-syntax-table)))
|
||
(modify-syntax-entry ?\( "w" table)
|
||
(modify-syntax-entry ?\) "w" table)
|
||
(modify-syntax-entry ?\[ "w" table)
|
||
(modify-syntax-entry ?\] "w" table)
|
||
(modify-syntax-entry ?\{ "w" table)
|
||
(modify-syntax-entry ?\} "w" table)
|
||
(modify-syntax-entry ?` "w" table)
|
||
(modify-syntax-entry ?' "w" table)
|
||
(modify-syntax-entry ?^ "w" table)
|
||
(modify-syntax-entry ?- "w" table)
|
||
(modify-syntax-entry ?_ "w" table)
|
||
(modify-syntax-entry ?| "w" table)
|
||
(modify-syntax-entry ?\\ "w" table)
|
||
table)
|
||
"Syntax table used when buttonizing messages.
|
||
This syntax table should make all the valid nick characters word
|
||
constituents.")
|
||
|
||
(defvar erc-button-keys-added nil
|
||
"Internal variable used to keep track of whether we've added the
|
||
global-level ERC button keys yet.")
|
||
|
||
(defun erc-button-setup ()
|
||
"Add ERC mode-level button movement keys. This is only done once."
|
||
;; Make XEmacs use `erc-button-face'.
|
||
(when (featurep 'xemacs)
|
||
(set (make-local-variable 'widget-button-face) nil))
|
||
;; Add keys.
|
||
(unless erc-button-keys-added
|
||
(define-key erc-mode-map (kbd "<backtab>") 'erc-button-previous)
|
||
(setq erc-button-keys-added t)))
|
||
|
||
(defun erc-button-add-buttons ()
|
||
"Find external references in the current buffer and make buttons of them.
|
||
\"External references\" are things like URLs, as
|
||
specified by `erc-button-alist'."
|
||
(interactive)
|
||
(save-excursion
|
||
(with-syntax-table erc-button-syntax-table
|
||
(let ((buffer-read-only nil)
|
||
(inhibit-point-motion-hooks t)
|
||
(inhibit-field-text-motion t)
|
||
(alist erc-button-alist)
|
||
regexp)
|
||
(erc-button-remove-old-buttons)
|
||
(dolist (entry alist)
|
||
(if (equal (car entry) (quote (quote nicknames)))
|
||
(erc-button-add-nickname-buttons entry)
|
||
(progn
|
||
(setq regexp (or (and (stringp (car entry)) (car entry))
|
||
(and (boundp (car entry))
|
||
(symbol-value (car entry)))))
|
||
(cond ((stringp regexp)
|
||
(erc-button-add-buttons-1 regexp entry))
|
||
((and (listp regexp) (stringp (car regexp)))
|
||
(dolist (r regexp)
|
||
(erc-button-add-buttons-1
|
||
(concat "\\<" (regexp-quote r) "\\>")
|
||
entry)))
|
||
((and (listp regexp) (listp (car regexp))
|
||
(stringp (caar regexp)))
|
||
(dolist (elem regexp)
|
||
(erc-button-add-buttons-1
|
||
(concat "\\<" (regexp-quote (car elem)) "\\>")
|
||
entry)))))))))))
|
||
|
||
(defun erc-button-add-nickname-buttons (entry)
|
||
"Search through the buffer for nicknames, and add buttons."
|
||
(let ((form (nth 2 entry))
|
||
(fun (nth 3 entry))
|
||
bounds word)
|
||
(when (or (eq t form)
|
||
(eval form))
|
||
(goto-char (point-min))
|
||
(while (forward-word 1)
|
||
(setq bounds (bounds-of-thing-at-point 'word))
|
||
(setq word (buffer-substring-no-properties
|
||
(car bounds) (cdr bounds)))
|
||
(when (or (and (erc-server-buffer-p) (erc-get-server-user word))
|
||
(and erc-channel-users (erc-get-channel-user word)))
|
||
(erc-button-add-button (car bounds) (cdr bounds)
|
||
fun t (list word)))))))
|
||
|
||
(defun erc-button-add-buttons-1 (regexp entry)
|
||
"Search through the buffer for matches to ENTRY and add buttons."
|
||
(goto-char (point-min))
|
||
(while (re-search-forward regexp nil t)
|
||
(let ((start (match-beginning (nth 1 entry)))
|
||
(end (match-end (nth 1 entry)))
|
||
(form (nth 2 entry))
|
||
(fun (nth 3 entry))
|
||
(data (mapcar 'match-string (nthcdr 4 entry))))
|
||
(when (or (eq t form)
|
||
(eval form))
|
||
(erc-button-add-button start end fun nil data regexp)))))
|
||
|
||
(defun erc-button-remove-old-buttons ()
|
||
"Remove all existing buttons.
|
||
This is called with narrowing in effect, just before the text is
|
||
buttonized again. Removing a button means to remove all the properties
|
||
that `erc-button-add-button' adds, except for the face."
|
||
(remove-text-properties
|
||
(point-min) (point-max)
|
||
'(erc-callback nil
|
||
erc-data nil
|
||
mouse-face nil
|
||
keymap nil)))
|
||
|
||
(defun erc-button-add-button (from to fun nick-p &optional data regexp)
|
||
"Create a button between FROM and TO with callback FUN and data DATA.
|
||
NICK-P specifies if this is a nickname button.
|
||
REGEXP is the regular expression which matched for this button."
|
||
;; Really nasty hack to <URL: > ise urls, and line-wrap them if
|
||
;; they're going to be wider than `erc-fill-column'.
|
||
;; This could be a lot cleaner, but it works for me -- lawrence.
|
||
(let (fill-column)
|
||
(when (and erc-button-wrap-long-urls
|
||
(string= regexp erc-button-url-regexp)
|
||
(> (- to from)
|
||
(setq fill-column (- (if (numberp erc-button-wrap-long-urls)
|
||
erc-button-wrap-long-urls
|
||
erc-fill-column)
|
||
(length erc-fill-prefix)))))
|
||
(setq to (prog1 (point-marker) (insert ">"))
|
||
from (prog2 (goto-char from) (point-marker) (insert "<URL: ")))
|
||
(let ((pos (copy-marker from)))
|
||
(while (> (- to pos) fill-column)
|
||
(goto-char (+ pos fill-column))
|
||
(insert "\n" erc-fill-prefix) ; This ought to figure out
|
||
; what type of filling we're
|
||
; doing, and indent accordingly.
|
||
(move-marker pos (point))))))
|
||
(if nick-p
|
||
(when erc-button-nickname-face
|
||
(erc-button-add-face from to erc-button-nickname-face))
|
||
(when erc-button-face
|
||
(erc-button-add-face from to erc-button-face)))
|
||
(add-text-properties
|
||
from to
|
||
(nconc (and erc-button-mouse-face
|
||
(list 'mouse-face erc-button-mouse-face))
|
||
(list 'erc-callback fun)
|
||
(list 'keymap erc-button-keymap)
|
||
(list 'rear-nonsticky t)
|
||
(and data (list 'erc-data data))))
|
||
(when (featurep 'xemacs)
|
||
(widget-convert-button 'link from to :action 'erc-button-press-button
|
||
:suppress-face t
|
||
;; Make XEmacs use our faces.
|
||
:button-face (if nick-p
|
||
erc-button-nickname-face
|
||
erc-button-face)
|
||
;; Make XEmacs behave with mouse-clicks, for
|
||
;; some reason, widget stuff overrides the
|
||
;; 'keymap text-property.
|
||
:mouse-down-action 'erc-button-click-button)))
|
||
|
||
(defun erc-button-add-face (from to face)
|
||
"Add FACE to the region between FROM and TO."
|
||
;; If we just use `add-text-property', then this will overwrite any
|
||
;; face text property already used for the button. It will not be
|
||
;; merged correctly. If we use overlays, then redisplay will be
|
||
;; very slow with lots of buttons. This is why we manually merge
|
||
;; face text properties.
|
||
(let ((old (erc-list (get-text-property from 'face)))
|
||
(pos from)
|
||
(end (next-single-property-change from 'face nil to))
|
||
new)
|
||
;; old is the face at pos, in list form. It is nil if there is no
|
||
;; face at pos. If nil, the new face is FACE. If not nil, the
|
||
;; new face is a list containing FACE and the old stuff. end is
|
||
;; where this face changes.
|
||
(while (< pos to)
|
||
(setq new (if old (cons face old) face))
|
||
(put-text-property pos end 'face new)
|
||
(setq pos end
|
||
old (erc-list (get-text-property pos 'face))
|
||
end (next-single-property-change pos 'face nil to)))))
|
||
|
||
;; widget-button-click calls with two args, we ignore the first.
|
||
;; Since Emacs runs this directly, rather than with
|
||
;; widget-button-click, we need to fake an extra arg in the
|
||
;; interactive spec.
|
||
(defun erc-button-click-button (_ignore event)
|
||
"Call `erc-button-press-button'."
|
||
(interactive "P\ne")
|
||
(save-excursion
|
||
(mouse-set-point event)
|
||
(erc-button-press-button)))
|
||
|
||
;; XEmacs calls this via widget-button-press with a bunch of arguments
|
||
;; which we don't care about.
|
||
(defun erc-button-press-button (&rest _ignore)
|
||
"Check text at point for a callback function.
|
||
If the text at point has a `erc-callback' property,
|
||
call it with the value of the `erc-data' text property."
|
||
(interactive)
|
||
(let* ((data (get-text-property (point) 'erc-data))
|
||
(fun (get-text-property (point) 'erc-callback)))
|
||
(unless fun
|
||
(message "No button at point"))
|
||
(when (and fun (symbolp fun) (not (fboundp fun)))
|
||
(error "Function %S is not bound" fun))
|
||
(apply fun data)))
|
||
|
||
(defun erc-button-next-function ()
|
||
"Pseudo completion function that actually jumps to the next button.
|
||
For use on `completion-at-point-functions'."
|
||
;; FIXME: This is an abuse of completion-at-point-functions.
|
||
(when (< (point) (erc-beg-of-input-line))
|
||
(let ((start (point)))
|
||
(lambda ()
|
||
(let ((here start))
|
||
;; FIXME: Use next-single-property-change.
|
||
(while (and (get-text-property here 'erc-callback)
|
||
(not (= here (point-max))))
|
||
(setq here (1+ here)))
|
||
(while (not (or (get-text-property here 'erc-callback)
|
||
(= here (point-max))))
|
||
(setq here (1+ here)))
|
||
(if (< here (point-max))
|
||
(goto-char here)
|
||
(error "No next button"))
|
||
t)))))
|
||
|
||
(defun erc-button-next ()
|
||
"Go to the next button in this buffer."
|
||
(interactive)
|
||
(let ((f (erc-button-next-function)))
|
||
(if f (funcall f))))
|
||
|
||
(defun erc-button-previous ()
|
||
"Go to the previous button in this buffer."
|
||
(interactive)
|
||
(let ((here (point)))
|
||
(when (< here (erc-beg-of-input-line))
|
||
(while (and (get-text-property here 'erc-callback)
|
||
(not (= here (point-min))))
|
||
(setq here (1- here)))
|
||
(while (and (not (get-text-property here 'erc-callback))
|
||
(not (= here (point-min))))
|
||
(setq here (1- here)))
|
||
(if (> here (point-min))
|
||
(goto-char here)
|
||
(error "No previous button"))
|
||
t)))
|
||
|
||
(defun erc-browse-emacswiki (thing)
|
||
"Browse to thing in the emacs-wiki."
|
||
(browse-url (concat erc-emacswiki-url thing)))
|
||
|
||
(defun erc-browse-emacswiki-lisp (thing)
|
||
"Browse to THING in the emacs-wiki elisp area."
|
||
(browse-url (concat erc-emacswiki-lisp-url thing)))
|
||
|
||
;;; Nickname buttons:
|
||
|
||
(defcustom erc-nick-popup-alist
|
||
'(("DeOp" . (erc-cmd-DEOP nick))
|
||
("Kick" . (erc-cmd-KICK (concat nick " "
|
||
(read-from-minibuffer
|
||
(concat "Kick " nick ", reason: ")))))
|
||
("Msg" . (erc-cmd-MSG (concat nick " "
|
||
(read-from-minibuffer
|
||
(concat "Message to " nick ": ")))))
|
||
("Op" . (erc-cmd-OP nick))
|
||
("Query" . (erc-cmd-QUERY nick))
|
||
("Whois" . (erc-cmd-WHOIS nick))
|
||
("Lastlog" . (erc-cmd-LASTLOG nick)))
|
||
"An alist of possible actions to take on a nickname.
|
||
An entry looks like (\"Action\" . SEXP) where SEXP is evaluated with
|
||
the variable `nick' bound to the nick in question.
|
||
|
||
Examples:
|
||
(\"DebianDB\" .
|
||
(shell-command
|
||
(format
|
||
\"ldapsearch -x -P 2 -h db.debian.org -b dc=debian,dc=org ircnick=%s\"
|
||
nick)))"
|
||
:group 'erc-button
|
||
:type '(repeat (cons (string :tag "Op")
|
||
sexp)))
|
||
|
||
(defun erc-nick-popup (nick)
|
||
(let* ((completion-ignore-case t)
|
||
(action (completing-read (format-message
|
||
"What action to take on `%s'? " nick)
|
||
erc-nick-popup-alist))
|
||
(code (cdr (assoc action erc-nick-popup-alist))))
|
||
(when code
|
||
(erc-set-active-buffer (current-buffer))
|
||
(eval code `((nick . ,nick))))))
|
||
|
||
;;; Callback functions
|
||
(defun erc-button-describe-symbol (symbol-name)
|
||
"Describe SYMBOL-NAME.
|
||
Use `describe-function' for functions, `describe-variable' for variables,
|
||
and `apropos' for other symbols."
|
||
(let ((symbol (intern-soft symbol-name)))
|
||
(cond ((and symbol (fboundp symbol))
|
||
(describe-function symbol))
|
||
((and symbol (boundp symbol))
|
||
(describe-variable symbol))
|
||
(t (apropos symbol-name)))))
|
||
|
||
(defun erc-button-beats-to-time (beats)
|
||
"Display BEATS in a readable time format."
|
||
(let* ((seconds (- (* (string-to-number beats) 86.4)
|
||
3600
|
||
(- (car (current-time-zone)))))
|
||
(hours (mod (floor seconds 3600) 24))
|
||
(minutes (mod (round seconds 60) 60)))
|
||
(message "@%s is %d:%02d local time"
|
||
beats hours minutes)))
|
||
|
||
(provide 'erc-button)
|
||
|
||
;;; erc-button.el ends here
|
||
;; Local Variables:
|
||
;; indent-tabs-mode: nil
|
||
;; End:
|