Skip to content

Commit 64b28df

Browse files
committed
Provide different methods for tooltips (popup, panel and status)
1 parent 52062d0 commit 64b28df

2 files changed

Lines changed: 117 additions & 69 deletions

File tree

SublimeCodeIntel.py

Lines changed: 108 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,94 @@ def pos2bytes(content, pos):
166166
return len(content[:pos].encode('utf-8'))
167167

168168

169-
def calltip(view, ltype, msg=None, timeout=None, delay=0, lid='CodeIntel', logger=None):
169+
class TooltipOutputCommand(sublime_plugin.TextCommand):
170+
def run(self, edit, output='', clear=True):
171+
if clear:
172+
region = sublime.Region(0, self.view.size())
173+
self.view.erase(edit, region)
174+
self.view.insert(edit, 0, output)
175+
176+
177+
def tooltip_popup(view, snippets):
178+
vid = view.id()
179+
completions[vid] = snippets
180+
view.run_command('auto_complete', {
181+
'disable_auto_insert': True,
182+
'api_completions_only': True,
183+
'next_completion_if_showing': False,
184+
'auto_complete_commit_on_tab': True,
185+
})
186+
187+
188+
def tooltip(view, calltips, original_pos):
189+
view_settings = view.settings()
190+
codeintel_snippets = view_settings.get('codeintel_snippets', True)
191+
codeintel_tooltips = view_settings.get('codeintel_tooltips', 'popup')
192+
193+
snippets = []
194+
for calltip in calltips:
195+
tip_info = calltip.split('\n')
196+
text = ' '.join(tip_info[1:])
197+
snippet = None
198+
# Insert parameters as snippet:
199+
m = re.search(r'([^\s]+)\(([^\[\(\)]*)', tip_info[0])
200+
if m:
201+
params = [p.strip() for p in m.group(2).split(',')]
202+
if params:
203+
snippet = []
204+
for i, p in enumerate(params):
205+
if p:
206+
var, _, _ = p.partition('=')
207+
if ' ' in var:
208+
var = var.split(' ')[1]
209+
if var[0] == '$':
210+
var = var[1:]
211+
snippet.append('${%s:%s}' % (i + 1, var))
212+
snippet = ', '.join(snippet)
213+
text += ' - ' + tip_info[0] # Add function to the end
214+
else:
215+
text = tip_info[0] + ' ' + text # No function match, just add the first line
216+
if not codeintel_snippets:
217+
snippet = None
218+
snippets.extend(((' ' if i > 0 else '') + l, snippet or '${0}') for i, l in enumerate(tip_info))
219+
220+
if codeintel_tooltips == 'popup':
221+
tooltip_popup(view, snippets)
222+
elif codeintel_tooltips in ('status', 'panel'):
223+
if codeintel_tooltips == 'status':
224+
set_status(view, 'tip', text, timeout=15000)
225+
else:
226+
window = view.window()
227+
output_panel = window.get_output_panel('tooltips')
228+
output_panel.set_read_only(False)
229+
text = '\n'.join(list(zip(*snippets))[0])
230+
output_panel.run_command('tooltip_output', {'output': text})
231+
output_panel.set_read_only(True)
232+
window.run_command('show_panel', {'panel': 'output.tooltips'})
233+
sublime.set_timeout(lambda: window.run_command('hide_panel', {'panel': 'output.tooltips'}), 15000)
234+
235+
if snippets and codeintel_snippets:
236+
# Insert function call snippets:
237+
# func = m.group(1)
238+
# scope = view.scope_name(pos)
239+
# view.run_command('new_snippet', {'contents': snippets[0][0], 'tab_trigger': func, 'scope': scope}) # FIXME: Doesn't add the new snippet... is it possible to do so?
240+
def _insert_snippet():
241+
# Check to see we are still at a position where the snippet is wanted:
242+
view_sel = view.sel()
243+
if not view_sel:
244+
return
245+
sel = view_sel[0]
246+
pos = sel.end()
247+
if not pos or pos != original_pos:
248+
return
249+
view.run_command('insert_snippet', {'contents': snippets[0][0]})
250+
sublime.set_timeout(_insert_snippet, 500) # Delay snippet insertion a bit... it's annoying some times
251+
252+
253+
def set_status(view, ltype, msg=None, timeout=None, delay=0, lid='CodeIntel', logger=None):
170254
if timeout is None:
171255
timeout = {'error': 3000, 'warning': 5000, 'info': 10000,
172-
'event': 10000, 'tip': 15000}.get(ltype, 3000)
256+
'event': 10000}.get(ltype, 3000)
173257

174258
if msg is None:
175259
msg, ltype = ltype, 'debug'
@@ -185,30 +269,24 @@ def calltip(view, ltype, msg=None, timeout=None, delay=0, lid='CodeIntel', logge
185269
finally:
186270
status_lock.release()
187271

188-
def _calltip_set():
272+
def _set_status():
189273
view_sel = view.sel()
190274
lineno = view.rowcol(view_sel[0].end())[0] if view_sel else 0
191275
status_lock.acquire()
192276
try:
193277
current_type, current_msg, current_order = status_msg.get(lid, [None, None, 0])
194278
if msg != current_msg and order == current_order:
195-
if msg:
196-
print("+", "%s: %s" % (ltype.capitalize(), msg), file=condeintel_log_file)
197-
(logger or log.info)(msg)
198-
if ltype != 'debug':
199-
view.set_status(lid, "%s: %s" % (ltype.capitalize(), msg))
200-
status_msg[lid] = [ltype, msg, order]
201-
if 'warning' not in lid:
202-
status_lineno[lid] = lineno
203-
else:
204-
view.erase_status(lid)
205-
status_msg[lid][1] = None
206-
if lid in status_lineno:
207-
del status_lineno[lid]
279+
print("+", "%s: %s" % (ltype.capitalize(), msg), file=condeintel_log_file)
280+
(logger or log.info)(msg)
281+
if ltype != 'debug':
282+
view.set_status(lid, "%s: %s" % (ltype.capitalize(), msg))
283+
status_msg[lid] = [ltype, msg, order]
284+
if 'warning' not in lid:
285+
status_lineno[lid] = lineno
208286
finally:
209287
status_lock.release()
210288

211-
def _calltip_erase():
289+
def _erase_status():
212290
status_lock.acquire()
213291
try:
214292
if msg == status_msg.get(lid, [None, None, 0])[1]:
@@ -219,16 +297,17 @@ def _calltip_erase():
219297
finally:
220298
status_lock.release()
221299

222-
sublime.set_timeout(_calltip_set, delay or 0)
223-
224300
if msg:
225-
sublime.set_timeout(_calltip_erase, timeout)
301+
sublime.set_timeout(_set_status, delay or 0)
302+
sublime.set_timeout(_erase_status, timeout)
303+
else:
304+
sublime.set_timeout(_erase_status, delay or 0)
226305

227306

228307
def logger(view, ltype, msg=None, timeout=None, delay=0, lid='CodeIntel'):
229308
if msg is None:
230309
msg, ltype = ltype, 'info'
231-
calltip(view, ltype, msg, timeout=timeout, delay=delay, lid=lid + '-' + ltype, logger=getattr(log, ltype, None))
310+
set_status(view, ltype, msg, timeout=timeout, delay=delay, lid=lid + '-' + ltype, logger=getattr(log, ltype, None))
232311

233312

234313
def guess_lang(view=None, path=None):
@@ -301,10 +380,8 @@ def _autocomplete_callback(view, path, original_pos, lang):
301380

302381
if not next or next != '_' and not next.isalnum():
303382
vid = view.id()
304-
content = view.substr(sublime.Region(0, view.size()))
305383

306384
def _trigger(calltips, cplns=None):
307-
view_settings = view.settings()
308385
if cplns is not None or calltips is not None:
309386
codeintel_log.info("Autocomplete called (%s) [%s]", lang, ','.join(c for c in ['cplns' if cplns else None, 'calltips' if calltips else None] if c))
310387

@@ -323,49 +400,10 @@ def _trigger(calltips, cplns=None):
323400
'next_completion_if_showing': False,
324401
'auto_complete_commit_on_tab': True,
325402
})
403+
if calltips:
404+
tooltip(view, calltips, original_pos)
326405

327-
if calltips is None:
328-
return
329-
tip_info = calltips[0].split('\n')
330-
tooltip = ' '.join(tip_info[1:])
331-
332-
# Insert function call snippets:
333-
if view_settings.get('codeintel_snippets', True):
334-
# Insert parameters as snippet:
335-
if content[sel.begin() - 1] == '(' and content[sel.begin()] == ')':
336-
m = re.search(r'([^\s]+)\(([^\[\(\)]*)', tip_info[0])
337-
if m:
338-
params = [p.strip() for p in m.group(2).split(',')]
339-
if params:
340-
snippet = []
341-
for i, p in enumerate(params):
342-
if p:
343-
var, _, _ = p.partition('=')
344-
if ' ' in var:
345-
var = var.split(' ')[1]
346-
if var[0] == '$':
347-
var = var[1:]
348-
snippet.append('${%s:%s}' % (i + 1, var))
349-
contents = ', '.join(snippet)
350-
# func = m.group(1)
351-
# scope = view.scope_name(pos)
352-
# view.run_command('new_snippet', {'contents': contents, 'tab_trigger': func, 'scope': scope}) # FIXME: Doesn't add the new snippet... is it possible to do so?
353-
def _insert_snippet():
354-
# Check to see we are still at a position where the snippet is wanted:
355-
view_sel = view.sel()
356-
if not view_sel:
357-
return
358-
sel = view_sel[0]
359-
pos = sel.end()
360-
if not pos or pos != original_pos:
361-
return
362-
view.run_command('insert_snippet', {'contents': contents})
363-
sublime.set_timeout(_insert_snippet, 500) # Delay snippet insertion a bit... it's annoying some times
364-
tooltip += ' - ' + tip_info[0] # Add function to the end
365-
else:
366-
tooltip = tip_info[0] + ' ' + tooltip # No function match, just add the first line
367-
# Trigger a tooltip
368-
calltip(view, 'tip', tooltip)
406+
content = view.substr(sublime.Region(0, view.size()))
369407
codeintel(view, path, content, lang, pos, forms, _trigger)
370408
# If it's a fill char, queue using lower values and preemptive behavior
371409
queue(view, _autocomplete_callback, timeout, busy_timeout, preemptive, args=args, kwargs=kwargs)
@@ -716,8 +754,8 @@ def _codeintel_scan():
716754
msgs = []
717755
if env._valid:
718756
if forms:
719-
calltip(view, 'tip', "")
720-
calltip(view, 'event', "")
757+
set_status(view, 'tip', "")
758+
set_status(view, 'event', "")
721759
msg = "CodeIntel(%s) for %s@%s [%s]" % (', '.join(forms), path, pos, lang)
722760
msgs.append(('info', "\n%s\n%s" % (msg, "-" * len(msg))))
723761

@@ -817,7 +855,7 @@ def _codeintel(buf, msgs):
817855
continue
818856
merge = ''
819857
if not result and msg.startswith('evaluating '):
820-
calltip(view, 'warning', msg)
858+
set_status(view, 'warning', msg)
821859
result = True
822860

823861
ret = []
@@ -923,6 +961,7 @@ def get_revision(path=None):
923961
ALL_SETTINGS = [
924962
'codeintel',
925963
'codeintel_snippets',
964+
'codeintel_tooltips',
926965
'codeintel_enabled_languages',
927966
'codeintel_live',
928967
'codeintel_live_enabled_languages',
@@ -1035,7 +1074,7 @@ def on_selection_modified(self, view):
10351074
finally:
10361075
status_lock.release()
10371076
for vid in slns:
1038-
calltip(view, "", lid=vid)
1077+
set_status(view, "", lid=vid)
10391078

10401079
def on_query_completions(self, view, prefix, locations):
10411080
vid = view.id()

SublimeCodeIntel.sublime-settings

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@
1313
/* Disable Sublime Text autocomplete: */
1414
"sublime_auto_complete": true,
1515

16+
/*
17+
Tooltips method:
18+
19+
"popup" - Uses Autocomplete popup for tooltips.
20+
"panel" - Uses the output panel for tooltips.
21+
"status" - Uses the status bar for tooltips (was the default).
22+
*/
23+
"codeintel_tooltips": "popup",
24+
1625
/*
1726
Insert functions snippets.
1827
*/

0 commit comments

Comments
 (0)