GNU bug report logs - #57955
29.0.50; Allow session-local ERC modules

Previous Next

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

Full log


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)]

This bug report was last modified 120 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.