Package: emacs;
Reported by: Stefan Monnier <monnier <at> iro.umontreal.ca>
Date: Fri, 29 Mar 2024 16:17:01 UTC
Severity: normal
Tags: patch
Done: Stefan Monnier <monnier <at> iro.umontreal.ca>
Bug is archived. No further changes may be made.
View this message in rfc822 format
From: Stefan Monnier <monnier <at> iro.umontreal.ca> To: Eli Zaretskii <eliz <at> gnu.org> Cc: acm <at> muc.de, yantar92 <at> posteo.net, 70077 <at> debbugs.gnu.org Subject: bug#70077: An easier way to track buffer changes Date: Tue, 09 Apr 2024 19:30:17 -0400
>> +Using @code{before-change-functions} and @code{after-change-functions} >> +can be difficult in practice because of a number of pitfalls, such as >> +the fact that the two calls are not always properly paired, or some >> +calls may be missing, either because of bugs in the C code or because of >> +inappropriate use of @code{inhibit-modification-hooks}. > > I don't think we should talk about bugs in C code in the manual, at > least not so explicitly. I would rephrase > > the fact that the two calls are not always properly paired, or some > calls may be missing, either because some Emacs primitives cannot > properly pair them or because of incorrect use of > @code{inhibit-modification-hooks}. Thanks. >> +@code{track-changes-register}, passing it a @var{signal} function as >> +argument. This will return a tracker @var{id} which is used to identify >> +your tracker to the other functions of the library. The other main >> +function of the library is @code{track-changes-fetch} which lets you >> +fetch the changes you have not yet processed. > > The last sentence is redundant, since you are about to describe > track-changes-fetch shortly. Good point. >> +When the buffer is modified, the library will call the @var{signal} >> +function to inform you of that change and will immediately start >> +accumulating subsequent changes into a single combined change. >> +The @var{signal} function serves only to warn that a modification >> +occurred but does not receive a description of the change. Also the >> +library will not call it again until after you processed >> +the change. > > The last sentence should IMO say "...until after you retrieved the > change by calling @code{track-changes-fetch}." The important part > here is to say what "process" means in practice, instead of leaving it > unsaid. But then we get: ...until after you retrieved the change by calling @code{track-changes-fetch}. To process changes, you need to call @code{track-changes-fetch}, ... which again is kind of redundant/heavy. I went with: To start tracking changes, you have to call @code{track-changes-register}, passing it a @var{signal} function as argument. This returns a tracker @var{id} which is used to identify your change tracker to the other functions of the library. When the buffer is modified, the library calls the @var{signal} function to inform you of that change and immediately starts accumulating subsequent changes into a single combined change. The @var{signal} function serves only to warn that a modification occurred but does not receive a description of the change. Also the library will not call it again until after you retrieved the change. To retrieve changes, you need to call @code{track-changes-fetch}, which provides you with the bounds of the changes accumulated since the last call, as well as the previous content of that region. It also ``re-arms'' the @var{signal} function so that the library will call it again after the next buffer modification. [ Where I also switched to the present tense. Not sure why I used the future tense. ] >> +@defun track-changes-register signal &key nobefore disjoint immediate >> +This function creates a new @emph{tracker}. Trackers are kept abstract, > > I suggest to use "change tracker" instead of just "tracker". On my > daytime job, "tracker" has a very different meaning, so I stumble each > time I see this used like that. > > Also, I suggest to use @dfn for its markup (and add a @cindex for it > for good measure). I turned some uses of "tracker" into "change tracker" and I think it's indeed an improvement. I tried to do it more systematically but with all the "track-changes" and "change trackers" I started to feel like I was writing a tongue twister. >> +By default, the call to the @var{signal} function does not happen >> +immediately, but is instead postponed with a 0 seconds timer. > ^^^^^^^^^^^^^^^ > A cross-reference to where timers are described is in order there. Thanks. >> +usually desired to make sure the @var{signal} function is not called too >> +frequently and runs in a permissive context, freeing the client from >> +performance concerns or worries about which operations might be >> +problematic. If a client wants to have more control, they can provide >> +a non-nil value as the @var{immediate} argument in which case the > ^^^ > @code{nil} Duh. >> +Once @var{func} finishes, @code{track-changes-fetch} re-enables the >> +@var{signal} function so that it will be called the next time a change >> +occurs. This is the reason why it calls @var{func} instead of returning >> +a description: it makes sure that the @var{signal} function will not be >> +called while you're still processing past changes. > > I think there's a subtler issue here that needs to be described > explicitly: if the entire processing of the change is not done inside > FUNC, there's no guarantee that by the time some other function > processes it, the change is still valid and in particular SIGNAL will > not have been called again. This is not a trivial aspect, since a > program can use FUNC to do just some partial processing, like squirrel > the changes to some buffer for later processing. I tried to be more explicit about that as follows: Once @var{func} finishes, @code{track-changes-fetch} re-enables the @var{signal} function so that it will be called the next time a change occurs. This is the reason why it calls @var{func} instead of returning a description: it lets you process the change without worrying about the risk that the @var{signal} function gets triggered in the middle of it, because the @var{signal} is re-enabled only after @var{func} finishes. Note that calling SIGNAL before the change is "processed" is not necessarily a problem. E.g. Eglot does exactly that: its FUNC just turns the NEWBEG/NEWEND/BEFORE into something like OLDBEG/OLDEND/AFTER and pushes it on a list for later processing, it then waits for more changes to come before later sending them to the server. >> * New Modes and Packages in Emacs 30.1 >> +** New package Track-Changes. > This should be marked with "+++". Thanks. >> No, what it's saying is that these slots can contain values of any type >> (since any type is a subtype of t). >> This `Type` information is a way to document what kind of values can be >> found in those slots. Very often we don't bother specifying it, in >> which case `t` is used as a default. > Then maybe show "Any" or even "N/A". t is simply not informative at > all. Actually, it is counter-productive: it makes a simple issue look > complex and strange. OK, I just used the empty string. Stefan
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.