Repository navigation
Expand file tree
/
Copy pathcursorword.lua
More file actions
216 lines (182 loc) · 6.22 KB
/
Copy pathcursorword.lua
File metadata and controls
216 lines (182 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
-- MIT License Copyright (c) 2021 Evgeni Chasnovski
---@brief [[
--- Custom minimal and fast module for autohighlighting word under cursor with
--- customizable delay. It is triggered only if current cursor character is a
--- |[:keyword:]|. "Word under cursor" is meant as in Vim's |<cword>|:
--- something user would get as 'iw' text object. Highlighting stops in insert
--- and terminal modes.
---
--- # Setup
---
--- This module needs a setup with `require('mini.cursorword').setup({})`
--- (replace `{}` with your `config` table). It will create global Lua table
--- `MiniCursorword` which you can use for scripting or manually (with
--- `:lua MiniCursorword.*`).
---
--- Default `config`:
--- <code>
--- {
--- -- Delay (in ms) between when cursor moved and when highlighting appeared
--- delay = 100,
--- }
--- </code>
--- # Highlight groups
---
--- 1. `MiniCursorword` - highlight group of cursor word. Default: plain
--- underline.
---
--- To change any highlight group, modify it directly with |:highlight|.
---
--- # Disabling
---
--- To disable core functionality, set `g:minicursorword_disable` (globally) or
--- `b:minicursorword_disable` (for a buffer) to `v:true`. Note: after
--- disabling there might be highlighting left; it will be removed after next
--- highlighting update.
---@brief ]]
---@tag MiniCursorword mini.cursorword
-- Module and its helper
local MiniCursorword = {}
local H = {}
--- Module setup
---
---@param config table: Module config table.
---@usage `require('mini.cursorword').setup({})` (replace `{}` with your `config` table)
function MiniCursorword.setup(config)
-- Export module
_G.MiniCursorword = MiniCursorword
-- Setup config
config = H.setup_config(config)
-- Apply config
H.apply_config(config)
-- Module behavior
vim.api.nvim_exec(
[[augroup MiniCursorword
au!
au CursorMoved * lua MiniCursorword.auto_highlight()
au InsertEnter,TermEnter,QuitPre * lua MiniCursorword.auto_unhighlight()
au FileType TelescopePrompt let b:minicursorword_disable=v:true
augroup END]],
false
)
-- Create highlighting
vim.api.nvim_exec([[hi default MiniCursorword term=underline cterm=underline gui=underline]], false)
end
-- Module config
MiniCursorword.config = {
-- Delay (in ms) between when cursor moved and when highlighting appeared
delay = 100,
}
--- Auto highlight word under cursor
---
--- Designed to be used with |autocmd|. No need to use it directly,
--- everything is setup in |MiniCursorword.setup|.
function MiniCursorword.auto_highlight()
-- Stop any possible previous delayed highlighting
H.timer:stop()
-- Stop highlighting immediately if module is disabled when cursor is not on
-- 'keyword'
if H.is_disabled() or not H.is_cursor_on_keyword() then
H.unhighlight()
return
end
-- Get current information
local win_id = vim.fn.win_getid()
local win_match = H.window_matches[win_id] or {}
local curword = H.get_cursor_word()
-- Don't do anything if currently highlighted word equals one under cursor
if win_match.word == curword then
return
end
-- Stop highlighting previous match (if it exists)
H.unhighlight()
-- Delay highlighting
H.timer:start(
MiniCursorword.config.delay,
0,
vim.schedule_wrap(function()
-- Ensure that always only one word is highlighted
H.unhighlight()
H.highlight()
end)
)
end
--- Auto unhighlight word under cursor
---
--- Designed to be used with |autocmd|. No need to use it directly, everything
--- is setup in |MiniCursorword.setup|.
function MiniCursorword.auto_unhighlight()
-- Stop any possible previous delayed highlighting
H.timer:stop()
H.unhighlight()
end
-- Helper data
---- Module default config
H.default_config = MiniCursorword.config
---- Delay timer
H.timer = vim.loop.new_timer()
---- Information about last match highlighting (stored *per window*):
---- - Key: windows' unique buffer identifiers.
---- - Value: table with:
---- - `id` field for match id (from `vim.fn.matchadd()`).
---- - `word` field for matched word.
H.window_matches = {}
-- Helper functions
---- Settings
function H.setup_config(config)
-- General idea: if some table elements are not present in user-supplied
-- `config`, take them from default config
vim.validate({ config = { config, 'table', true } })
config = vim.tbl_deep_extend('force', H.default_config, config or {})
vim.validate({ delay = { config.delay, 'number' } })
return config
end
function H.apply_config(config)
MiniCursorword.config = config
end
function H.is_disabled()
return vim.g.minicursorword_disable == true or vim.b.minicursorword_disable == true
end
---- Highlighting
function H.highlight()
-- A modified version of https://stackoverflow.com/a/25233145
-- Using `matchadd()` instead of a simpler `:match` to tweak priority of
-- 'current word' highlighting: with `:match` it is higher than for
-- `incsearch` which is not convenient.
local win_id = vim.api.nvim_get_current_win()
if not vim.api.nvim_win_is_valid(win_id) then
return
end
local curword = H.get_cursor_word()
-- Make highlighting pattern 'very nomagic' ('\V') and to match whole word
-- ('\<' and '\>')
local curpattern = string.format([[\V\<%s\>]], curword)
-- Don't add match id on top of existing one
if H.window_matches[win_id] == nil then
-- Add match highlight with very low priority and store match information
local match_id = vim.fn.matchadd('MiniCursorword', curpattern, -1)
H.window_matches[win_id] = { id = match_id, word = curword }
end
end
function H.unhighlight()
local win_id = vim.api.nvim_get_current_win()
if not vim.api.nvim_win_is_valid(win_id) then
return
end
local win_match = H.window_matches[win_id]
if win_match ~= nil then
-- Use `pcall` because there is an error if match id is not present. It can
-- happen if something else called `clearmatches`.
pcall(vim.fn.matchdelete, win_match.id)
H.window_matches[win_id] = nil
end
end
function H.is_cursor_on_keyword()
local col = vim.fn.col('.')
local curchar = vim.fn.getline('.'):sub(col, col)
return vim.fn.match(curchar, '[[:keyword:]]') >= 0
end
function H.get_cursor_word()
return vim.fn.escape(vim.fn.expand('<cword>'), [[\/]])
end
return MiniCursorword