11import { 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' ;
39import { EntityContextType } from 'monaco-sql-languages/esm/main' ;
410
511import { getCatalogs , getDataBases , getSchemas , getTables , getViews } from './dbMetaProvider' ;
12+ import { AttrName , EntityContext } from 'dt-sql-parser/dist/parser/common/entityCollector' ;
613
714const haveCatalogSQLType = ( languageId : string ) => {
815 return [ 'flinksql' , 'trinosql' ] . includes ( languageId . toLowerCase ( ) ) ;
@@ -12,28 +19,18 @@ const namedSchemaSQLType = (languageId: string) => {
1219 return [ 'trinosql' , 'hivesql' , 'sparksql' ] . includes ( languageId ) ;
1320} ;
1421
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 [ ] > => {
2531 const haveCatalog = haveCatalogSQLType ( languageId ) ;
2632 const getDBOrSchema = namedSchemaSQLType ( languageId ) ? getSchemas : getDataBases ;
2733
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-
3734 let syntaxCompletionItems : ICompletionItem [ ] = [ ] ;
3835
3936 /** 是否已经存在 catalog 补全项 */
@@ -58,6 +55,13 @@ export const completionService: CompletionService = async function (
5855 const words = wordRanges . map ( ( wr ) => wr . text ) ;
5956 const wordCount = words . length ;
6057
58+ /**
59+ * 在做上下文判断时,如果已经键入了空格,则表示已经离开了该上下文。
60+ * 如: SELECT id | FROM t1
61+ * 光标所处位置在id后且键入了空格,虽然收集到的上下文信息中包含了`EntityContextType.COLUMN`,但不应该继续补全字段, table同理
62+ */
63+ if ( isWordRangesEndWithWhiteSpace ( wordRanges ) ) continue ;
64+
6165 if (
6266 syntaxContextType === EntityContextType . CATALOG ||
6367 syntaxContextType === EntityContextType . DATABASE_CREATE
@@ -108,8 +112,21 @@ export const completionService: CompletionService = async function (
108112 }
109113
110114 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+ } ) ) || [ ] ;
111127 syntaxCompletionItems = syntaxCompletionItems . concat (
112- await getTables ( languageId )
128+ await getTables ( languageId ) ,
129+ createTables
113130 ) ;
114131 existTableCompletions = true ;
115132 }
@@ -182,6 +199,168 @@ export const completionService: CompletionService = async function (
182199 }
183200 }
184201 }
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+ }
185335 }
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+
186365 return [ ...syntaxCompletionItems , ...keywordsCompletionItems ] ;
187366} ;
0 commit comments