Skip to content

help: render-markdown is not activated when lazy loaded with vim-plug #309

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

Closed
Dupond opened this issue Jan 27, 2025 · 9 comments
Closed
Labels
question Further information is requested

Comments

@Dupond
Copy link

Dupond commented Jan 27, 2025

Neovim version (nvim -v)

0.10.3

Neovim distribution

N/A

Description

Hello!

I use Neovim with ArchLinux.

Here's my ~/.config/nvim/init.vim (all of it):

EDIT: I've tested with the following minimal ~/.config/nvim/init.vim file:

call plug#begin('~/.config/nvim/bundle')
    Plug 'https://github.com/MeanderingProgrammer/render-markdown.nvim', { 'for': 'markdown' }
call plug#end()

However, when I open a test.md file, render-markdown is not activated by default, and I have to enable it manually using :RenderMarkdown.

If I don't lazy-load the plugin by removing the { 'for': 'markdown' } part, then when I open test.mdit's working perfectly fine: render-markdown is activated by default.

What am I doing wrong? Thank you very much for your help!

@Dupond Dupond added the question Further information is requested label Jan 27, 2025
@Dupond
Copy link
Author

Dupond commented Jan 28, 2025

Here's what :checkhealth render-markdown says:

render-markdown: require("render-markdown.health").check()

render-markdown.nvim [version] ~
- OK plugin 7.8.9
- OK neovim >= 0.10

render-markdown.nvim [configuration] ~
- OK valid

render-markdown.nvim [nvim-treesitter] ~
- OK installed
- OK markdown: parser installed
- OK markdown: highlight enabled
- OK markdown_inline: parser installed
- OK markdown_inline: highlight enabled

render-markdown.nvim [icons] ~
- OK using: mini.icons

render-markdown.nvim [executables] ~
- OK none to check

render-markdown.nvim [conflicts] ~
- OK headlines: not installed
- OK obsidian: not installed

It says "OK markdown: highlight enabled", but it still doesn't work by default, and I still have to enable it manually with :RenderMarkdown enable.

Furthermore, the titles (e.g. # Title or ## Title) are all linked to the Title highlight group, independently from their level, so I can't highlight them separately by using RenderMarkdownH1, RenderMarkdownH2, etc. For example, hi RenderMarkdownH1 ctermfg=red ctermbg=blue cterm=bold has no effect at all. The only way to highlight the titles depending on their level is by using hi @markup.heading.1. However, they are correctly highlighted if I uninstall nvim-treesitter... but then render-markdown doesn't work well, of course :) Maybe it's a tree-sitter bug? If that's the case, I apologize for the noise...

Thank you very much for any help!

@MeanderingProgrammer
Copy link
Owner

There's a couple things to note here, but I'll start with a a high level overview of how this plugin works to clear up some confusion.

So neovim uses treesitter to perform syntax highlighting. treesitter is a generic and performant way to take a text file and parse it into a queryable tree. For example to find all the functions in a lua file, or more appropriately getting the headings in a markdown file. You can checkout the parsed tree by running :InspectTree after opening a file.

You can setup treesitter manually but most people use the nvim-treesitter plugin to handle installing / updating parsers. nvim-treesitter also comes bundled with highlight queries. These are stored in highlights.scm files for each language, the markdown one is here. This file assigns different parts of the parsed tree to highlight groups, which is what ultimately results in syntax highlighting.

To make writing this plugin easier and perform faster I do not do any direct parsing logic over the text. Instead I run queries against the existing treesitter tree for the current file and apply different styles over the elements. For example querying for the headings and adding the icon + some highlighting or getting list items and changing the marker icon. This is opposed to an alternative where I do some for each line in file if line startswith # type logic.

This means this plugin requires you to have treesitter running with a parser for markdown and since syntax highlighting effects how this plugin looks I also check that you have highlights enabled. So in the healthcheck when you see:

render-markdown.nvim [nvim-treesitter] ~
- OK installed
- OK markdown: parser installed
- OK markdown: highlight enabled
- OK markdown_inline: parser installed
- OK markdown_inline: highlight enabled

This does not mean that the plugin will work necessarily. Instead it tells you (and me when I help debug issues for users) whether you have the necessary dependencies up & running.

@MeanderingProgrammer
Copy link
Owner

Furthermore, the titles (e.g. # Title or ## Title) are all linked to the Title highlight group, independently from their level, so I can't highlight them separately by using RenderMarkdownH1, RenderMarkdownH2, etc. For example, hi RenderMarkdownH1 ctermfg=red ctermbg=blue cterm=bold has no effect at all. The only way to highlight the titles depending on their level is by using hi @markup.heading.1. However, they are correctly highlighted if I uninstall nvim-treesitter... but then render-markdown doesn't work well, of course :) Maybe it's a tree-sitter bug? If that's the case, I apologize for the noise...

The RenderMarkdownH1 ... RenderMarkdownH6 groups are only used for the icon that gets added by this plugin, descriptions are available here: https://github.com/MeanderingProgrammer/render-markdown.nvim?tab=readme-ov-file#colors. So setting a value for RenderMarkdownH1 would only change the icon but if the plugin is not running in general and you're not seeing an icon than I would expect nothing to change.

If you want to change the color per level using treesitter based highlighting than you'll need to set values for the general treesitter highlight groups @markup.heading.1.markdown ... @markup.heading.6.markdown. Though your color scheme should definitely be providing values for these, though not necessarily.

I assume when you disable nvim-treesitter you end up using the older regex based syntax highlighting (though I'm not sure), and I don't really know how that logic works. But I assume the issue here is that the colorscheme you are using provides highlight groups for regex based highlight groups and not the treesitter ones. To help debug things here can you switch to something like tokyonight for now, since I know that one provides all the standard highlight groups.

@MeanderingProgrammer
Copy link
Owner

lazy loaded with vim-plug

To bring this all back to your original problem, I do not recommend lazy loading this plugin in general. This plugin is already very efficient and only really does work on markdown buffers. This plugin creates an autocommand to listen for new buffers and then only attaches to them if they are markdown files. Attaching to a buffer involves adding more autocommands but only for the specific buffers.

By lazy loading you are saving startup time in joining your config and creating the top level autocommand. As well as a filetype check per new buffer before you open your first markdown file. Neither of these should be noticeable.

While lazy loading should work I don't explicitly support it and I don't validate that it continues to work. The issue is that different plugin managers have different ways of doing the lazy loading and depending on their implementation as well as the rest of your configuration there may be problems.

@MeanderingProgrammer
Copy link
Owner

I imagine what's happening here is related to the fact that this plugin relies on the FileType event to attach to buffers: https://github.com/MeanderingProgrammer/render-markdown.nvim/blob/main/lua/render-markdown/manager.lua#L18C34-L18C42.

When you try to lazy-load with vim-plug based on the filetype by the time this plugin does its setup and creates the autocommand the FileType event for the current buffer has already been executed. Basically we'll miss the current buffer because of when we're able to listen for new buffers being opened.

I would expect this to always be an issue with lazy loading but it's not a problem with lazy.nvim at least. I'm unsure if this is because of how the plugin manager is implemented or if lazy.nvim re-triggers events after lazy loading plugins to avoid this kind of a problem.

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

Issue: #309

Depending on how the plugin manager implements lazy loading, this plugin can
miss the initial buffer that triggered it to load.

This is because we setup a `FileType` autocommand to check which buffers
we should attach to. If the user configures this plugin to load based on
a filetype that event will not be processed by the autocommand (since it
has not been created yet). Instead everything will be setup but the
current buffer will end up ignored.

I'm unsure why this is not an issue with `lazy.nvim`, maybe it executes
autocommands after the initial load. This would make sense, but I have
not verified it.

To get around this attempt to attach to the current buffer at the start,
similar to the command based lazy loading edge case.
@MeanderingProgrammer
Copy link
Owner

I've added a small change to maybe handle this issue here: 1ba6fb7

Please update and LMK if it works!

@Dupond
Copy link
Author

Dupond commented Jan 29, 2025

Thank you so much for all these explanations! I've read them twice: everything is much clearer for me now.

I imagine what's happening here is related to the fact that this plugin relies on the FileType event to attach to buffers

Exactly! If I type :setlocal filetype=markdown after opening a markdown file, RenderMarkdown is correctly applied to the active buffer.

I've added a small change to maybe handle this issue here

Yes! It works! Thank you very much!

So what would be the correct way to apply the settings when lazy loading?

Here's what I've added to my init.vim:

call plug#begin('~/.config/nvim/bundle')
    Plug 'https://github.com/MeanderingProgrammer/render-markdown.nvim', { 'for' : 'markdown' }
    Plug 'https://github.com/nvim-treesitter/nvim-treesitter', { 'do': ':TSUpdate', 'for' : 'markdown' }
    Plug 'https://github.com/echasnovski/mini.icons', { 'for' : 'markdown' }
call plug#end()

function! SetRenderMarkdownOptions()
    if exists(":RenderMarkdown")
lua << EOF
require('nvim-treesitter.configs').setup({
    ensure_installed = { 'markdown', 'markdown_inline', },
    highlight = { enable = true },
})
require("render-markdown").setup({
    latex = { enabled = false, },
})
EOF
    endif
endfunction
augroup RenderMarkdownSettings
    autocmd!
    autocmd BufNewFile,BufRead * call SetRenderMarkdownOptions()
augroup END

Is this the right way to do it?

Thanks for the time spent explaining all this to me, and solving the problem!

@MeanderingProgrammer
Copy link
Owner

So what would be the correct way to apply the settings when lazy loading?

What I was getting at in all the details was that I have no suggestions for lazy loading this plugin. There are ways that will work for now, and you're welcome to use one you find but it may break in the future, both due to changes in plugin managers as well as this plugin. Just a few too many edge cases with when things load, what dependencies are available, and what events get triggered when. The fact that lazy loading worked in lazy.nvim but not vim-plug is an example of this.

Is this the right way to do it?

I would avoid calling the setup functions every time you open a buffer (BufRead). There's some state that gets cached that we clear when setup is called, may result in some weird behaviors if you open multiple markdown files.

I'm also generally confused with how this works. So while you're editing non-markdown files vim-plug will not load the plugin due to the 'for' : 'markdown', I'm guessing the plugins not being loaded is handled by the early termination using exists(":RenderMarkdown").

Then you open a markdown file which will cause vim-plug to load the plugin but that will most likely be executed after your autocommand since BufRead gets executed before FileType, at least I think it does. So your settings will only get applied when you open a second markdown buffer just due to the timing of events, at least that's what I believe would happen.

I don't really have a suggestion for how to do this properly but the way you have it might cause problems. But if it works it works, so feel free to keep it.

Maybe try to find another vim-plug configuration that lazy loads nvim-treesitter to point you in the right direction.

Again my general advice is don't lazy load and just call setup. If you're really trying to make lazy loading work lazy.nvim seems to handle things the best, at least as far as I've seen.

Thanks for the time spent explaining all this to me, and solving the problem!

No problem, happy to help :)

@Dupond
Copy link
Author

Dupond commented Jan 29, 2025

Thanks for your advice! Maybe I'll have a look at lazy.nvim, but you're right, I'll probably simply won't lazy load at all.

On my PC, TreeSitter and RenderMarkdown take about 100 ms to load in total. That's almost half the loading time of Neovim in my config; that's why I considered the lazy load solution.

MeanderingProgrammer added a commit that referenced this issue Feb 2, 2025
## Details

Related issues:

- #309
- #315
- #317

Okay this is getting convoluted to handle the differences in lazy
loading between `lazy.nvim` and `vim-plug`. The issues are as follows:

- `lazy.nvim` & Lazy Loaded: Attempting to attach to current buffer at
  the start will not pickup user configuration (called from plugin
  directory before setup) so may not listen to all needed events. This
  means we should not attach to current buffer and rely on the
  `FileType` auto command.
- `vim-plug` & Lazy Loaded: The initial load swallows the `FileType`
  event for the current buffer (`lazy.nvim` seems to trigger the event)
  so first buffer will be ignored. This means we cannot rely on the
  `FileType` auto command and need to attach to the current buffer.

To fix this we need to be aware of the plugin manager being used so we
can do the right thing, which sucks, but oh well.

The solution expands on our existing logic to hook into `lazy.nvim`
configuration for this plugin. When the user has NOT configured lazy
loading (via filetypes nor commands) we attempt to attach to the current
buffer. This means if they have we will skip attaching and will rely on
the `FileType` event.

This solves the `lazy.nvim` problem of not using the updated user
configuration. This does mean when not lazy loading with `lazy.nvim` we
will attempt to attach to the current buffer. This will initially fail
since the filetype will not be set for the buffer but will succeed for
the buffer later once the `FileType` event is triggered.

For `vim-plug` we don't have any hooks so no matter what it will look
like we are NOT lazy loading. This means we will attempt to attach to
the current buffer fixing the issue of lazy loading with `vim-plug`.

Have also added a check that buffer is valid in the `should_attach`
function. The issue related to this should be fixed from the other
updates but having it in place seems like a good idea in either case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants