other nicks are tried in the list order.
@end defopt
-@defopt erc-format-nick-function
-A function to format a nickname for message display
-
-You can set this to @code{erc-format-@@nick} to display user mode prefix
+@defopt erc-show-speaker-membership-status
+A boolean for including a channel member's @dfn{status prefix} in
+their display name when they speak.
@end defopt
-@example
-(setq erc-format-nick-function 'erc-format-@@nick)
-@end example
-
@defopt erc-nick-uniquifier
The string to append to the nick if it is already in use.
@end defopt
possibly truncated arguments, with the exception of stateful list
modes, like "b".
+** In-buffer "status messages" are now a thing.
+The ancient option 'erc-ensure-target-buffer-on-privmsg' has been
+repurposed slightly to express a third state denoted by the symbol
+'status'. It tells ERC to revert to the old default behavior in which
+separate, "pseudo" target buffers for status-prefixed conversing
+co-existed alongside actual target buffers. Instead of this awkward
+arrangement, ERC now acts like other clients by default and inserts
+so-called "status messages" in situ, right between other messages.
+Similar insertion-routing behavior now also applies to CTCP ACTIONs
+directed at status-prefixed channels. Unfortunately, outgoing "/msg
+@#chan hi" messages aren't yet shown in the same fashion, but the
+groundwork has been laid, making such an addition almost trivial.
+
+** An easier way to see channel-membership prefixes on speakers.
+The option 'erc-format-@nick' has been deprecated in favor of the new
+boolean option 'erc-show-speaker-membership-status', a simple switch
+to enable the displaying of status prefixes on the speaker nicks of
+incoming chat messages. Prefixes on your speaker nick for outgoing
+chat messages continue to always be present.
+
** Miscellaneous UX changes.
Some minor quality-of-life niceties have finally made their way to
ERC. For example, fool visibility has become togglable with the new
been deprecated in favor of 'erc-define-message-format-catalog', a new
macro for defining template "catalogs" at the top level of libraries.
+*** Interface for determining display names renamed.
+The option 'erc-format-nick-function' has been renamed to
+'erc-speaker-from-channel-member-function' to better reflect its
+actual role. So too has the related function 'erc-format-nick', which
+is now 'erc-determine-speaker-from user'.
+
+*** A template-based approach to formatting inserted chat messages.
+Predicting and influencing how ERC formats messages containing a
+leading "<speaker>" has never been straightforward. The characters
+bracketing the speaker and the faces used for each component have
+always been hard-coded, with 'erc-format-query-as-channel-p' being the
+only knob of any consequence. With this release, ERC begins its
+transition to a unified formatting paradigm that builds upon the
+already familiar "language catalog" templating system. Using a
+separate "speaker catalog" keyed by contextual symbols, like
+'query-privmsg', ERC (and eventually everyone) will more easily be
+able to influence how inserted messages take shape in buffers.
+
+*** New format templates for inserted CTCP ACTION messages.
+In 5.5 and earlier, ERC displayed outgoing CTCP ACTION messages in
+'erc-input-face' alone (before buttonizing). Incoming ACTION messages
+mirrored this, except with 'erc-action-face' throughout. Going
+forward, inserted outgoing "/ME" messages will also incorporate
+'erc-action-face', only underneath 'erc-input-face', with
+'erc-my-nick-face' sitting atop both in the leading "speaker" nickname
+portion (again, pre-buttonizing). This new behavior sidesteps the
+traditional format template 'erc-message-english-ACTION' from the
+default "language catalog" in favor of an entry from the new internal
+"speaker catalog". Users needing to access the old behavior can do so
+by toggling a provided compatibility switch. See source code around
+the function 'erc-send-action' for details.
+
*** Miscellaneous changes
Two helper macros from GNU ELPA's Compat library are now available to
third-party modules as 'erc-compat-call' and 'erc-compat-function'.
(declare-function erc-display-server-message "erc" (_proc parsed))
(declare-function erc-emacs-time-to-erc-time "erc" (&optional specified-time))
(declare-function erc-format-message "erc" (msg &rest args))
-(declare-function erc-format-privmessage "erc" (nick msg privp msgp))
(declare-function erc-get-buffer "erc" (target &optional proc))
(declare-function erc-handle-login "erc" nil)
(declare-function erc-handle-user-status-change "erc" (type nlh &optional l))
(declare-function erc-update-mode-line-buffer "erc" (buffer))
(declare-function erc-wash-quit-reason "erc" (reason nick login host))
+(declare-function erc--determine-speaker-message-format-args "erc"
+ (nick target message queryp privmsgp statusmsgp inputp
+ &optional prefix disp-nick))
(declare-function erc-display-message "erc"
(parsed type buffer msg &rest args))
(declare-function erc-get-buffer-create "erc"
?s (if (/= erc-server-lag 1) "s" "")))
(erc-update-mode-line))))
+(defun erc--statusmsg-target (target)
+ "Return actual target from given TARGET if it has a leading prefix char."
+ (and-let* ((erc-ensure-target-buffer-on-privmsg)
+ ((not (eq erc-ensure-target-buffer-on-privmsg 'status)))
+ ((not (erc-channel-p target)))
+ (chars (erc--get-isupport-entry 'STATUSMSG 'single))
+ ((string-search (string (aref target 0)) chars))
+ (trimmed (substring target 1))
+ ((erc-channel-p trimmed)))
+ trimmed))
+
+;; Moved to this file from erc.el in ERC 5.6.
+(defvar-local erc-current-message-catalog 'english
+ "Current language or context catalog for formatting inserted messages.
+See `erc-format-message'.")
+
+;; This variable can be made public if the current design proves
+;; sufficient.
+(defvar erc--message-speaker-catalog '-speaker
+ "The \"speaker\" catalog symbol used to format PRIVMSGs and NOTICEs.
+
+This symbol defines a \"catalog\" of variables and functions
+whose names reflect their membership via a corresponding CATALOG
+component, as in \"erc-message-CATALOG-KEY\". Here, KEY refers
+to a common set of interface members (variables or functions),
+that an implementer must define:
+
+- `statusmsg' and `statusmsg-input': PRIVMSGs whose target is a
+ status-prefixed channel; the latter is the \"echoed\" version
+
+- `chan-privmsg', `query-privmsg', `chan-notice', `query-notice':
+ standard chat messages traditionally prefixed by a <nickname>
+ indicating the message's \"speaker\"
+
+- `input-chan-privmsg', `input-query-privmsg', `input-query-notice',
+ `input-chan-notice': \"echoed\" versions of the above
+
+- `ctcp-action', `ctcp-action-input', `ctcp-action-statusmsg',
+ `ctcp-action-statusmsg-input': \"CTCP ACTION\" versions of the
+ above
+
+The other part of this interface is the per-key collection of
+`format-spec' parameters members must support. For simplicity,
+this catalog currently defines a common set for all keys, some of
+which may be assigned the empty string when not applicable:
+
+ %n - nickname
+ %m - message body
+ %p - nickname's status prefix (when applicable)
+ %s - current target's STATUSMSG prefix (when applicable)
+
+As an added means of communicating with various modules, if this
+catalog's symbol has the property `erc--msg-prop-overrides',
+consumers calling `erc-display-message' will see the value added
+to the `erc--msg-props' \"environment\" in modification hooks,
+like `erc-insert-modify-hook'.")
+
+(defvar erc--speaker-status-prefix-wanted-p (gensym "erc-")
+ "Sentinel to detect whether `erc-format-@nick' has just run.")
+
(define-erc-response-handler (PRIVMSG NOTICE)
"Handle private messages, including messages in channels." nil
(let ((sender-spec (erc-response.sender parsed))
(msgp (string= cmd "PRIVMSG"))
(noticep (string= cmd "NOTICE"))
;; S.B. downcase *both* tgt and current nick
- (privp (erc-current-nick-p tgt))
+ (medown (erc-downcase (erc-current-nick)))
+ (inputp (string= medown (erc-downcase nick)))
+ (privp (string= (erc-downcase tgt) medown))
(erc--display-context `((erc-buffer-display . ,(intern cmd))
,@erc--display-context))
- (erc--msg-prop-overrides `((erc--msg . msg)
- ,@erc--msg-prop-overrides))
- s buffer
+ (erc--msg-prop-overrides `((erc--tmp) ,@erc--msg-prop-overrides))
+ (erc--speaker-status-prefix-wanted-p nil)
+ (erc-current-message-catalog erc--message-speaker-catalog)
+ s buffer statusmsg cmem-prefix
fnick)
(setq buffer (erc-get-buffer (if privp nick tgt) proc))
;; Even worth checking for empty target here? (invalid anyway)
(push `(erc-receive-query-display . ,(intern cmd))
erc--display-context)
(setq buffer (erc--open-target nick)))
- ;; A channel buffer has been killed but is still joined.
- (when erc-ensure-target-buffer-on-privmsg
- (setq buffer (erc--open-target tgt)))))
+ (cond
+ ;; Target is a channel and contains leading @+ chars.
+ ((and-let* ((trimmed(erc--statusmsg-target tgt)))
+ (setq buffer (erc-get-buffer trimmed proc)
+ statusmsg (and buffer (substring tgt 0 1)))))
+ ;; A channel buffer has been killed but is still joined.
+ (erc-ensure-target-buffer-on-privmsg
+ (setq buffer (erc--open-target tgt))))))
(when buffer
(with-current-buffer buffer
(when privp (erc--unhide-prompt))
privp nil nil nil nil nil host login nil nil t)
(defvar erc--cmem-from-nick-function)
(defvar erc-format-nick-function)
+ (defvar erc-show-speaker-membership-status)
+ (defvar erc-speaker-from-channel-member-function)
(let ((cdata (funcall erc--cmem-from-nick-function
(erc-downcase nick) sndr parsed)))
- (setq fnick (funcall erc-format-nick-function
- (car cdata) (cdr cdata))))))
+ (setq fnick (funcall erc-speaker-from-channel-member-function
+ (car cdata) (cdr cdata))
+ cmem-prefix (and (or erc--speaker-status-prefix-wanted-p
+ erc-show-speaker-membership-status
+ inputp)
+ (cdr cdata))))))
(cond
((erc-is-message-ctcp-p msg)
- (setq s (if msgp
+ ;; FIXME explain undefined return values being assigned to `s'.
+ (setq s (if-let ((parsed
+ (erc--ctcp-response-from-parsed
+ :parsed parsed :buffer buffer :statusmsg statusmsg
+ :prefix cmem-prefix :dispname fnick))
+ (msgp))
(erc-process-ctcp-query proc parsed nick login host)
(erc-process-ctcp-reply proc parsed nick login host
(match-string 1 msg)))))
(t
(setq erc-server-last-peers (cons nick (cdr erc-server-last-peers)))
- (defvar erc-format-query-as-channel-p)
- (setq s (erc-format-privmessage
- (or fnick nick) msg
- ;; If buffer is a query buffer,
- ;; format the nick as for a channel.
- (and (not (and buffer
- (erc-query-buffer-p buffer)
- erc-format-query-as-channel-p))
- privp)
- msgp))))
+ (with-current-buffer (or buffer (current-buffer))
+ ;; Re-bind in case either buffer has a local value.
+ (let ((erc-current-message-catalog erc--message-speaker-catalog))
+ (setq s (erc--determine-speaker-message-format-args
+ nick msg privp msgp inputp statusmsg
+ cmem-prefix fnick))))))
(when s
(if (and noticep privp)
(progn
+ (push (cons 'erc--msg (car s)) erc--msg-prop-overrides)
+ (setq s (apply #'erc-format-message s))
(run-hook-with-args 'erc-echo-notice-always-hook
s parsed buffer nick)
(run-hook-with-args-until-success
'erc-echo-notice-hook s parsed buffer nick))
- (erc-display-message parsed nil buffer s)))))))
+ (apply #'erc-display-message parsed nil buffer
+ (ensure-list s))))))))
(define-erc-response-handler (QUIT)
"Another user has quit IRC." nil
(contents "" :type string)
(tags '() :type list))
+(cl-defstruct (erc--ctcp-response
+ (:include erc-response)
+ (:constructor
+ erc--ctcp-response-from-parsed
+ (&key parsed buffer statusmsg prefix dispname
+ &aux (unparsed (erc-response.unparsed parsed))
+ (sender (erc-response.sender parsed))
+ (command (erc-response.command parsed))
+ (command-args (erc-response.command-args parsed))
+ (contents (erc-response.contents parsed))
+ (tags (erc-response.tags parsed)))))
+ "Data for a processed CTCP query or reply."
+ (buffer nil :type (or buffer null))
+ (statusmsg nil :type (or null string))
+ (prefix nil :type (or erc-channel-user null))
+ (dispname nil :type (or string null)))
+
(cl-defstruct erc--isupport-data
"Abstract \"class\" for parsed ISUPPORT data.
For use with the macro `erc--with-isupport-data'."
(defun erc-dcc-chat-parse-output (proc str)
(save-match-data
(let ((posn 0)
+ (erc--msg-prop-overrides `((erc--spkr . ,erc-dcc-from)))
+ (nick (propertize (erc--speakerize-nick erc-dcc-from)
+ 'font-lock-face 'erc-nick-default-face))
line)
(while (string-match "\n" str posn)
(setq line (substring str posn (match-beginning 0)))
(setq posn (match-end 0))
(erc-display-message
nil nil proc
- 'dcc-chat-privmsg ?n (propertize erc-dcc-from 'font-lock-face
- 'erc-nick-default-face) ?m line))
+ 'dcc-chat-privmsg ?n nick ?m line))
(setq erc-dcc-unprocessed-output (substring str posn)))))
(defun erc-dcc-chat-buffer-killed ()
variable can be converted to a public one if needed by third
parties.")
-(defvar-local erc-fill--wrap-last-msg nil)
-(defvar erc-fill--wrap-max-lull (* 24 60 60))
+(defvar-local erc-fill--wrap-last-msg nil "Marker for merging speakers.")
+(defvar erc-fill--wrap-max-lull (* 24 60 60) "Max secs for merging speakers.")
(defun erc-fill--wrap-continued-message-p ()
"Return non-nil when the current speaker hasn't changed.
-That is, indicate whether the text just inserted is from the same
-sender as that of the previous \"PRIVMSG\". As a side effect,
-advance `erc-fill--wrap-last-msg' unless the message has been
-marked as being ephemeral."
- (and
- (not (erc--check-msg-prop 'erc--ephemeral))
- (progn ; preserve blame for now, unprogn on next major change
- (prog1
- (and-let*
- ((m (or erc-fill--wrap-last-msg
- (setq erc-fill--wrap-last-msg (point-min-marker))
- nil))
- ((< (1+ (point-min)) (- (point) 2)))
- (props (save-restriction
- (widen)
- (and-let*
- ((speaker (get-text-property m 'erc--spkr))
- ((not (eq (get-text-property m 'erc--ctcp)
- 'ACTION)))
- ((not (invisible-p m))))
- (cons (get-text-property m 'erc--ts) speaker))))
- (ts (pop props))
- (props)
- ((not (time-less-p (erc-stamp--current-time) ts)))
- ((time-less-p (time-subtract (erc-stamp--current-time) ts)
- erc-fill--wrap-max-lull))
- ;; Assume presence of leading angle bracket or hyphen.
- (nick (erc--check-msg-prop 'erc--spkr))
- ((not (erc--check-msg-prop 'erc--ctcp 'ACTION)))
- ((erc-nick-equal-p props nick))))
- (set-marker erc-fill--wrap-last-msg (point-min))))))
+But only if the `erc--msg' text property also hasn't. That is,
+indicate whether the chat message just inserted is from the same
+person as the prior one and is formatted in the same manner. As
+a side effect, advance `erc-fill--wrap-last-msg' unless the
+message has been marked `erc--ephemeral'."
+ (and-let*
+ (((not (erc--check-msg-prop 'erc--ephemeral)))
+ ;; Always set/move `erc-fill--wrap-last-msg' from here on down.
+ (m (or (and erc-fill--wrap-last-msg
+ (prog1 (marker-position erc-fill--wrap-last-msg)
+ (set-marker erc-fill--wrap-last-msg (point-min))))
+ (ignore (setq erc-fill--wrap-last-msg (point-min-marker)))))
+ ((>= (point) 4)) ; skip the first message
+ (props (save-restriction
+ (widen)
+ (and-let* ((speaker (get-text-property m 'erc--spkr))
+ (type (get-text-property m 'erc--msg))
+ ((not (invisible-p m))))
+ (list (get-text-property m 'erc--ts) type speaker))))
+ (ts (nth 0 props))
+ (type (nth 1 props))
+ (speaker (nth 2 props))
+ ((not (time-less-p (erc-stamp--current-time) ts)))
+ ((time-less-p (time-subtract (erc-stamp--current-time) ts)
+ erc-fill--wrap-max-lull))
+ ((erc--check-msg-prop 'erc--msg type))
+ ((erc-nick-equal-p speaker (erc--check-msg-prop 'erc--spkr))))))
(defun erc-fill--wrap-measure (beg end)
"Return display spec width for inserted region between BEG and END.
((equal "" dval)))
(remove-text-properties
dbeg (text-property-not-all dbeg end 'display dval) '(display)))
- (let* ((pos (if (eq 'date-left (get-text-property beg 'erc-stamp-type))
- (field-beginning beg)
+ ;; This "should" work w/o `front-sticky' and `rear-nonsticky'.
+ (let* ((pos (if-let (((eq 'erc-timestamp (field-at-pos beg)))
+ (b (field-beginning beg))
+ ((eq 'datestamp (get-text-property b 'erc--msg))))
+ b
beg))
(erc--msg-props (map-into (text-properties-at pos) 'hash-table))
(erc-stamp--current-time (gethash 'erc--ts erc--msg-props)))
value of t means the option's value doesn't require trimming.")
(defun erc-stamp--propertize-left-date-stamp ()
- (add-text-properties (point-min) (1- (point-max))
- '(field erc-timestamp erc-stamp-type date-left))
+ (add-text-properties (point-min) (1- (point-max)) '(field erc-timestamp))
(erc--hide-message 'timestamp)
(run-hooks 'erc-stamp--insert-date-hook))
their markers accordingly. The following properties have meaning
as of ERC 5.6:
- - `erc--msg': a symbol, guaranteed present; values include:
- `msg', signifying a `PRIVMSG' or an incoming `NOTICE';
- `unknown', a fallback for `erc-display-message'; a catalog
- key, such as `s401' or `finished'; an `erc-display-message'
- TYPE parameter, like `notice'
+ - `erc--msg': a symbol, guaranteed present; possible values
+ include `unknown', a fallback used by `erc-display-message'; a
+ catalog key, such as `s401' or `finished'; an
+ `erc-display-message' TYPE parameter, like `notice'
- `erc--cmd': a message's associated IRC command, as read by
`erc--get-eq-comparable-cmd'; currently either a symbol, like
"Send STRING to TARGET, possibly immediately, with FORCE."
(erc-send-ctcp-message target (format "ACTION %s" string) force))
+(defvar erc--use-language-catalog-for-ctcp-action-p nil
+ "When non-nil, use `ACTION' entry from language catalog for /ME's.
+Otherwise, use `ctcp-action' or `ctcp-action-input' from the
+internal `-speaker' catalog. This is an escape hatch to restore
+pre-5.6 behavior for the `font-lock-face' property of incoming
+and outgoing \"CTCP ACTION\" messages, whose pre-buttonized state
+was a single interval of `erc-input-face' or `erc-action-face'.
+Newer modules, like `fill-wrap' and `nicks', are incompatible with
+this format style. If you use this, please ask ERC to expose it
+as a public variable via \\[erc-bug] or similar.")
+
(defun erc--send-action-display (string)
- "Display STRING as an outgoing \"CTCP ACTION\" message."
+ "Display STRING as an outgoing \"CTCP ACTION\" message.
+Propertize the message according to the compatibility flag
+`erc--use-language-catalog-for-ctcp-action-p'."
;; Allow hooks acting on inserted PRIVMSG and NOTICES to process us.
- (defvar erc--merge-prop-behind-p)
- (let* ((nick (erc-current-nick))
- (erc--msg-prop-overrides `((erc--msg . msg)
- (erc--ctcp . ACTION)
- (erc--spkr . ,nick)
- ,@erc--msg-prop-overrides))
- (erc--merge-prop-behind-p t))
- (setq nick (propertize nick 'erc--speaker nick
- 'font-lock-face 'erc-my-nick-face))
- (erc-display-message nil '(t input action) (current-buffer)
- 'ACTION ?n nick ?a string ?u "" ?h "")))
+ (let ((erc--msg-prop-overrides `((erc--ctcp . ACTION)
+ ,@erc--msg-prop-overrides))
+ (nick (erc-current-nick)))
+ (if erc--use-language-catalog-for-ctcp-action-p
+ (progn (erc--ensure-spkr-prop nick)
+ (erc-display-message nil 'input (current-buffer) 'ACTION
+ ?n (propertize nick 'erc--speaker nick)
+ ?a string ?u "" ?h ""))
+ (let ((erc-current-message-catalog erc--message-speaker-catalog))
+ (erc-display-message nil nil (current-buffer) 'ctcp-action-input
+ ?p (erc-get-channel-membership-prefix nick)
+ ?n (erc--speakerize-nick nick) ?m string)))))
(defun erc--send-action (target string force)
"Display STRING, then send to TARGET as a \"CTCP ACTION\" message."
;; Display interface
-(defun erc--ensure-spkr-prop (nick)
- "Maybe add NICK to `erc--msg-props' or `erc--msg-prop-overrides'."
- (cond (erc--msg-props (puthash 'erc--spkr nick erc--msg-props))
+(defun erc--ensure-spkr-prop (nick &optional overrides)
+ "Add NICK as `erc--spkr' to the current \"msg props\" environment.
+Prefer `erc--msg-props' over `erc--msg-prop-overrides' when both
+are available. Also include any members of the alist OVERRIDES,
+when present. Assume NICK itself to be free of any text props,
+and return it."
+ (cond (erc--msg-props
+ (puthash 'erc--spkr nick erc--msg-props)
+ (dolist (entry overrides)
+ (puthash (car entry) (cdr entry) erc--msg-props)))
(erc--msg-prop-overrides
- (push (cons 'erc--spkr nick) erc--msg-prop-overrides))))
+ (setq erc--msg-prop-overrides
+ `((erc--spkr . ,nick) ,@overrides ,@erc--msg-prop-overrides))))
+ nick)
(defun erc-string-invisible-p (string)
"Check whether STRING is invisible or not.
(funcall erc--send-message-nested-function line force)
(erc--send-message-external line force)))
-;; FIXME fully simulate `erc-display-msg'. This doesn't currently add
-;; the correct text properties. For example, the LINE should have
-;; `erc-default-face'.
(defun erc--send-message-external (line force)
- (erc-message "PRIVMSG" (concat (erc-default-target) " " line) force)
- (erc-display-line
- (concat (erc-format-my-nick) line)
- (current-buffer))
+ "Send a \"PRIVMSG\" to the default target with optional FORCE.
+Expect caller to bind `erc-default-recipients' if needing to
+specify a status-prefixed target."
+ ;; Almost like an echoed message, but without the `erc--cmd'.
+ (let* ((erc-current-message-catalog erc--message-speaker-catalog)
+ (target (erc-default-target))
+ (erc--msg-prop-overrides `((erc--tmp) ,@erc--msg-prop-overrides))
+ ;; This util sets the `erc--spkr' property in ^.
+ (trimmed (erc--statusmsg-target target))
+ (stmsgindc (and trimmed (substring target 0 1)))
+ (queryp (and erc--target (not (erc--target-channel-p erc--target))))
+ (args (erc--determine-speaker-message-format-args
+ (erc-current-nick) line queryp 'privmsgp 'inputp
+ stmsgindc 'prefix)))
+ (erc-message "PRIVMSG" (concat target " " line) force)
+ (push (cons 'erc--msg (car args)) erc--msg-prop-overrides)
+ (apply #'erc-display-message nil nil (current-buffer) args))
;; FIXME - treat multiline, run hooks, or remove me?
+ ;; FIXME explain this ^ in more detail or remove.
t)
(defun erc--send-message-nested (input-line force)
rear-nonsticky erc-prompt field front-sticky read-only
;; stamp
cursor-intangible cursor-sensor-functions isearch-open-invisible
- erc-stamp-type
;; match
invisible intangible
;; button
:package-version '(ERC . "5.6") ; revived
:group 'erc-buffers
:group 'erc-query
- :type 'boolean)
+ :type '(choice boolean
+ (choice :tag "Create pseudo queries for STATUSMSGs"
+ status)))
(defcustom erc-format-query-as-channel-p t
"If non-nil, format text from others in a query buffer like in a channel.
'font-lock-face msg-face str)
str))
-(defcustom erc-format-nick-function 'erc-format-nick
- "Function to format a nickname for message display."
+;; The format strings in the following `-speaker' catalog shouldn't
+;; contain any non-protocol words, so they make sense in any language.
+
+(defvar erc--message-speaker-statusmsg
+ #("(%p%n%s) %m"
+ 0 1 (font-lock-face erc-default-face)
+ 1 3 (font-lock-face erc-nick-prefix-face)
+ 3 5 (font-lock-face erc-nick-default-face)
+ 5 7 (font-lock-face erc-notice-face)
+ 7 11 (font-lock-face erc-default-face))
+ "Message template for in-channel status messages.")
+
+(defvar erc--message-speaker-statusmsg-input
+ #("(%p%n%s) %m"
+ 0 1 (font-lock-face erc-default-face)
+ 1 3 (font-lock-face erc-my-nick-prefix-face)
+ 3 5 (font-lock-face erc-my-nick-face)
+ 5 7 (font-lock-face erc-notice-face)
+ 7 8 (font-lock-face erc-default-face)
+ 8 11 (font-lock-face erc-input-face))
+ "Message template for echoed status messages.")
+
+(defvar erc--message-speaker-input-chan-privmsg
+ #("<%p%n> %m"
+ 0 1 (font-lock-face erc-default-face)
+ 1 3 (font-lock-face erc-my-nick-prefix-face)
+ 3 5 (font-lock-face erc-my-nick-face)
+ 5 7 (font-lock-face erc-default-face)
+ 7 9 (font-lock-face erc-input-face))
+ "Message template for prompt input or echoed PRIVMSG from own nick.")
+
+(defvar erc--message-speaker-input-query-privmsg
+ #("*%n* %m"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 3 (font-lock-face erc-my-nick-face)
+ 3 5 (font-lock-face erc-direct-msg-face)
+ 5 7 (font-lock-face erc-input-face))
+ "Message template for prompt input or echoed PRIVMSG query from own nick.")
+
+(defvar erc--message-speaker-input-query-notice
+ #("-%n- %m"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 3 (font-lock-face erc-my-nick-face)
+ 3 5 (font-lock-face erc-direct-msg-face)
+ 5 7 (font-lock-face erc-input-face))
+ "Message template for echoed or spoofed query NOTICE from own nick.")
+
+(defvar erc--message-speaker-input-chan-notice
+ #("-%p%n- %m"
+ 0 1 (font-lock-face erc-default-face)
+ 1 3 (font-lock-face erc-my-nick-prefix-face)
+ 3 5 (font-lock-face erc-my-nick-face)
+ 5 7 (font-lock-face erc-default-face)
+ 7 9 (font-lock-face erc-input-face))
+ "Message template for prompt input or echoed NOTICE from own nick.")
+
+(defvar erc--message-speaker-chan-privmsg
+ #("<%p%n> %m"
+ 0 1 (font-lock-face erc-default-face)
+ 1 3 (font-lock-face erc-nick-prefix-face)
+ 3 5 (font-lock-face erc-nick-default-face)
+ 5 9 (font-lock-face erc-default-face))
+ "Message template for a PRIVMSG in a channel.")
+
+(defvar erc--message-speaker-query-privmsg
+ #("*%n* %m"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 3 (font-lock-face erc-nick-msg-face)
+ 3 7 (font-lock-face erc-direct-msg-face))
+ "Message template for a PRIVMSG in query buffer.")
+
+(defvar erc--message-speaker-chan-notice
+ #("-%p%n- %m"
+ 0 1 (font-lock-face erc-default-face)
+ 1 3 (font-lock-face erc-nick-prefix-face)
+ 3 5 (font-lock-face erc-nick-default-face)
+ 5 9 (font-lock-face erc-default-face))
+ "Message template for a NOTICE in a channel.")
+
+(defvar erc--message-speaker-query-notice
+ #("-%n- %m"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 3 (font-lock-face erc-nick-msg-face)
+ 3 7 (font-lock-face erc-direct-msg-face))
+ "Message template for a NOTICE in a query buffer.")
+
+(defvar erc--message-speaker-ctcp-action
+ #("* %p%n %m"
+ 0 2 (font-lock-face erc-action-face)
+ 2 4 (font-lock-face (erc-nick-prefix-face erc-action-face))
+ 4 9 (font-lock-face erc-action-face))
+ "Message template for a CTCP ACTION from another user.")
+
+(defvar erc--message-speaker-ctcp-action-input
+ #("* %p%n %m"
+ 0 2 (font-lock-face #1=(erc-input-face erc-action-face))
+ 2 4 (font-lock-face (erc-my-nick-prefix-face . #1#))
+ 4 6 (font-lock-face (erc-my-nick-face . #1#))
+ 6 9 (font-lock-face #1#))
+ "Message template for a CTCP ACTION from current client.")
+
+(defvar erc--message-speaker-ctcp-action-statusmsg
+ #("* (%p%n%s) %m"
+ 0 3 (font-lock-face erc-action-face)
+ 3 5 (font-lock-face (erc-nick-prefix-face erc-action-face))
+ 5 7 (font-lock-face erc-action-face)
+ 7 9 (font-lock-face (erc-notice-face erc-action-face))
+ 9 13 (font-lock-face erc-action-face))
+ "Template for a CTCP ACTION status message from another chan op.")
+
+(defvar erc--message-speaker-ctcp-action-statusmsg-input
+ #("* (%p%n%s) %m"
+ 0 3 (font-lock-face #1=(erc-input-face erc-action-face))
+ 3 5 (font-lock-face (erc-my-nick-prefix-face . #1#))
+ 5 7 (font-lock-face (erc-my-nick-face . #1#))
+ 7 9 (font-lock-face (erc-notice-face . #1#))
+ 9 13 (font-lock-face #1#))
+ "Template for a CTCP ACTION status message from current client.")
+
+(defun erc--speakerize-nick (nick &optional disp)
+ "Propertize NICK with `erc--speaker' if not already present.
+Do so to DISP instead if it's non-nil. In either case, assign
+NICK, sans properties, as the `erc--speaker' value. As a side
+effect, pair the latter string (the same `eq'-able object) with
+the symbol `erc--spkr' in the \"msg prop\" environment for any
+imminent `erc-display-message' invocations. While doing so,
+include any overrides defined in `erc--message-speaker-catalog'."
+ (let ((plain-nick (substring-no-properties nick)))
+ (erc--ensure-spkr-prop plain-nick (get erc--message-speaker-catalog
+ 'erc--msg-prop-overrides))
+ (if (text-property-not-all 0 (length (or disp nick))
+ 'erc--speaker nil (or disp nick))
+ (or disp nick)
+ (propertize (or disp nick) 'erc--speaker plain-nick))))
+
+(defun erc--determine-speaker-message-format-args
+ (nick message queryp privmsgp inputp &optional statusmsg prefix disp-nick)
+ "Return a list consisting of a \"speaker\"-template key and spec args.
+Consider the three flags QUERYP, PRIVMSGP, and INPUTP, as well as
+the possibly null STATUSMSG string. (Combined, these describe
+the context of a newly arrived \"PRIVMSG\" or, when PRIVMSGP is
+nil, a \"NOTICE\"). Interpret QUERYP to mean that MESSAGE is
+directed at the ERC client itself (a direct message), and INPUTP
+to mean MESSAGE is an outgoing or echoed message originating from
+or meant to simulate prompt input. Interpret a non-nil STATUSMSG
+to mean MESSAGE should be formatted as a special channel message
+intended for privileged members of the same or greater status.
+
+After deciding on the template key for the current \"speaker\"
+catalog, use the remaining arguments, possibly along with
+STATUSMSG, to construct the appropriate spec-args plist forming
+the returned list's tail. In this plist, pair the char ?n with
+NICK, the nickname of the speaker and ?m with MESSAGE, the
+message body. When non-nil, assume DISP-NICK to be a possibly
+phony display name to take the place of NICK for ?n. When PREFIX
+is non-nil, look up NICK's channel-membership status, possibly
+using PREFIX itself if it's an `erc-channel-user' object, which
+it must be when called outside of a channel buffer. Pair the
+result with the ?p specifier. When STATUSMSG is non-nil, pair it
+with the ?s specifier. Ensure unused spec values are the empty
+string rather than nil."
+ (when prefix
+ (setq prefix (erc-get-channel-membership-prefix
+ (if (erc-channel-user-p prefix) prefix nick))))
+ (when (and queryp erc--target erc-format-query-as-channel-p
+ (not (erc--target-channel-p erc--target)))
+ (setq queryp nil))
+ (list (cond (statusmsg (if inputp 'statusmsg-input 'statusmsg))
+ (privmsgp (if queryp
+ (if inputp 'input-query-privmsg 'query-privmsg)
+ (if inputp 'input-chan-privmsg 'chan-privmsg)))
+ (t (if queryp
+ (if inputp 'input-query-notice 'query-notice)
+ (if inputp 'input-chan-notice 'chan-notice))))
+ ?p (or prefix "") ?n (erc--speakerize-nick nick disp-nick)
+ ?s (or statusmsg "") ?m message))
+
+(defcustom erc-show-speaker-membership-status nil
+ "Whether to prefix speakers with their channel status.
+For example, when this option is non-nil and some nick \"Alice\"
+has operator status in the current channel, ERC displays their
+leading \"speaker\" label as <@Alice> instead of <Alice>."
+ :package-version '(ERC . "5.6")
+ :group 'erc-display
+ :type 'boolean)
+
+(define-obsolete-variable-alias 'erc-format-nick-function
+ 'erc-speaker-from-channel-member-function "30.1")
+(defcustom erc-speaker-from-channel-member-function
+ #'erc-determine-speaker-from-user
+ "Function to determine a message's displayed \"speaker\" label.
+Called with an `erc-server-user' object and an `erc-channel-user'
+object, both possibly nil. Use this option to do things like
+provide localized display names. To ask ERC to prepend
+channel-membership \"status\" prefixes, like \"@\", to the
+returned name, see `erc-show-speaker-membership-status'."
+ :package-version '(ERC . "5.6")
:group 'erc-display
- :type 'function)
+ :type '(choice (function-item erc-determine-speaker-from-user) function))
-(defun erc-format-nick (&optional user _channel-data)
- "Return the nickname of USER.
-See also `erc-format-nick-function'."
+(define-obsolete-function-alias 'erc-format-nick
+ #'erc-determine-speaker-from-user "30.1")
+(defun erc-determine-speaker-from-user (&optional user _channel-data)
+ "Return nickname slot of `erc-server-user' USER, when non-nil."
(when user (erc-server-user-nickname user)))
(define-obsolete-function-alias 'erc-get-user-mode-prefix
"Format the nickname of USER showing if USER has a voice, is an
operator, half-op, admin or owner. Owners have \"~\", admins have
\"&\", operators have \"@\" and users with voice have \"+\" as a
-prefix. Use CHANNEL-DATA to determine op and voice status. See
-also `erc-format-nick-function'."
+prefix. Use CHANNEL-DATA to determine op and voice status."
+ (declare (obsolete "see option `erc-show-speaker-membership-status'" "30.1"))
(when user
(let ((nick (erc-server-user-nickname user)))
- (concat (propertize
- (erc-get-channel-membership-prefix channel-data)
- 'font-lock-face 'erc-nick-prefix-face)
- nick))))
+ (if (not erc--speaker-status-prefix-wanted-p)
+ (prog1 nick
+ (setq erc--speaker-status-prefix-wanted-p 'erc-format-@nick))
+ (concat (propertize
+ (erc-get-channel-membership-prefix channel-data)
+ 'font-lock-face 'erc-nick-prefix-face)
+ nick)))))
(defun erc-format-my-nick ()
"Return the beginning of this user's message, correctly propertized."
(concat
(propertize open 'font-lock-face 'erc-default-face)
(propertize mode 'font-lock-face 'erc-my-nick-prefix-face)
- (propertize nick 'font-lock-face 'erc-my-nick-face 'erc--speaker nick)
+ (propertize nick 'erc--speaker nick 'font-lock-face 'erc-my-nick-face)
(propertize close 'font-lock-face 'erc-default-face)))
(let ((prefix "> "))
(propertize prefix 'font-lock-face 'erc-default-face))))
+(defun erc--format-speaker-input-message (message)
+ "Assemble outgoing MESSAGE entered at the prompt for insertion.
+Intend \"input\" to refer to interactive prompt input as well as
+the group of associated message-format templates from the
+\"speaker\" catalog. Format the speaker portion in a manner
+similar to that performed by `erc-format-my-nick', but use either
+`erc--message-speaker-input-chan-privmsg' or
+`erc--message-speaker-input-query-privmsg' as a formatting
+template, with MESSAGE being the actual message body. Return a
+copy with possibly shared text-property values."
+ (if-let ((erc-show-my-nick)
+ (nick (erc-current-nick))
+ (pfx (erc-get-channel-membership-prefix nick))
+ (erc-current-message-catalog erc--message-speaker-catalog)
+ (key (if (or erc-format-query-as-channel-p
+ (erc--target-channel-p erc--target))
+ 'input-chan-privmsg
+ 'input-query-privmsg)))
+ (progn
+ (cond (erc--msg-props (puthash 'erc--msg key erc--msg-props))
+ (erc--msg-prop-overrides (push (cons 'erc--msg key)
+ erc--msg-prop-overrides)))
+ (erc-format-message key ?p pfx ?n (erc--speakerize-nick nick)
+ ?m message))
+ (propertize (concat "> " message) 'font-lock-face 'erc-input-face)))
+
(defun erc-echo-notice-in-default-buffer (s parsed buffer _sender)
"Echo a private notice in the default buffer, namely the
target buffer specified by BUFFER, or there is no target buffer,
(while queries
(let* ((type (upcase (car (split-string (car queries)))))
(hook (intern-soft (concat "erc-ctcp-query-" type "-hook")))
- (erc--msg-prop-overrides `((erc--msg . msg)
- (erc--ctcp . ,(intern type))
+ (erc--msg-prop-overrides `((erc--ctcp . ,(intern type))
,@erc--msg-prop-overrides)))
(if (and hook (boundp hook))
(if (string-equal type "ACTION")
(let ((s (match-string 1 msg))
(buf (or (erc-get-buffer to proc)
(erc-get-buffer nick proc)
- (process-buffer proc))))
- (erc--ensure-spkr-prop nick)
- (setq nick (propertize nick 'erc--speaker nick))
- (erc-display-message
- parsed 'action buf
- 'ACTION ?n nick ?u login ?h host ?a s))))
+ (process-buffer proc)))
+ (selfp (erc-current-nick-p nick)))
+ (if erc--use-language-catalog-for-ctcp-action-p
+ (progn
+ (erc--ensure-spkr-prop nick)
+ (setq nick (propertize nick 'erc--speaker nick))
+ (erc-display-message parsed (if selfp 'input 'action) buf
+ 'ACTION ?n nick ?u login ?h host ?a s))
+ (let* ((obj (and (erc--ctcp-response-p parsed) parsed))
+ (buffer (and obj (erc--ctcp-response-buffer obj)))
+ (stsmsg (and obj (erc--ctcp-response-statusmsg obj)))
+ (prefix (and obj (erc--ctcp-response-prefix obj)))
+ (dispnm (and obj (erc--ctcp-response-dispname obj)))
+ (erc-current-message-catalog erc--message-speaker-catalog))
+ (erc-display-message
+ parsed nil (or buffer buf)
+ (if selfp
+ (if stsmsg 'ctcp-action-statusmsg-input 'ctcp-action-input)
+ (if stsmsg 'ctcp-action-statusmsg 'ctcp-action))
+ ?s (or stsmsg to)
+ ?p (or (and (erc-channel-user-p prefix)
+ (erc-get-channel-membership-prefix prefix))
+ "")
+ ?n (erc--speakerize-nick nick dispnm)
+ ?m s))))))
(defvar erc-ctcp-query-CLIENTINFO-hook '(erc-ctcp-query-CLIENTINFO))
(erc--assert-input-bounds)
(let ((insert-position (marker-position (goto-char erc-insert-marker)))
(erc--msg-props (or erc--msg-props
- (let ((ovs erc--msg-prop-overrides))
+ (let ((ovs (seq-filter
+ #'cdr erc--msg-prop-overrides)))
(map-into `((erc--msg . msg) ,@(reverse ovs))
- 'hash-table))))
- beg)
- (insert (erc-format-my-nick))
- (setq beg (point))
- (insert line)
- (erc-put-text-property beg (point) 'font-lock-face 'erc-input-face)
- (insert "\n")
+ 'hash-table)))))
+ (insert (erc--format-speaker-input-message line) "\n")
(save-restriction
(narrow-to-region insert-position (point))
(run-hooks 'erc-send-modify-hook)
(string-replace "%" "%%" reason))
"")))))
-
-(defvar-local erc-current-message-catalog 'english)
-
(defun erc-retrieve-catalog-entry (key &optional catalog)
"Retrieve `format-spec' entry for symbol KEY in CATALOG.
Without symbol CATALOG, use `erc-current-message-catalog'. If
--- /dev/null
+;;; erc-scenarios-base-statusmsg.el --- statusmsg tests -*- 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/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+ (let ((load-path (cons (ert-resource-directory) load-path)))
+ (require 'erc-scenarios-common)))
+
+(ert-deftest erc-scenarios-base-statusmsg ()
+
+ (erc-scenarios-common-with-cleanup
+ ((erc-scenarios-common-dialog "base/display-message")
+ (dumb-server (erc-d-run "localhost" t 'statusmsg))
+ (erc-autojoin-channels-alist '((foonet "#mine")))
+ (erc-modules (cons 'fill-wrap erc-modules))
+ (port (process-contact dumb-server :service))
+ (erc-show-speaker-membership-status nil)
+ (erc-server-flood-penalty 0.1)
+ (expect (erc-d-t-make-expecter)))
+
+ (ert-info ("Connect")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "tester"
+ :user "tester"
+ :full-name "tester")
+ (funcall expect 5 "This server is in debug mode")))
+
+ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#mine"))
+
+ (ert-info ("Receive status messages unprefixed")
+ (funcall expect 5 "+dummy")
+ (funcall expect 5 "(dummy+) hello")
+ (should (eq 'statusmsg (erc--get-inserted-msg-prop 'erc--msg)))
+ (should (equal "dummy" (erc--get-inserted-msg-prop 'erc--spkr)))
+ (should (eq (get-text-property (1- (point)) 'font-lock-face)
+ 'erc-default-face))
+ (funcall expect 5 "(dummy+) there")
+ (should (equal "" (get-text-property (pos-bol) 'display)))
+
+ ;; CTCP ACTION
+ (funcall expect 5 "* (dummy+) sad")
+ (should (eq 'ctcp-action-statusmsg
+ (erc--get-inserted-msg-prop 'erc--msg)))
+ (should (eq (get-text-property (1- (point)) 'font-lock-face)
+ 'erc-action-face))
+ (funcall expect 5 "* (dummy+) glad")
+ (should (equal "" (get-text-property (pos-bol) 'display))))
+
+ (ert-info ("Send status messages")
+ ;; We don't have `echo-message' yet, so ERC doesn't currently
+ ;; insert commands like "/msg +#mine foo".
+ (let ((erc-default-recipients '("+#mine")))
+ (erc-send-message "howdy"))
+ (funcall expect 5 "(@tester+) howdy")
+ (should (eq 'statusmsg-input (erc--get-inserted-msg-prop 'erc--msg)))
+ (should (equal "tester" (erc--get-inserted-msg-prop 'erc--spkr)))
+ (should (eq (get-text-property (1- (point)) 'font-lock-face)
+ 'erc-input-face))
+ (let ((erc-default-recipients '("+#mine")))
+ (erc-send-message "tenderfoot"))
+ (funcall expect 5 "(@tester+) tenderfoot")
+ (should (equal "" (get-text-property (pos-bol) 'display)))
+
+ ;; Simulate some "echoed" CTCP ACTION messages since we don't
+ ;; actually support that yet.
+ (funcall expect 5 "* (@tester+) mad")
+ (should (eq 'ctcp-action-statusmsg-input
+ (erc--get-inserted-msg-prop 'erc--msg)))
+ (should (equal (get-text-property (1- (point)) 'font-lock-face)
+ '(erc-input-face erc-action-face)))
+ (funcall expect 5 "* (@tester+) chad")
+ (should (equal "" (get-text-property (pos-bol) 'display))))
+
+ (ert-info ("Receive status messages prefixed")
+ (setq erc-show-speaker-membership-status t)
+ (erc-scenarios-common-say "/me ready") ; sync
+ (funcall expect 5 "* @tester ready")
+ (funcall expect 5 "(+dummy+) okie")
+
+ ;; CTCP ACTION
+ (funcall expect 5 "* (+dummy+) dokie")
+ (funcall expect 5 "* +dummy out")))))
+
+;;; erc-scenarios-base-statusmsg.el ends here
(ert-info ("Stamps appear in left margin and are invisible")
(should (eq 'erc-timestamp (field-at-pos (pos-bol))))
(should (= (pos-bol) (field-beginning (pos-bol))))
- (should (eq 'msg (get-text-property (pos-bol) 'erc--msg)))
+ (should (eq 'query-notice (get-text-property (pos-bol) 'erc--msg)))
(should (eq 'NOTICE (get-text-property (pos-bol) 'erc--cmd)))
(should (= ?- (char-after (field-end (pos-bol)))))
(should (equal (get-text-property (1+ (field-end (pos-bol)))
calls
erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
(cl-letf (((symbol-function 'erc-display-message)
- (lambda (_ _ _ line) (push line calls)))
+ (lambda (_ _ _ msg &rest args)
+ (push (apply #'erc-format-message msg args) calls)))
((symbol-function 'erc-server-send)
(lambda (line _) (push line calls)))
((symbol-function 'erc-server-buffer)
(should-not erc-server-last-peers)
(erc-message "PRIVMSG" ". hi")
(should-not erc-server-last-peers)
- (should (eq 'no-target (pop calls)))
+ (should (equal "No target" (pop calls)))
(erc-message "PRIVMSG" ", hi")
(should-not erc-server-last-peers)
(should (string-match "alice :hi" (pop calls)))))
(kill-buffer "ExampleNet")
(kill-buffer "#chan")))
-(ert-deftest erc-format-privmessage ()
- ;; Basic PRIVMSG
- (should (erc-tests--equal-including-properties
- (erc-format-privmessage (copy-sequence "bob")
- (copy-sequence "oh my")
- nil 'msgp)
- #("<bob> oh my"
- 0 1 (font-lock-face erc-default-face)
- 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face)
- 4 11 (font-lock-face erc-default-face))))
-
- ;; Basic NOTICE
- (should (erc-tests--equal-including-properties
- (erc-format-privmessage (copy-sequence "bob")
- (copy-sequence "oh my")
- nil nil)
- #("-bob- oh my"
- 0 1 (font-lock-face erc-default-face)
- 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face)
- 4 11 (font-lock-face erc-default-face))))
-
- ;; Prefixed PRIVMSG
- (let* ((user (make-erc-server-user :nickname (copy-sequence "Bob")))
+;; This is an adapter that uses formatting templates from the
+;; `-speaker' catalog to mimic `erc-format-privmessage', for testing
+;; purposes.
+(defun erc-tests--format-privmessage (nick msg privp msgp &optional inputp pfx)
+ (let ((erc-current-message-catalog erc--message-speaker-catalog))
+ (apply #'erc-format-message
+ (erc--determine-speaker-message-format-args nick msg privp msgp
+ inputp nil pfx))))
+
+;; This asserts that `erc--determine-speaker-message-format-args'
+;; behaves identically to `erc-format-privmessage', the function whose
+;; role it basically replaced.
+(ert-deftest erc--determine-speaker-message-format-args ()
+ ;; Basic PRIVMSG.
+ (let ((expect #("<bob> oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face)
+ 4 11 (font-lock-face erc-default-face)))
+ (args (list (concat "bob") (concat "oh my") nil 'msgp)))
+ (should (erc-tests--equal-including-properties
+ (apply #'erc-format-privmessage args)
+ expect))
+ (should (erc-tests--equal-including-properties
+ (apply #'erc-tests--format-privmessage args)
+ expect)))
+
+ ;; Basic NOTICE.
+ (let ((expect #("-bob- oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face)
+ 4 11 (font-lock-face erc-default-face)))
+ (args (list (copy-sequence "bob") (copy-sequence "oh my") nil nil)))
+ (should (erc-tests--equal-including-properties
+ (apply #'erc-format-privmessage args)
+ expect))
+ (should (erc-tests--equal-including-properties
+ (apply #'erc-tests--format-privmessage args)
+ expect)))
+
+ ;; Status-prefixed PRIVMSG.
+ (let* ((expect
+ #("<@Bob> oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 2 (font-lock-face erc-nick-prefix-face help-echo "operator")
+ 2 5 (erc--speaker "Bob" font-lock-face erc-nick-default-face)
+ 5 12 (font-lock-face erc-default-face)))
+ (user (make-erc-server-user :nickname (copy-sequence "Bob")))
(cuser (make-erc-channel-user :op t))
(erc-channel-users (make-hash-table :test #'equal)))
(puthash "bob" (cons user cuser) erc-channel-users)
+ (with-suppressed-warnings ((obsolete erc-format-@nick))
+ (should (erc-tests--equal-including-properties
+ (erc-format-privmessage (erc-format-@nick user cuser)
+ (copy-sequence "oh my")
+ nil 'msgp)
+ expect)))
+ (let ((nick "Bob")
+ (msg "oh my"))
+ (should (erc-tests--equal-including-properties
+ (erc-tests--format-privmessage nick msg nil 'msgp nil cuser)
+ expect)) ; overloaded on PREFIX arg
+ (should (erc-tests--equal-including-properties
+ (erc-tests--format-privmessage nick msg nil 'msgp nil t)
+ expect))
+ ;; The new version makes a copy instead of adding properties to
+ ;; the input.
+ (should-not
+ (text-property-not-all 0 (length nick) 'font-lock-face nil nick))
+ (should-not
+ (text-property-not-all 0 (length msg) 'font-lock-face nil msg)))))
+
+(ert-deftest erc--determine-speaker-message-format-args/queries-as-channel ()
+ (should erc-format-query-as-channel-p)
+
+ (with-current-buffer (get-buffer-create "bob")
+ (erc-mode)
+ (setq erc--target (erc--target-from-string "alice"))
+
+ (insert "PRIVMSG\n"
+ (erc-tests--format-privmessage "bob" "oh my" 'queryp 'msgp))
+ (should (erc-tests--equal-including-properties
+ #("<bob> oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face)
+ 4 11 (font-lock-face erc-default-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (insert "\nNOTICE\n"
+ (erc-tests--format-privmessage "bob" "oh my" 'queryp nil))
+ (should (erc-tests--equal-including-properties
+ #("-bob- oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face)
+ 4 11 (font-lock-face erc-default-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (insert "\nInput PRIVMSG\n"
+ (erc-tests--format-privmessage "bob" "oh my"
+ 'queryp 'privmsgp 'inputp))
+ (should (erc-tests--equal-including-properties
+ #("<bob> oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face)
+ 4 6 (font-lock-face erc-default-face)
+ 6 11 (font-lock-face erc-input-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (insert "\nInput NOTICE\n"
+ (erc-tests--format-privmessage "bob" "oh my" 'queryp nil 'inputp))
(should (erc-tests--equal-including-properties
- (erc-format-privmessage (erc-format-@nick user cuser)
- (copy-sequence "oh my")
- nil 'msgp)
- #("<@Bob> oh my"
+ #("-bob- oh my"
0 1 (font-lock-face erc-default-face)
- 1 2 (font-lock-face erc-nick-prefix-face help-echo "operator")
- 2 5 (erc--speaker "Bob" font-lock-face erc-nick-default-face)
- 5 12 (font-lock-face erc-default-face))))))
+ 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face)
+ 4 6 (font-lock-face erc-default-face)
+ 6 11 (font-lock-face erc-input-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (when noninteractive (kill-buffer))))
+
+(ert-deftest erc--determine-speaker-message-format-args/queries ()
+ (should erc-format-query-as-channel-p)
+
+ (with-current-buffer (get-buffer-create "bob")
+ (erc-mode)
+ (setq-local erc-format-query-as-channel-p nil)
+ (setq erc--target (erc--target-from-string "alice"))
+
+ (insert "PRIVMSG\n"
+ (erc-tests--format-privmessage "bob" "oh my" 'queryp 'msgp))
+ (should (erc-tests--equal-including-properties
+ #("*bob* oh my"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-nick-msg-face)
+ 4 11 (font-lock-face erc-direct-msg-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (insert "\nNOTICE\n"
+ (erc-tests--format-privmessage "bob" "oh my" 'queryp nil))
+ (should (erc-tests--equal-including-properties
+ #("-bob- oh my"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-nick-msg-face)
+ 4 11 (font-lock-face erc-direct-msg-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (insert "\nInput PRIVMSG\n"
+ (erc-tests--format-privmessage "bob" "oh my"
+ 'queryp 'privmsgp 'inputp))
+ (should (erc-tests--equal-including-properties
+ #("*bob* oh my"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face)
+ 4 6 (font-lock-face erc-direct-msg-face)
+ 6 11 (font-lock-face erc-input-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (insert "\nInput NOTICE\n"
+ (erc-tests--format-privmessage "bob" "oh my" 'queryp nil 'inputp))
+ (should (erc-tests--equal-including-properties
+ #("-bob- oh my"
+ 0 1 (font-lock-face erc-direct-msg-face)
+ 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face)
+ 4 6 (font-lock-face erc-direct-msg-face)
+ 6 11 (font-lock-face erc-input-face))
+ (buffer-substring (pos-bol) (pos-eol))))
+
+ (when noninteractive (kill-buffer))))
+
+(defun erc-tests--format-my-nick (message)
+ (concat (erc-format-my-nick)
+ (propertize message 'font-lock-face 'erc-input-face)))
+
+;; This tests that the default behavior of the replacement formatting
+;; function for prompt input, `erc--format-speaker-input-message'
+;; matches that of the original being replaced, `erc-format-my-nick',
+;; though it only handled the speaker portion.
+(ert-deftest erc--format-speaker-input-message ()
+ ;; No status prefix.
+ (let ((erc-server-current-nick "tester")
+ (expect #("<tester> oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 7 (font-lock-face erc-my-nick-face erc--speaker "tester")
+ 7 9 (font-lock-face erc-default-face)
+ 9 14 (font-lock-face erc-input-face))))
+ (should (equal (erc-tests--format-my-nick "oh my") expect))
+ (should (equal (erc--format-speaker-input-message "oh my") expect)))
+
+ ;; With channel-operator status prefix.
+ (let* ((erc-server-current-nick "tester")
+ (cmem (cons (make-erc-server-user :nickname "tester")
+ (make-erc-channel-user :op t)))
+ (erc-channel-users (map-into (list "tester" cmem)
+ '(hash-table :test equal)))
+ (expect #("<@tester> oh my"
+ 0 1 (font-lock-face erc-default-face)
+ 1 2 (font-lock-face erc-my-nick-prefix-face)
+ 2 5 (font-lock-face erc-my-nick-face erc--speaker "bob")
+ 5 7 (font-lock-face erc-default-face)
+ 7 12 (font-lock-face erc-input-face))))
+ (should (equal (erc-tests--format-my-nick "oh my") expect))
+ (should (equal (erc--format-speaker-input-message "oh my") expect))))
(ert-deftest erc--route-insertion ()
(erc-tests--send-prep)
--- /dev/null
+;; -*- mode: lisp-data; -*-
+((nick 10 "NICK tester"))
+((user 10 "USER tester 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.02 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1")
+ (0.01 ":irc.foonet.org 003 tester :This server was created Thu, 07 Dec 2023 08:04:35 UTC")
+ (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)")
+ (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections")
+ (0.00 ":irc.foonet.org 254 tester 2 :channels formed")
+ (0.00 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers")
+ (0.02 ":irc.foonet.org 265 tester 4 5 :Current local users 4, max 5")
+ (0.00 ":irc.foonet.org 266 tester 4 5 :Current global users 4, max 5")
+ (0.00 ":irc.foonet.org 422 tester :MOTD File is missing")
+ (0.00 ":irc.foonet.org 221 tester +i")
+ (0.00 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
+
+((mode-tester 10 "MODE tester +i"))
+
+((join-mine 10 "JOIN #mine")
+ (0.01 ":irc.foonet.org 221 tester +i")
+ (0.00 ":tester!~u@2jv6nwu4af69s.irc JOIN #mine")
+ (0.02 ":irc.foonet.org 353 tester = #mine :@tester +dummy")
+ (0.01 ":irc.foonet.org 366 tester #mine :End of NAMES list"))
+
+((mode-mine 10 "MODE #mine")
+ (0.00 ":irc.foonet.org 324 tester #mine +Cnt")
+ (0.02 ":irc.foonet.org 329 tester #mine 1702026418")
+ (0.04 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :hello")
+ (0.03 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :there")
+ (0.05 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION sad\1")
+ (0.03 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION glad\1"))
+
+((privmsg-statusmsg 10 "PRIVMSG +#mine :howdy"))
+((privmsg-statusmsg-action 10 "PRIVMSG +#mine :tenderfoot")
+ ;; These are simulated "echoed messages"
+ (0.05 ":tester!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION mad\1")
+ (0.05 ":tester!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION chad\1"))
+
+((privmsg-prefixed 10 "PRIVMSG #mine :\1ACTION ready\1")
+ (0.04 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :okie")
+ (0.05 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION dokie\1")
+ (0.04 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG #mine :\1ACTION out\1"))
-#("\n\n\n[Thu Jan 1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 0)) display #8="") 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #10#) 499 505 (wrap-prefix #1# line-prefix #10#) 506 507 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #8#) 507 510 (wrap-prefix #1# line-prefix #11# display #8#) 510 512 (wrap-prefix #1# line-prefix #11# display #8#) 512 515 (wrap-prefix #1# line-prefix #11#) 516 517 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #12=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #12#) 518 521 (wrap-prefix #1# line-prefix #12#) 521 527 (wrap-prefix #1# line-prefix #12#) 528 529 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #13#) 532 539 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file
+#("\n\n\n[Thu Jan 1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--spkr "alice" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--spkr "bob" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 0)) display #8="") 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #10#) 499 505 (wrap-prefix #1# line-prefix #10#) 506 507 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #8#) 507 510 (wrap-prefix #1# line-prefix #11# display #8#) 510 512 (wrap-prefix #1# line-prefix #11# display #8#) 512 515 (wrap-prefix #1# line-prefix #11#) 516 517 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #12=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #12#) 518 521 (wrap-prefix #1# line-prefix #12#) 521 527 (wrap-prefix #1# line-prefix #12#) 528 529 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #13#) 532 539 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file
-#("\n\n\n[Thu Jan 1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 0)) display #8="") 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #10#) 499 505 (wrap-prefix #1# line-prefix #10#) 505 506 (display #("~\n" 0 2 (font-lock-face shadow))) 506 507 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #8#) 507 510 (wrap-prefix #1# line-prefix #11# display #8#) 510 512 (wrap-prefix #1# line-prefix #11# display #8#) 512 515 (wrap-prefix #1# line-prefix #11#) 516 517 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #12=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #12#) 518 521 (wrap-prefix #1# line-prefix #12#) 521 527 (wrap-prefix #1# line-prefix #12#) 528 529 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #13#) 532 539 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file
+#("\n\n\n[Thu Jan 1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--spkr "alice" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--spkr "bob" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 0)) display #8="") 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #10#) 499 505 (wrap-prefix #1# line-prefix #10#) 505 506 (display #("~\n" 0 2 (font-lock-face shadow))) 506 507 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #8#) 507 510 (wrap-prefix #1# line-prefix #11# display #8#) 510 512 (wrap-prefix #1# line-prefix #11# display #8#) 512 515 (wrap-prefix #1# line-prefix #11#) 516 517 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #12=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #12#) 518 521 (wrap-prefix #1# line-prefix #12#) 521 527 (wrap-prefix #1# line-prefix #12#) 528 529 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #13#) 532 539 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file
-#("\n\n\n[Thu Jan 1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 #10=(2))) display #8=#("> " 0 1 (font-lock-face shadow))) 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #11#) 499 505 (wrap-prefix #1# line-prefix #11#) 506 507 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 27 #10#)) display #8#) 507 510 (wrap-prefix #1# line-prefix #12# display #8#) 510 512 (wrap-prefix #1# line-prefix #12# display #8#) 512 515 (wrap-prefix #1# line-prefix #12#) 516 517 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #13=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #13#) 518 521 (wrap-prefix #1# line-prefix #13#) 521 527 (wrap-prefix #1# line-prefix #13#) 528 529 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #14=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #14#) 532 539 (wrap-prefix #1# line-prefix #14#))
\ No newline at end of file
+#("\n\n\n[Thu Jan 1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--spkr "alice" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--spkr "bob" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 #10=(2))) display #8=#("> " 0 1 (font-lock-face shadow))) 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #11#) 499 505 (wrap-prefix #1# line-prefix #11#) 506 507 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 27 #10#)) display #8#) 507 510 (wrap-prefix #1# line-prefix #12# display #8#) 510 512 (wrap-prefix #1# line-prefix #12# display #8#) 512 515 (wrap-prefix #1# line-prefix #12#) 516 517 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #13=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #13#) 518 521 (wrap-prefix #1# line-prefix #13#) 521 527 (wrap-prefix #1# line-prefix #13#) 528 529 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #14=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #14#) 532 539 (wrap-prefix #1# line-prefix #14#))
\ No newline at end of file