[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
- Extending define-derived-mode,
Yuan Fu <=