Subject: bug#77658: 30.1.50; Eglot: wrong-type-argument: consp, #<marker at 1 in file> when saving

From: Stefan Monnier <[email-masked]>
Date: Sun, 27 Apr 2025 00:11:32 -0400

> I believe what is happening here, is that `insert-file-contents`, when
> inserting an empty file into an empty buffer (maybe in other
> situations as well), may cause `before-change-functions` to be run but
> `after-change-functions` never runs.  Therefore,
> `eglot--before-changes` inserts an element into
> `eglot--recent-changes` that `eglot--after-changes` never gets
> a chance to fix up.

Indeed, `insert-file-contents` does not run `after-change-functions` if
it does not actually modify the buffer, whereas it does always run
`before-change-functions`.  You can verify that for yourself with:

    (defvar-local my-changes nil)
    (add-hook 'after-change-functions
              (lambda (&rest args) (push `(after . ,args) my-changes)))
    (add-hook 'before-change-functions
              (lambda (&rest args) (push `(before . ,args) my-changes)))

and then do `M-: (insert-file-contents "/dev/null") RET` and check the
value of `my-changes`.

As you noted, this is not considered as a bug.
So, the problem is on Eglot's side because it makes sadly invalid
assumptions about how before and after change-functions are paired.

The Track-Changes library should handle this case correctly, AFAIK.
It might be worth trying out your recipe with an Eglot that uses
that library (again).

        Stefan

PS: I hope at some point João will reconsider his decision to not use
Track-Changes, although I understand his motivation: while Eglot doesn't
benefit much from Track-Changes because Eglot has already gone through
most of the pain of handling the corner cases of
`before/after-change-functions`, Track-Changes (and other users of
`before/after-change-functions`) would benefit from Eglot using it
because Eglot is used widely and because Track-Changes comes with
extensive sanity checks to detect when `before/after-change-functions`
are not run correctly (in which case it can report those problems, and
it also knows how to recover from them).  Furthermore it also records
extensive data about the circumstances of those problems, to help find
their origin.