|
| 1 | +local api = vim.api |
| 2 | +local fn = vim.fn |
| 3 | +local scrollview = require('scrollview') |
| 4 | +local utils = require('scrollview.utils') |
| 5 | + |
| 6 | +local M = {} |
| 7 | + |
| 8 | +local SPACES = 0 |
| 9 | +local TABS = 1 |
| 10 | + |
| 11 | +local should_show = function(option, expandtab) |
| 12 | + if option == 'always' then |
| 13 | + return true |
| 14 | + elseif option == 'never' then |
| 15 | + return false |
| 16 | + elseif option == 'expandtab' then |
| 17 | + return expandtab |
| 18 | + elseif option == 'noexpandtab' then |
| 19 | + return not expandtab |
| 20 | + else |
| 21 | + -- Unknown option. Don't show. |
| 22 | + return false |
| 23 | + end |
| 24 | +end |
| 25 | + |
| 26 | +function M.init(enable) |
| 27 | + if api.nvim_create_autocmd == nil then |
| 28 | + return |
| 29 | + end |
| 30 | + |
| 31 | + local group = 'indent' |
| 32 | + scrollview.register_sign_group(group) |
| 33 | + local names = { |
| 34 | + [SPACES] = scrollview.register_sign_spec({ |
| 35 | + group = group, |
| 36 | + highlight = 'ScrollViewIndentSpaces', |
| 37 | + priority = vim.g.scrollview_indent_spaces_priority, |
| 38 | + symbol = vim.g.scrollview_indent_spaces_symbol, |
| 39 | + variant = 'spaces', |
| 40 | + }).name, |
| 41 | + [TABS] = scrollview.register_sign_spec({ |
| 42 | + group = group, |
| 43 | + highlight = 'ScrollViewIndentTabs', |
| 44 | + priority = vim.g.scrollview_indent_tabs_priority, |
| 45 | + symbol = vim.g.scrollview_indent_tabs_symbol, |
| 46 | + variant = 'tabs', |
| 47 | + }).name, |
| 48 | + } |
| 49 | + |
| 50 | + scrollview.set_sign_group_state(group, enable) |
| 51 | + |
| 52 | + scrollview.set_sign_group_callback(group, function() |
| 53 | + -- Track visited buffers, to prevent duplicate computation when multiple |
| 54 | + -- windows are showing the same buffer. |
| 55 | + local visited = {} |
| 56 | + for _, winid in ipairs(scrollview.get_sign_eligible_windows()) do |
| 57 | + local bufnr = api.nvim_win_get_buf(winid) |
| 58 | + local expandtab = api.nvim_buf_get_option(bufnr, 'expandtab') |
| 59 | + if not visited[bufnr] then |
| 60 | + local bufvars = vim.b[bufnr] |
| 61 | + local lines = { |
| 62 | + [SPACES] = {}, |
| 63 | + [TABS] = {}, |
| 64 | + } |
| 65 | + local changedtick = vim.b[bufnr].changedtick |
| 66 | + local changedtick_cached = bufvars.scrollview_indent_changedtick_cached |
| 67 | + local bufnr_cached = bufvars.scrollview_indent_bufnr_cached |
| 68 | + local spaces_condition_cached = |
| 69 | + bufvars.scrollview_indent_spaces_condition_cached |
| 70 | + local tabs_condition_cached = |
| 71 | + bufvars.scrollview_indent_tabs_condition_cached |
| 72 | + local expandtab_cached = |
| 73 | + bufvars.scrollview_indent_expandtab_option_cached |
| 74 | + local cache_hit = changedtick_cached == changedtick |
| 75 | + and bufnr_cached == bufnr |
| 76 | + and expandtab_cached == expandtab |
| 77 | + and spaces_condition_cached == vim.g.scrollview_indent_spaces_condition |
| 78 | + and tabs_condition_cached == vim.g.scrollview_indent_tabs_condition |
| 79 | + if cache_hit then |
| 80 | + lines[SPACES] = bufvars.scrollview_indent_spaces_cached |
| 81 | + lines[TABS] = bufvars.scrollview_indent_tabs_cached |
| 82 | + else |
| 83 | + local line_count = api.nvim_buf_line_count(bufnr) |
| 84 | + local show_spaces_signs = |
| 85 | + should_show(vim.g.scrollview_indent_spaces_condition, expandtab) |
| 86 | + local show_tabs_signs = |
| 87 | + should_show(vim.g.scrollview_indent_tabs_condition, expandtab) |
| 88 | + for line = 1, line_count do |
| 89 | + local str = fn.getbufline(bufnr, line)[1] |
| 90 | + local sub = string.sub(str, 1, 1) |
| 91 | + if sub == ' ' then |
| 92 | + if show_spaces_signs then |
| 93 | + table.insert(lines[SPACES], line) |
| 94 | + end |
| 95 | + elseif sub == '\t' then |
| 96 | + if show_tabs_signs then |
| 97 | + table.insert(lines[TABS], line) |
| 98 | + end |
| 99 | + end |
| 100 | + end |
| 101 | + -- luacheck: ignore 122 (setting read-only field b.?.? of global vim) |
| 102 | + bufvars.scrollview_indent_expandtab_option_cached = expandtab |
| 103 | + -- luacheck: ignore 122 (setting read-only field b.?.? of global vim) |
| 104 | + bufvars.scrollview_indent_spaces_condition_cached = |
| 105 | + vim.g.scrollview_indent_spaces_condition |
| 106 | + -- luacheck: ignore 122 (setting read-only field b.?.? of global vim) |
| 107 | + bufvars.scrollview_indent_tabs_condition_cached = |
| 108 | + vim.g.scrollview_indent_tabs_condition |
| 109 | + -- luacheck: ignore 122 (setting read-only field w.?.? of global vim) |
| 110 | + bufvars.scrollview_indent_changedtick_cached = changedtick |
| 111 | + -- luacheck: ignore 122 (setting read-only field w.?.? of global vim) |
| 112 | + bufvars.scrollview_indent_bufnr_cached = bufnr |
| 113 | + -- luacheck: ignore 122 (setting read-only field w.?.? of global vim) |
| 114 | + bufvars.scrollview_indent_spaces_cached = lines[SPACES] |
| 115 | + -- luacheck: ignore 122 (setting read-only field w.?.? of global vim) |
| 116 | + bufvars.scrollview_indent_tabs_cached = lines[TABS] |
| 117 | + end |
| 118 | + -- luacheck: ignore 122 (setting read-only field w.?.? of global vim) |
| 119 | + bufvars[names[SPACES]] = lines[SPACES] |
| 120 | + bufvars[names[TABS]] = lines[TABS] |
| 121 | + visited[bufnr] = true |
| 122 | + end |
| 123 | + end |
| 124 | + end) |
| 125 | + |
| 126 | + api.nvim_create_autocmd('TextChangedI', { |
| 127 | + callback = function() |
| 128 | + if not scrollview.is_sign_group_active(group) then return end |
| 129 | + local bufnr = api.nvim_get_current_buf() |
| 130 | + local expandtab = api.nvim_buf_get_option(bufnr, 'expandtab') |
| 131 | + local line = fn.line('.') |
| 132 | + local str = fn.getbufline(bufnr, line)[1] |
| 133 | + local sub = string.sub(str, 1, 1) |
| 134 | + for _, mode in ipairs({SPACES, TABS}) do |
| 135 | + local expect_sign = false |
| 136 | + if mode == SPACES then |
| 137 | + local show_signs = |
| 138 | + should_show(vim.g.scrollview_indent_spaces_condition, expandtab) |
| 139 | + expect_sign = sub == ' ' and show_signs |
| 140 | + elseif mode == TABS then |
| 141 | + local show_tabs = |
| 142 | + should_show(vim.g.scrollview_indent_tabs_condition, expandtab) |
| 143 | + expect_sign = sub == '\t' and show_tabs |
| 144 | + else |
| 145 | + error('Unknown mode: ' .. mode) |
| 146 | + end |
| 147 | + local lines = vim.b[bufnr][names[mode]] |
| 148 | + local idx = -1 |
| 149 | + if lines ~= nil then |
| 150 | + idx = utils.binary_search(lines, line) |
| 151 | + if lines[idx] ~= line then |
| 152 | + idx = -1 |
| 153 | + end |
| 154 | + end |
| 155 | + local has_sign = idx ~= -1 |
| 156 | + if expect_sign ~= has_sign then |
| 157 | + scrollview.refresh() |
| 158 | + break |
| 159 | + end |
| 160 | + end |
| 161 | + end |
| 162 | + }) |
| 163 | + |
| 164 | + api.nvim_create_autocmd('OptionSet', { |
| 165 | + pattern = 'expandtab', |
| 166 | + callback = function() |
| 167 | + if not scrollview.is_sign_group_active(group) then return end |
| 168 | + if vim.g.scrollview_indent_spaces_condition == 'expandtab' |
| 169 | + or vim.g.scrollview_indent_spaces_condition == 'noexpandtab' |
| 170 | + or vim.g.scrollview_indent_tabs_condition == 'expandtab' |
| 171 | + or vim.g.scrollview_indent_tabs_condition == 'noexpandtab' then |
| 172 | + scrollview.refresh() |
| 173 | + end |
| 174 | + end |
| 175 | + }) |
| 176 | +end |
| 177 | + |
| 178 | +return M |
0 commit comments