echasnovski / mini.nvim

Library of 40+ independent Lua modules improving overall Neovim (version 0.8 and higher) experience with minimal effort
MIT License
5k stars 185 forks source link

Highlight pattern inside specific highlight #920

Closed thmsaurel closed 4 months ago

thmsaurel commented 4 months ago

Contributing guidelines

Module(s)

mini.hipatterns

Description

The idea is to highlight pattern only inside a specific highlight.

E.g, highlight only the "todo" pattern when it's inside a comment.

-- this "TODO" is highlighted
print('this "TODO" is not') 

Maybe there is an easy way to do it using a function, in case it should be helpful to put it as example inside help.

echasnovski commented 4 months ago

Thanks for the suggestion!

General structure of highlighter is already fixed and it can define word to be highlighted only via a single Lua pattern. This design choice is both for speed (Lua patterns are very fast) and implementation simplicity.

The closest to a solution of this task is creating a callable highlighter that constructs a pattern based on its comment structure. A general function might utilize 'commentstring' option.

A more targeted examples for filetypes are possible inside 'after/ftplugin/lua.lua' with the code similar to this example in help:

vim.b.minihipatterns_config = {
  highlighters = {
    todo_in_comment = { pattern = '^%s*%-%-.-()TODO()', group = 'MiniHipatternsTodo' },
  },
}

Although use case seems interesting, I don't think complicating highlighter definition is worth it. Closing as not planned.

thmsaurel commented 4 months ago

Thanks for your answer

Just for context, the suggested behavior is inspired by: https://github.com/stsewd/tree-sitter-comment.

The closest to a solution of this task is creating a callable highlighter that constructs a pattern based on its comment structure. A general function might utilize 'commentstring' option.

It was more or less my initial idea for "Comment". But, if the wanted pattern is inside a "String" or a "Variable", it shall rely on pure syntax (or treesitter).

A more targeted examples for filetypes are possible inside 'after/ftplugin/lua.lua' with the code similar to this example in help:

I saw it. Its main issue is the configuration must be done for each filetype you use. The idea was to have a easier way to set it.

Although use case seems interesting, I don't think complicating highlighter definition is worth it. Closing as not planned.

I understand. I was hoping it was already implemented and I wasn't able to find the way to do it.

pkazmier commented 4 months ago

How about something like this:

local hipatterns = require("mini.hipatterns")

local highlight_if_ts_capture = function(capture, hl_group)
  return function(buf_id, match, data)
    local captures = vim.treesitter.get_captures_at_pos(buf_id, data.line - 1, data.from_col - 1)
    local pred = function(t) return t.capture == capture end
    local not_in_capture = vim.tbl_isempty(vim.tbl_filter(pred, captures))
    if not_in_capture then return nil end
    return hl_group
  end
end

require("mini.hipatterns").setup({
  highlighters = {
    todo = {
      pattern = "TODO",
      group = highlight_if_ts_capture("comment", "MiniHipatternsTodo"),
    },
  },
})

The highlight_if_ts_capture will only highlight if your pattern is within the specified treesitter capture group. I'm not an expert though and not sure of the order of operations re: treesitter parsing vs hipatterns running. In my basic tests, it seemed to work though.

echasnovski commented 4 months ago

The highlight_if_ts_capture will only highlight if your pattern is within the specified treesitter capture group. I'm not an expert though and not sure of the order of operations re: treesitter parsing vs hipatterns running. In my basic tests, it seemed to work though.

Yes, seems to work. Nice solution!

The only downside here is that this group function is going to run very frequently when editing lines with "TODO" inside of them (both in Normal and Insert mode). Yes, this is as optimized as possible on 'mini.hipatterns' side: called only when those lines change and after debouncing, i.e. allowing changes occur within config.delay.text_change milliseconds and then highlight all at once.

But I'd still consider calling vim.treesitter.get_captures_at_pos() at that rate quite expensive, yet doable for some people.

Adding this to 'mini.nvim' either as code or documentation seems somewhat too much. Let's leave it here.