Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Customize which elements render in certain render modes #269

Closed
ficcdaf opened this issue Dec 22, 2024 · 8 comments
Closed

feat: Customize which elements render in certain render modes #269

ficcdaf opened this issue Dec 22, 2024 · 8 comments
Labels
enhancement New feature or request

Comments

@ficcdaf
Copy link

ficcdaf commented Dec 22, 2024

Neovim version (nvim -v)

NVIM v0.11.0-dev-1397+gfd05c7f19d

Neovim distribution

LazyVim (non extras.lang.markdown installation)

Description

I have set the following in my config:

    anti_conceal = {
      enabled = true,
      ignore = {
        code_background = true,
        sign = true,
      },
    },

This works as expected when moving the cursor around in normal mode (very smart feature, by the way!) I'm wondering if it's possible to make signs and code blocks still be "rendered" in insert mode. It's just a bit jarring when I'm editing a code block, and I enter insert mode, and all of a sudden the background highlight disappears. Thanks!

@ficcdaf ficcdaf added the question Further information is requested label Dec 22, 2024
@MeanderingProgrammer
Copy link
Owner

You can specify the modes in which the plugin does rendering in general to include insert mode via the render_modes config. However currently there is no way to specify which components to render in general in different modes. It makes sense as a feature, will give it some thought!

@ficcdaf
Copy link
Author

ficcdaf commented Dec 23, 2024

Great :) I think it would be really nice to have! Let me know if you need help implementing it.

@ficcdaf ficcdaf changed the title help: Is it possible for *only* certain rendered elements to remain visible in insert mode? feat: Customize which elements render in certain render modes Dec 23, 2024
@MeanderingProgrammer MeanderingProgrammer added enhancement New feature or request and removed question Further information is requested labels Dec 23, 2024
@MeanderingProgrammer
Copy link
Owner

So I have an implementation that allows all of the enable flags to be a list of modes, but I'm not thrilled with the results, mostly because of the verbosity.

You would need to set a subset of the render_modes on every component you want to disable in insert mode. For example:

require('render-markdown').setup({
    render_modes = { 'n', 'c', 't', 'i' },
    heading = { enabled = { 'n', 'c', 't' } },
    paragraph = { enabled = { 'n', 'c', 't' } },
    dash = { enabled = { 'n', 'c', 't' } },
    bullet = { enabled = { 'n', 'c', 't' } },
    checkbox = { enabled = { 'n', 'c', 't' } },
    quote = { enabled = { 'n', 'c', 't' } },
    pipe_table = { enabled = { 'n', 'c', 't' } },
    link = { enabled = { 'n', 'c', 't' } },
    inline_highlight = { enabled = { 'n', 'c', 't' } },
})

So the plugin will now render in insert mode (because of the i in render_modes) but all components except signs and code blocks will be disabled in insert mode (because of the enabled values not having an i).

Similarly I could add a disabled configuration for each component, but that's really only a mild improvement:

require('render-markdown').setup({
    render_modes = { 'n', 'c', 't', 'i' },
    heading = { disabled = { 'i' } },
    paragraph = { disabled = { 'i' } },
    dash = { disabled = { 'i' } },
    bullet = { disabled = { 'i' } },
    checkbox = { disabled = { 'i' } },
    quote = { disabled = { 'i' } },
    pipe_table = { disabled = { 'i' } },
    link = { disabled = { 'i' } },
    inline_highlight = { disabled = { 'i' } },
})

An alternative to these would be to allow some kind of categorization based on mode and then allow components to specify which categories they're active for. Something like:

require('render-markdown').setup({
    render_modes = { 'n', 'c', 't' },  -- The default value, only here for reference
    categories = {
        insert = { 'i' },
    },
    code = { active = { 'insert' } },
    sign = { active = { 'insert' } },
})

So basically if the mode is not categorized everything is rendered as usual. However if the mode does fit into a category only active components will be run.

Curious what your input on these options is and if you have a preference:

  1. Enable components by mode
  2. Disable components by mode
  3. Categorize modes and render when active

I think I'm leaning towards the 3rd one, though it's a little more involved, but i can also see it being difficult to explain / use.
Or if you have some other idea in mind please LMK!

@ficcdaf
Copy link
Author

ficcdaf commented Jan 1, 2025

I like the last idea a lot -- basically, users could set the render_modes as before. Defining the custom categories table seems a bit unnecessarily complicated, though. How about this:

Instead of the active table, each component could have a force_render option, which defaults to nil. Force render could be set to a list of string (same format as render_modes). Basically what could happen is, if the user doesn't set any force_render option, that component will follow the render_modes behavior. But if they do, for example force_render = {'i'}, then that component would be rendered in all the modes defined in render_modes, plus in insert mode.

This way, you can still mostly follow the default setting and occasionally override it for certain components if you want. Maybe there could be a force_not_render option as well, which does the opposite. What do you think of this?

@MeanderingProgrammer
Copy link
Owner

MeanderingProgrammer commented Jan 2, 2025

Thanks for the suggestion, I see why this would work well for users. I think I can implement something fairly close to it. I do have 2 open questions:

  1. Should a forced render be considered rendered or not? This only really impacts the window options we set, like toggling the conceallevel. I think it kind of needs to be considered rendered since several components wouldn't work without a high conceal level. At the same time for your particular use case you just want signs and code blocks and as a side effect links would end up concealed. Is this okay or should I make this somehow configurable as well?
  2. Making just the signs from headings render in some modes and not others is proving tricky. It's easy when we're rendering a heading to check the configuration for both the component and the overall signs configuration and skip if either is disabled. It's much more tricky to do something like, oh the signs are enabled globally, so render those for any component they're in. So would the ability to enable headings as a whole in insert mode be alright, or is that too visually distracting?

Notes to self for implementation:

  • Need to take force_render values across components into account when deciding autocommand events & whether to render or not.
  • When rendering a component need to check if current mode in render modes. If not then component must have mode in force_render.

@ficcdaf
Copy link
Author

ficcdaf commented Jan 3, 2025

  1. Yeah, I imagine it would be considered rendered, otherwise it may introduce a lot of extra bugs
  2. Hmm... at the end of the day, for me personally the signs aren't the biggest deal, it's really just the code block background! so if it's proving too difficult to implement, I'd say it's not worth it.

MeanderingProgrammer added a commit that referenced this issue Jan 4, 2025
## Details

Request: #269

Previously the only level of control users had was what modes to perform
rendering in and what components (headings, code blocks, etc.) should be
rendered when we do this.

This is a good amount of control however some users would prefer to have
a cleaner experience in insert mode while doing a subset of the
rendering done in normal mode.

To make this possible was a decent refactor but actually not too bad
because of how information is centralized in a couple key objects.

For users the way this works is the current top level `render_modes`
value now defines the set of modes where all enabled components are
rendered. Now in addition to this each individual component has its own
`render_modes` config which can specify additional modes (or all with a
boolean) in which only these components will be rendered.

So the workflow to decide rendering is now:

- Is component enabled
- Is the current mode in the top level config
- If not check if the mode is defined in the component level configuration
- Any unmatched components are skipped

Also needed to alter the logic to decide is the current mode rendered or not.
It is now the super set of all modes defined in the top level
configuration plus any defined within components. This does not matter
for the most part but does alter the events we listen on (if a component
needs to be rendered in insert mode we need to listen to insert mode
text changes now) along with the window options we set, most importantly
the conceallevel will remain at the rendered level since this is
important for many rendering features.

Example:

```lua
require('render-markdown').setup({
    render_modes = { 'n', 'c', 't' },
    heading = { render_modes = { 'i' } },
    code = { render_modes = { 'i' } },
})
```

With this configuration all components will be rendered in `normal`,
`command`, and `terminal` modes, additionally headings and code blocks
will be rendered in `insert` mode, but no others.

The default value for every component's `render_modes` is set to
`false`, which is equivalent to the empty list. This means by default
nothing is changed and we continue to render according to only the top
level `render_modes` value.

Some other minor things needed to change but nothing of particular note.
@MeanderingProgrammer
Copy link
Owner

Well took a little bit but added this here: 4d8e991

I decided to just call it render_modes, keep everything consistent. You can just specify them on individual components in addition to the top level value which basically defines the default for each component. LMK what you think!

@ficcdaf
Copy link
Author

ficcdaf commented Jan 7, 2025

this looks great, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants