mickeynp / combobulate

Structured Editing and Navigation in Emacs with Tree-Sitter
GNU General Public License v3.0
945 stars 54 forks source link

New architecture #7

Closed DamienCassou closed 1 year ago

DamienCassou commented 2 years ago

Problem

I see several problems with the way combobulate's code is split into files:

  1. There is no library of functions another elisp hacker could build on without adding a dependency on combobulate's user interface.
  2. Combobulate decides which language is used in a given buffer by looking at the current major-mode. This is not good enough for Javascript vs. Typescript.
  3. combobulate.el requires language-specific combobulate's file, making it awkward for an external package (or an end-user) to specify another language or tweak an existing one.

Possible solution

I suggest a different architecture:

This architecture doesn't say where to define key bindings. There are several solutions:

  1. The user is responsible for doing that (might make sense early in the project's life) and the README gives an example setup.
  2. A combobulate.el file (depending only on libcombobulate.el) defines all general-purpose key bindings and the user must setup key bindings for the language-specific commands.
  3. Each language-specific file defines its own minor-mode (e.g., combobulate-javascript-mode) with its own key bindings; general-purpose key bindings can be defined in a combobulate-core.el file that all language-specific files import.
  4. There are other solutions.

This is an architectural pattern I've followed for several projects and it always helped me. For example:

I usually decompose my project into several git repositories but that's not mandatory (I just like the project to have clear boundaries).

mickeynp commented 2 years ago

These are all valid points, Damien. Thank you taking the time to writing this out.

Right now, my main interest is experimenting with the ergonomics of tree-sitter-aware semantic tools. I will indeed split out (and remove) dependencies to third-party libs as I never wanted to thrust those on people; they're there right now for no other reason than to test what is possible --- combulate's just an alpha prototype, and it's got more important issues than those right now to tackle than MVC.

Namely:

  1. Having to build and maintain sparse trees of 'interesting' nodes. This is slow.

  2. Dealing with the complexities around whether point is intersecting the right node. If point is to the right of <foo/>-!- (for example) it's effectively outside it, and detecting adjacency without interval trees (which I've also experimented with, but are hard to keep in sync with TS without reinventing the whole thing) or another datastructure that is quick to check for adjacency due to the sluggish performance of having to walk the tree to check every time. This is a serious issue, actually, because node spans are just the length of their syntactic unit, notwithstanding generalised ones that might span a whole semantic class of things, like a whole function or a class. Consider:

    def foo():
    print("foo")
    -!-

    Point is at module and not inside a function. Technically correct.... but unhelpful. You could nav backward until you encounter a non-whitespace char and then back once more --- I did this with an experimental indentation engine I wrote for Python using TS -- but that brings a whole host of awkward corner cases along with it.

  3. How can I extend Emacs's builtin navigation and editing facilities in a way that is mostly transparent and builds on what's already there with additional semantic and syntactic awareness

  4. Handling undo, and dealing with the tree sitter parse tree going out of sync with the buffer text during buffer changes made in elisp.