Package: emacs;
Reported by: "J.P." <jp <at> neverwas.me>
Date: Tue, 20 Sep 2022 13:06:02 UTC
Severity: wishlist
Tags: patch
Found in version 29.0.50
To reply to this bug, email your comments to 57955 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
emacs-erc <at> gnu.org, bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Tue, 20 Sep 2022 13:06:02 GMT) Full text and rfc822 format available."J.P." <jp <at> neverwas.me>
:emacs-erc <at> gnu.org, bug-gnu-emacs <at> gnu.org
.
(Tue, 20 Sep 2022 13:06:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: bug-gnu-emacs <at> gnu.org Subject: 29.0.50; Allow session-local ERC modules Date: Tue, 20 Sep 2022 06:05:38 -0700
[Message part 1 (text/plain, inline)]
Tags: patch Hi people, Since its inception, ERC has aimed to support local modules, that is, modules local to a connection. (If you need convincing of this, take a look at `define-erc-module'.) This makes sense for a good many reasons, chief among them simplified semantics when arranging for buffer-local variables and hooks. Ancillary benefits include let-binding `erc-modules' around entry-point invocations and selectively disabling modules for particular sessions (e.g., after capability negotiation). Unfortunately, this dream of ERC's authors was never fully realized. Take a look at `erc-open', where you'll find would-be local vars being set too early and thus clobbered when `erc-mode' (the major mode) is activated. Various kludges have come along to circumvent this. For example, the log module uses `erc-connect-pre-hook' to conduct its buffer-local business. But it shouldn't have to. ERC can do better. This patch aims to address this problem by partially changing the purpose of the function `erc-update-modules' in a backward compatible way. Instead of activating local modules immediately, it now returns them in a list to be activated at a time and place of the caller's choosing. The most opportune place for this, in terms of `erc-open', is after all the core local variables have been determined, which exposes them to module setup code. As a bonus, the major mode hook is likewise deferred to this point. This patch also reworks the module-to-features map and preferred-name migration logic, which was incomplete. As far as third-party packages are concerned, it's only been tested with erc-hl-nicks, thus far, but it "should" work with others too. (Please let me know if that's a lie.) Thanks, J.P. P.S. BTW, if anyone is friendly with the hl-nicks author, please ask them to reach out regarding an unrelated custom.el issue; attempts to contact them via GitHub have so far proven unsuccessful. In GNU Emacs 29.0.50 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.24.34, cairo version 1.17.6) of 2022-09-19 built on localhost Repository revision: 132d5cb0a3ec94afbb49772631861e00160ffffb Repository branch: master Windowing system distributor 'The X.Org Foundation', version 11.0.12014000 System Description: Fedora Linux 36 (Workstation Edition) Configured using: 'configure --enable-check-lisp-object-type --enable-checking=yes,glyphs 'CFLAGS=-O0 -g3' PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig' Configured features: ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB Important settings: value of $LANG: en_US.UTF-8 value of $XMODIFIERS: @im=ibus locale-coding-system: utf-8-unix Major mode: Lisp Interaction Minor modes in effect: tooltip-mode: t global-eldoc-mode: t eldoc-mode: t show-paren-mode: t electric-indent-mode: t mouse-wheel-mode: t tool-bar-mode: t menu-bar-mode: t file-name-shadow-mode: t global-font-lock-mode: t font-lock-mode: t blink-cursor-mode: t line-number-mode: t indent-tabs-mode: t transient-mark-mode: t auto-composition-mode: t auto-encryption-mode: t auto-compression-mode: t Load-path shadows: None found. Features: (shadow sort mail-extr emacsbug message mailcap yank-media puny dired dired-loaddefs rfc822 mml mml-sec password-cache epa derived epg rfc6068 epg-config gnus-util text-property-search time-date subr-x mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader cl-loaddefs cl-lib sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils rmc iso-transl tooltip eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq simple cl-generic indonesian philippine cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech european ethiopic indian cyrillic chinese composite emoji-zwj charscript charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs faces cus-face macroexp files window text-properties overlay sha1 md5 base64 format env code-pages mule custom widget keymap hashtable-print-readable backquote threads dbusbind inotify lcms2 dynamic-setting system-font-setting font-render-setting cairo move-toolbar gtk x-toolkit xinput2 x multi-tty make-network-process emacs) Memory information: ((conses 16 36059 6198) (symbols 48 5107 0) (strings 32 13115 1641) (string-bytes 1 372299) (vectors 16 9247) (vector-slots 8 146583 10252) (floats 8 21 25) (intervals 56 220 0) (buffers 1000 10))
[0002-Support-local-ERC-modules-in-erc-mode-buffers.patch (text/x-patch, attachment)]
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Tue, 20 Sep 2022 17:44:02 GMT) Full text and rfc822 format available.Message #8 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: Michael Albinus <michael.albinus <at> gmx.de> To: "J.P." <jp <at> neverwas.me> Cc: 57955 <at> debbugs.gnu.org, emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Tue, 20 Sep 2022 19:43:34 +0200
"J.P." <jp <at> neverwas.me> writes: > Hi people, Hi, > Since its inception, ERC has aimed to support local modules, that is, > modules local to a connection. (If you need convincing of this, take a > look at `define-erc-module'.) This makes sense for a good many reasons, > chief among them simplified semantics when arranging for buffer-local > variables and hooks. Ancillary benefits include let-binding > `erc-modules' around entry-point invocations and selectively disabling > modules for particular sessions (e.g., after capability negotiation). Without knowing erc in general and your patch in detail: this sounds like you could profit from connection-local variables. Did you check this? > Thanks, > J.P. Best regards, Michael.
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Wed, 21 Sep 2022 13:17:02 GMT) Full text and rfc822 format available.Message #11 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: Michael Albinus <michael.albinus <at> gmx.de> Cc: 57955 <at> debbugs.gnu.org, emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Wed, 21 Sep 2022 06:15:48 -0700
Hi Michael, Michael Albinus <michael.albinus <at> gmx.de> writes: > Without knowing erc in general and your patch in detail: this sounds > like you could profit from connection-local variables. Did you check > this? Not quite yet (only superficially). At first glance, I'm not sure they're a perfect fit for this specific issue, but I'll definitely investigate further. Either way, I'm thinking they'd be a great solution (or inspiration) for an initiative we have on the horizon, namely, devising a means of applying user options in a more granular, contextual manner [1]. Also (if you happen to recall), a few of our recent exchanges ended with me pledging to follow through on one thing or another. And yet, most of those promises remain unfulfilled. Please know that I do plan on addressing them "eventually" and that I very much appreciate your help (and your patience). Thanks, J.P. [1] In case you're interested, by "context," I'm referring to various logical (somewhat overlapping) IRC boundaries, such as - message: event-local, i.e., source-wise and message-type-wise - channel: target-local - network: connection-local Basically, I'm looking for something akin to a "context variable" facility, except not so much for managing concurrency but instead for transparently stashing and restoring message-processing environments matched against headers and protocol state. More info: https://lists.gnu.org/archive/html/emacs-erc/2021-10/msg00003.html
Stefan Kangas <stefankangas <at> gmail.com>
to control <at> debbugs.gnu.org
.
(Thu, 13 Oct 2022 13:48:04 GMT) Full text and rfc822 format available.bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Wed, 26 Oct 2022 13:17:02 GMT) Full text and rfc822 format available.Message #16 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Wed, 26 Oct 2022 06:16:34 -0700
I'd like to propose that these changes be included in ERC 5.5 and Emacs 29. If anyone has any concerns, please speak up. Otherwise, expect this patch to be installed at some point as part of the larger SASL change set in bug#29108. Thanks.
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Tue, 15 Nov 2022 15:08:02 GMT) Full text and rfc822 format available.Message #19 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Tue, 15 Nov 2022 07:07:30 -0800
"J.P." <jp <at> neverwas.me> writes: > I'd like to propose that these changes be included in ERC 5.5 and Emacs > 29. If anyone has any concerns, please speak up. Otherwise, expect this > patch to be installed at some point as part of the larger SASL change > set in bug#29108. Thanks. Brief update. The scope of this change has shrunk considerably. The deferred loading and migrations stuff still applies, but the main user-facing aspect, namely, support for let-binding a module's options on entry-point invocation, has been abandoned (for now). After looking into connection-local variables a bit, following Michael's suggestion up thread, I have come to the opinion that the let-binding idea was not fully formed and that options granularity is worthy of more meditation and discussion and thus not a realistic goal for Emacs 5.5. Thanks.
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Mon, 22 May 2023 04:06:02 GMT) Full text and rfc822 format available.Message #22 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Sun, 21 May 2023 21:05:41 -0700
"J.P." <jp <at> neverwas.me> writes: > Brief update. The scope of this change has shrunk considerably. The > deferred loading and migrations stuff still applies, but the main > user-facing aspect, namely, support for let-binding a module's options > on entry-point invocation, has been abandoned (for now). > > After looking into connection-local variables a bit, following Michael's > suggestion up thread, I have come to the opinion that the let-binding > idea was not fully formed and that options granularity is worthy of more > meditation and discussion and thus not a realistic goal for Emacs 5.5. To help with managing local modules, I've added some (possibly temporary) convenience functions and other supporting items as part of bug#60936. The most useful are: * macro `erc--restore-initialize-priors' This restores local variables from a previous session on major-mode hook or slightly later. The effect is similar to that provided by the `permanent-local' property, which we may end up settling for if the dream of context-local user options evaporates for good. This may also be of interest to global modules intended primarily for interactive use, such as the proposed `bufbar' and `nickbar' (bug#63595). * variable `erc--updating-modules-p' This is non-nil when running `erc-update-modules' in `erc-open'. It allows global modules to suppress superfluous buffer initialization pre-major-mode while still making that same init code available on demand for interactive invocations and indirect activation by dependent modules. Please keep in mind that these may change or disappear at any time.
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Mon, 09 Oct 2023 04:03:02 GMT) Full text and rfc822 format available.Message #25 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Sun, 08 Oct 2023 21:02:02 -0700
[Message part 1 (text/plain, inline)]
It turns out these changes removed some behavior involving the loading of modules that's long been part of ERC's implicit interface. Basically, when encountering a third-party module `mymod', ERC has always attempted to `require' the (possibly nonexistent) feature `erc-mymod', and to do so unconditionally. However, this bug changed that policy to instead only attempt such loading if the corresponding command `erc-mymod-mode' is undefined. That was likely unwise because some packages do questionable things like ;;;###autoload (eval-after-load 'erc '(define-erc-module mymod nil "Doc" () ())) which fails to provide the vital `symbol-file' association between the minor-mode command and the library (because the command itself isn't autoloaded). Such uses ignore the decades-old example in the doc string of `define-erc-module', which clearly recommends ;;;###autoload(autoload 'erc-mymode-mode "erc-mymode") (define-erc-module mymod nil "Doc" () ()) as the surefire approach. That said, in cases where the library's name matches the (prefixed) module name, no autoload cookie is necessary. Unfortunately, many of these packages are in maintenance mode, with authors unwilling to respond to suggestions to update such aberrant uses. Thus, I think it's in ERC's best interest to accommodate them as it always has. Please see the second patch below. Thanks.
[0001-5.6-Honor-nil-values-in-erc-restore-initialize-prior.patch (text/x-patch, attachment)]
[0002-5.6-Sort-and-dedupe-when-loading-modules-in-erc-open.patch (text/x-patch, attachment)]
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Sat, 14 Oct 2023 00:24:01 GMT) Full text and rfc822 format available.Message #28 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Fri, 13 Oct 2023 17:23:01 -0700
I've added something similar to the proposed change as https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=d46c016f If you'll recall, I initially left this bug open as a reminder that we still lack a solution to the glaring problem of configuration scoping. To summarize, this concerns the pressing need to allow users to specify different values for new (and hopefully existing) global options based on context (like network, channel, speaker, etc.). One idea bandied about has been adapting connection-local variables to be more abstract and eventually integrating them with Customize and `use-package'. As thing stand, though, each local module must itself decide whether it's session-local, buffer-local, or both. And it must contend with stashing and restoring its configured state on its own. I'll likely close this bug after opening another to address these broader configuration concerns. Thanks.
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Wed, 18 Oct 2023 13:38:01 GMT) Full text and rfc822 format available.Message #31 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Wed, 18 Oct 2023 06:36:26 -0700
[Message part 1 (text/plain, inline)]
"J.P." <jp <at> neverwas.me> writes: > I've added something similar to the proposed change as > > https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=d46c016f Actually, this has proved insufficient for detecting top-level and `eval-after-load' calls to `erc-update-modules'. The attached tweak hopefully addresses that.
[0001-5.6-Warn-about-top-level-erc-update-modules-calls.patch (text/x-patch, attachment)]
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Sat, 10 Feb 2024 20:37:02 GMT) Full text and rfc822 format available.Message #34 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Sat, 10 Feb 2024 12:36:04 -0800
A new Emacs user recently complained about the lack of an example showing local modules being `let'-bound around calls to ERC's entry point functions in lisp code. Perhaps we should add such an example to the Modules chapter and also possibly update the "Multiple networks" example in Advanced > SASL to include a non-SASL connection: (defun my-erc-up (network) (interactive "Snetwork: ") (pcase network ('libera (let ((erc-modules (cons 'sasl erc-modules)) (erc-sasl-mechanism 'external)) (erc-tls :server "irc.libera.chat" :port 6697 :client-certificate t))) ('example (let ((erc-modules (cons 'sasl erc-modules)) (erc-sasl-auth-source-function #'erc-sasl-auth-source-password-as-host)) (erc-tls :server "irc.example.net" :port 6697 :user "alyssa" :password "Example.Net"))) (_ (call-interactively #'erc-tls))))
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Fri, 01 Mar 2024 00:26:01 GMT) Full text and rfc822 format available.Message #37 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Thu, 29 Feb 2024 16:25:08 -0800
"J.P." <jp <at> neverwas.me> writes: > A new Emacs user recently complained about the lack of an example > showing local modules being `let'-bound around calls to ERC's entry > point functions in lisp code. Perhaps we should add such an example to > the Modules chapter and also possibly update the "Multiple networks" > example in Advanced > SASL to include a non-SASL connection: > > (defun my-erc-up (network) > (interactive "Snetwork: ") > > (pcase network > ('libera > (let ((erc-modules (cons 'sasl erc-modules)) > (erc-sasl-mechanism 'external)) > (erc-tls :server "irc.libera.chat" :port 6697 > :client-certificate t))) > ('example > (let ((erc-modules (cons 'sasl erc-modules)) > (erc-sasl-auth-source-function > #'erc-sasl-auth-source-password-as-host)) > (erc-tls :server "irc.example.net" :port 6697 > :user "alyssa" > :password "Example.Net"))) > (_ (call-interactively #'erc-tls)))) I've added an example similar to this one.
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Sun, 09 Feb 2025 20:47:01 GMT) Full text and rfc822 format available.Message #40 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Sun, 09 Feb 2025 12:46:14 -0800
[Message part 1 (text/plain, inline)]
I'd like to reexamine the scope of this bug because it's a partial blocker for bug#49860 (IRCv3). You'll recall among its original goals were two overlapping concerns: a. Granular configuration of a local module's user options b. Persistence of a local module's data across reconnections One idea bandied about for addressing the first was to recommend and accommodate buffer-local options, that is, recommend that options in a local module's Custom group be explicitly declared buffer-local with the :local `defcustom' keyword and that they be given local bindings on module activation, thus initializing them with values from the current environment. While this is technically feasible, a few notable complications would need sorting out [1]. A complementary aspect addressing the second goal of convenient persistence was also previously floated and amounted to leveraging the `permanent-local' symbol property on a local module's own variables of interest to sustain them across reconnections. With local user options, this would likely involve the `permanent-only' argument to the :local `defcustom' keyword explained in (info "(elisp) Variable Definitions"). For the stated purpose of sustaining variables of interest across reconnection boundaries, this approach remains viable [2], at least for third party modules that don't know about the internal persistence mechanism [3]. So, in light of the new proposal for "scoped" configuration now officially on the table [4], it might behoove us to just pretend the granularity objective is henceforth solely the domain of that proposal's bug (bug#76019). That'll allow us, here, in this bug, to focus entirely on the second objective about persistence and to hopefully arrive at something worthy of some finality for 5.7. To that end, here's some related territory possibly worth exploring: 1. A public utility function to access the prior buffer's local variables during reconnection 2. A managed facility for declaring arbitrary persisted data with supporting CRUD operations 3. Optional helpers for an option's :set function that update persisted values in affected buffers or inform users to cycle the mode or restart the session 4. Documenting differences in how a local module's mode command variants behave with the various flavors of local modules, like session-wide, target-only, etc. 5. An advanced tutorial on how to write a local module using only the public API via a fully functional demo To get started, I've attached a PoC of a possible approach for point 2 (the CRUD thing). It turns out my having explored the idea some has led me to the opinion that it's probably better to stick to points 4 and 5 only and to let module authors deal with the rest. Basically, I'm not sure asking anyone to adopt yet another magical abstraction layer just to persist state is any less mentally taxing than asking them to wrangle it all themselves using lower level Emacs facilities, so long as we provide clear guidelines and examples with any necessary boilerplate. Of course, this observation disregards maintainability concerns, so we'd need to be pretty certain all related infrastructure is mostly here to stay (famous last words). More to come on this shortly. Thanks. [1] Possible complications with the :local `defcustom' keyword idea: . Most users are unfamiliar with buffer-local options. And, AFAICT, Customize doesn't itself prescribe how such an option's variable should be made buffer local nor how or whether relevant updates ought to be propagated across existing local bindings when the default value is updated. It would seem such concerns are the responsibility of the application. But these are *user* options, and users can't be bothered to learn the idiosyncrasies of each app just to configure it to behave as expected. They'll either move on or risk contending with unwelcome surprises. . The buffer-wise "scope" doesn't always align perfectly with contexts endemic to IRC, the most important being the connection-wise session, which spans multiple buffers (internally, those having the same `erc-networks--id'). Users on 29+ might be able to use `setopt' to update the value cleanly within a session, for example, in a server buffer, and have the change shared with all targets as well. But, users on 27 and 28 can't be expected to invoke the option's :set function outside of Custom buffers, although advanced users can manually apply updates via the module's explicit enable/disable command variants. . Modules oftentimes ignore the value of an option after initialization and instead use something derived from the original value and then progressively refined. Local modules also perform other initialization tasks based on the value of an option, such as subscribe to certain hooks. While buffer-local options may agree sufficiently with this pattern, so long as they're bound before module setup code runs in a new or reused buffer, the pattern dictates that a module capture a "snapshot" of an option's value anyway, so there's no reason to prefer buffer-local bindings over, say, more ephemeral and arguably easier to reason about `let' bindings. . Per-target options won't magically work when local in a target buffer because ERC often decides on target-related business with the server buffer current. Indeed, the target buffer in question may not even exist yet, which happens most often in response handlers, such as `erc-server-PRIVMSG'. Although this situation can be remedied, doing it in a backward compatible way seems a chore. [2] A local module's mode variable itself can't be `permanent-local' because the majority of setup it performs won't survive a major-mode reset, thus creating an "inconsistent state" during the crucial reinitialization period when modules inspect and even modify one another. It's then that they also need to possibly recall the original value of variables not owned by them or even ERC (and these definitely can't be made `permanent-local'). [3] The internal inter-session persistence mechanism consists mainly of a crude restoration ritual for transferring values from old buffers to new via the variables `erc--server-reconnecting' and `erc--target-priors'. These are bound at module initialization time to an alist containing the local variables of the "reassociated" buffer, if any. Aside from those symbol names not being great and there being no public interface, there's also no way to recover if something goes awry during (re)initialization: restarting the session from scratch won't work so long as module-managed local variables are still bound to unusable values in the old buffer. Basically, the offending local module must run its "disable body" somewhere: either in the old buffer, before reassociating, or in the new one upon failure. Clearly, a friendlier and ideally more robust user-facing solution is necessary. [4] https://debbugs.gnu.org/cgi/bugreport.cgi?bug=76019
[0001-5.7-Skip-already-enabled-local-modules-in-erc-open.patch (text/x-patch, attachment)]
[0002-5.7-Provide-API-for-persisting-local-module-state-in.patch (text/x-patch, attachment)]
bug-gnu-emacs <at> gnu.org
:bug#57955
; Package emacs
.
(Wed, 19 Feb 2025 04:19:01 GMT) Full text and rfc822 format available.Message #43 received at 57955 <at> debbugs.gnu.org (full text, mbox):
From: "J.P." <jp <at> neverwas.me> To: 57955 <at> debbugs.gnu.org Cc: emacs-erc <at> gnu.org Subject: Re: bug#57955: 29.0.50; Allow session-local ERC modules Date: Tue, 18 Feb 2025 20:17:58 -0800
[Message part 1 (text/plain, inline)]
"J.P." <jp <at> neverwas.me> writes: > So, in light of the new proposal for "scoped" configuration now > officially on the table [4], it might behoove us to just pretend the > granularity objective is henceforth solely the domain of that proposal's > bug (bug#76019). That'll allow us, here, in this bug, to focus entirely > on the second objective about persistence and to hopefully arrive at > something worthy of some finality for 5.7. To that end, here's some > related territory possibly worth exploring: > > 1. A public utility function to access the prior buffer's local > variables during reconnection > > 2. A managed facility for declaring arbitrary persisted data with > supporting CRUD operations > > 3. Optional helpers for an option's :set function that update > persisted values in affected buffers or inform users to cycle the > mode or restart the session > > 4. Documenting differences in how a local module's mode command > variants behave with the various flavors of local modules, like > session-wide, target-only, etc. > > 5. An advanced tutorial on how to write a local module using only the > public API via a fully functional demo > > To get started, I've attached a PoC of a possible approach for point 2 > (the CRUD thing). It turns out my having explored the idea some has led > me to the opinion that it's probably better to stick to points 4 and 5 > only and to let module authors deal with the rest. Basically, I'm not > sure asking anyone to adopt yet another magical abstraction layer just > to persist state is any less mentally taxing than asking them to wrangle > it all themselves using lower level Emacs facilities, so long as we > provide clear guidelines and examples with any necessary boilerplate. Of > course, this observation disregards maintainability concerns, so we'd > need to be pretty certain all related infrastructure is mostly here to > stay (famous last words). More to come on this shortly. Here is an initial draft attempting to address points 4 and 5 in the list above (patch also attached): File: erc.info, Node: Module Example, Next: Module Usage, Prev: Module Loading, Up: Modules 4.3 Example =========== This is a walk-through of a working module presented in separate chunks. If you'd prefer to view it as a whole, you can install it as a third-party package through ERC's devel archive: <https://emacs-erc.gitlab.io/bugs/archive/erc-view.html>. ;;; erc-view.el -- Automatic view-mode for ERC -*- lexical-binding: t; -*- ;; Maintainer: The ERC Maintainers <emacs-erc <at> gnu.org> ;; Keywords: convenience ;; Version: 0.1 ;; Package-Requires: ((emacs "30.1")) ;; URL: https://gitlab.com/emacs-erc/erc-view ;;; Commentary: ;; This is a demo local module for ERC. It arranges for automatically ;; enabling `view-mode' when leaving the prompt area and automatically ;; disabling it when reentering. It also ensures `view-mode' stays ;; enabled or disabled when reconnecting. ;;; Code: You need to import ERC's main library somehow. The easiest way is directly, via a simple ‘(require 'erc)’, although this module does so indirectly because it also uses definitions from ‘erc-goodies’: (require 'erc-goodies) (require 'view) Avoid headaches by aligning the name of your module with its containing library and Custom group. It's best to have one group and one module per library. (defgroup erc-view nil "Automatically enter and exit `view-mode' in ERC." :version "0.1" :group 'erc) (defcustom erc-view-enable-when-exiting-prompt t "Whether to enable `view-mode' when exiting the prompt area." :type 'boolean) (defcustom erc-view-disable-when-entering-prompt t "Whether to disable `view-mode' when entering the prompt area." :type 'boolean) (defcustom erc-view-backspace-at-prompt-scrolls-down t "Whether a \\`<backspace>' at the prompt scrolls down to enter `view-mode'." :type 'boolean) You'll almost always want to define internal variables as buffer-local. (defvar-local erc-view--enabled-p nil "Current reconnect-aware activation state of `view-mode'.") In some cases, you may need a variable's value to survive the reapplication of ERC's ‘major-mode’ performed in each reassociated buffer upon reconnecting. Do this by leveraging the ‘permanent-local’ symbol property. (*note (elisp)Creating Buffer-Local::.) (put 'erc-view--enabled-p 'permanent-local t) There are a few caveats regarding the durability of permanent values. By convention, disabling a module's minor mode kills local bindings. Mode commands, like ‘erc-view-mode’, do so in the current buffer only, while unidirectional ones, like ‘erc-view-mode-disable’, do so connection-wide. There are also occasions in which persistence is undefined, most notably when “grafting” an old buffer's contents onto a current buffer. This occurs in server buffers upon “logical connection” (at ‘MOTD’'s end), when a user reconnects with a new invocation of an entry-point command, like ‘erc-tls’, instead of via the auto-reconnect facility or by issuing a ‘/reconnect’ at the prompt. Unaffected are entry-point invocations that include an ‘:id’ keyword because reassociation happens immediately in such cases, before ERC even initializes any modules. Grafting can also happen in target buffers, most often after a user reconnects under a new nick and conducts business in the same channels and queries as before, only to renick _back_ to the previous nick via a ‘/nick oldme’ or similar. As of version 5.7, ERC retains the current buffer's permanent value in all such situations, meaning ERC ignores permanent values from previous buffers and retains default values assigned during module initialization. Moving on, if your module needs to bind keys, define its keymap _before_ the module itself, and use the standard minor-mode naming convention of ‘erc-my-module-mode-map’. (defvar-keymap erc-view-mode-map :doc "Keymap for `view-mode' in ERC." "<remap> <delete-backward-char>" #'erc-view--enable-on-backspace) (defvar-keymap erc-view-mode-overriding-map :parent view-mode-map :doc "Overriding keymap for `view-mode' when `erc-view-mode' is active. Hitting \\`<RET>' atop a button prompts for an action by default. Use \\`C-j' or \\`j' for scrolling up by a line." "C" nil ; View-kill-and-leave "E" #'erc-view--exit-to-bottom ; View-exit-and-edit "Q" nil ; View-quit-all "k" #'View-scroll-line-backward ; Vim backwards line "j" #'View-scroll-line-forward ; Vim forwards line "S-<return>" #'View-scroll-line-backward) You'll almost always want to define your module as buffer-local. Do this by including a ‘localp’ flag as the final parameter to ‘define-erc-module’, after the “disable body”. If your module only operates in one kind of buffer, disable it elsewhere in the “enable body”. For example, if it should only run in server buffers, disable it in target buffers by doing something like ‘(if (erc-target) (erc-my-module-mode -1) (erc-my-module--setup))’. And in all cases, please remember to mention the module's intended “scope” in the doc string. Some informal adjectives that may help with that are: • query-local • channel-local • target-local (query or channel) • server-local • session-local (server and target) • buffer-local (server or target) You may also wish to mention this in the Custom group's doc string. (define-erc-module view nil "Enable `view-mode' if it was on previously. This module is buffer-local. If you also use the `scrolltobottom' module, you probably want to enable the option `erc-scrolltobottom-all'." ((add-hook 'view-mode-hook #'erc-view--remember 0 t) (add-hook 'post-command-hook #'erc-view--enforce-prompt-boundary 0 t) (setf (alist-get 'view-mode minor-mode-overriding-map-alist) erc-view-mode-overriding-map) (unless (local-variable-p 'erc-view--enabled-p) (setq-local erc-view--enabled-p nil)) (view-mode (if erc-view--enabled-p +1 -1))) ((kill-local-variable 'erc-view--enabled-p) (remove-hook 'post-command-hook #'erc-view--enforce-prompt-boundary t) (remove-hook 'view-mode-hook #'erc-view--remember t) (setf (alist-get 'view-mode minor-mode-overriding-map-alist nil 'remove) nil)) localp) Always define your module early, before any code that refers to its mode command or minor-mode variable. (defun erc-view--enable-on-backspace (lines) "Enable `view-mode' at the prompt by hitting \\`<backspace>'." (interactive "P") (if (and erc-view-backspace-at-prompt-scrolls-down (not view-mode) (= (point) erc-input-marker)) (progn (view-mode +1) (View-scroll-page-backward lines)) (call-interactively #'delete-backward-char))) (defun erc-view--enforce-prompt-boundary () "Enable or disable `view-mode' when crossing prompt boundary." (when-let* ((new (if (>= (point) erc-input-marker) (and view-mode erc-view-disable-when-entering-prompt -1) (and (not view-mode) erc-view-enable-when-exiting-prompt +1)))) (run-at-time 0 nil (lambda (buffer new) (with-current-buffer buffer (view-mode new))) (current-buffer) new))) (defun erc-view--exit-to-bottom () "Scroll to prompt, exit `view-mode', and move to EOB." (interactive) (let (view-no-disable-on-exit) (View-scroll-to-buffer-end) (View-exit) (goto-char (point-max)))) (defun erc-view--remember () "Remember the value of `view-mode'. Disable `erc-move-to-prompt-setup' locally when `view-mode' is enabled." (cl-assert (local-variable-p 'erc-view--enabled-p)) (setq erc-view--enabled-p view-mode) (when erc-move-to-prompt-mode (if view-mode (remove-hook 'pre-command-hook #'erc-move-to-prompt t) (erc-move-to-prompt-setup)))) Don't forget to ‘provide’ your module so that ‘erc-update-modules’ can find it. (provide 'erc-view) ;;; erc-view.el ends here Mimicking the above should just about cover most use cases. If your module isn't loading correctly, it's likely a naming, layout, or packaging issue. If you _must_ defy the convention recommended earlier regarding a library-group-module correspondence or if you've designed your module mainly to be toggled interactively rather than added to ‘erc-modules’, try placing a line like the following above the module's definition. ;;;###autoload(autoload 'erc-my-module-mode "erc-my-module" nil t) Just remember, doing so means you'll need to (re)generate the autoload file when hacking locally (*note (emacs)Fetching Package Sources::).
[0001-5.7-Split-ERC-module-documentation-into-subnodes.patch (text/x-patch, attachment)]
[0002-5.7-Add-module-example-to-ERC-s-documentation.patch (text/x-patch, attachment)]
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.