emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Extending define-derived-mode


From: Yuan Fu
Subject: Re: Extending define-derived-mode
Date: Wed, 31 May 2023 14:31:06 -0700


> On May 30, 2023, at 7:16 AM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> 
>> You are basically talking about different modes that support the same
>> programming language or file format.  We never had this in Emacs, with
>> (AFAIK) the single exception of perl-mode vs cperl-mode, which never
>> played well.
> 
> Not quite: within Emacs there's at least pascal.el vs opascal.el and
> sgml-mode vs nxml-mode, and depending on how you define it there's also
> postscript-mode vs doc-view-mode, as well as c-mode vs image-mode (for
> XPM files).
> 
> And if we consider packages not bundled with Emacs, there are many more
> case:s such as latex-mode vs LaTeX-mode, python.el vs python-mode.el,
> js.el vs js2.el vs js3.el, octave-mode vs matlab-mode.
> 
>> I think some thought was invested in trying to reconcile
>> them (Stefan, am I right?), but we never came up with a good solution.
>> Not sure if that is a general problem or not.
> 
> We don't really have a good answer yet, no.
> `major-mode-remap-alist` is aimed at this problem, but brand new so it's
> not clear how useful it will be for that and it's definitely not
> a complete solution.
> 
>>> 1. Fallback modes: user enables xxx-ts-mode, but there’s no
>>>   tree-sitter grammar for xxx, so Emacs falls back to xxx-mode
>>>   instead. This feature is also desirable for some non-tree-sitter
>>>   modes, like TeX modes. Ideally the dispatch should happen before
>>>   major mode does anything.
>> 
>> This fallback must be user-controlled.
> 
> I don't see this as a big problem, actually (there are already several
> mechanisms that can do that).  The question of how "user enables
> xxx-ts-mode" is probably harder.

Couldn’t they use major-mode-remap-alist?

There are mechanisms to do that, yes, if you are talking about the advices for 
latex-mode and bash-ts-mode. But I’d love to see a standard, cleaner way to do 
it.

> 
>>> 2.1 For xxx-mode and xxx-ts-mode, there should be shared hook. More
>>>    generally, we want to be able to have a shared hook for similar
>>>    modes (same language, similar language, etc).
> 
> As Eli explains, this is not always desirable.  And at other times it
> *is* desirable: the users's hook function can set/use features that are
> specific to one of the alternative, but they can also set/use features
> that are shared between the alternatives :-(

For sure, those that aren’t sharable should go into the not-shared hooks. I’m 
mainly saying that there should be a shared hook, so users _can_ share some of 
the configs.

> 
> Most users use only one of the alternatives, tho, so it's usually not
> a big problem (other than introducing incompatibilities when Emacs's
> defaults change from one alternative to another).

Keep in mind that when people try out tree-sitter modes, they are unlikely to 
just throw away their config for the old mode; also since tree-sitter and 
grammars aren’t the easiest to install, people working on multiple machines 
probably want both tree-sitter and no-tree-sitter modes configured and ready to 
go. So I think we’ll see a lot of people having config for both modes (me 
included).

> 
> It can be more annoying for `.dir-locals.el` per-mode settings.

And in general, any configuration that takes a major-mode symbol as the key. 
There are quite a few of them in Emacs. I think this is a big motivation for 
having multiple inheritance for derived-mode-p, and sharing a base mode.

> 
>>>    More generally, if there is a language X and a derived language Y,
>>>    and we have x-mode, x-ts-mode, y-mode, y-ts-mode, how should
>>>    inheritance of code and hooks works among them? y-mode probably
>>>    wants to inherit from x-mode, and y-ts-mode probably wants to
>>>    inherit hook (but not necessarily code [1]) from x-ts-mode.
> 
> `y-ts-mode` can explicitly run `y-mode-hook` (or `x-ts-mode-hook`).
> 
> We may more generally want to extend our notion of "derived" mode to
> allow "multiple inheritance".  For the actual activation code part,
> multiple inheritance is a hard problem that we probably don't want to
> tackle, but we can easily run several parent hooks, setup multiple
> keymap karents, and make `derived-mode-p` support multiple parents.

I agree that we don’t want multiple-inheritance for activation code. Also, as 
Juri pointed out, we can encapsulate code into functions and call functions in 
major mode body. Multiple-inheritance for hooks and maps has the potential 
disadvantage of being confusing. Right now it’s clear what hooks are run when a 
major mode turns on, but with multiple-inheritance it may not be. (I know I 
brought up the multiple-inheritance thing in the first place, I’m just writing 
whatever comes to my mind :-)

How do you setup multiple keymap parents? I thought a keymap can only have one 
parent?

Here’s another wild idea: we keep single-inheritance for define-derived-mode; 
major modes for the same language inherits from the same base mode; add a 
feature where xxx-base-mode is automatically defined when someone defines a 
major mode with xxx-base-mode as parent, so we don’t need to pre-define 
base-modes for every possible language; separate to major modes, we add a 
tag/category system, where modes can adopt multiple tags/categories, and a 
function like mode-has-category-p can work similarly to derived-mode-p.

> 
>>> 3. Unrelated to tree-sitter, here’s something I personally want:
>>>   it would be nice if every major mode can
>>>   have a hook that’s not run by its derived modes. Use case:
>>>   sage-mode inherits python-mode. I have eglot-ensure in
>>>   python-mode-hook but don’t want it when using sage-mode. Right now
>>>   I have to wrap eglot-ensure with a lambda function so that it only
>>>   runs when major-mode = python-mode.
>> 
>> What is wrong with that solution?  Except for the maybe minor
>> inconvenience of having to use a lambda-function, what different way
>> of doing this would you envision except telling define-derived-mode to
>> run some hook only under this-and-that condition?
> 
> While I tend to agree that it's not a big deal, I also agree that it's
> arguably cleaner if parent modes are kept "abstract", so rather than
> have `c++-mode` inherit from `c-mode`, you make them both inherit from
> a `c-base-mode`.  It tends to smell of overkill, tho.

Maybe defien-derived-mode can additionally define a function that only runs the 
major mode body but doesn’t setup anything that are autogenerated (eg, keymap, 
hooks, etc). This way another major mode is free to reuse other mode’s setup 
while not inheriting from that mode.

Yuan


reply via email to

[Prev in Thread] Current Thread [Next in Thread]