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

feature: Support for Markdown text highlight using == e.g. ==I'm a highlighted text== #212

Closed
morgaan opened this issue Oct 21, 2024 · 9 comments
Labels
enhancement New feature or request

Comments

@morgaan
Copy link

morgaan commented Oct 21, 2024

Is your feature request related to a problem? Please describe.

Sort of. Obsidian.nvim supports text highlighting using == pairs, as it seems to be the case as well for Obsidian, Quilt, iA Writer, HackMD, Typora to name a few. This converts toward <mark>I'm a highlighted text</mark>.

In order to benefit from this awesome plugin of yours, it's preferable to disable Obsidian.nvim ui module. Since your plugin does not yet support the highlighting syntax, this surface a regression if we turn off Obsidian.nvim ui module.

The highlight option in Markdown is super valuable for folks using a markdown wiki as their Second Brain and practicing Progressive Summarization

Describe the solution you'd like

The solution I would see is the support for the ==highlighted text== syntax. A configuration could be:

require('render-markdown').setup({
    highlight = {
        enabled = true,
        background = 'RenderMarkdownHightlightBg'
        foreground = 'RenderMarkdownHightlight'
    },
})

You may have a much better and straightforward approach.

Describe alternatives you've considered

Tried to follow this Stackoverflow thread answer without luck

Additional information

No response

@morgaan morgaan added the enhancement New feature or request label Oct 21, 2024
@MeanderingProgrammer
Copy link
Owner

Duplicate of: #173

I rely completely on tree-sitter to do the heavy lifting of getting relevant sections of the buffer to add decorations to. This has the upsides of being fast, well defined, and easy to use, but comes with the downside that if I can't pull the node directly I'm probably not going to add it to the plugin. Custom parsing is just not something I want to include in this plugin for my own sanity.

Since the == syntax is not part of github flavored markdown it's not included in the tree-sitter parser. If it gets added at some point I can add this feature.

I get it's a kind of regression and the feature feels like it would be simple to add, but that's unfortunately just not the case, at least currently.

I do include a custom_handlers implementation of this feature that you can try out in your config in the original issue, it's by no means perfect but might work well enough.

@MeanderingProgrammer MeanderingProgrammer closed this as not planned Won't fix, can't repro, duplicate, stale Oct 22, 2024
@morgaan
Copy link
Author

morgaan commented Oct 22, 2024

Oh thank you so much, I knew you would have a much better and straightforward approach.

The custom_handlers you produced works well if the highlighted text is contained on the same line. As I do wrap text that exceeds 80 characters width (for readability), if I want to highlight across the wrapped lines then it's unfortunately not working.

I'm unfortunately not really familiar with the Lua language and the treesitter framework. Would this be an easy patch to the handler to enhance the capture of the highlight across multiple lines?

I believe that this handler would be a nice addition to your Custom handlers page, as it is not yet crowded with recipes as well as this is already the second time you are asked about this, and more over that as long as treesitter markdown grammar implementation does not support it, it is super valuable asset.

Thank you for the support so far

@MeanderingProgrammer
Copy link
Owner

It's not super straightforward. Once you have the text from local text = vim.treesitter.get_node_text(root, buf)

You would run the pattern match directly on the text rather than splitting to each line. Then you would need to do some offset calculation based on the range returned from calling find.

It gets tricky since you have to find offset from nearest line boundaries as well as the number of lines covered to figure out the start / end row / column.

@MeanderingProgrammer
Copy link
Owner

MeanderingProgrammer commented Oct 23, 2024

I played around with a multi-line version and came up with this:

require('render-markdown').setup({
  custom_handlers = {
    markdown = {
      extends = true,
      parse = function(root, buf)
        local marks = {}

        ---@param row { [1]: integer, [2]: integer }
        ---@param col { [1]: integer, [2]: integer }
        ---@param conceal? string
        ---@param hl_group? string
        local function append(row, col, conceal, hl_group)
          table.insert(marks, {
            conceal = row[1] == row[2],
            start_row = row[1],
            start_col = col[1],
            opts = { end_row = row[2], end_col = col[2], conceal = conceal, hl_group = hl_group },
          })
        end

        local text = vim.treesitter.get_node_text(root, buf)
        local top_row = root:range()

        ---@param index integer
        ---@return integer, integer
        local function row_col(index)
          local lines = vim.split(text:sub(1, index), '\n', { plain = true })
          return top_row + #lines - 1, #lines[#lines]
        end

        ---@type integer|nil
        local index = 1
        while index ~= nil do
          local start_index, end_index = text:find('(=)=[^=]+=(=)', index)
          if start_index ~= nil and end_index ~= nil then
            local start_row, start_col = row_col(start_index - 1)
            local end_row, end_col = row_col(end_index)
            -- Hide first 2 equal signs
            append({ start_row, start_row }, { start_col, start_col + 2 }, '', nil)
            -- Highlight contents
            append({ start_row, end_row }, { start_col, end_col }, nil, 'DiffDelete')
            -- Hide last 2 equal signs
            append({ end_row, end_row }, { end_col - 2, end_col }, '', nil)
            index = end_index + 1
          else
            index = nil
          end
        end

        return marks
      end,
    },
  },
})

@morgaan
Copy link
Author

morgaan commented Oct 23, 2024

I played around with a multi-line version

This works like a charm 🥇. Thank you so much! You saved my day here. I hope this will benefit more people down the line 🙏.

It may be worth to include it among your examples/recipes of custom handlers, this remains obviously your call 😉

Thank you again so much!

@MeanderingProgrammer
Copy link
Owner

This is now handled natively by the plugin after: d6a82d7

@morgaan
Copy link
Author

morgaan commented Nov 26, 2024

Oh thank you so much for this addition ❤️

I just gave it a try, highlighting some french text with accents, and it behaved weirdly. Just as if accents were left out of the column tracking or something. It's getting worse as we move the cursor away for the line. I attached a video capture of this in the hope that it give you a better visual representation of what I mean.

video.recording.of.render.markdown.highlight.having.issue.to.cope.with.accents.mp4

Here is the text used for the attached video capture if you want to give a try at fixing this:

Un jour Cosette se regarda par hasard dans son miroir et se dit : Tiens ! Il lui semblait presque qu’elle était jolie. Ceci la jeta dans un trouble singulier. Jusqu’à ce moment elle n’avait point songé à sa figure. Elle se voyait dans son miroir, mais elle ne s’y regardait pas. Et puis, on lui avait souvent dit qu’elle était laide ; Jean Valjean seul disait doucement : Mais non ! mais non ! Quoi qu’il en fût, Cosette s’était toujours crue laide, et avait grandi dans cette idée avec la résignation facile de l’enfance. Voici que tout d’un coup son miroir lui disait comme Jean Valjean : Mais non ! Elle ne dormit pas de la nuit. — Si j’étais jolie ? pensait-elle, comme cela serait drôle que je fusse jolie

MeanderingProgrammer added a commit that referenced this issue Nov 27, 2024
## Details

Issue: #212

Calculating the end index of highlighted ranges was done based on
display width but should be done using byte width. Fix is simple and
replaces the usage of `Str.width` with the Lua `#`. Need a better way to
keep the concepts of displayed width and byte width and when to use
which straight. For the most part displayed width is the correct thing
but when placing marks it is not.
@MeanderingProgrammer
Copy link
Owner

Ah, thanks, messed up the usage of displayed width vs. byte width. Should be fixed after: 3a319cd.

@morgaan
Copy link
Author

morgaan commented Nov 27, 2024

Just tried the fix. It works! Thank you so so much 🏆

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