├── LICENSE ├── README.md ├── lua └── nvim-custom-diagnostic-highlight.lua └── screenshot.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Roberto Alegro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nvim-custom-diagnostic-highlight 2 | 3 | A simple plugin to apply a highlight group to unused variables and functions similar to what [coc-nvim][coc] does. 4 | 5 | It can also apply any custom highlight to other diagnostics 6 | 7 | ![Screenshot](screenshot.png) 8 | 9 | Note that the _unused_ fields are darkened and other diagnostics are not changed. 10 | 11 | By default it works for _unused_ fields, but the plugin is configurable for whatever diagnostics you want 12 | 13 | ## Installation 14 | 15 | Simply import the plugin with your favorite package manager and call `.setup` on it 16 | 17 | ```lua 18 | -- Packer 19 | use { 20 | 'Kasama/nvim-custom-diagnostic-highlight', 21 | config = function() 22 | require('nvim-custom-diagnostic-highlight').setup {} 23 | end 24 | } 25 | 26 | -- Plug 27 | Plug 'Kasama/nvim-custom-diagnostic-highlight' 28 | require('nvim-custom-diagnostic-highlight').setup {} 29 | 30 | -- ...etc 31 | ``` 32 | 33 | ## Basic Usage 34 | 35 | By default this plugin registers a diagnostics handler to highlight unused-diagnosed variables, functions, imports, etc. 36 | 37 | The most basic usage is to have `require('nvim-custom-diagnostic-highlight').setup {}` in your configurations. 38 | 39 | ## Configuration 40 | 41 | The most up-to-date default configuration can be found at `./lua/nvim-custom-diagnostic-highlight.lua` 42 | 43 | The default configuration includes: 44 | ```lua 45 | local final_opts = { 46 | register_handler = true, -- Wether to register the handler automatically 47 | handler_name = 'kasama/nvim-custom-diagnostic-highlight', -- The name of the handler to be registered (has no effect if register_handler = false) 48 | highlight_group = 'Conceal', -- The Highlight group to set at the diagnostic 49 | patterns_override = { -- Lua patterns to be tested against the diagnostic message. Overrides default behavior 50 | '%sunused', '^unused', 'not used', 'never used', 51 | 'not read', 'never read', 'empty block', 'not accessed' 52 | }, 53 | extra_patterns = {}, -- Extra lua patterns to add. Does NOT override and will be added to the above 54 | diagnostic_handler_namespace = 'unused_hl_ns', -- Name of the handler namespace that will contain the highlight (needs to be unique) 55 | defer_until_n_lines_away = false, -- If set to a number, then highlighting is deferred until the cursor is N lines away from 56 | -- diagnostics. Useful to avoid unwanted highlights in the currently edited position. 57 | defer_highlight_update_events = {'CursorHold', 'CursorHoldI'}, -- Events on which deferred highlights will be updated (passed to nvim_create_autocmd) 58 | } 59 | ``` 60 | 61 | Note that both `handler_name` and `diagnostic_handler_namespace` must be unique values 62 | 63 | ## Advanced usage 64 | 65 | This plugin by default is setup to highlight unused variables, but it can be used to add _any_ highlight to _any_ diagnostic. 66 | 67 | It can also be used multiple times. The example below sets up once with the default behavior and another highlighting `import` related diagnostics with a custom highlight group. 68 | 69 | ```lua 70 | local unused_handler = require('nvim-custom-diagnostic-highlight').setup { 71 | register_handler = false, 72 | diagnostic_handler_namespace = 'unused_handler' 73 | } 74 | local import_handler = require('nvim-custom-diagnostic-highlight').setup { 75 | register_handler = false, 76 | highlight_group = 'MyCustomHighlightGroup', 77 | patterns_override = { 'import' }, 78 | diagnostic_handler_namespace = 'import_handler' 79 | } 80 | 81 | vim.cmd [[highlight MyCustomHighlightGroup ctermfg=168 ctermbg=16 guifg=#e06c75 guibg=#282c34]] 82 | 83 | vim.diagnostic.handlers['my/unused'] = unused_handler 84 | vim.diagnostic.handlers['my/import'] = import_handler 85 | ``` 86 | 87 | There are many possibilities. 88 | 89 | [coc]: https://github.com/neoclide/coc.nvim 90 | -------------------------------------------------------------------------------- /lua/nvim-custom-diagnostic-highlight.lua: -------------------------------------------------------------------------------- 1 | ---@brief [[ 2 | ---A simple plugin to add custom diagnostic highlights 3 | ---@brief ]] 4 | 5 | ---@tag nvim-custom-diagnostic-highlight 6 | 7 | local nvim_custom_diagnostic_highlight = {} 8 | local augroup = vim.api.nvim_create_augroup('NvimCustomDiagnosticHighlight', { clear = true }) 9 | 10 | local any = function(fun, param) 11 | local r = false 12 | for _, v in ipairs(param) do 13 | r = r or fun(v) 14 | if r then break end 15 | end 16 | return r 17 | end 18 | 19 | -- Excerpt from neovim code 20 | local function get_bufnr(bufnr) 21 | if not bufnr or bufnr == 0 then 22 | return vim.api.nvim_get_current_buf() 23 | end 24 | return bufnr 25 | end 26 | 27 | -- Each diagnostic namespace stores user data, we store our data under user_data[diagnostic_handler_namespace] 28 | local function get_user_data(diagnostic_ns, handler_ns) 29 | local data = diagnostic_ns.user_data[handler_ns] 30 | if not data then 31 | data = { 32 | -- Anonymous namespace used for highlights 33 | hl_namespace = vim.api.nvim_create_namespace(''), 34 | -- Autocommands used for deferring highlights, stored to remove them in hide() handler 35 | -- Just clearing augroup wouldn't work because we need to do this on per-namespace-per-buffer basis 36 | -- It is a map of sets: { buf1 = { id1 = true, id2 = true, ... } } 37 | autocmds = {}, 38 | } 39 | diagnostic_ns.user_data[handler_ns] = data 40 | end 41 | return data 42 | end 43 | 44 | -- Generate a function that, when called, will check if cursor is currently outside 45 | -- of the n-lines range around the diagnostic at (lnum, end_lnum). 46 | local function is_n_lines_away(bufnr, n_lines, lnum, end_lnum) 47 | end_lnum = end_lnum or lnum 48 | return function() 49 | -- When we are in a different buffer, then this means we are not at that diagnostic 50 | if vim.api.nvim_get_current_buf() ~= bufnr then 51 | return true 52 | end 53 | -- Else check cursor position in current window. We don't care for other windows 54 | -- because user most likely cares only about the current editing position. 55 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 56 | return row <= (lnum + 1) - n_lines or row >= (end_lnum + 1) + n_lines 57 | end 58 | end 59 | 60 | nvim_custom_diagnostic_highlight.setup = function(plugin_opts) 61 | 62 | local final_opts = { 63 | register_handler = true, 64 | handler_name = 'kasama/nvim-custom-diagnostic-highlight', 65 | highlight_group = 'Conceal', 66 | patterns_override = { '%sunused', '^unused', 'not used', 'never used', 'not read', 'never read', 'empty block', 'not accessed' }, 67 | extra_patterns = {}, 68 | diagnostic_handler_namespace = 'unused_hl_ns', 69 | defer_until_n_lines_away = false, 70 | defer_highlight_update_events = { 'CursorHold', 'CursorHoldI' }, 71 | } 72 | 73 | for k, v in pairs(plugin_opts) do 74 | final_opts[k] = v 75 | end 76 | 77 | local handler = { 78 | show = function(namespace, bufnr, diagnostics, opts) 79 | bufnr = get_bufnr(bufnr) 80 | opts = opts or {} 81 | 82 | for _, diagnostic in ipairs(diagnostics) do 83 | local higroup = final_opts.highlight_group 84 | 85 | local patterns = {} 86 | 87 | for _, p in ipairs(final_opts.patterns_override) do 88 | table.insert(patterns, p) 89 | end 90 | for _, p in ipairs(final_opts.extra_patterns) do 91 | table.insert(patterns, p) 92 | end 93 | 94 | local should_highlight_diagnostic = any( 95 | function(pattern) 96 | return string.match(string.lower(diagnostic.message), pattern) ~= nil 97 | end, 98 | patterns 99 | ) 100 | 101 | local diagnostic_namespace = vim.diagnostic.get_namespace(namespace) 102 | local user_data = get_user_data(diagnostic_namespace, final_opts.diagnostic_handler_namespace) 103 | 104 | if should_highlight_diagnostic then 105 | local set_highlight = function() 106 | vim.highlight.range( 107 | bufnr, 108 | user_data.hl_namespace, 109 | higroup, 110 | { diagnostic.lnum, diagnostic.col }, 111 | { diagnostic.end_lnum, diagnostic.end_col }, 112 | { priority = vim.highlight.priorities.diagnostics } 113 | ) 114 | end 115 | local should_highlight = final_opts.defer_until_n_lines_away and 116 | is_n_lines_away(bufnr, final_opts.defer_until_n_lines_away, diagnostic.lnum, diagnostic.end_lnum) 117 | 118 | -- Defer if deferred highlighting is enabled and highlighting cannot be done now 119 | -- Even here it's better to only check the current window, because user most likely 120 | -- cares only for the position they are currently editing. 121 | local defer = should_highlight and not should_highlight() 122 | 123 | if not defer then 124 | set_highlight() 125 | else 126 | -- Store creates autocmds in user data to later delete those that didn't delete themselves 127 | if not user_data.autocmds[bufnr] then 128 | user_data.autocmds[bufnr] = {} 129 | end 130 | local autocmds_set = user_data.autocmds[bufnr] 131 | 132 | local id 133 | id = vim.api.nvim_create_autocmd(final_opts.defer_highlight_update_events, { 134 | group = augroup, 135 | -- Do not set buffer=bufnr, because we want this to activate when user jumps out of 136 | -- the current buf and if this autocmd was created on CursorHold, then it wouldn't fire. 137 | -- Note that for deletion we still want to store autocmd ID in user_data.autocmds[bufnr]. 138 | desc = 'Deferred custom diagnostic highlight', 139 | callback = function() 140 | -- No need to check other windows because cursor moves only in the current window 141 | -- (or at least, user moves their cursor _explicitly_ only in the current window). 142 | if should_highlight() then 143 | set_highlight() 144 | -- Delete this autocmd by returning true and remove it from our set 145 | autocmds_set[id] = nil 146 | return true 147 | end 148 | end 149 | }) 150 | autocmds_set[id] = true 151 | end 152 | end 153 | end 154 | end, 155 | hide = function(namespace, bufnr) 156 | local ns = vim.diagnostic.get_namespace(namespace) 157 | local user_data = get_user_data(ns, final_opts.diagnostic_handler_namespace) 158 | 159 | pcall(vim.api.nvim_buf_clear_namespace, bufnr, user_data.hl_namespace, 0, -1) 160 | 161 | for id, _ in pairs(user_data.autocmds[bufnr] or {}) do 162 | pcall(vim.api.nvim_del_autocmd, id) 163 | end 164 | user_data.autocmds[bufnr] = {} 165 | end, 166 | } 167 | 168 | nvim_custom_diagnostic_highlight.handler = handler 169 | 170 | if final_opts.register_handler then 171 | vim.diagnostic.handlers[final_opts.handler_name] = handler; 172 | end 173 | 174 | return handler 175 | end 176 | 177 | 178 | return nvim_custom_diagnostic_highlight 179 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kasama/nvim-custom-diagnostic-highlight/c126fa5b44a21df779c36eea28e73d3f89e85801/screenshot.png --------------------------------------------------------------------------------