]> Sergey Matveev's repositories - vim-lsp.git/blob - autoload/lsp/inlayhints.vim
Add support for UTF-8 and UTF-16 offset encoding
[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([{name: 'LspInlayHintsType', default: true, linksto: 'Label'}])
13   hlset([{name: 'LspInlayHintsParam', default: true, linksto: 'Conceal'}])
14   prop_type_add('LspInlayHintsType', {highlight: 'LspInlayHintsType'})
15   prop_type_add('LspInlayHintsParam', {highlight: 'LspInlayHintsParam'})
16 enddef
17
18 # Clear all the inlay hints text properties in the current buffer
19 def InlayHintsClear(lspserver: dict<any>)
20   prop_remove({type: 'LspInlayHintsType', bufnr: bufnr('%'), all: true})
21   prop_remove({type: 'LspInlayHintsParam', bufnr: bufnr('%'), all: true})
22 enddef
23
24 # LSP inlay hints reply message handler
25 export def InlayHintsReply(lspserver: dict<any>, inlayHints: any)
26   if inlayHints->empty()
27     return
28   endif
29
30   InlayHintsClear(lspserver)
31
32   if mode() != 'n'
33     # Update inlay hints only in normal mode
34     return
35   endif
36
37   var bnr = bufnr('%')
38   for hint in inlayHints
39     var label = ''
40     if hint.label->type() == v:t_list
41       label = hint.label->copy()->map((_, v) => v.value)->join(', ')
42     else
43       label = hint.label
44     endif
45
46     var kind = hint->has_key('kind') ? hint.kind->string() : '1'
47     try
48       lspserver.decodePosition(bnr, hint.position)
49       var byteIdx = util.GetLineByteFromPos(bnr, hint.position)
50       if kind == "'type'" || kind == '1'
51         prop_add(hint.position.line + 1, byteIdx + 1,
52           {type: 'LspInlayHintsType', text: label, bufnr: bnr})
53       elseif kind == "'parameter'" || kind == '2'
54         prop_add(hint.position.line + 1, byteIdx + 1,
55           {type: 'LspInlayHintsParam', text: label, bufnr: bnr})
56       endif
57     catch /E966\|E964/ # Invalid lnum | Invalid col
58       # Inlay hints replies arrive asynchronously and the document might have
59       # been modified in the mean time.  As the reply is stale, ignore invalid
60       # line number and column number errors.
61     endtry
62   endfor
63 enddef
64
65 # Timer callback to display the inlay hints.
66 def InlayHintsCallback(lspserver: dict<any>, timerid: number)
67   lspserver.inlayHintsShow()
68   b:LspInlayHintsNeedsUpdate = false
69 enddef
70
71 # Update all the inlay hints.  A timer is used to throttle the updates.
72 def LspInlayHintsUpdate()
73   if !get(b:, 'LspInlayHintsNeedsUpdate', true)
74     return
75   endif
76
77   var timerid = get(b:, 'LspInlayHintsTimer', -1)
78   if timerid != -1
79     timerid->timer_stop()
80     b:LspInlayHintsTimer = -1
81   endif
82
83   var lspserver: dict<any> = buf.CurbufGetServerChecked()
84   if lspserver->empty()
85     return
86   endif
87
88   timerid = timer_start(300, function('InlayHintsCallback', [lspserver]))
89   b:LspInlayHintsTimer = timerid
90 enddef
91
92 # Text is modified. Need to update the inlay hints.
93 def LspInlayHintsChanged()
94   b:LspInlayHintsNeedsUpdate = true
95 enddef
96
97 # Trigger an update of the inlay hints in the current buffer.
98 export def LspInlayHintsUpdateNow()
99   b:LspInlayHintsNeedsUpdate = true
100   LspInlayHintsUpdate()
101 enddef
102
103 # Stop updating the inlay hints.
104 def LspInlayHintsUpdateStop()
105   var timerid = get(b:, 'LspInlayHintsTimer', -1)
106   if timerid != -1
107     timerid->timer_stop()
108     b:LspInlayHintsTimer = -1
109   endif
110 enddef
111
112 # Do buffer-local initialization for displaying inlay hints
113 export def BufferInit(lspserver: dict<any>, bnr: number)
114   if !lspserver.isInlayHintProvider && !lspserver.isClangdInlayHintsProvider
115     # no support for inley hints
116     return
117   endif
118
119   # Inlays hints are disabled
120   if !opt.lspOptions.showInlayHints
121     return
122   endif
123
124   var acmds: list<dict<any>> = []
125
126   # Update the inlay hints (if needed) when the cursor is not moved for some
127   # time.
128   acmds->add({bufnr: bnr,
129                 event: ['CursorHold'],
130                 group: 'LSPBufferAutocmds',
131                 cmd: 'LspInlayHintsUpdate()'})
132   # After the text in the current buffer is modified, the inlay hints need to
133   # be updated.
134   acmds->add({bufnr: bnr,
135                 event: ['TextChanged'],
136                 group: 'LSPBufferAutocmds',
137                 cmd: 'LspInlayHintsChanged()'})
138   # Editing a file should trigger an inlay hint update.
139   acmds->add({bufnr: bnr,
140                 event: ['BufReadPost'],
141                 group: 'LSPBufferAutocmds',
142                 cmd: 'LspInlayHintsUpdateNow()'})
143   # Inlay hints need not be updated if a buffer is no longer active.
144   acmds->add({bufnr: bnr,
145                 event: ['BufLeave'],
146                 group: 'LSPBufferAutocmds',
147                 cmd: 'LspInlayHintsUpdateStop()'})
148
149   autocmd_add(acmds)
150 enddef
151
152 # vim: tabstop=8 shiftwidth=2 softtabstop=2