1
1
import { languages } from 'monaco-editor/esm/vs/editor/editor.api' ;
2
- import { CompletionService , ICompletionItem } from 'monaco-sql-languages/esm/languageService' ;
2
+ import {
3
+ CommonEntityContext ,
4
+ CompletionService ,
5
+ ICompletionItem ,
6
+ Suggestions ,
7
+ WordRange
8
+ } from 'monaco-sql-languages/esm/languageService' ;
3
9
import { EntityContextType } from 'monaco-sql-languages/esm/main' ;
4
10
5
11
import { getCatalogs , getDataBases , getSchemas , getTables , getViews } from './dbMetaProvider' ;
12
+ import { AttrName , EntityContext } from 'dt-sql-parser/dist/parser/common/entityCollector' ;
6
13
7
14
const haveCatalogSQLType = ( languageId : string ) => {
8
15
return [ 'flinksql' , 'trinosql' ] . includes ( languageId . toLowerCase ( ) ) ;
@@ -12,28 +19,18 @@ const namedSchemaSQLType = (languageId: string) => {
12
19
return [ 'trinosql' , 'hivesql' , 'sparksql' ] . includes ( languageId ) ;
13
20
} ;
14
21
15
- export const completionService : CompletionService = async function (
16
- model ,
17
- _position ,
18
- _completionContext ,
19
- suggestions
20
- ) {
21
- if ( ! suggestions ) {
22
- return Promise . resolve ( [ ] ) ;
23
- }
24
- const languageId = model . getLanguageId ( ) ;
22
+ const isWordRangesEndWithWhiteSpace = ( wordRanges : WordRange [ ] ) => {
23
+ return wordRanges . length > 1 && wordRanges . at ( - 1 ) ?. text === ' ' ;
24
+ } ;
25
+
26
+ const getSyntaxCompletionItems = async (
27
+ languageId : string ,
28
+ syntax : Suggestions [ 'syntax' ] ,
29
+ entities : EntityContext [ ] | null
30
+ ) : Promise < ICompletionItem [ ] > => {
25
31
const haveCatalog = haveCatalogSQLType ( languageId ) ;
26
32
const getDBOrSchema = namedSchemaSQLType ( languageId ) ? getSchemas : getDataBases ;
27
33
28
- const { keywords, syntax } = suggestions ;
29
-
30
- const keywordsCompletionItems : ICompletionItem [ ] = keywords . map ( ( kw ) => ( {
31
- label : kw ,
32
- kind : languages . CompletionItemKind . Keyword ,
33
- detail : '关键字' ,
34
- sortText : '2' + kw
35
- } ) ) ;
36
-
37
34
let syntaxCompletionItems : ICompletionItem [ ] = [ ] ;
38
35
39
36
/** 是否已经存在 catalog 补全项 */
@@ -58,6 +55,13 @@ export const completionService: CompletionService = async function (
58
55
const words = wordRanges . map ( ( wr ) => wr . text ) ;
59
56
const wordCount = words . length ;
60
57
58
+ /**
59
+ * 在做上下文判断时,如果已经键入了空格,则表示已经离开了该上下文。
60
+ * 如: SELECT id | FROM t1
61
+ * 光标所处位置在id后且键入了空格,虽然收集到的上下文信息中包含了`EntityContextType.COLUMN`,但不应该继续补全字段, table同理
62
+ */
63
+ if ( isWordRangesEndWithWhiteSpace ( wordRanges ) ) continue ;
64
+
61
65
if (
62
66
syntaxContextType === EntityContextType . CATALOG ||
63
67
syntaxContextType === EntityContextType . DATABASE_CREATE
@@ -108,8 +112,21 @@ export const completionService: CompletionService = async function (
108
112
}
109
113
110
114
if ( ! existTableCompletions ) {
115
+ const createTables =
116
+ entities
117
+ ?. filter (
118
+ ( entity ) =>
119
+ entity . entityContextType === EntityContextType . TABLE_CREATE
120
+ )
121
+ . map ( ( tb ) => ( {
122
+ label : tb . text ,
123
+ kind : languages . CompletionItemKind . Field ,
124
+ detail : 'table' ,
125
+ sortText : '1' + tb . text
126
+ } ) ) || [ ] ;
111
127
syntaxCompletionItems = syntaxCompletionItems . concat (
112
- await getTables ( languageId )
128
+ await getTables ( languageId ) ,
129
+ createTables
113
130
) ;
114
131
existTableCompletions = true ;
115
132
}
@@ -182,6 +199,168 @@ export const completionService: CompletionService = async function (
182
199
}
183
200
}
184
201
}
202
+
203
+ if ( syntaxContextType === EntityContextType . COLUMN ) {
204
+ const inSelectStmtContext = entities ?. some (
205
+ ( entity ) =>
206
+ entity . entityContextType === EntityContextType . TABLE &&
207
+ entity . belongStmt . isContainCaret
208
+ ) ;
209
+ // 上下文中建的所有表
210
+ const allCreateTables =
211
+ ( entities ?. filter (
212
+ ( entity ) => entity . entityContextType === EntityContextType . TABLE_CREATE
213
+ ) as CommonEntityContext [ ] ) || [ ] ;
214
+
215
+ if ( inSelectStmtContext ) {
216
+ // select语句中的来源表
217
+ // todo filter 子查询中的表
218
+ const fromTables =
219
+ entities ?. filter (
220
+ ( entity ) =>
221
+ entity . entityContextType === EntityContextType . TABLE &&
222
+ entity . belongStmt . isContainCaret
223
+ ) || [ ] ;
224
+ // 从上下文中找到来源表的定义信息
225
+ const fromTableDefinitionEntities = allCreateTables . filter ( ( tb ) =>
226
+ fromTables ?. some ( ( ft ) => ft . text === tb . text )
227
+ ) ;
228
+ const tableNameAliasMap = fromTableDefinitionEntities . reduce (
229
+ ( acc : Record < string , string > , tb ) => {
230
+ acc [ tb . text ] =
231
+ fromTables ?. find ( ( ft ) => ft . text === tb . text ) ?. [ AttrName . alias ] ?. text ||
232
+ tb . text ;
233
+ return acc ;
234
+ } ,
235
+ { }
236
+ ) ;
237
+
238
+ let fromTableColumns : ( ICompletionItem & {
239
+ _tableName ?: string ;
240
+ _columnText ?: string ;
241
+ } ) [ ] = [ ] ;
242
+
243
+ if ( wordRanges . length <= 1 ) {
244
+ const columnRepeatCountMap = new Map < string , number > ( ) ;
245
+ fromTableColumns = fromTableDefinitionEntities
246
+ . map ( ( tb ) => {
247
+ const displayTbName =
248
+ tableNameAliasMap [ tb . text ] === tb . text
249
+ ? tb . text
250
+ : tableNameAliasMap [ tb . text ] ;
251
+ return (
252
+ tb . columns ?. map ( ( column ) => {
253
+ const columnName = column . text ;
254
+ const repeatCount = columnRepeatCountMap . get ( columnName ) || 0 ;
255
+ columnRepeatCountMap . set ( columnName , repeatCount + 1 ) ;
256
+ return {
257
+ label :
258
+ column . text +
259
+ ( column [ AttrName . colType ] ?. text
260
+ ? `(${ column [ AttrName . colType ] . text } )`
261
+ : '' ) ,
262
+ insertText : column . text ,
263
+ kind : languages . CompletionItemKind . EnumMember ,
264
+ detail : `来源表 ${ displayTbName } 的字段` ,
265
+ sortText : '0' + displayTbName + column . text + repeatCount ,
266
+ _tableName : displayTbName ,
267
+ _columnText : column . text
268
+ } ;
269
+ } ) || [ ]
270
+ ) ;
271
+ } )
272
+ . flat ( ) ;
273
+
274
+ // 如果有多个重名字段,则插入的字段自动包含表名
275
+ fromTableColumns = fromTableColumns . map ( ( column ) => {
276
+ const columnRepeatCount =
277
+ columnRepeatCountMap . get ( column . label as string ) || 0 ;
278
+ const isFromMultipleTables = fromTables . length > 1 ;
279
+ return columnRepeatCount > 1 && isFromMultipleTables
280
+ ? {
281
+ ...column ,
282
+ insertText : `${ column . _tableName } .${ column . _columnText } `
283
+ }
284
+ : column ;
285
+ } ) ;
286
+
287
+ // 输入字段时提供可选表
288
+ const tableOrAliasCompletionItems = fromTables . map ( ( tb ) => {
289
+ const displayTbName = tableNameAliasMap [ tb . text ]
290
+ ? tableNameAliasMap [ tb . text ]
291
+ : tb . text ;
292
+ return {
293
+ label : displayTbName ,
294
+ kind : languages . CompletionItemKind . Field ,
295
+ detail : `table` ,
296
+ sortText : '1' + displayTbName
297
+ } ;
298
+ } ) ;
299
+
300
+ syntaxCompletionItems = syntaxCompletionItems . concat (
301
+ tableOrAliasCompletionItems
302
+ ) ;
303
+ } else if ( wordRanges . length >= 2 && words [ 1 ] === '.' ) {
304
+ const tbNameOrAlias = words [ 0 ] ;
305
+ fromTableColumns = fromTableDefinitionEntities
306
+ . filter (
307
+ ( tb ) =>
308
+ tb . text === tbNameOrAlias ||
309
+ tableNameAliasMap [ tb . text ] === tbNameOrAlias
310
+ )
311
+ . map ( ( tb ) => {
312
+ const displayTbName = tableNameAliasMap [ tb . text ]
313
+ ? tableNameAliasMap [ tb . text ]
314
+ : tb . text ;
315
+ return (
316
+ tb . columns ?. map ( ( column ) => ( {
317
+ label :
318
+ column . text +
319
+ ( column [ AttrName . colType ] ?. text
320
+ ? `(${ column [ AttrName . colType ] . text } )`
321
+ : '' ) ,
322
+ insertText : column . text ,
323
+ kind : languages . CompletionItemKind . EnumMember ,
324
+ detail : `来源表 ${ displayTbName } 的字段` ,
325
+ sortText : '0' + displayTbName + column . text
326
+ } ) ) || [ ]
327
+ ) ;
328
+ } )
329
+ . flat ( ) ;
330
+ }
331
+
332
+ syntaxCompletionItems = syntaxCompletionItems . concat ( fromTableColumns ) ;
333
+ }
334
+ }
185
335
}
336
+
337
+ return syntaxCompletionItems ;
338
+ } ;
339
+
340
+ export const completionService : CompletionService = async function (
341
+ model ,
342
+ _position ,
343
+ _completionContext ,
344
+ suggestions ,
345
+ entities
346
+ ) {
347
+ if ( ! suggestions ) {
348
+ return Promise . resolve ( [ ] ) ;
349
+ }
350
+ const languageId = model . getLanguageId ( ) ;
351
+
352
+ const { keywords, syntax } = suggestions ;
353
+ console . log ( 'syntax' , syntax ) ;
354
+ console . log ( 'entities' , entities ) ;
355
+
356
+ const keywordsCompletionItems : ICompletionItem [ ] = keywords . map ( ( kw ) => ( {
357
+ label : kw ,
358
+ kind : languages . CompletionItemKind . Keyword ,
359
+ detail : '关键字' ,
360
+ sortText : '2' + kw
361
+ } ) ) ;
362
+
363
+ const syntaxCompletionItems = await getSyntaxCompletionItems ( languageId , syntax , entities ) ;
364
+
186
365
return [ ...syntaxCompletionItems , ...keywordsCompletionItems ] ;
187
366
} ;
0 commit comments