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

Commit 6f9b579

Browse files
authored
Merge pull request #191 from mtxr/feature/improve-completions-speed
Improve completions and connections listing performance
2 parents 376a727 + f780421 commit 6f9b579

File tree

3 files changed

+113
-65
lines changed

3 files changed

+113
-65
lines changed

Default.sublime-commands

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
"command": "st_desc_function"
3333
},
3434
{
35-
"caption": "ST: Remove Table and Functions cache",
36-
"command": "st_clear_cache"
35+
"caption": "ST: Refresh Connection Data",
36+
"command": "st_refresh_connection_data"
3737
},
3838
{
3939
"caption": "ST: Save Query",

SQLTools.py

Lines changed: 60 additions & 52 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,21 +316,46 @@ 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+
callbacksRun = 0
325+
318326
if not ST.conn:
319327
return
320328

321329
def tbCallback(tables):
322-
setattr(ST, 'tables', tables)
330+
ST.tables = tables
331+
332+
nonlocal callbacksRun
333+
callbacksRun += 1
334+
if callbacksRun == 3:
335+
ST.completion = Completion(ST.tables, ST.columns, ST.functions, settings=settings)
336+
323337
if tablesCallback:
324338
tablesCallback()
325339

326340
def colCallback(columns):
327-
setattr(ST, 'columns', columns)
341+
ST.columns = columns
342+
343+
nonlocal callbacksRun
344+
callbacksRun += 1
345+
if callbacksRun == 3:
346+
ST.completion = Completion(ST.tables, ST.columns, ST.functions, settings=settings)
347+
328348
if columnsCallback:
329349
columnsCallback()
330350

331351
def funcCallback(functions):
332-
setattr(ST, 'functions', functions)
352+
ST.functions = functions
353+
354+
nonlocal callbacksRun
355+
callbacksRun += 1
356+
if callbacksRun == 3:
357+
ST.completion = Completion(ST.tables, ST.columns, ST.functions, settings=settings)
358+
333359
if functionsCallback:
334360
functionsCallback()
335361

@@ -345,18 +371,8 @@ 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-
ST.reset_cache(tablesCallback, columnsCallback, functionsCallback)
349-
350-
Log('Connection {0} selected'.format(ST.conn))
351-
352-
@staticmethod
353-
def reset_cache(tablesCallback=None, columnsCallback=None, functionsCallback=None):
354-
# clear list of identifiers in case connection is changed
355-
ST.tables = []
356-
ST.columns = []
357-
ST.functions = []
358-
359374
ST.loadConnectionData(tablesCallback, columnsCallback, functionsCallback)
375+
Log('Connection {0} selected'.format(ST.conn))
360376

361377
@staticmethod
362378
def selectConnection(tablesCallback=None, columnsCallback=None, functionsCallback=None):
@@ -393,10 +409,21 @@ def on_query_completions(view, prefix, locations):
393409
if ST.conn is None:
394410
return None
395411

412+
if ST.completion is None:
413+
return None
414+
415+
if ST.completion.isDisabled():
416+
return None
417+
396418
if not len(locations):
397419
return None
398420

399-
selectors = settings.get('selectors', [])
421+
# disable completions inside strings
422+
if view.match_selector(locations[0], 'string'):
423+
return None
424+
425+
# show completions only for specific selectors
426+
selectors = ST.completion.getSelectors()
400427
selectorMatched = False
401428
if selectors:
402429
for selector in selectors:
@@ -407,18 +434,6 @@ def on_query_completions(view, prefix, locations):
407434
if not selectorMatched:
408435
return None
409436

410-
# completions enabled? if yes, determine which type
411-
completionType = settings.get('autocompletion', 'smart')
412-
if not completionType:
413-
return None # autocompletion disabled
414-
completionType = str(completionType).strip()
415-
if completionType not in ['basic', 'smart']:
416-
completionType = 'smart'
417-
418-
# no completions inside strings
419-
if view.match_selector(locations[0], 'string'):
420-
return None
421-
422437
# sublimePrefix = prefix
423438
# sublimeCompletions = view.extract_completions(sublimePrefix, locations[0])
424439

@@ -439,33 +454,24 @@ def on_query_completions(view, prefix, locations):
439454
except Exception as e:
440455
Log(e)
441456

442-
# determine desired keywords case from settings
443-
formatSettings = settings.get('format', {})
444-
keywordCase = formatSettings.get('keyword_case', 'upper')
445-
uppercaseKeywords = keywordCase.lower().startswith('upper')
457+
# use current paragraph as sql text to parse
458+
sqlRegion = expand_to_paragraph(view, currentPoint)
459+
sql = view.substr(sqlRegion)
460+
sqlToCursorRegion = sublime.Region(sqlRegion.begin(), currentPoint)
461+
sqlToCursor = view.substr(sqlToCursorRegion)
446462

447-
inhibit = False
448-
completion = Completion(uppercaseKeywords, ST.tables, ST.columns, ST.functions)
449-
450-
if completionType == 'basic':
451-
ST.autoCompleteList = completion.getBasicAutoCompleteList(prefix)
452-
else:
453-
# use current paragraph as sql text to parse
454-
sqlRegion = expand_to_paragraph(view, currentPoint)
455-
sql = view.substr(sqlRegion)
456-
sqlToCursorRegion = sublime.Region(sqlRegion.begin(), currentPoint)
457-
sqlToCursor = view.substr(sqlToCursorRegion)
458-
ST.autoCompleteList, inhibit = completion.getAutoCompleteList(prefix, sql, sqlToCursor)
463+
# get completions
464+
autoCompleteList, inhibit = ST.completion.getAutoCompleteList(prefix, sql, sqlToCursor)
459465

460466
# safe check here, so even if we return empty completions and inhibit is true
461467
# we return empty completions to show default sublime completions
462-
if ST.autoCompleteList is None or len(ST.autoCompleteList) == 0:
468+
if autoCompleteList is None or len(autoCompleteList) == 0:
463469
return None
464470

465471
if inhibit:
466-
return (ST.autoCompleteList, sublime.INHIBIT_WORD_COMPLETIONS)
472+
return (autoCompleteList, sublime.INHIBIT_WORD_COMPLETIONS)
467473

468-
return ST.autoCompleteList
474+
return autoCompleteList
469475

470476

471477
# #
@@ -544,12 +550,14 @@ def cb(index):
544550
# from "function_name(int)"
545551
ST.selectFunction(cb)
546552

547-
class StClearCache(WindowCommand):
553+
554+
class StRefreshConnectionData(WindowCommand):
548555
@staticmethod
549556
def run():
550557
if not ST.conn:
551558
return
552-
ST.reset_cache()
559+
ST.loadConnectionData()
560+
553561

554562
class StExplainPlan(WindowCommand):
555563
@staticmethod

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)