mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 00:10:25 +08:00 
			
		
		
		
	fix: sql代码提示修复:支持跨schema提示
This commit is contained in:
		@@ -93,6 +93,52 @@ export class DbInst {
 | 
			
		||||
        return tables;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async loadTableSuggestions(dbName: string, range: any, reload?: boolean) {
 | 
			
		||||
        const tables = await this.loadTables(dbName, reload);
 | 
			
		||||
        // 表名联想
 | 
			
		||||
        let suggestions: languages.CompletionItem[] = [];
 | 
			
		||||
        tables?.forEach((tableMeta: any, index: any) => {
 | 
			
		||||
            const { tableName, tableComment } = tableMeta;
 | 
			
		||||
            suggestions.push({
 | 
			
		||||
                label: {
 | 
			
		||||
                    label: tableName + ' - ' + tableComment,
 | 
			
		||||
                    description: 'table',
 | 
			
		||||
                },
 | 
			
		||||
                kind: monaco.languages.CompletionItemKind.File,
 | 
			
		||||
                detail: tableComment,
 | 
			
		||||
                insertText: tableName + ' ',
 | 
			
		||||
                range,
 | 
			
		||||
                sortText: 300 + index + '',
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        return { suggestions };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 加载列信息提示 */
 | 
			
		||||
    async loadTableColumnSuggestions(db: string, tableName: string, range: any) {
 | 
			
		||||
        let dbHits = await this.loadDbHints(db);
 | 
			
		||||
        let columns = dbHits[tableName];
 | 
			
		||||
        let suggestions: languages.CompletionItem[] = [];
 | 
			
		||||
        columns?.forEach((a: string, index: any) => {
 | 
			
		||||
            // 字段数据格式  字段名 字段注释,  如: create_time  [datetime][创建时间]
 | 
			
		||||
            const nameAndComment = a.split('  ');
 | 
			
		||||
            const fieldName = nameAndComment[0];
 | 
			
		||||
            suggestions.push({
 | 
			
		||||
                label: {
 | 
			
		||||
                    label: a,
 | 
			
		||||
                    description: 'column',
 | 
			
		||||
                },
 | 
			
		||||
                kind: monaco.languages.CompletionItemKind.Property,
 | 
			
		||||
                detail: '', // 不显示detail, 否则选中时备注等会被遮挡
 | 
			
		||||
                insertText: fieldName, // create_time
 | 
			
		||||
                range,
 | 
			
		||||
                sortText: 100 + index + '', // 使用表字段声明顺序排序,排序需为字符串类型
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return { suggestions };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取表的所有列信息
 | 
			
		||||
     * @param table 表名
 | 
			
		||||
@@ -482,7 +528,7 @@ export type FieldsMeta = {
 | 
			
		||||
 * @param db 库名
 | 
			
		||||
 * @param dbs 该库所有库名
 | 
			
		||||
 */
 | 
			
		||||
export function registerDbCompletionItemProvider(language: string, dbId: number, db: string, dbs: [] = []) {
 | 
			
		||||
export function registerDbCompletionItemProvider(language: string, dbId: number, db: string, dbs: any[] = []) {
 | 
			
		||||
    registerCompletionItemProvider(language, {
 | 
			
		||||
        triggerCharacters: ['.', ' '],
 | 
			
		||||
        provideCompletionItems: async (model: editor.ITextModel, position: Position): Promise<languages.CompletionList | null | undefined> => {
 | 
			
		||||
@@ -518,29 +564,6 @@ export function registerDbCompletionItemProvider(language: string, dbId: number,
 | 
			
		||||
            let lastToken = tokens[tokens.length - 1].toLowerCase();
 | 
			
		||||
            const secondToken = (tokens.length > 2 && tokens[tokens.length - 2].toLowerCase()) || '';
 | 
			
		||||
 | 
			
		||||
            // console.log("光标前文本:=>" + textBeforePointerMulti)
 | 
			
		||||
            // console.log("最后输入的:=>" + lastToken)
 | 
			
		||||
 | 
			
		||||
            let suggestions: languages.CompletionItem[] = [];
 | 
			
		||||
 | 
			
		||||
            let alias = '';
 | 
			
		||||
            if (lastToken.indexOf('.') > -1 || secondToken.indexOf('.') > -1) {
 | 
			
		||||
                // 如果是.触发代码提示,则进行【 库.表名联想 】 或 【 表别名.表字段联想 】
 | 
			
		||||
                alias = lastToken.substring(0, lastToken.lastIndexOf('.'));
 | 
			
		||||
                if (lastToken.trim().startsWith('.')) {
 | 
			
		||||
                    alias = secondToken;
 | 
			
		||||
                }
 | 
			
		||||
                // 如果字符串粘连起了如:'a.creator,a.',需要重新取出别名
 | 
			
		||||
                let aliasArr = lastToken.split(',');
 | 
			
		||||
                if (aliasArr.length > 1) {
 | 
			
		||||
                    lastToken = aliasArr[aliasArr.length - 1];
 | 
			
		||||
                    alias = lastToken.substring(0, lastToken.lastIndexOf('.'));
 | 
			
		||||
                    if (lastToken.trim().startsWith('.')) {
 | 
			
		||||
                        alias = secondToken;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 获取光标所在行之前的所有文本内容
 | 
			
		||||
            const textBeforeCursor = model.getValueInRange({
 | 
			
		||||
                startLineNumber: 1,
 | 
			
		||||
@@ -579,39 +602,62 @@ export function registerDbCompletionItemProvider(language: string, dbId: number,
 | 
			
		||||
                sqlStatement = textBeforeCursor + textAfterCursor;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const tableName = getTableName4SqlCtx(sqlStatement, alias);
 | 
			
		||||
            // 提出到表名,则将表对应的字段也添加进提示建议
 | 
			
		||||
            if (tableName) {
 | 
			
		||||
                let dbHits = await dbInst.loadDbHints(db);
 | 
			
		||||
                let columns = dbHits[tableName];
 | 
			
		||||
                columns?.forEach((a: string, index: any) => {
 | 
			
		||||
                    // 字段数据格式  字段名 字段注释,  如: create_time  [datetime][创建时间]
 | 
			
		||||
                    const nameAndComment = a.split('  ');
 | 
			
		||||
                    const fieldName = nameAndComment[0];
 | 
			
		||||
            let suggestions: languages.CompletionItem[] = [];
 | 
			
		||||
 | 
			
		||||
            // 库名提示
 | 
			
		||||
            if (dbs && dbs.length > 0) {
 | 
			
		||||
                dbs.forEach((a: any) => {
 | 
			
		||||
                    suggestions.push({
 | 
			
		||||
                        label: {
 | 
			
		||||
                            label: a,
 | 
			
		||||
                            description: 'column',
 | 
			
		||||
                            description: 'schema',
 | 
			
		||||
                        },
 | 
			
		||||
                        kind: monaco.languages.CompletionItemKind.Property,
 | 
			
		||||
                        detail: '', // 不显示detail, 否则选中时备注等会被遮挡
 | 
			
		||||
                        insertText: fieldName, // create_time
 | 
			
		||||
                        kind: monaco.languages.CompletionItemKind.Folder,
 | 
			
		||||
                        insertText: a,
 | 
			
		||||
                        range,
 | 
			
		||||
                        sortText: 100 + index + '', // 使用表字段声明顺序排序,排序需为字符串类型
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                // 若存在字段提示,并且有别名,则提示字段即可,不完善后续的表名以及函数等
 | 
			
		||||
                if (suggestions.length > 0 && alias) {
 | 
			
		||||
                    return {
 | 
			
		||||
                        suggestions: suggestions,
 | 
			
		||||
                    };
 | 
			
		||||
            let alias = '';
 | 
			
		||||
            if (lastToken.indexOf('.') > -1 || secondToken.indexOf('.') > -1) {
 | 
			
		||||
                // 如果是.触发代码提示,则进行【 库.表名联想 】 或 【 表别名.表字段联想 】
 | 
			
		||||
                alias = lastToken.substring(0, lastToken.lastIndexOf('.'));
 | 
			
		||||
                if (lastToken.trim().startsWith('.')) {
 | 
			
		||||
                    alias = secondToken;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 如果字符串粘连起了如:'a.creator,a.',需要重新取出别名
 | 
			
		||||
                let aliasArr = lastToken.split(',');
 | 
			
		||||
                if (aliasArr.length > 1) {
 | 
			
		||||
                    lastToken = aliasArr[aliasArr.length - 1];
 | 
			
		||||
                    alias = lastToken.substring(0, lastToken.lastIndexOf('.'));
 | 
			
		||||
                    if (lastToken.trim().startsWith('.')) {
 | 
			
		||||
                        alias = secondToken;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 如果是【库.表名联想】.前的字符串是库名
 | 
			
		||||
                if (dbs.indexOf(alias) >= 0) {
 | 
			
		||||
                    return await dbInst.loadTableSuggestions(alias, range);
 | 
			
		||||
                }
 | 
			
		||||
                // 表下列名联想  .前的字符串是表名或表别名
 | 
			
		||||
                const sqlInfo = getTableName4SqlCtx(sqlStatement, alias, db);
 | 
			
		||||
                // 提出到表名,则将表对应的字段也添加进提示建议
 | 
			
		||||
                if (sqlInfo) {
 | 
			
		||||
                    return await dbInst.loadTableColumnSuggestions(sqlInfo.db, sqlInfo.tableName, range);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const tables = await dbInst.loadTables(db);
 | 
			
		||||
            // 空格触发也会提示字段信息
 | 
			
		||||
            const sqlInfo = getTableName4SqlCtx(sqlStatement, alias, db);
 | 
			
		||||
            if (sqlInfo) {
 | 
			
		||||
                const columnSuggestions = await dbInst.loadTableColumnSuggestions(sqlInfo.db, sqlInfo.tableName, range);
 | 
			
		||||
                suggestions.push(...columnSuggestions.suggestions);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 表名联想
 | 
			
		||||
            // 当前库的表名联想
 | 
			
		||||
            const tables = await dbInst.loadTables(db);
 | 
			
		||||
            tables.forEach((tableMeta: any, index: any) => {
 | 
			
		||||
                const { tableName, tableComment } = tableMeta;
 | 
			
		||||
                suggestions.push({
 | 
			
		||||
@@ -695,21 +741,6 @@ export function registerDbCompletionItemProvider(language: string, dbId: number,
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // 库名提示
 | 
			
		||||
            if (dbs && dbs.length > 0) {
 | 
			
		||||
                dbs.forEach((a: any) => {
 | 
			
		||||
                    suggestions.push({
 | 
			
		||||
                        label: {
 | 
			
		||||
                            label: a,
 | 
			
		||||
                            description: 'schema',
 | 
			
		||||
                        },
 | 
			
		||||
                        kind: monaco.languages.CompletionItemKind.Folder,
 | 
			
		||||
                        insertText: a,
 | 
			
		||||
                        range,
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 默认提示
 | 
			
		||||
            return {
 | 
			
		||||
                suggestions: suggestions,
 | 
			
		||||
@@ -718,31 +749,33 @@ export function registerDbCompletionItemProvider(language: string, dbId: number,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getTableName4SqlCtx(sql: string, alias: string = '') {
 | 
			
		||||
function getTableName4SqlCtx(sql: string, alias: string = '', defaultDb: string): { tableName: string; tableAlias: string; db: string } | undefined {
 | 
			
		||||
    // 去除多余的换行、空格和制表符
 | 
			
		||||
    sql = sql.replace(/[\r\n\s\t]+/g, ' ');
 | 
			
		||||
 | 
			
		||||
    // 提取所有可能的表名和别名
 | 
			
		||||
    const regex = /(?:(?:FROM|JOIN|UPDATE)\s+(\S+)\s+(?:AS\s+)?(\S+))/gi;
 | 
			
		||||
    const regex = /(?:FROM|JOIN|UPDATE)\s+(\S+)\s+(?:AS\s+)?(\S+)/gi;
 | 
			
		||||
    let matches;
 | 
			
		||||
    const tables = [];
 | 
			
		||||
 | 
			
		||||
    // 使用正则表达式匹配所有的表和别名
 | 
			
		||||
    while ((matches = regex.exec(sql)) !== null) {
 | 
			
		||||
        const tableName = matches[1].replace(/[`"]/g, '');
 | 
			
		||||
        let tableName = matches[1].replace(/[`"]/g, '');
 | 
			
		||||
        let db = defaultDb;
 | 
			
		||||
        if (tableName.indexOf('.') >= 0) {
 | 
			
		||||
            let info = tableName.split('.');
 | 
			
		||||
            db = info[0];
 | 
			
		||||
            tableName = info[1];
 | 
			
		||||
        }
 | 
			
		||||
        const tableAlias = matches[2] ? matches[2].replace(/[`"]/g, '') : tableName;
 | 
			
		||||
        tables.push({ tableName, tableAlias });
 | 
			
		||||
        tables.push({ tableName, tableAlias, db });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // console.log('sql....', sql);
 | 
			
		||||
    // console.log('alias....', alias);
 | 
			
		||||
    // console.log('parset tables...', tables);
 | 
			
		||||
    if (alias) {
 | 
			
		||||
        // 如果指定了别名参数,则返回对应的表名
 | 
			
		||||
        const table = tables.find((t) => t.tableAlias === alias);
 | 
			
		||||
        return table ? table.tableName : '';
 | 
			
		||||
        return tables.find((t) => t.tableAlias === alias);
 | 
			
		||||
    } else {
 | 
			
		||||
        // 如果未指定别名参数,则返回第一个表名
 | 
			
		||||
        return tables.length > 0 ? tables[0].tableName : '';
 | 
			
		||||
        return tables.length > 0 ? tables[0] : undefined;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user