From: "J.P." <jp@neverwas.me>
To: 73798@debbugs.gnu.org
Cc: emacs-erc@gnu.org
Subject: bug#73798: 31.0.50; ERC 5.7: New extensibility focused match API
Date: Thu, 31 Oct 2024 22:22:41 -0700 [thread overview]
Message-ID: <87ldy3v87y.fsf__38041.5920096979$1730438608$gmane$org@neverwas.me> (raw)
In-Reply-To: <87y12rifv2.fsf@neverwas.me> (J. P.'s message of "Sun, 13 Oct 2024 19:21:37 -0700")
"J.P." <jp@neverwas.me> writes:
> For a detailed example showing how to use this API for more involved
> matching that doesn't involve highlighting, see the ‘notifications’
> module, which lives in ‘erc-desktop-notifications.el’. Ignore the parts
> that involve adapting the global setup (and teardown) business to a
> buffer-local context. Since your module is declared ‘local’, as per the
> modern convention, you won't be needing such code, so feel free to use
> utility functions like ‘erc-match-add-local-type’ directly in your
> module's definition.
Actually, I'm not so sure it's wise to hold up the `notifications'
module as a shining example of how to use this API. That's because it
currently suffers from some potentially confusing quirks, some of which
I'd like to address using internal functions that aren't really meant
for third parties.
> Here, the user leverages a handy subtype of ‘erc-match’, called
> ‘erc-match-opt-keyword’, which actually descends directly from another,
> intermediate ‘erc-match’ type:
>
> -- Struct: erc-match-traditional category face data part
>
> Use this type or one of its descendants (see below) if you want
> ‘erc-text-matched-hook’ to run alongside (after) the ‘handler’
> slot's default highlighter, ‘erc-match-highlight’, on every match
> for which the ‘category’ slot's value is non-‘nil’ (it becomes the
> argument provided for the hook's MATCH-TYPE parameter).
>
> Much more important, however, is ‘part’. This slot determines what
> portion of the message is being highlighted or otherwise operated
> on. It can be any symbol, but the ones with predefined methods are
> ‘nick’, ‘message’, ‘all’, ‘keyword’, ‘nick-or-keyword’, and
> ‘nick-or-mention’.
This should probably also mention what the `data' slot does because it
features in both examples.
> And, finally, here's a more elaborate, module-like example demoing
> highlighting based on the ‘erc-match-traditional’ type:
>
> ;; -*- lexical-binding: t; -*-
>
> (require 'erc-match)
> (require 'erc-button)
>
> (defvar my-keywords
> `((foonet ("#chan" ,(rx bow (or "foo" "bar" "baz") eow)))))
>
> (defface my-keyword '((t (:underline (:color "tomato" :style wave))))
> "My face.")
>
> (defun my-get-keyword ()
> (and-let* ((chans (alist-get (erc-network) my-keywords))
> ((cdr (assoc (erc-target) chans))))))
>
> (cl-defstruct (my-match (:include erc-match-opt-keyword
> (part 'keyword)
There's no need to override `part' here because `keyword' is already the
default.
> (data (my-get-keyword))
> (face 'my-keyword))
> (:constructor my-match)))
>
> (setopt erc-match-types (add-to-list 'erc-match-types #'my-match))
>
> (cl-defmethod erc-match-highlight-by-part ((instance my-match)
> (_ (eql keyword)))
> "Highlight keywords by merging instead of clobbering."
> (dolist (pat (my-match-data instance))
> (goto-char (my-match-body-beg instance))
> (while (re-search-forward pat nil t)
> (erc-button-add-face (match-beginning 0) (match-end 0)
> (my-match-face instance)))))
>
One thing that's possibly unclear about this example is why the
`my-match' definition overrides `face' and `data' only to use their
accessors in the body of the method. IOW, readers may wonder why it
doesn't just use these init forms directly inline. Moreover, while
`data' holds an opaque object that users are technically invited to
repurpose as needed, doing so only really makes sense if they're
subclassing a traditional options-based type. Indeed, for novel
purposes, it's much saner for users to just define their own slot and
use it for the usual reasons, e.g., to share processed data in various
stages of refinement. This example would do well to incorporate such
usage.
In any case, it's become clear to me that a practical demonstration of
this API might be necessary to fully grasp its facets and trade offs. To
that end, I offer the following full-featured demo module:
https://emacs-erc.gitlab.io/bugs/archive/jabbycat.html
To try it out, you can start Emacs like
$ HOME=$(mktemp -d) ./src/emacs
and then eval
(require 'package)
(push '("erc-bugs" . "https://emacs-erc.gitlab.io/bugs/archive/")
package-archives)
(package-install 'erc-73798)
(sit-for 1) ;; see [1] below
(package-install 'jabbycat)
(setopt jabbycat-server "xmpp.myvps.example.org:5222"
jabbycat-recipient "me@xmpp.myvps.example.org"
jabbycat-username "jabbycat@xmpp.myvps.example.org"
jabbycat-password "changeme"
erc-jabbycat-match-patterns '((Libera.Chat ("#test" . ".")))
erc-modules (add-to-list 'erc-modules 'jabbycat))
(erc-tls)
If not already obvious, you either need to apply the latest patch set
from this bug or (as shown above) run the bug-specific version of ERC
with those changes pre-applied.
The source code is currently hosted at
https://gitlab.com/emacs-erc/jabbycat
(though a possibly less objectionable mirror may be provided eventually).
Thanks.
[1] For some reason, without the pause, `url-insert-file-contents' gives
signal(file-error ("https://fake.example.org/foo.tar" "No Data"))
package--with-response-buffer-1("https://fake.example.org/"
#f(compiled-function () ...>)
:file "foo.tar" :async nil
:error-function #<subr ...> :noerror nil)
package-install-from-archive(#s(package-desc :name foo ... :signed nil))
package-download-transaction((#s(package-desc ... :signed nil)))
package-install(foo)
next prev parent reply other threads:[~2024-11-01 5:22 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <87y12rifv2.fsf@neverwas.me>
2024-10-25 23:48 ` bug#73798: 31.0.50; ERC 5.7: New extensibility focused match API J.P.
[not found] ` <87froj4ude.fsf@neverwas.me>
2024-10-25 23:50 ` J.P.
2024-11-01 5:22 ` J.P. [this message]
[not found] ` <87ldy3v87y.fsf@neverwas.me>
2024-11-01 13:39 ` J.P.
2024-10-14 2:21 J.P.
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='87ldy3v87y.fsf__38041.5920096979$1730438608$gmane$org@neverwas.me' \
--to=jp@neverwas.me \
--cc=73798@debbugs.gnu.org \
--cc=emacs-erc@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.