19
19
def _stripQuotes (ident ):
20
20
return ident .strip ('"\' `' )
21
21
22
+
22
23
# used for formatting output
23
24
def _stripQuotesOnDemand (ident , doStrip = True ):
24
25
if doStrip :
25
26
return _stripQuotes (ident )
26
27
return ident
27
28
29
+
28
30
def _startsWithQuote (ident ):
29
31
# str.startswith can be matched against a tuple
30
32
quotes = ('`' , '"' )
31
33
return ident .startswith (quotes )
32
34
35
+
33
36
def _stripPrefix (text , prefix ):
34
37
if text .startswith (prefix ):
35
38
return text [len (prefix ):]
36
39
return text
37
40
38
41
39
- class CompletionItem (namedtuple ('CompletionItem' , ['type' , 'ident' , 'score' ])):
42
+ class CompletionItem (namedtuple ('CompletionItem' , ['type' , 'ident' ])):
40
43
"""
41
44
Represents a potential or actual completion item.
42
45
* type - Type of item e.g. (Table, Function, Column)
43
46
* ident - identifier e.g. ("tablename.column", "database.table", "alias")
44
- * score - the lower score, the better is match for completion item
45
47
"""
46
48
__slots__ = ()
47
49
@@ -100,9 +102,10 @@ def prefixMatchScore(self, search, exactly=False):
100
102
targetList = target .split ('.' )
101
103
targetObject = _stripQuotes (targetList .pop ())
102
104
targetParent = _stripQuotes (targetList .pop ())
103
- if (searchParent == targetParent and
104
- self ._stringMatched (targetObject , searchObject , exactly )):
105
- return 1 # highest score
105
+ if (searchParent == targetParent ):
106
+ if self ._stringMatched (targetObject , searchObject , exactly ):
107
+ return 1 # highest score
108
+ return 0
106
109
107
110
# second part matches ?
108
111
if '.' in target :
@@ -119,6 +122,13 @@ def prefixMatchScore(self, search, exactly=False):
119
122
return 0
120
123
return 0
121
124
125
+ def prefixMatchListScore (self , searchList , exactly = False ):
126
+ for item in searchList :
127
+ score = self .prefixMatchScore (item , exactly )
128
+ if score :
129
+ return score
130
+ return 0
131
+
122
132
# format completion item according to sublime text completions format
123
133
def format (self , stripQuotes = False ):
124
134
typeDisplay = ''
@@ -147,9 +157,9 @@ def format(self, stripQuotes=False):
147
157
148
158
class Completion :
149
159
def __init__ (self , uppercaseKeywords , allTables , allColumns , allFunctions ):
150
- self .allTables = [CompletionItem ('Table' , table , 0 ) for table in allTables ]
151
- self .allColumns = [CompletionItem ('Column' , column , 0 ) for column in allColumns ]
152
- self .allFunctions = [CompletionItem ('Function' , func , 0 ) for func in allFunctions ]
160
+ self .allTables = [CompletionItem ('Table' , table ) for table in allTables ]
161
+ self .allColumns = [CompletionItem ('Column' , column ) for column in allColumns ]
162
+ self .allFunctions = [CompletionItem ('Function' , func ) for func in allFunctions ]
153
163
154
164
self .allKeywords = []
155
165
for keyword in keywords_list :
@@ -158,7 +168,7 @@ def __init__(self, uppercaseKeywords, allTables, allColumns, allFunctions):
158
168
else :
159
169
keyword = keyword .lower ()
160
170
161
- self .allKeywords .append (CompletionItem ('Keyword' , keyword , 0 ))
171
+ self .allKeywords .append (CompletionItem ('Keyword' , keyword ))
162
172
163
173
def getBasicAutoCompleteList (self , prefix ):
164
174
prefix = prefix .lower ()
@@ -168,17 +178,17 @@ def getBasicAutoCompleteList(self, prefix):
168
178
for item in self .allColumns :
169
179
score = item .prefixMatchScore (prefix )
170
180
if score :
171
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
181
+ autocompleteList .append (item )
172
182
173
183
for item in self .allTables :
174
184
score = item .prefixMatchScore (prefix )
175
185
if score :
176
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
186
+ autocompleteList .append (item )
177
187
178
188
for item in self .allFunctions :
179
189
score = item .prefixMatchScore (prefix )
180
190
if score :
181
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
191
+ autocompleteList .append (item )
182
192
183
193
if len (autocompleteList ) == 0 :
184
194
return None
@@ -263,94 +273,93 @@ def _noDotsCompletions(self, prefix, identifiers, joinAlias=None):
263
273
Order: statement aliases -> statement cols -> statement tables -> statement functions,
264
274
then: other cols -> other tables -> other functions that match the prefix in their names
265
275
"""
266
- # get join conditions
267
- joinConditions = []
268
- if joinAlias :
269
- joinConditions = self ._joinConditionCompletions (identifiers , joinAlias )
270
276
271
277
# use set, as we are interested only in unique identifiers
272
278
sqlAliases = set ()
273
- sqlTables = set ()
274
- sqlColumns = set ()
275
- sqlFunctions = set ()
279
+ sqlTables = []
280
+ sqlColumns = []
281
+ sqlFunctions = []
282
+ otherTables = []
283
+ otherColumns = []
284
+ otherFunctions = []
285
+ otherKeywords = []
286
+ otherJoinConditions = []
287
+
288
+ # utilitary temp lists
289
+ identTables = set ()
290
+ identColumns = set ()
291
+ identFunctions = set ()
276
292
277
293
for ident in identifiers :
278
294
if ident .has_alias ():
279
- sqlAliases .add (CompletionItem ('Alias' , ident .alias , 0 ))
295
+ aliasItem = CompletionItem ('Alias' , ident .alias )
296
+ score = aliasItem .prefixMatchScore (prefix )
297
+ if score and aliasItem .ident != prefix :
298
+ sqlAliases .add (aliasItem )
280
299
281
300
if ident .is_function :
282
- functions = [
283
- fun
284
- for fun in self .allFunctions
285
- if fun .prefixMatchScore (ident .full_name , exactly = True ) > 0
286
- ]
287
- sqlFunctions .update (functions )
288
- else :
289
- tables = [
290
- table
291
- for table in self .allTables
292
- if table .prefixMatchScore (ident .full_name , exactly = True ) > 0
293
- ]
294
- sqlTables .update (tables )
295
- prefixForColumnMatch = ident .name + '.'
296
- columns = [
297
- col
298
- for col in self .allColumns
299
- if col .prefixMatchScore (prefixForColumnMatch , exactly = True ) > 0
300
- ]
301
- sqlColumns .update (columns )
302
-
303
- autocompleteList = []
304
-
305
- for condition in joinConditions :
306
- if condition .ident .lower ().startswith (prefix ):
307
- autocompleteList .append (CompletionItem (condition .type , condition .ident , 1 ))
308
-
309
- # first of all list aliases and identifiers related to currently parsed statement
310
- for item in sqlAliases :
311
- score = item .prefixMatchScore (prefix )
312
- if score and item .ident != prefix :
313
- autocompleteList .append (CompletionItem (item .type , item .ident , score ))
301
+ identFunctions .add (ident .full_name )
302
+ elif ident .is_table_alias :
303
+ identTables .add (ident .full_name )
304
+ identColumns .add (ident .name + '.' + prefix )
314
305
315
- for item in sqlColumns :
316
- score = item .prefixMatchScore (prefix )
306
+ for table in self . allTables :
307
+ score = table .prefixMatchScore (prefix , exactly = False )
317
308
if score :
318
- autocompleteList .append (CompletionItem (item .type , item .ident , score ))
309
+ if table .prefixMatchListScore (identTables , exactly = True ) > 0 :
310
+ sqlTables .append (table )
311
+ else :
312
+ otherTables .append (table )
319
313
320
- for item in sqlTables :
321
- score = item .prefixMatchScore (prefix )
314
+ for col in self . allColumns :
315
+ score = col .prefixMatchScore (prefix , exactly = False )
322
316
if score :
323
- autocompleteList .append (CompletionItem (item .type , item .ident , score ))
317
+ if col .prefixMatchListScore (identColumns , exactly = False ) > 0 :
318
+ sqlColumns .append (col )
319
+ else :
320
+ otherColumns .append (col )
324
321
325
- for item in sqlFunctions :
326
- score = item .prefixMatchScore (prefix )
322
+ for fun in self . allFunctions :
323
+ score = fun .prefixMatchScore (prefix , exactly = False )
327
324
if score :
328
- autocompleteList .append (CompletionItem (item .type , item .ident , score ))
325
+ if fun .prefixMatchListScore (identFunctions , exactly = True ) > 0 :
326
+ sqlColumns .append (fun )
327
+ else :
328
+ otherColumns .append (fun )
329
329
330
- # add keywords to auto-complete results
330
+ # keywords
331
331
for item in self .allKeywords :
332
332
score = item .prefixMatchScore (prefix )
333
333
if score :
334
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
334
+ otherKeywords .append (item )
335
335
336
- # add the rest of the columns, tables and functions that also match the prefix
337
- for item in self .allColumns :
338
- score = item .prefixMatchScore (prefix )
339
- if score :
340
- if item not in sqlColumns :
341
- autocompleteList .append (CompletionItem (item .type , item .ident , score ))
336
+ # join conditions
337
+ if joinAlias :
338
+ joinConditions = self ._joinConditionCompletions (identifiers , joinAlias )
342
339
343
- for item in self .allTables :
344
- score = item .prefixMatchScore (prefix )
345
- if score :
346
- if item not in sqlTables :
347
- autocompleteList .append (CompletionItem (item .type , item .ident , score ))
340
+ for condition in joinConditions :
341
+ if condition .ident .lower ().startswith (prefix ):
342
+ otherJoinConditions .append (condition )
348
343
349
- for item in self .allFunctions :
350
- score = item .prefixMatchScore (prefix )
351
- if score :
352
- if item not in sqlFunctions :
353
- autocompleteList .append (CompletionItem (item .type , item .ident , score ))
344
+ # collect the results in prefered order
345
+ autocompleteList = []
346
+
347
+ # first of all list join conditions (if applicable)
348
+ autocompleteList .extend (otherJoinConditions )
349
+
350
+ # then aliases and identifiers related to currently parsed statement
351
+ autocompleteList .extend (sqlAliases )
352
+
353
+ # then cols, tables, functions related to current statement
354
+ autocompleteList .extend (sqlColumns )
355
+ autocompleteList .extend (sqlTables )
356
+ autocompleteList .extend (sqlFunctions )
357
+
358
+ # then other matching cols, tables, functions
359
+ autocompleteList .extend (otherKeywords )
360
+ autocompleteList .extend (otherColumns )
361
+ autocompleteList .extend (otherTables )
362
+ autocompleteList .extend (otherFunctions )
354
363
355
364
return autocompleteList , False
356
365
@@ -390,7 +399,7 @@ def _singleDotCompletions(self, prefix, identifiers, joinAlias=None):
390
399
aliasPrefix = prefixParent + '.'
391
400
if condition .ident .lower ().startswith (aliasPrefix ):
392
401
autocompleteList .append (CompletionItem (condition .type ,
393
- _stripPrefix (condition .ident , aliasPrefix ), 1 ))
402
+ _stripPrefix (condition .ident , aliasPrefix )))
394
403
395
404
# first of all expand table aliases to real table names and try
396
405
# to match their columns with prefix of these expanded identifiers
@@ -400,23 +409,23 @@ def _singleDotCompletions(self, prefix, identifiers, joinAlias=None):
400
409
for item in self .allColumns :
401
410
score = item .prefixMatchScore (prefix_to_match )
402
411
if score :
403
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
412
+ autocompleteList .append (item )
404
413
405
414
# try to match all our other objects (tables, columns, functions) with prefix
406
415
for item in self .allColumns :
407
416
score = item .prefixMatchScore (prefix )
408
417
if score :
409
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
418
+ autocompleteList .append (item )
410
419
411
420
for item in self .allTables :
412
421
score = item .prefixMatchScore (prefix )
413
422
if score :
414
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
423
+ autocompleteList .append (item )
415
424
416
425
for item in self .allFunctions :
417
426
score = item .prefixMatchScore (prefix )
418
427
if score :
419
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
428
+ autocompleteList .append (item )
420
429
421
430
inhibit = len (autocompleteList ) > 0
422
431
# in case prefix parent is a query alias we simply don't know what those
@@ -432,7 +441,7 @@ def _multiDotCompletions(self, prefix, identifiers):
432
441
for item in self .allColumns :
433
442
score = item .prefixMatchScore (prefix )
434
443
if score :
435
- autocompleteList .append (CompletionItem ( item . type , item . ident , score ) )
444
+ autocompleteList .append (item )
436
445
437
446
if len (autocompleteList ) > 0 :
438
447
return autocompleteList , True
@@ -450,7 +459,7 @@ def _joinConditionCompletions(self, identifiers, joinAlias=None):
450
459
451
460
for ident in identifiers :
452
461
if ident .has_alias () and not ident .is_function :
453
- sqlTableAliases .add (CompletionItem ('Alias' , ident .alias , 0 ))
462
+ sqlTableAliases .add (CompletionItem ('Alias' , ident .alias ))
454
463
455
464
prefixForColumnMatch = ident .name + '.'
456
465
columns = [
@@ -486,7 +495,7 @@ def _joinConditionCompletions(self, identifiers, joinAlias=None):
486
495
sideA = joinAlias + '.' + joinColumn .name
487
496
sideB = otherAlias + '.' + otherColumn .name
488
497
489
- joinCandidatesCompletions .append (CompletionItem ('Condition' , sideA + ' = ' + sideB , 0 ))
490
- joinCandidatesCompletions .append (CompletionItem ('Condition' , sideB + ' = ' + sideA , 0 ))
498
+ joinCandidatesCompletions .append (CompletionItem ('Condition' , sideA + ' = ' + sideB ))
499
+ joinCandidatesCompletions .append (CompletionItem ('Condition' , sideB + ' = ' + sideA ))
491
500
492
501
return joinCandidatesCompletions
0 commit comments