]> Sergey Matveev's repositories - vim-lsp.git/blob - autoload/lsp/inlayhints.vim
Update the client capabilities. Pass buffer number to autocmd handler functions...
[vim-lsp.git] / autoload / lsp / inlayhints.vim
1 vim9script
2
3 # Functions for dealing with inlay hints
4
5 import './util.vim'
6 import './buffer.vim' as buf
7 import './options.vim' as opt
8
9 # Initialize the highlight group and the text property type used for
10 # inlay hints.
11 export def InitOnce()
12   hlset([
13     {name: 'LspInlayHintsType', default: true, linksto: 'Label'},
14     {name: 'LspInlayHintsParam', default: true, linksto: 'Conceal'}
15   ])
16   prop_type_add('LspInlayHintsType', {highlight: 'LspInlayHintsType'})
17   prop_type_add('LspInlayHintsParam', {highlight: 'LspInlayHintsParam'})
18
19   autocmd_add([{group: 'LspOptionsChanged',
20                 event: 'User',
21                 pattern: '*',
22                 cmd: 'LspInlayHintsOptionsChanged()'}])
23 enddef
24
25 # Clear all the inlay hints text properties in the current buffer
26 def InlayHintsClear(bnr: number)
27   prop_remove({type: 'LspInlayHintsType', bufnr: bnr, all: true})
28   prop_remove({type: 'LspInlayHintsParam', bufnr: bnr, all: true})
29 enddef
30
31 # LSP inlay hints reply message handler
32 export def InlayHintsReply(lspserver: dict<any>, bnr: number, inlayHints: any)
33   if inlayHints->empty()
34     return
35   endif
36
37   InlayHintsClear(bnr)
38
39   if mode() != 'n'
40     # Update inlay hints only in normal mode
41     return
42   endif
43
44   for hint in inlayHints
45     var label = ''
46     if hint.label->type() == v:t_list
47       label = hint.label->copy()->map((_, v) => v.value)->join(', ')
48     else
49       label = hint.label
50     endif
51
52     var kind = hint->has_key('kind') ? hint.kind->string() : '1'
53     try
54       lspserver.decodePosition(bnr, hint.position)
55       var byteIdx = util.GetLineByteFromPos(bnr, hint.position)
56       if kind == "'type'" || kind == '1'
57         prop_add(hint.position.line + 1, byteIdx + 1,
58           {type: 'LspInlayHintsType', text: label, bufnr: bnr})
59       elseif kind == "'parameter'" || kind == '2'
60         prop_add(hint.position.line + 1, byteIdx + 1,
61           {type: 'LspInlayHintsParam', text: label, bufnr: bnr})
62       endif
63     catch /E966\|E964/ # Invalid lnum | Invalid col
64       # Inlay hints replies arrive asynchronously and the document might have
65       # been modified in the mean time.  As the reply is stale, ignore invalid
66       # line number and column number errors.
67     endtry
68   endfor
69 enddef
70
71 # Timer callback to display the inlay hints.
72 def InlayHintsTimerCb(lspserver: dict<any>, bnr: number, timerid: number)
73   lspserver.inlayHintsShow(bnr)
74   setbufvar(bnr, 'LspInlayHintsNeedsUpdate', false)
75 enddef
76
77 # Update all the inlay hints.  A timer is used to throttle the updates.
78 def LspInlayHintsUpdate(bnr: number)
79   if !bnr->getbufvar('LspInlayHintsNeedsUpdate', true)
80     return
81   endif
82
83   var timerid = bnr->getbufvar('LspInlayHintsTimer', -1)
84   if timerid != -1
85     timerid->timer_stop()
86     setbufvar(bnr, 'LspInlayHintsTimer', -1)
87   endif
88
89   var lspserver: dict<any> = buf.BufLspServerGet(bnr, 'inlayHint')
90   if lspserver->empty()
91     return
92   endif
93
94   if get(g:, 'LSPTest')
95     # When running tests, update the inlay hints immediately
96     InlayHintsTimerCb(lspserver, bnr, -1)
97   else
98     timerid = timer_start(300, function('InlayHintsTimerCb', [lspserver, bnr]))
99     setbufvar(bnr, 'LspInlayHintsTimer', timerid)
100   endif
101 enddef
102
103 # Text is modified. Need to update the inlay hints.
104 def LspInlayHintsChanged(bnr: number)
105   setbufvar(bnr, 'LspInlayHintsNeedsUpdate', true)
106 enddef
107
108 # Trigger an update of the inlay hints in the current buffer.
109 export def LspInlayHintsUpdateNow(bnr: number)
110   setbufvar(bnr, 'LspInlayHintsNeedsUpdate', true)
111   LspInlayHintsUpdate(bnr)
112 enddef
113
114 # Stop updating the inlay hints.
115 def LspInlayHintsUpdateStop(bnr: number)
116   var timerid = bnr->getbufvar('LspInlayHintsTimer', -1)
117   if timerid != -1
118     timerid->timer_stop()
119     setbufvar(bnr, 'LspInlayHintsTimer', -1)
120   endif
121 enddef
122
123 # Do buffer-local initialization for displaying inlay hints
124 export def BufferInit(lspserver: dict<any>, bnr: number)
125   if !lspserver.isInlayHintProvider && !lspserver.isClangdInlayHintsProvider
126     # no support for inlay hints
127     return
128   endif
129
130   # Inlays hints are disabled
131   if !opt.lspOptions.showInlayHints
132     return
133   endif
134
135   var acmds: list<dict<any>> = []
136
137   # Update the inlay hints (if needed) when the cursor is not moved for some
138   # time.
139   acmds->add({bufnr: bnr,
140                 event: ['CursorHold'],
141                 group: 'LspInlayHints',
142                 cmd: $'LspInlayHintsUpdate({bnr})'})
143   # After the text in the current buffer is modified, the inlay hints need to
144   # be updated.
145   acmds->add({bufnr: bnr,
146                 event: ['TextChanged'],
147                 group: 'LspInlayHints',
148                 cmd: $'LspInlayHintsChanged({bnr})'})
149   # Editing a file should trigger an inlay hint update.
150   acmds->add({bufnr: bnr,
151                 event: ['BufReadPost'],
152                 group: 'LspInlayHints',
153                 cmd: $'LspInlayHintsUpdateNow({bnr})'})
154   # Inlay hints need not be updated if a buffer is no longer active.
155   acmds->add({bufnr: bnr,
156                 event: ['BufLeave'],
157                 group: 'LspInlayHints',
158                 cmd: $'LspInlayHintsUpdateStop({bnr})'})
159
160   autocmd_add(acmds)
161 enddef
162
163 # Track the current inlay hints enabled/disabled state.  Used when the
164 # "showInlayHints" option value is changed.
165 var save_showInlayHints = opt.lspOptions.showInlayHints
166
167 # Enable inlay hints.  For all the buffers with an attached language server
168 # that supports inlay hints, refresh the inlay hints.
169 export def InlayHintsEnable()
170   opt.lspOptions.showInlayHints = true
171   for binfo in getbufinfo()
172     var lspservers: list<dict<any>> = buf.BufLspServersGet(binfo.bufnr)
173     if lspservers->empty()
174       continue
175     endif
176     for lspserver in lspservers
177       if !lspserver.isInlayHintProvider &&
178           !lspserver.isClangdInlayHintsProvider
179         continue
180       endif
181       BufferInit(lspserver, binfo.bufnr)
182       LspInlayHintsUpdateNow(binfo.bufnr)
183     endfor
184   endfor
185   save_showInlayHints = true
186 enddef
187
188 # Disable inlay hints for the current Vim session.  Clear the inlay hints in
189 # all the buffers.
190 export def InlayHintsDisable()
191   opt.lspOptions.showInlayHints = false
192   for binfo in getbufinfo()
193     var lspserver: dict<any> = buf.BufLspServerGet(binfo.bufnr, 'inlayHint')
194     if lspserver->empty()
195       continue
196     endif
197     LspInlayHintsUpdateStop(binfo.bufnr)
198     :silent! autocmd_delete([{bufnr: binfo.bufnr, group: 'LspInlayHints'}])
199     InlayHintsClear(binfo.bufnr)
200   endfor
201   save_showInlayHints = false
202 enddef
203
204 # Some options are changed.  If 'showInlayHints' option is changed, then
205 # either enable or disable inlay hints.
206 export def LspInlayHintsOptionsChanged()
207   if save_showInlayHints && !opt.lspOptions.showInlayHints
208     InlayHintsDisable()
209   elseif !save_showInlayHints && opt.lspOptions.showInlayHints
210     InlayHintsEnable()
211   endif
212 enddef
213
214 # vim: tabstop=8 shiftwidth=2 softtabstop=2