Skip to content

Commit 086e194

Browse files
committed
Add an indent sign group
1 parent d979972 commit 086e194

File tree

6 files changed

+236
-0
lines changed

6 files changed

+236
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ with the same positioning logic as the scrollbar.
8686
* `cursor`: cursor position
8787
* `diagnostics`: errors, warnings, info, and hints
8888
* `folds`: closed folds
89+
* `indent`: unexpected indentation characters (e.g., tabs when `expandtab` is
90+
set)
8991
* `latestchange`: latest change
9092
* `loclist`: items on the location list
9193
* `marks`

autoload/scrollview.vim

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,20 @@ let g:scrollview_folds_priority = get(g:, 'scrollview_folds_priority', 30)
215215
let g:scrollview_folds_symbol =
216216
\ get(g:, 'scrollview_folds_symbol', nr2char(0x25b6))
217217

218+
" *** Indent signs ***
219+
let g:scrollview_indent_spaces_condition =
220+
\ get(g:, 'scrollview_indent_spaces_condition', 'noexpandtab')
221+
let g:scrollview_indent_spaces_priority =
222+
\ get(g:, 'scrollview_indent_spaces_priority', 25)
223+
let g:scrollview_indent_spaces_symbol =
224+
\ get(g:, 'scrollview_indent_spaces_symbol', '-')
225+
let g:scrollview_indent_tabs_condition =
226+
\ get(g:, 'scrollview_indent_tabs_condition', 'expandtab')
227+
let g:scrollview_indent_tabs_priority =
228+
\ get(g:, 'scrollview_indent_tabs_priority', 25)
229+
let g:scrollview_indent_tabs_symbol =
230+
\ get(g:, 'scrollview_indent_tabs_symbol', '>')
231+
218232
" *** Latest change signs ***
219233
let g:scrollview_latestchange_priority =
220234
\ get(g:, 'scrollview_latestchange_priority', 10)

doc/scrollview.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ Group Information
173173
`cursor` cursor position
174174
`diagnostics` errors, warnings, info, and hints via |vim.diagnostic|
175175
`folds` closed folds
176+
`indent` unexpected indentation characters (e.g., tabs when |expandtab|
177+
is set)
176178
`latestchange` latest change
177179
`loclist` items on the location list
178180
`marks`
@@ -529,6 +531,38 @@ scrollview_folds_symbol *scrollview_folds_symbol*
529531
out-of-bounds. Considered only when the plugin is
530532
loaded.
531533

534+
scrollview_indent_spaces_condition *scrollview_indent_spaces_condition*
535+
|String| specifying the condition when signs will be
536+
shown for leading spaces. Possible values are `'always'`
537+
`'never'`, `'expandtab'`, and `'noexpandtab'`. Defaults to
538+
`'noexpandtab'`, which will show signs for leading
539+
spaces when the |expandtab| option is off.
540+
541+
scrollview_indent_spaces_priority *scrollview_indent_spaces_priority*
542+
|Number| specifying the priority for indent spaces signs.
543+
Defaults to `25`. Considered only when the plugin is
544+
loaded.
545+
546+
scrollview_indent_spaces_symbol *scrollview_indent_spaces_symbol*
547+
|String| specifying the symbol for indent spaces signs.
548+
Defaults to `'-'`.
549+
550+
scrollview_indent_tabs_condition *scrollview_indent_tabs_condition*
551+
|String| specifying the condition when signs will be
552+
shown for leading tabs. Possible values are `'always'`
553+
`'never'`, `'expandtab'`, and `'noexpandtab'`. Defaults to
554+
`'expandtab'`, which will show signs for leading
555+
tabs when the |expandtab| option is set.
556+
557+
scrollview_indent_tabs_priority *scrollview_indent_tabs_priority*
558+
|Number| specifying the priority for indent tabs signs.
559+
Defaults to `25`. Considered only when the plugin is
560+
loaded.
561+
562+
scrollview_indent_tabs_symbol *scrollview_indent_tabs_symbol*
563+
|String| specifying the symbol for indent tabs signs.
564+
Defaults to `'>'`.
565+
532566
scrollview_latestchange_priority *scrollview_latestchange_priority*
533567
|Number| specifying the priority for latestchange signs.
534568
Defaults to `10`. Considered only when the plugin is

doc/tags

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ scrollview_hide_bar_for_insert scrollview.txt /*scrollview_hide_bar_for_insert*
8282
scrollview_hide_on_intersect scrollview.txt /*scrollview_hide_on_intersect*
8383
scrollview_hover scrollview.txt /*scrollview_hover*
8484
scrollview_include_end_region scrollview.txt /*scrollview_include_end_region*
85+
scrollview_indent_spaces_condition scrollview.txt /*scrollview_indent_spaces_condition*
86+
scrollview_indent_spaces_priority scrollview.txt /*scrollview_indent_spaces_priority*
87+
scrollview_indent_spaces_symbol scrollview.txt /*scrollview_indent_spaces_symbol*
88+
scrollview_indent_tabs_condition scrollview.txt /*scrollview_indent_tabs_condition*
89+
scrollview_indent_tabs_priority scrollview.txt /*scrollview_indent_tabs_priority*
90+
scrollview_indent_tabs_symbol scrollview.txt /*scrollview_indent_tabs_symbol*
8591
scrollview_latestchange_priority scrollview.txt /*scrollview_latestchange_priority*
8692
scrollview_latestchange_symbol scrollview.txt /*scrollview_latestchange_symbol*
8793
scrollview_line_limit scrollview.txt /*scrollview_line_limit*

lua/scrollview/signs/indent.lua

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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

plugin/scrollview.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ if has('nvim-0.9.2')
5757
else
5858
highlight default link ScrollViewHover WildMenu
5959
endif
60+
highlight default link ScrollViewIndentSpaces LineNr
61+
highlight default link ScrollViewIndentTabs LineNr
6062
highlight default link ScrollViewLatestChange SpecialKey
6163
highlight default link ScrollViewLocList LineNr
6264
highlight default link ScrollViewMarks Identifier

0 commit comments

Comments
 (0)