summaryrefslogtreecommitdiff
path: root/lisp/erc/erc.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/erc/erc.el')
-rw-r--r--lisp/erc/erc.el289
1 files changed, 157 insertions, 132 deletions
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index feee89d7fea..f5c9decc3a2 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -62,17 +62,17 @@
;;; History:
;;
-(defconst erc-version-string (format "\C-bERC\C-b (IRC client for Emacs %s)" emacs-version)
- "ERC version. This is used by function `erc-version'.")
-
;;; Code:
+(load "erc-loaddefs" nil t)
+
(eval-when-compile (require 'cl-lib))
(require 'font-lock)
(require 'pp)
(require 'thingatpt)
(require 'auth-source)
(require 'erc-compat)
+(eval-when-compile (require 'subr-x))
(defvar erc-official-location
"https://www.emacswiki.org/emacs/ERC (mailing list: erc-discuss@gnu.org)"
@@ -399,25 +399,28 @@ If no server buffer exists, return nil."
;; This is useful for ordered name completion.
(last-message-time nil))
-(defsubst erc-get-channel-user (nick)
+(define-inline erc-get-channel-user (nick)
"Find the (USER . CHANNEL-DATA) element corresponding to NICK
in the current buffer's `erc-channel-users' hash table."
- (gethash (erc-downcase nick) erc-channel-users))
+ (inline-quote (gethash (erc-downcase ,nick) erc-channel-users)))
-(defsubst erc-get-server-user (nick)
+(define-inline erc-get-server-user (nick)
"Find the USER corresponding to NICK in the current server's
`erc-server-users' hash table."
- (erc-with-server-buffer
- (gethash (erc-downcase nick) erc-server-users)))
+ (inline-letevals (nick)
+ (inline-quote (erc-with-server-buffer
+ (gethash (erc-downcase ,nick) erc-server-users)))))
-(defsubst erc-add-server-user (nick user)
+(define-inline erc-add-server-user (nick user)
"This function is for internal use only.
Adds USER with nickname NICK to the `erc-server-users' hash table."
- (erc-with-server-buffer
- (puthash (erc-downcase nick) user erc-server-users)))
+ (inline-letevals (nick user)
+ (inline-quote
+ (erc-with-server-buffer
+ (puthash (erc-downcase ,nick) ,user erc-server-users)))))
-(defsubst erc-remove-server-user (nick)
+(define-inline erc-remove-server-user (nick)
"This function is for internal use only.
Removes the user with nickname NICK from the `erc-server-users'
@@ -425,8 +428,10 @@ hash table. This user is not removed from the
`erc-channel-users' lists of other buffers.
See also: `erc-remove-user'."
- (erc-with-server-buffer
- (remhash (erc-downcase nick) erc-server-users)))
+ (inline-letevals (nick)
+ (inline-quote
+ (erc-with-server-buffer
+ (remhash (erc-downcase ,nick) erc-server-users)))))
(defun erc-change-user-nickname (user new-nick)
"This function is for internal use only.
@@ -497,45 +502,55 @@ Removes all users in the current channel. This is called by
erc-channel-users)
(clrhash erc-channel-users)))
-(defsubst erc-channel-user-owner-p (nick)
+(define-inline erc-channel-user-owner-p (nick)
"Return non-nil if NICK is an owner of the current channel."
- (and nick
- (hash-table-p erc-channel-users)
- (let ((cdata (erc-get-channel-user nick)))
- (and cdata (cdr cdata)
- (erc-channel-user-owner (cdr cdata))))))
-
-(defsubst erc-channel-user-admin-p (nick)
+ (inline-letevals (nick)
+ (inline-quote
+ (and ,nick
+ (hash-table-p erc-channel-users)
+ (let ((cdata (erc-get-channel-user ,nick)))
+ (and cdata (cdr cdata)
+ (erc-channel-user-owner (cdr cdata))))))))
+
+(define-inline erc-channel-user-admin-p (nick)
"Return non-nil if NICK is an admin in the current channel."
- (and nick
+ (inline-letevals (nick)
+ (inline-quote
+ (and ,nick
(hash-table-p erc-channel-users)
- (let ((cdata (erc-get-channel-user nick)))
+ (let ((cdata (erc-get-channel-user ,nick)))
(and cdata (cdr cdata)
- (erc-channel-user-admin (cdr cdata))))))
+ (erc-channel-user-admin (cdr cdata))))))))
-(defsubst erc-channel-user-op-p (nick)
+(define-inline erc-channel-user-op-p (nick)
"Return non-nil if NICK is an operator in the current channel."
- (and nick
+ (inline-letevals (nick)
+ (inline-quote
+ (and ,nick
(hash-table-p erc-channel-users)
- (let ((cdata (erc-get-channel-user nick)))
+ (let ((cdata (erc-get-channel-user ,nick)))
(and cdata (cdr cdata)
- (erc-channel-user-op (cdr cdata))))))
+ (erc-channel-user-op (cdr cdata))))))))
-(defsubst erc-channel-user-halfop-p (nick)
+(define-inline erc-channel-user-halfop-p (nick)
"Return non-nil if NICK is a half-operator in the current channel."
- (and nick
+ (inline-letevals (nick)
+ (inline-quote
+ (and ,nick
(hash-table-p erc-channel-users)
- (let ((cdata (erc-get-channel-user nick)))
+ (let ((cdata (erc-get-channel-user ,nick)))
(and cdata (cdr cdata)
- (erc-channel-user-halfop (cdr cdata))))))
+ (erc-channel-user-halfop (cdr cdata))))))))
-(defsubst erc-channel-user-voice-p (nick)
+(define-inline erc-channel-user-voice-p (nick)
"Return non-nil if NICK has voice in the current channel."
- (and nick
+ (inline-letevals (nick)
+ (inline-quote
+ (and ,nick
(hash-table-p erc-channel-users)
- (let ((cdata (erc-get-channel-user nick)))
+ (let ((cdata (erc-get-channel-user ,nick)))
(and cdata (cdr cdata)
- (erc-channel-user-voice (cdr cdata))))))
+ (erc-channel-user-voice (cdr cdata))))))))
(defun erc-get-channel-user-list ()
"Return a list of users in the current channel. Each element
@@ -1036,6 +1051,21 @@ Note that it's useless to set `erc-send-this' to nil and
anyway."
:group 'erc-hooks
:type 'hook)
+(make-obsolete-variable 'erc-send-pre-hook 'erc-pre-send-functions "27.1")
+
+(defcustom erc-pre-send-functions nil
+ "List of functions called to possibly alter the string that is sent.
+The functions are called with one argument, a `erc-input' struct,
+and should alter that struct.
+
+The struct has three slots:
+
+ `string': The current input string.
+ `insertp': Whether the string should be inserted into the erc buffer.
+ `sendp': Whether the string should be sent to the irc server."
+ :group 'erc
+ :type '(repeat function)
+ :version "27.1")
(defvar erc-insert-this t
"Insert the text into the target buffer or not.
@@ -1046,6 +1076,7 @@ if they wish to avoid insertion of a particular string.")
"Send the text to the target or not.
Functions on `erc-send-pre-hook' can set this variable to nil
if they wish to avoid sending of a particular string.")
+(make-obsolete-variable 'erc-send-this 'erc-pre-send-functions "27.1")
(defcustom erc-insert-modify-hook ()
"Insertion hook for functions that will change the text's appearance.
@@ -1260,7 +1291,7 @@ erc-NAME-enable, and erc-NAME-disable.
Example:
- ;;;###autoload (autoload \\='erc-replace-mode \"erc-replace\")
+ ;;;###autoload(autoload \\='erc-replace-mode \"erc-replace\")
(define-erc-module replace nil
\"This mode replaces incoming text according to `erc-replace-alist'.\"
((add-hook \\='erc-insert-modify-hook
@@ -1343,10 +1374,11 @@ capabilities."
(add-hook hook fun nil t)
fun))
-(defsubst erc-log (string)
+(define-inline erc-log (string)
"Logs STRING if logging is on (see `erc-log-p')."
- (when erc-log-p
- (erc-log-aux string)))
+ (inline-quote
+ (when erc-log-p
+ (erc-log-aux ,string))))
(defun erc-server-buffer ()
"Return the server buffer for the current buffer's process.
@@ -1590,18 +1622,18 @@ symbol, it may have these values:
(dolist (candidate (list buf-name (concat buf-name "/" server)))
(if (and (not buffer-name)
erc-reuse-buffers
- (get-buffer candidate)
- (or target
+ (or (not (get-buffer candidate))
+ (or target
+ (with-current-buffer (get-buffer candidate)
+ (and (erc-server-buffer-p)
+ (not (erc-server-process-alive)))))
(with-current-buffer (get-buffer candidate)
- (and (erc-server-buffer-p)
- (not (erc-server-process-alive)))))
- (with-current-buffer (get-buffer candidate)
- (and (string= erc-session-server server)
- (erc-port-equal erc-session-port port))))
+ (and (string= erc-session-server server)
+ (erc-port-equal erc-session-port port)))))
(setq buffer-name candidate)))
;; if buffer-name is unset, neither candidate worked out for us,
;; fallback to the old <N> uniquification method:
- (or buffer-name (generate-new-buffer-name buf-name)) ))
+ (or buffer-name (generate-new-buffer-name (concat buf-name "/" server)))))
(defun erc-get-buffer-create (server port target)
"Create a new buffer based on the arguments."
@@ -1924,15 +1956,15 @@ removed from the list will be disabled."
(defun erc-setup-buffer (buffer)
"Consults `erc-join-buffer' to find out how to display `BUFFER'."
(pcase erc-join-buffer
- (`window
+ ('window
(if (active-minibuffer-window)
(display-buffer buffer)
(switch-to-buffer-other-window buffer)))
- (`window-noselect
+ ('window-noselect
(display-buffer buffer))
- (`bury
+ ('bury
nil)
- (`frame
+ ('frame
(when (or (not erc-reuse-frames)
(not (get-buffer-window buffer t)))
(let ((frame (make-frame (or erc-frame-alist
@@ -2411,11 +2443,7 @@ If STRING is nil, the function does nothing."
((null (car elt)) ; (nil PROPERTY VALUE BEG . END)
(let ((cons (nthcdr 3 elt)))
(cl-incf (car cons) shift)
- (cl-incf (cdr cons) shift)))
- ((and (featurep 'xemacs)
- (extentp (car elt))) ; (EXTENT START END)
- (cl-incf (nth 1 elt) shift)
- (cl-incf (nth 2 elt) shift)))
+ (cl-incf (cdr cons) shift))))
(setq list (cdr list))))))
(defvar erc-valid-nick-regexp "[]a-zA-Z^[;\\`_{}|][]^[;\\`_{}|a-zA-Z0-9-]*"
@@ -2506,10 +2534,7 @@ Returns NICK unmodified unless `erc-lurker-trim-nicks' is
non-nil."
(if erc-lurker-trim-nicks
(replace-regexp-in-string
- (format "[%s]"
- (mapconcat (lambda (char)
- (regexp-quote (char-to-string char)))
- erc-lurker-ignore-chars ""))
+ (regexp-opt-charset (string-to-list erc-lurker-ignore-chars))
"" nick)
nick))
@@ -2549,10 +2574,8 @@ consumption for long-lived IRC or Emacs sessions."
(maphash
(lambda (nick last-PRIVMSG-time)
(when
- (> (float-time (time-subtract
- (current-time)
- last-PRIVMSG-time))
- erc-lurker-threshold-time)
+ (time-less-p erc-lurker-threshold-time
+ (time-since last-PRIVMSG-time))
(remhash nick hash)))
hash)
(if (zerop (hash-table-count hash))
@@ -2617,9 +2640,8 @@ server within `erc-lurker-threshold-time'. See also
(gethash (erc-lurker-maybe-trim nick)
(gethash server erc-lurker-state (make-hash-table)))))
(or (null last-PRIVMSG-time)
- (> (float-time
- (time-subtract (current-time) last-PRIVMSG-time))
- erc-lurker-threshold-time))))
+ (time-less-p erc-lurker-threshold-time
+ (time-since last-PRIVMSG-time)))))
(defcustom erc-common-server-suffixes
'(("openprojects.net\\'" . "OPN")
@@ -3398,7 +3420,7 @@ Otherwise leave the channel indicated by LINE."
(defun erc-cmd-PING (recipient)
"Ping RECIPIENT."
- (let ((time (format "%f" (erc-current-time))))
+ (let ((time (format-time-string "%s.%6N")))
(erc-log (format "cmd: PING: %s" time))
(erc-cmd-CTCP recipient "PING" time)))
@@ -3472,7 +3494,6 @@ If S is non-nil, it will be used as the quit reason."
(defun erc-quit-reason-various (s)
"Choose a quit reason based on S (a string)."
- (when (featurep 'xemacs) (require 'poe))
(let ((res (car (assoc-default (or s "")
erc-quit-reason-various-alist 'string-match))))
(cond
@@ -3500,7 +3521,6 @@ If S is non-nil, it will be used as the quit reason."
(defun erc-part-reason-various (s)
"Choose a part reason based on S (a string)."
- (when (featurep 'xemacs) (require 'poe))
(let ((res (car (assoc-default (or s "")
erc-part-reason-various-alist 'string-match))))
(cond
@@ -3601,8 +3621,7 @@ the message given by REASON."
(defun erc-cmd-SV ()
"Say the current ERC and Emacs version into channel."
- (erc-send-message (format "I'm using ERC with %s %s (%s%s)%s."
- (if (featurep 'xemacs) "XEmacs" "GNU Emacs")
+ (erc-send-message (format "I'm using ERC with GNU Emacs %s (%s%s)%s."
emacs-version
system-configuration
(concat
@@ -3677,8 +3696,10 @@ be displayed."
((string-match "^\\s-*\\([&#+!]\\S-+\\)\\s-\\(.*\\)$" topic)
(let ((ch (match-string 1 topic))
(topic (match-string 2 topic)))
- (erc-log (format "cmd: TOPIC [%s]: %s" ch topic))
- (erc-server-send (format "TOPIC %s :%s" ch topic) nil ch))
+ ;; Ignore all-whitespace topics.
+ (unless (equal (string-trim topic) "")
+ (erc-log (format "cmd: TOPIC [%s]: %s" ch topic))
+ (erc-server-send (format "TOPIC %s :%s" ch topic) nil ch)))
t)
;; /topic #channel
((string-match "^\\s-*\\([&#+!]\\S-+\\)" topic)
@@ -3941,9 +3962,7 @@ If FACE is non-nil, it will be used to propertize the prompt. If it is nil,
(let ((minibuffer-allow-text-properties t)
(read-map minibuffer-local-map))
(insert (read-from-minibuffer "Message: "
- (string (if (featurep 'xemacs)
- last-command-char
- last-command-event))
+ (string last-command-event)
read-map))
(erc-send-current-line)))
@@ -4270,7 +4289,7 @@ and as second argument the event parsed as a vector."
(defun erc-is-message-ctcp-and-not-action-p (message)
"Check if MESSAGE is a CTCP message or not."
(and (erc-is-message-ctcp-p message)
- (not (string-match "^\C-a\\ACTION.*\C-a$" message))))
+ (not (string-match "^\C-aACTION.*\C-a$" message))))
(defun erc-format-privmessage (nick msg privp msgp)
"Format a PRIVMSG in an insertable fashion."
@@ -4476,7 +4495,7 @@ See also: `erc-echo-notice-in-user-buffers',
(mapcar #'upcase
(cdr (split-string mode)))))
erc-channel-banlist)))
- ((string-match "^+" mode)
+ ((string-match "^\\+" mode)
;; Add the banned mask(s) to the ban list
(mapc
(lambda (mask)
@@ -4624,7 +4643,7 @@ See also `erc-display-message'."
(user-full-name)
(user-login-name)
(system-name))))
- (ns (erc-time-diff erc-server-last-sent-time (erc-current-time))))
+ (ns (erc-time-diff erc-server-last-sent-time nil)))
(when (> ns 0)
(setq s (concat s " Idle for " (erc-sec-to-time ns))))
(erc-send-ctcp-notice nick s)))
@@ -4713,8 +4732,7 @@ See also `erc-display-message'."
nil
(let ((time (match-string 1 msg)))
(condition-case nil
- (let ((delta (erc-time-diff (string-to-number time)
- (erc-current-time))))
+ (let ((delta (erc-time-diff (string-to-number time) nil)))
(erc-display-message
nil 'notice 'active
'CTCP-PING ?n nick
@@ -4772,10 +4790,7 @@ If non-nil, return from being away."
(erc-default-target)
(if away-time
(format "is back (gone for %s)"
- (erc-sec-to-time
- (erc-time-diff
- (erc-emacs-time-to-erc-time away-time)
- (erc-current-time))))
+ (erc-sec-to-time (erc-time-diff away-time nil)))
"is back")))))))))
(erc-update-mode-line)))
@@ -5367,10 +5382,10 @@ submitted line to be intentional."
(defun erc-send-current-line ()
"Parse current line and send it to IRC."
(interactive)
- (let ((now (float-time)))
+ (let ((now (current-time)))
(if (or (not erc-accidental-paste-threshold-seconds)
- (< erc-accidental-paste-threshold-seconds
- (- now erc-last-input-time)))
+ (time-less-p erc-accidental-paste-threshold-seconds
+ (time-subtract now erc-last-input-time)))
(save-restriction
(widen)
(if (< (point) (erc-beg-of-input-line))
@@ -5416,6 +5431,9 @@ submitted line to be intentional."
(defvar erc-command-regexp "^/\\([A-Za-z']+\\)\\(\\s-+.*\\|\\s-*\\)$"
"Regular expression used for matching commands in ERC.")
+(cl-defstruct erc-input
+ string insertp sendp)
+
(defun erc-send-input (input)
"Treat INPUT as typed in by the user. It is assumed that the input
and the prompt is already deleted.
@@ -5431,27 +5449,46 @@ This returns non-nil only if we actually send anything."
(beep))
nil)
(t
- (defvar str) ;; FIXME: Make it obey the "erc-" prefix convention.
+ ;; This dynamic variable is used by `erc-send-pre-hook'. It's
+ ;; obsolete, and when it's finally removed, this binding should
+ ;; also be removed.
+ (with-suppressed-warnings ((lexical str))
+ (defvar str))
(let ((str input)
- (erc-insert-this t))
- (setq erc-send-this t)
+ (erc-insert-this t)
+ (erc-send-this t)
+ state)
+ ;; The calling convention of `erc-send-pre-hook' is that it
+ ;; should change the dynamic variable `str' or set
+ ;; `erc-send-this' to nil. This has now been deprecated:
+ ;; Instead `erc-pre-send-functions' is used as a filter to do
+ ;; allow both changing and suppressing the string.
(run-hook-with-args 'erc-send-pre-hook input)
- (when erc-send-this
- (if (or (string-match "\n" str)
- (not (string-match erc-command-regexp str)))
- (mapc
- (lambda (line)
- (mapc
- (lambda (line)
- ;; Insert what has to be inserted for this.
- (erc-display-msg line)
- (erc-process-input-line (concat line "\n")
- (null erc-flood-protect) t))
- (or (and erc-flood-protect (erc-split-line line))
- (list line))))
- (split-string str "\n"))
- (erc-process-input-line (concat str "\n") t nil))
- t)))))
+ (setq state (make-erc-input :string str
+ :insertp erc-insert-this
+ :sendp erc-send-this))
+ (dolist (func erc-pre-send-functions)
+ ;; The functions can return nil to inhibit sending.
+ (funcall func state))
+ (when (and (erc-input-sendp state)
+ erc-send-this)
+ (let ((string (erc-input-string state)))
+ (if (or (string-match "\n" string)
+ (not (string-match erc-command-regexp string)))
+ (mapc
+ (lambda (line)
+ (mapc
+ (lambda (line)
+ ;; Insert what has to be inserted for this.
+ (when (erc-input-insertp state)
+ (erc-display-msg line))
+ (erc-process-input-line (concat line "\n")
+ (null erc-flood-protect) t))
+ (or (and erc-flood-protect (erc-split-line line))
+ (list line))))
+ (split-string string "\n"))
+ (erc-process-input-line (concat string "\n") t nil))
+ t))))))
(defun erc-display-command (line)
(when erc-insert-this
@@ -5720,8 +5757,6 @@ If \"l\" is pressed, `erc-set-channel-limit' gets called.
If \"k\" is pressed, `erc-set-channel-key' gets called.
Anything else will be sent to `erc-toggle-channel-mode'."
(interactive "kChannel mode (RET to set more than one): ")
- (when (featurep 'xemacs)
- (setq key (char-to-string (event-to-character (aref key 0)))))
(cond ((equal key "\C-g")
(keyboard-quit))
((equal key "\C-m")
@@ -6020,23 +6055,20 @@ non-nil value is found.
;; time routines
-(defun erc-string-to-emacs-time (string)
- "Convert the long number represented by STRING into an Emacs format.
-Returns a list of the form (HIGH LOW), compatible with Emacs time format."
- (let* ((n (string-to-number (concat string ".0"))))
- (list (truncate (/ n 65536))
- (truncate (mod n 65536)))))
+(define-obsolete-function-alias 'erc-string-to-emacs-time 'string-to-number
+ "27.1")
(defalias 'erc-emacs-time-to-erc-time 'float-time)
(defalias 'erc-current-time 'float-time)
(defun erc-time-diff (t1 t2)
- "Return the time difference in seconds between T1 and T2."
- (abs (- t2 t1)))
+ "Return the absolute value of the difference in seconds between T1 and T2."
+ (abs (float-time (time-subtract t1 t2))))
(defun erc-time-gt (t1 t2)
"Check whether T1 > T2."
- (> t1 t2))
+ (declare (obsolete time-less-p "27.1"))
+ (time-less-p t2 t1))
(defun erc-sec-to-time (ns)
"Convert NS to a time string HH:MM.SS."
@@ -6368,14 +6400,9 @@ if `erc-away' is non-nil."
(funcall erc-header-line-face-method))
(t
'erc-header-line))))
- (cond ((featurep 'xemacs)
- (setq modeline-buffer-identification
- (list (format-spec erc-mode-line-format spec)))
- (setq modeline-process (list process-status)))
- (t
- (setq mode-line-buffer-identification
- (list (format-spec erc-mode-line-format spec)))
- (setq mode-line-process (list process-status))))
+ (setq mode-line-buffer-identification
+ (list (format-spec erc-mode-line-format spec)))
+ (setq mode-line-process (list process-status))
(when (boundp 'header-line-format)
(let ((header (if erc-header-line-format
(format-spec erc-header-line-format spec)
@@ -6403,9 +6430,7 @@ if `erc-away' is non-nil."
(if face
(erc-propertize header 'face face)
header)))))))
- (if (featurep 'xemacs)
- (redraw-modeline)
- (force-mode-line-update))))
+ (force-mode-line-update)))
(defun erc-update-mode-line (&optional buffer)
"Update the mode line in BUFFER.