emacs-devel
[Top][All Lists]
Advanced

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

Extending define-derived-mode


From: Yuan Fu
Subject: Extending define-derived-mode
Date: Mon, 29 May 2023 22:16:49 -0700

When we were adding tree-sitter modes a couple of month ago, it was
clear that the current major mode model needs some upgrade, I’d like
to discuss the things we need and how can we address them.

Features we want:

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.

2. Inheritance:

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).

2.2 For xxx-mode and xxx-ts-mode, they should be able to share some
    setup, and diverge on some other.

2.3 Right now we have a little inconsistency regarding hook
    inheritance. js-json-mode inherits from js-mode, json-ts-mode
    inherits from js-ts-mode. So there isn’t a shared hook that runs
    in both js-json-mode and json-ts-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.

[1] If Y is different enough from X such that it has its own
tree-sitter grammar, y-ts-mode cannot inherit x-ts-mode’s code because
they target different grammar.

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.


Implementation wise, here are my thoughts regarding each feature:

1. Shouldn’t be hard to add some facility to define-derived-mode that
   runs before everything and dispatches to different major modes.
   Maybe define-derive-mode can have a :dispatch keyword which takes a
   arbitrary function; and if the function returns nil, rest of the
   major mode setup are skipped.

2. For those who don’t know, right now we define a xxx-base-mode,
   which is then inherited by xxx-mode and xxx-ts-mode. My
   understanding is that xxx-base-mode is considered a temporary
   solution and will be replaced. Personally I don’t mind
   xxx-base-mode, why can’t we keep it?

   From what I can see, major mode inheritance is used for three
   purposes: a) inheriting from a “virtual” mode (eg, prog-mode) for
   hooks and keymaps; b) y-mode deriving from x-mode, where Y is a
   derived language from X; c) y-2-mode deriving from y-mode as an
   extension, where y-2-mode can be a user’s personal customization,
   or distributed as packages (js2-mode).

   I think the current model serves a) and c) just fine. But for b),
   and for when there are multiple major modes for the same language,
   the single-inheritance model can be a bit lacking. I don’t have a
   clear vision right now, perhaps we want multiple inheritance for
   hooks, perhaps we can simply allow define-derived-mode to specify
   additional hooks to run, and create some language hooks that are
   shared by every mode serving that language. I’d like to hear your
   thoughts.

3. Like 1., shouldn’t be hard to modify define-derived-mode to add a
   hook that’s not run by its derived modes.

Yuan




reply via email to

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