Skip to content
This repository was archived by the owner on Mar 12, 2020. It is now read-only.

Commit 6a5a7eb

Browse files
committed
Improve performance of completions
Performance is improved by initializing most of the completions related stuff just after loading the identifiers, not each time a completion is requested. Also, slightly improves the performance of connection listing.
1 parent 1fccdb0 commit 6a5a7eb

File tree

2 files changed

+106
-54
lines changed

2 files changed

+106
-54
lines changed

SQLTools.py

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,17 @@ def getConnections():
9191
startPlugin()
9292

9393
options = connections.get('connections', {})
94+
allSettings = settings.all()
9495

9596
for name, config in options.items():
96-
connectionsObj[name] = createConnection(name, config, settings=settings.all())
97+
connectionsObj[name] = createConnection(name, config, settings=allSettings)
9798

9899
# project settings
99100
projectData = Window().project_data()
100101
if projectData:
101102
options = projectData.get('connections', {})
102103
for name, config in options.items():
103-
connectionsObj[name] = createConnection(name, config, settings=settings.all())
104+
connectionsObj[name] = createConnection(name, config, settings=allSettings)
104105

105106
return connectionsObj
106107

@@ -290,12 +291,12 @@ def getCurrentSyntax():
290291

291292

292293
class ST(EventListener):
294+
connectionList = None
293295
conn = None
294296
tables = []
295297
columns = []
296298
functions = []
297-
connectionList = None
298-
autoCompleteList = []
299+
completion = None
299300

300301
@staticmethod
301302
def bootstrap():
@@ -315,24 +316,49 @@ def checkDefaultConnection():
315316

316317
@staticmethod
317318
def loadConnectionData(tablesCallback=None, columnsCallback=None, functionsCallback=None):
319+
# clear the list of identifiers (in case connection is changed)
320+
ST.tables = []
321+
ST.columns = []
322+
ST.functions = []
323+
ST.completion = None
324+
318325
if not ST.conn:
319326
return
320327

321328
def tbCallback(tables):
322-
setattr(ST, 'tables', tables)
329+
ST.tables = tables
330+
331+
nonlocal callbacksRun
332+
callbacksRun += 1
333+
if callbacksRun == 3:
334+
ST.completion = Completion(ST.tables, ST.columns, ST.functions, settings=settings)
335+
323336
if tablesCallback:
324337
tablesCallback()
325338

326339
def colCallback(columns):
327-
setattr(ST, 'columns', columns)
340+
ST.columns = columns
341+
342+
nonlocal callbacksRun
343+
callbacksRun += 1
344+
if callbacksRun == 3:
345+
ST.completion = Completion(ST.tables, ST.columns, ST.functions, settings=settings)
346+
328347
if columnsCallback:
329348
columnsCallback()
330349

331350
def funcCallback(functions):
332-
setattr(ST, 'functions', functions)
351+
ST.functions = functions
352+
353+
nonlocal callbacksRun
354+
callbacksRun += 1
355+
if callbacksRun == 3:
356+
ST.completion = Completion(ST.tables, ST.columns, ST.functions, settings=settings)
357+
333358
if functionsCallback:
334359
functionsCallback()
335360

361+
callbacksRun = 0
336362
ST.conn.getTables(tbCallback)
337363
ST.conn.getColumns(colCallback)
338364
ST.conn.getFunctions(funcCallback)
@@ -345,10 +371,6 @@ def setConnection(index, tablesCallback=None, columnsCallback=None, functionsCal
345371
connListNames = list(ST.connectionList.keys())
346372
connListNames.sort()
347373
ST.conn = ST.connectionList.get(connListNames[index])
348-
# clear list of identifiers in case connection is changed
349-
ST.tables = []
350-
ST.columns = []
351-
ST.functions = []
352374

353375
ST.loadConnectionData(tablesCallback, columnsCallback, functionsCallback)
354376

@@ -389,10 +411,21 @@ def on_query_completions(view, prefix, locations):
389411
if ST.conn is None:
390412
return None
391413

414+
if ST.completion is None:
415+
return None
416+
417+
if ST.completion.isDisabled():
418+
return None
419+
392420
if not len(locations):
393421
return None
394422

395-
selectors = settings.get('selectors', [])
423+
# disable completions inside strings
424+
if view.match_selector(locations[0], 'string'):
425+
return None
426+
427+
# show completions only for specific selectors
428+
selectors = ST.completion.getSelectors()
396429
selectorMatched = False
397430
if selectors:
398431
for selector in selectors:
@@ -403,18 +436,6 @@ def on_query_completions(view, prefix, locations):
403436
if not selectorMatched:
404437
return None
405438

406-
# completions enabled? if yes, determine which type
407-
completionType = settings.get('autocompletion', 'smart')
408-
if not completionType:
409-
return None # autocompletion disabled
410-
completionType = str(completionType).strip()
411-
if completionType not in ['basic', 'smart']:
412-
completionType = 'smart'
413-
414-
# no completions inside strings
415-
if view.match_selector(locations[0], 'string'):
416-
return None
417-
418439
# sublimePrefix = prefix
419440
# sublimeCompletions = view.extract_completions(sublimePrefix, locations[0])
420441

@@ -435,33 +456,24 @@ def on_query_completions(view, prefix, locations):
435456
except Exception as e:
436457
Log(e)
437458

438-
# determine desired keywords case from settings
439-
formatSettings = settings.get('format', {})
440-
keywordCase = formatSettings.get('keyword_case', 'upper')
441-
uppercaseKeywords = keywordCase.lower().startswith('upper')
442-
443-
inhibit = False
444-
completion = Completion(uppercaseKeywords, ST.tables, ST.columns, ST.functions)
459+
# use current paragraph as sql text to parse
460+
sqlRegion = expand_to_paragraph(view, currentPoint)
461+
sql = view.substr(sqlRegion)
462+
sqlToCursorRegion = sublime.Region(sqlRegion.begin(), currentPoint)
463+
sqlToCursor = view.substr(sqlToCursorRegion)
445464

446-
if completionType == 'basic':
447-
ST.autoCompleteList = completion.getBasicAutoCompleteList(prefix)
448-
else:
449-
# use current paragraph as sql text to parse
450-
sqlRegion = expand_to_paragraph(view, currentPoint)
451-
sql = view.substr(sqlRegion)
452-
sqlToCursorRegion = sublime.Region(sqlRegion.begin(), currentPoint)
453-
sqlToCursor = view.substr(sqlToCursorRegion)
454-
ST.autoCompleteList, inhibit = completion.getAutoCompleteList(prefix, sql, sqlToCursor)
465+
# get completions
466+
autoCompleteList, inhibit = ST.completion.getAutoCompleteList(prefix, sql, sqlToCursor)
455467

456468
# safe check here, so even if we return empty completions and inhibit is true
457469
# we return empty completions to show default sublime completions
458-
if ST.autoCompleteList is None or len(ST.autoCompleteList) == 0:
470+
if autoCompleteList is None or len(autoCompleteList) == 0:
459471
return None
460472

461473
if inhibit:
462-
return (ST.autoCompleteList, sublime.INHIBIT_WORD_COMPLETIONS)
474+
return (autoCompleteList, sublime.INHIBIT_WORD_COMPLETIONS)
463475

464-
return ST.autoCompleteList
476+
return autoCompleteList
465477

466478

467479
# #

SQLToolsAPI/Completion.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,32 @@ def format(self, stripQuotes=False):
161161

162162

163163
class Completion:
164-
def __init__(self, uppercaseKeywords, allTables, allColumns, allFunctions):
164+
def __init__(self, allTables, allColumns, allFunctions, settings=None):
165165
self.allTables = [CompletionItem('Table', table) for table in allTables]
166166
self.allColumns = [CompletionItem('Column', column) for column in allColumns]
167167
self.allFunctions = [CompletionItem('Function', func) for func in allFunctions]
168168

169+
# we don't save the settings (we don't need them after init)
170+
if settings is None:
171+
settings = {}
172+
173+
# get completion selectors from settings
174+
self.selectors = settings.get('selectors', [])
175+
176+
# determine type of completions
177+
self.completionType = settings.get('autocompletion', 'smart')
178+
if not self.completionType:
179+
self.completionType = None # autocompletion disabled
180+
else:
181+
self.completionType = str(self.completionType).strip()
182+
if self.completionType not in ['basic', 'smart']:
183+
self.completionType = 'smart'
184+
185+
# determine desired keywords case from settings
186+
formatSettings = settings.get('format', {})
187+
keywordCase = formatSettings.get('keyword_case', 'upper')
188+
uppercaseKeywords = keywordCase.lower().startswith('upper')
189+
169190
self.allKeywords = []
170191
for keyword in keywords_list:
171192
if uppercaseKeywords:
@@ -175,7 +196,34 @@ def __init__(self, uppercaseKeywords, allTables, allColumns, allFunctions):
175196

176197
self.allKeywords.append(CompletionItem('Keyword', keyword))
177198

178-
def getBasicAutoCompleteList(self, prefix):
199+
def getSelectors(self):
200+
return self.selectors
201+
202+
def isDisabled(self):
203+
return self.completionType is None
204+
205+
def getAutoCompleteList(self, prefix, sql, sqlToCursor):
206+
if self.isDisabled():
207+
return None
208+
209+
autocompleteList = []
210+
inhibit = False
211+
if self.completionType == 'smart':
212+
autocompleteList, inhibit = self._getAutoCompleteListSmart(prefix, sql, sqlToCursor)
213+
else:
214+
autocompleteList = self._getAutoCompleteListBasic(prefix)
215+
216+
if not autocompleteList:
217+
return None, False
218+
219+
# return completions with or without quotes?
220+
# determined based on ident after last dot
221+
startsWithQuote = _startsWithQuote(prefix.split(".").pop())
222+
autocompleteList = [item.format(startsWithQuote) for item in autocompleteList]
223+
224+
return autocompleteList, inhibit
225+
226+
def _getAutoCompleteListBasic(self, prefix):
179227
prefix = prefix.lower()
180228
autocompleteList = []
181229

@@ -198,13 +246,9 @@ def getBasicAutoCompleteList(self, prefix):
198246
if len(autocompleteList) == 0:
199247
return None
200248

201-
# return completions with or without quotes?
202-
# determined based on ident after last dot
203-
startsWithQuote = _startsWithQuote(prefix.split(".").pop())
204-
autocompleteList = [item.format(startsWithQuote) for item in autocompleteList]
205249
return autocompleteList
206250

207-
def getAutoCompleteList(self, prefix, sql, sqlToCursor):
251+
def _getAutoCompleteListSmart(self, prefix, sql, sqlToCursor):
208252
"""
209253
Generally, we recognize 3 different variations in prefix:
210254
* ident| // no dots (.) in prefix
@@ -262,10 +306,6 @@ def getAutoCompleteList(self, prefix, sql, sqlToCursor):
262306
if not autocompleteList:
263307
return None, False
264308

265-
# return completions with or without quotes?
266-
# determined based on ident after last dot
267-
startsWithQuote = _startsWithQuote(prefix.split(".").pop())
268-
autocompleteList = [item.format(startsWithQuote) for item in autocompleteList]
269309
return autocompleteList, inhibit
270310

271311
def _noDotsCompletions(self, prefix, identifiers, joinAlias=None):

0 commit comments

Comments
 (0)