From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Ihor Radchenko Newsgroups: gmane.emacs.bugs Subject: bug#70077: An easier way to track buffer changes Date: Sun, 07 Apr 2024 14:07:36 +0000 Message-ID: <87edbhnu53.fsf@localhost> References: Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="12644"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Alan Mackenzie , 70077@debbugs.gnu.org To: Stefan Monnier Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Apr 07 16:08:39 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rtTCI-00033K-L1 for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 07 Apr 2024 16:08:38 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rtTBd-0004gy-Um; Sun, 07 Apr 2024 10:07:57 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rtTBc-0004gG-3v for bug-gnu-emacs@gnu.org; Sun, 07 Apr 2024 10:07:56 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1rtTBb-0007cE-QR for bug-gnu-emacs@gnu.org; Sun, 07 Apr 2024 10:07:55 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1rtTBi-0005xN-JH for bug-gnu-emacs@gnu.org; Sun, 07 Apr 2024 10:08:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Ihor Radchenko Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 07 Apr 2024 14:08:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 70077 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 70077-submit@debbugs.gnu.org id=B70077.171249885722752 (code B ref 70077); Sun, 07 Apr 2024 14:08:02 +0000 Original-Received: (at 70077) by debbugs.gnu.org; 7 Apr 2024 14:07:37 +0000 Original-Received: from localhost ([127.0.0.1]:44261 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rtTBG-0005ug-OY for submit@debbugs.gnu.org; Sun, 07 Apr 2024 10:07:36 -0400 Original-Received: from mout01.posteo.de ([185.67.36.65]:33021) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rtTBB-0005tq-WC for 70077@debbugs.gnu.org; Sun, 07 Apr 2024 10:07:32 -0400 Original-Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id C4C3C240029 for <70077@debbugs.gnu.org>; Sun, 7 Apr 2024 16:07:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1712498836; bh=0q+XMW+bJ2nVOoAWS72MjFAriW4tJ+04YXa+tJ4erzU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type: From; b=OcccZB4hxJicjQVBmvGPEjYdzCPaTZ5ua7C6FgS9543MPBOQfnBE/LuR8CAnn5Orp FTTJkO2sWYC8H5tw06Ax5ZouWsa4eWwZnnI/0gMUI/2yEsFO2BhdPVC0lEqtZ+yPuU yGNg/7KsXv+sh9TxoSrvsU0DscW1doJEXfDPFhbZWlQ5/0Lj42xnJfmIntbZvx+bet 15G44rVsHCz0Giu8VphsjozHlLrlAs/6NAlYcEsOHSUBD8AhQp0AIrekxRTB3Bytf3 ns9cT5V+wPkmuD2cUjDuutWQBnTPWD2hewyeY1eMu94jpf7xKACCc2OjASDqdUdeas hGPEuEA/ZGegA== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4VCDYv5YKPz9rxK; Sun, 7 Apr 2024 16:07:15 +0200 (CEST) In-Reply-To: X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:282870 Archived-At: Stefan Monnier writes: > +(cl-defstruct (track-changes--state > + (:noinline t) > + (:constructor nil) > + (:constructor track-changes--state ())) > + "Object holding a description of a buffer state. > +BEG..END is the area that was changed and BEFORE is its previous content. > +If the current buffer currently holds the content of the next state, you can get > +the contents of the previous state with: > + > + (concat (buffer-substring (point-min) beg) > + before > + (buffer-substring end (point-max))) > + > +NEXT is the next state object (i.e. a more recent state). > +If NEXT is nil it means it's most recent state and it may be incomplete > +\(BEG/END/BEFORE may be nil), in which case those fields will take their > +values from `track-changes--before-(beg|end|before)' when the next > +state is create." This docstring is a bit confusing. If a state object is not the most recent, how come > + (concat (buffer-substring (point-min) beg) > + before > + (buffer-substring end (point-max))) produces the previous content? And if the state object is the most recent, "it may be incomplete"... So, when is it safe to use the above (concat ... ) call? > +(defvar-local track-changes--before-beg (point-min) > + "Beginning position of the remembered \"before string\".") > +(defvar-local track-changes--before-end (point-min) > + "End position of the text replacing the \"before string\".") Why (point-min)? It will make the values dependent on the buffer narrowing that happens to be active when the library if first loaded. Which cannot be right. > +(defvar-local track-changes--buffer-size nil > + "Current size of the buffer, as far as this library knows. > +This is used to try and detect cases where buffer modifications are \"lost\".") Just looking at the buffer size may miss unregistered edits that do not change the total size of the buffer. Although I do not know a better measure. `buffer-chars-modified-tic' may lead to false-positives (Bug#51766). > +(cl-defun track-changes-register ( signal &key nobefore disjoint immediate) > + "Register a new tracker and return a new tracker ID. > +SIGNAL is a function that will be called with one argument (the tracker ID) > +after the current buffer is modified, so that we can react to the change. > + ... > +If optional argument DISJOINT is non-nil, SIGNAL is called every time we are > +about to combine changes from \"distant\" parts of the buffer. > +This is needed when combining disjoint changes into one bigger change > +is unacceptable, typically for performance reasons. > +These calls are distinguished from normal calls by calling SIGNAL with > +a second argument which is the distance between the upcoming change and > +the previous changes. This is a bit confusing. The first paragraph says that SIGNAL is called with a single argument, but that it appears that two arguments may be passed. I'd rather tell the calling convention early in the docstring. > + (unless nobefore > + (setq track-changes--before-no nil) > + (add-hook 'before-change-functions #'track-changes--before nil t)) > + (add-hook 'after-change-functions #'track-changes--after nil t) Note that all the changes made in indirect buffers will be missed. See bug#60333. > +(defun track-changes-fetch (id func) > ... > + (unless (equal track-changes--buffer-size (buffer-size)) > + (track-changes--recover-from-error)) > + (let ((beg nil) > + (end nil) > + (before t) > + (lenbefore 0) > + (states ())) > + ;; Transfer the data from `track-changes--before-string' > + ;; to the tracker's state object, if needed. > + (track-changes--clean-state) > +(defun track-changes--recover-from-error () > ... > + (setq track-changes--state (track-changes--state))) This will create a dummy state with (beg (point-max)) (end (point-min)) such state will not pass (< beg end) assertion in `track-changes--clean-state' called in `track-changes-fetch' immediately after `track-changes--recover-from-error' > +(defun track-changes--in-revert (beg end before func) > ... > + (atomic-change-group > + (goto-char end) > + (insert-before-markers before) > + (delete-region beg end) What happens if there are markers inside beg...end? > +(defun track-changes-tests--random-word () > + (let ((chars ())) > + (dotimes (_ (1+ (random 12))) > + (push (+ ?A (random (1+ (- ?z ?A)))) chars)) > + (apply #'string chars))) If you are using random values for edits, how can such test be reproduced? Maybe first generate a random seed and then log it, so that the failing test can be repeated if necessary with seed assigned manually. -- Ihor Radchenko // yantar92, Org mode contributor, Learn more about Org mode at . Support Org development at , or support my work at