Add a configuration mechanism and use it to support user customization of keymaps, user commands and region decorations.#15
Conversation
The setup() function now takes a user option table. A new `opt` module manages both the user option table and a corresponding default option table, and serves requests for hierarchical option names (represented as dot-separated strings). The `opt` module's query function supports fallback to default and "repair" of incorrectly formatted user option tables. The new option mechanism is used to support user customization of the following: * keymaps * user commands * region decorations The region decoration mechanism has been enhanced to permit better differentiation between the various types of region. Modified get_larger_ancestor() to make it return the outermost of several colocated nodes. Rationale: If innermost node is returned, parent node decoration will not be visible when there are colocated nodes.
|
Hey thanks for this, and clearly the high level of effort! I'll review it this weekend. |
My pleasure! Sounds good. |
|
@Dkendal Did you ever get a chance to review? |
@Dkendal I completely understand if you just don't have time to review the PR right now, but would appreciate it if you'd confirm receipt of my previous comment… |
Dkendal
left a comment
There was a problem hiding this comment.
Sorry again for the delay, life gets busy!
There are quite a few good ideas in this, but in it's current form I don't want to merge.
In general, I'd like to move this plugin more in line with the advice listed here. Specifically this means:
- removing any configuration specific DSL,
- moving configuration to be as lazy as possible (no setup call)
- not providing default mappings, only
<plug>mappings
This PR contains a number of configuration specific DSLs that move away from this goal, and specifically for the keymap example I don't see a benefit over <plug> keymaps, and there is quite a lot of new configuration specific code that I'm reticent to maintain.
If it's okay with you I'd like to close this PR and cherry pick some of the ideas?
| highlights = { | ||
| Selection = function(o) return { bold = true, bg = o.visual.bg.hex } end, | ||
| SiblingStart = false, | ||
| Sibling = function(o) return { bg = o.visual.bg.mix(o.normal.bg, 50).hex } end, | ||
| -- Make Parent bg color noticeably lighter than Siblings. | ||
| Parent = function(o) return { bg = o.visual.bg.mix(o.normal.bg, 80).hex } end, | ||
| ParentStart = false, | ||
| }, |
There was a problem hiding this comment.
I like the idea of having these highlights configurable - however the choice of the current normal and visual highlight feels arbitrary. I would prefer either make the callback accept 0 parameters, or have one parameter which is the hsluv module.
I'm somewhat of the mind that it's more flexible to not include this in configuration, beyond static values - and just provide an example in the readme on how to define an autocommand that sets the highlights when the colourscheme changes.
| -- `true` to cause attributes like bold and italic to "bleed through" from Parent | ||
| -- to Siblings. | ||
| inherit_attrs = true, | ||
| -- `true` to replace default 'highlights' with user overrides, false or nil to merge | ||
| replace_defaults = false, |
There was a problem hiding this comment.
Related to the comments above, I would prefer to not introduce any custom configuration DSL, as it increases the documentation and learning surface area. I'm okay with exposing options that are just delegates to vim api functions, but for anything more advanced I'd prefer to only provide example in the README.
This is inline with the advice here.
| function M.setup_keymaps() | ||
| ---@type table<string, treeclimber.KeymapEntry>|boolean |
There was a problem hiding this comment.
I don't have a substantive argument here, but I would really prefer not to support a custom DSL for define keymaps.
The direction I'd like this plugin to move is to use <plug> keymaps which is inline with this advice. What benefit does adding this custom DSL provide?
|
@Dkendal I appreciate your taking the time to provide a detailed review. I've read over your comments, but it will be a day or two before I can take the time to go through the linked documentation and provide feedback... |
|
I’ve since made a number of changes on the main branch to implement the
customization for keymaps and highlights, but I haven’t implemented
customizations or alternative names for commands. I’m not really convinced
that the commands provided are likely to conflict, warranting the added
complexity for supporting customization for them, but i would consider it.
…On Mon, Sep 8, 2025 at 6:44 AM Brett Stahlman ***@***.***> wrote:
*bpstahlman* left a comment (Dkendal/nvim-treeclimber#15)
<#15 (comment)>
@Dkendal <https://github.com/Dkendal> I appreciate your taking the time
to provide a detailed review. I've read over your comments, but it will be
a day or two before I can take the time to go through the linked
documentation and provide feedback...
—
Reply to this email directly, view it on GitHub
<#15 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAYEBOZSOSKZZY5Y5XW246D3RVMXLAVCNFSM6AAAAAB36XQRMCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTENRVG4YDEMRSGA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Overview
Adds a configuration mechanism intended to simplify installation, loading and configuration of the treeclimber plugin, and uses it to provide user customization of the following:
Also, renames 2 misleading api function names in a backwards-compatible way.
Rationale: The names
select_siblings_forwardandselect_siblings_backwarddo not match their description, and thus, could be a source of confusion to new users. Aliases have been introduced to ensure the old functions can still serve as the target of mappings (though only the new names can be used as keys in the new option table).Motivation
Currently, a user who simply wishes to override one of the default keymaps must reimplement
setup_keymaps()and explicitly callsetup_user_commands()andsetup_augroups(). This approach exposes too much implementation detail to the user and increases the probability that a prospective user unfamiliar with the Neovim plugin system will simply give up. Moreover, although a considerable amount of work seems to have gone into the modules supporting HSLUV colors, the lack of a configuration mechanism prevents the user from benefiting from it. And because all regions other than the primary selection region use the same bg color, there is little benefit to the support of separate sibling and parent regions.When I first started using treeclimber, I was occasionally surprised by the parent-child relationships encountered while navigating the AST: e.g., the fact that a function's parameter list is a sibling of its body felt unintuitive. Having the region decorations provide clear disambiguation between parent and child, and even between adjacent children, can reduce confusion for the new treeclimber user (or a treeclimber user working with a new language).
Installation/Loading
With a plugin manager like Lazy, a default installation/load is still as simple as adding a file like this under ~/.config/nvim/lua/plugins:
Overriding the defaults is almost as easy: e.g., to customize the keymaps for
select_forward/backwardand add bold-italic to the decoration for the currently-selected node...Existing users should not be affected by these changes, as it's still possible to call the
setup()function explicitly, with or without an option table. As before, omitting the option table in the call tosetup()requests defaults.Option Table Support
Module
optprovides a generic interface to a hierarchical option table, which encapsulates a table of defaults and (optionally) a table of user overrides. The treeclimber defaults are defined in the singleton moduleconfig, which creates and manages anoptinstance capable of serving requests for specific option keys. The option table is organized in sub-trees: e.g.,uifor keymaps and user commands,displayfor highlights, etc. The default option table is documented with comments in the README.md. Theconfigmodule providesget()andget_default()methods, which accept option keys as a string of dot-separated components: e.g.,ui.keysanddisplay.regions.highlights. Theoptmodule does more than simply merge user overrides into the default table. The reason is that users occasionally provide malformed option tables, and when they do, plugins based on the idiomatic vim.api.tbl_deep_extend() approach tend to spew errors and die, leaving the plugin in an indeterminate state. The approach in this PR, built around theoptmodule, is to fall back to sane defaults with a warning.Keymap Customization
The option table's nested key
ui.keysmay be used to customize (without completely replacing) the default keymaps.This table contains a separate key for each treeclimber api function. The key value format is flexible enough to acommodate the most complex scenarios, while keeping the simple (and common) use case simple: e.g., to change the default keybinding for
select_forwardto<A-w>...To create different maps in visual/select and normal/operator-pending modes...
To disable a map altogether...
User Command Customization
The option table's nested key
ui.cmdsmay be used to customize the default user commands, changing the command name or disabling the command altogether.Region Highlighting Customization
The current implementation suffers from the following limitations pertaining to region decoration:
apply_decorationcauses all adjacent siblings to combine into a single visual group; thus, even if the user manually altered the bg colors, the desired differentiation between groups would not be achieved.The
display.regions.highlightskey allows the user to tailor the highlighting of the various groups and/or disable unwanted groups altogether. The format of the key is flexible and fully documented in the README. The simplest key values arevim.api.keyset.highlightobjects containing fg/bg colors and/or attributes like 'bold' and 'italic'. But they can also be callback functions that return such objects. Callbacks permit use of HSLUVHighlight objects that may not be available until colorscheme load, which is most likely after the user option table has been defined.There's also an option that determines whether attributes defined for the parent groups "bleed through" to the children (siblings). Because of the way Neovim handls extmark
hl_groups, attributes set on the Parent also appear in the contained Siblings. If bleed-through is not desired, it's necessary to break the Parent into discontiguous regions. (An optimization would permit use of a single Parent if it's determined that none of thehl_groups use such attributes, but the optimization has not been implemented.) There's also an option that determines whether a user-provided highlight combines with the parent or replaces it. This affects only attributes like bold and italic, since Neovim doesn't combine the bg colors.The default parent and sibling bg colors are now visuallly distinct, and the reworked
apply_decoration()intentionally allows the parent highlight to show through in the space between named children, making it easier to tell exactly where one sibling ends and another begins.