Subject: bug#68217: 30.0.50; Fix for eglot--format-markup to handle bad markdown

From: João Távora <[email-masked]>
Date: Tue, 2 Jan 2024 22:11:29 +0000

On Tue, Jan 2, 2024 at 6:29 PM Gary Oberbrunner <[email-masked]> wrote:
> The markup arg passed to eglot--format-markup is:
>
>   (:kind "markdown" :value "```python\n(class) openfx\n```")
>
> I'm using pyright-langserver as the LSP server; I expect it is
> returning malformed markup or something that eglot doesn't expect.

Just for a bit of context to readers, markdown-mode at least in
certain situations, uses Emacs major modes to achieve syntactic
fontification of markdown code blocks.

Here, I don't think it's malformed markdown per se, it's that between
the markdown code block that the server says is Python there seems
to be some odd Python snippet:

(class) fx

So yes, the server is returning something fishy, but the way we
react right now isn't reasonable.

For me, it seems likely that python-mode when invoked by
markdown-mode didn't care about this and fontified away (when
triggered with font-lock-ensure), but python-ts-mode doesn't
and signals this error.

It could be caused by markdown-mode defaulting to the (new?) TS
mode when fontifying such markup.  Why is it defaulting like that?
Maybe it's just doing what Emacs would do if the user visited a
Python file?

I think markdown-mode choosing the same mode  that Emacs would
choose for interactive operation is correct.  But I'd first like
to confirm it isn't doing this arbitrarily: in the recent
past, I've seen markdown-mode load up cc-mode when I'm not using
the TS mode by default.

Jason, can you confirm what the intended behaviour is?

Also, I think python-ts-mode is also correct to signal the error
when finding this odd form, at least when the fontification routines
are invoked non-interactively.

Yuan, can you confirm what the intended behaviour is?

> I note that the following patch appears to fix it for me:
>
> modified   eglot.el
> @@ -1813,7 +1813,9 @@ Doubles as an indicator of snippet support."
>              (message-log-max nil)
>              match)
>          (ignore-errors (delay-mode-hooks (funcall mode)))
> -        (font-lock-ensure)
> +        (condition-case-unless-debug oops
> +            (font-lock-ensure)
> +          (error (eglot--warn (error-message-string oops))))
>          (goto-char (point-min))
>          (let ((inhibit-read-only t))
>            (when (fboundp 'text-property-search-forward) ;; FIXME: use compat

Finally there is this muffling of the error unless debug-on-error is true.
I think this is the right thing to do, at least right now.  In
an alternate universe with a restart system, it would probably be more
elegantly solved, but that's what we have now.

João