Subject: bug#68246: 30.0.50; Add non-TS mode as extra parent of TS modes

From: Dmitry Gutov <[email-masked]>
Date: Wed, 17 Jan 2024 04:05:58 +0200

On 17/01/2024 00:00, João Távora wrote:
> On Tue, Jan 16, 2024, 17:45 Dmitry Gutov <[email-masked]> wrote:
>>
>> On 16/01/2024 12:34, João Távora wrote:
>>> I see no real categorization or classification.  I just see
>>> a many-to-one mapping of major modes to languages.
>>
>> It might even be many-to-many, at least in some cases.
>>
>> E.g. js-ts-mode being good for both :js and :jsx.
> 
>>
>> Not sure how useful this -to-many relation is going to be in the above
>> cases, but it's probably a good illustration of the possibility.
> 
> According to https://react.dev, jsx is a "JavaScript syntax
> extension".  So it would seem JSX is a superset of JS.  If
> js-ts-mode parses it perfectly, it could be called
> jsx-ts-mode instead.

The grammar tree-sitter-js supports JSX. So the mode is called js-ts-mode.

> But does it? I see Emacs modes specific for jsx out there, I
> suppose people use them for a reason.

They are older.

> There's also tsx-ts-mode
> and typescript-ts-mode.

Like I said:

   tsx-ts-mode is probably okay for both :tsx, :jsx and :js but not
   :typescript (in general, because of certain clashing syntax).

> At the end of the day, a language is not so easy to define,
> but it's not that problematic either, especially in the editor
> (in the compiler, it's much more important).
> 
> The best sources are a standard, when it exists, but each iteration,
> sometimes  each compiler is also its own language.  There's "GNU C",
> "ANSI C", C17, C23.  All handled by the C modes we have and the best
> way we have to designate this is just "C".  c++-mode also handles
> this code by the way, probably flawlessly, and yet we don't say
> c++-mode is for C and C++.
> 
> But if you want, I don't think there's any big problem
> in making get-language-for-mode return a list, with
> the most important likely language at the top.

It would be a problem if we decide that the major mode function runs the 
language-specific hook, and not set-auto-mode-0 like in my patch 
(because the mmode function would like run the hooks for all supported 
languages, rather than just the current one).

> I predict it'll be pretty rare, but I guess you could
> have this (excuse the ugly CamelCase for demo purposes)

It might indeed be rare enough for this to be a problem, and we might 
even decide to prohibit such usage, keeping the relation many-to-one.

> (setq auto-mode-alist '(("\\.js$" . :JavaScript)
>                          ("\\.jsx$" . :JavaScriptReact)))
> 
> (setq m-m-remap-alist '((:JavaScript . js-ts-mode)
>                          (:JavaScriptReact . js-ts-mode)))

It indeed should work okay with my proposal, but might be harder to do 
if the languages are inserted as part of the existing modes hierarchy 
(e.g. as "abstract" symbols). That is assuming we do want the language 
hook to run - which seems like important goal from my POV.

> And 'buffer-language' becomes more like:
> 
> (or buffer-overriding-language-keyword
>      (with-current-buffer buffer (get-language-for-mode major-mode))
>      (let (kw)
>         (and buffer-file-name
>              (keywordp
>                (setq kw
>                      ;; yes I know this needs regexps
>                      (alist-get buffer-file-name auto-mode-alist)))

There is a bunch of variables to look up: auto-mode-alist, 
magic-mode-alist, interpreter-mode-alist, magic-fallback-mode-alist. I 
didn't want to duplicate the logic from set-auto-mode, but this of 
course could be done.