diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index fa684269..d19a1960 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -1,4 +1,5 @@ import type { ParseError } from 'dt-sql-parser'; +import { ColumnEntityContext, CommonEntityContext, EntityContextType } from 'dt-sql-parser'; import { EntityContext } from 'dt-sql-parser/dist/parser/common/entityCollector'; import { WordPosition } from 'dt-sql-parser/dist/parser/common/textAndWord'; import * as monaco from 'monaco-editor'; @@ -17,6 +18,16 @@ import { } from './fillers/monaco-editor-core'; import type { CompletionSnippet, LanguageServiceDefaults } from './monaco.contribution'; +export interface ColumnInfo { + /** 字段名 */ + column: string; + /** 字段类型 */ + type: string | undefined; + /** 注释 */ + comment?: string; + /** 别名 */ + alias?: string; +} export interface WorkerAccessor { (...uris: Uri[]): Promise; } @@ -352,3 +363,131 @@ export class ReferenceAdapter implements languages.Refe }); } } +/** + * The adapter is for the hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + **/ +export class HoverAdapter implements languages.HoverProvider { + constructor( + private readonly _worker: WorkerAccessor, + private readonly _defaults: LanguageServiceDefaults + ) {} + provideHover( + model: editor.IReadOnlyModel, + position: Position, + _token: CancellationToken + ): languages.ProviderResult { + const resource = model.uri; + const lineContent = model.getLineContent(position.lineNumber); + if (lineContent.trim().startsWith('--')) return null; + return this._worker(resource) + .then((worker) => { + let code = model?.getValue() || ''; + if (typeof this._defaults.preprocessCode === 'function') { + code = this._defaults.preprocessCode(code); + } + return worker.getAllEntities(code, position); + }) + .then((entities) => { + if (!entities || !entities.length) return null; + let isAlias = false; + const curEntity = entities.find((entity: EntityContext) => { + const p = entity.position; + const alias = entity._alias; + isAlias = !!( + alias && + alias.startColumn <= position.column && + alias.endColumn >= position.column && + alias.line === position.lineNumber + ); + return ( + (p.startColumn <= position.column && + p.endColumn >= position.column && + p.line === position.lineNumber) || + isAlias + ); + }); + if (!curEntity) return null; + const tableCreate = findTableCreateEntity(curEntity, entities); + const columns = tableCreate ? toColumnsInfo(tableCreate) || [] : []; + const columnsDesc = columns.reduce((res, cur) => { + const { column, type, comment, alias } = cur; + return ( + res + + `\`${column}\` ${type ? `   **${type}**` : ''} ${ + comment ? `   *${comment}*` : '' + } ${alias ? `   *${alias}*` : ''} \n` + ); + }, ''); + const tableText = isAlias + ? (curEntity._alias?.text ?? curEntity.text) + : curEntity.text; + let range: monaco.Range; + if (isAlias && curEntity._alias) { + range = new monaco.Range( + curEntity._alias.line, + curEntity._alias.startColumn, + curEntity._alias.line, + curEntity._alias.endColumn + ); + } else { + const p = curEntity.position; + range = new monaco.Range(p.line, p.startColumn, p.line, p.endColumn); + } + const contents: monaco.IMarkdownString[] = [ + { value: `**${tableText}**` }, + { value: columnsDesc } + ]; + return { contents, range }; + }); + } +} + +/** + * According to the table name or table entity field, get the corresponding create table information + */ +export function findTableCreateEntity( + tableEntity: EntityContext | string, + allEntities: EntityContext[] +): CommonEntityContext | null { + if ( + typeof tableEntity !== 'string' && + tableEntity.entityContextType !== EntityContextType.TABLE + ) { + return null; + } + + const tableName: string = typeof tableEntity === 'string' ? tableEntity : tableEntity.text; + function removeQuotes(str: string): string { + return str.replace(/^['"`“”‘’«»]|['"`“”‘’«»]$/g, ''); + } + return ( + allEntities.find( + (en): en is CommonEntityContext => + en.entityContextType === EntityContextType.TABLE_CREATE && + removeQuotes(en.text) === removeQuotes(tableName) + ) ?? null + ); +} + +/** + * Transform table create entity to columns info + */ +export function toColumnsInfo(tableEntity: CommonEntityContext): ColumnInfo[] | null { + if ( + !tableEntity || + tableEntity.entityContextType !== EntityContextType.TABLE_CREATE || + !tableEntity.columns?.length + ) + return null; + const columnsInfo: ColumnInfo[] = []; + tableEntity.columns.forEach((col: ColumnEntityContext) => { + columnsInfo.push({ + column: col.text, + type: col._colType?.text, + comment: col._comment?.text, + alias: col._alias?.text + }); + }); + return columnsInfo; +} diff --git a/src/languages/flink/flink.contribution.ts b/src/languages/flink/flink.contribution.ts index b43ed3fc..1faa19e6 100644 --- a/src/languages/flink/flink.contribution.ts +++ b/src/languages/flink/flink.contribution.ts @@ -17,6 +17,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.FLINK, { completionItems: true, diagnostics: true, - references: true, - definitions: true + references: false, + definitions: false, + hover: false }); diff --git a/src/languages/hive/hive.contribution.ts b/src/languages/hive/hive.contribution.ts index 462a8378..cc78f42b 100644 --- a/src/languages/hive/hive.contribution.ts +++ b/src/languages/hive/hive.contribution.ts @@ -17,6 +17,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.HIVE, { completionItems: true, diagnostics: true, - references: true, - definitions: true + references: false, + definitions: false, + hover: false }); diff --git a/src/languages/impala/impala.contribution.ts b/src/languages/impala/impala.contribution.ts index 8ba80c7b..256cdc6d 100644 --- a/src/languages/impala/impala.contribution.ts +++ b/src/languages/impala/impala.contribution.ts @@ -17,6 +17,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.IMPALA, { completionItems: true, diagnostics: true, - references: true, - definitions: true + references: false, + definitions: false, + hover: false }); diff --git a/src/languages/mysql/mysql.contribution.ts b/src/languages/mysql/mysql.contribution.ts index 34d772bb..24335826 100644 --- a/src/languages/mysql/mysql.contribution.ts +++ b/src/languages/mysql/mysql.contribution.ts @@ -17,6 +17,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.MYSQL, { completionItems: true, diagnostics: true, - references: true, - definitions: true + references: false, + definitions: false, + hover: false }); diff --git a/src/languages/pgsql/pgsql.contribution.ts b/src/languages/pgsql/pgsql.contribution.ts index 46978f43..8e59a767 100644 --- a/src/languages/pgsql/pgsql.contribution.ts +++ b/src/languages/pgsql/pgsql.contribution.ts @@ -17,6 +17,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.PG, { completionItems: true, diagnostics: true, - references: true, - definitions: true + references: false, + definitions: false, + hover: false }); diff --git a/src/languages/spark/spark.contribution.ts b/src/languages/spark/spark.contribution.ts index 8efd836e..1943d962 100644 --- a/src/languages/spark/spark.contribution.ts +++ b/src/languages/spark/spark.contribution.ts @@ -17,6 +17,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.SPARK, { completionItems: true, diagnostics: true, - references: true, - definitions: true + references: false, + definitions: false, + hover: false }); diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index bcb2c6ea..f1dca65c 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -90,6 +90,10 @@ export interface ModeConfiguration { * Defines whether the built-in references provider is enabled. */ readonly references?: boolean; + /** + * Defines whether the built-in hover provider is enabled. + */ + readonly hover?: boolean; } /** @@ -205,6 +209,7 @@ export const modeConfigurationDefault: Required = { triggerCharacters: ['.', ' '] }, diagnostics: true, - definitions: true, - references: true + definitions: false, + references: false, + hover: false }; diff --git a/src/setupLanguageFeatures.ts b/src/setupLanguageFeatures.ts index cd3fc5dd..6e965958 100644 --- a/src/setupLanguageFeatures.ts +++ b/src/setupLanguageFeatures.ts @@ -29,6 +29,10 @@ export interface FeatureConfiguration { * Defines whether the built-in references provider is enabled. */ references?: boolean; + /** + * Defines whether the built-in hover provider is enabled. + */ + hover?: boolean; /** * Define a function to preprocess code. * By default, do not something. @@ -148,6 +152,10 @@ function processConfiguration( typeof configuration.definitions === 'boolean' ? configuration.definitions : (defaults?.modeConfiguration.definitions ?? modeConfigurationDefault.definitions); + const hover = + typeof configuration.hover === 'boolean' + ? configuration.hover + : (defaults?.modeConfiguration.hover ?? modeConfigurationDefault.hover); const snippets = typeof configuration.completionItems !== 'boolean' && @@ -165,6 +173,7 @@ function processConfiguration( snippets }, references, - definitions + definitions, + hover }; } diff --git a/src/setupLanguageMode.ts b/src/setupLanguageMode.ts index 7c575393..e61e32b1 100644 --- a/src/setupLanguageMode.ts +++ b/src/setupLanguageMode.ts @@ -51,6 +51,14 @@ export function setupLanguageMode( ) ); } + if (modeConfiguration.hover) { + providers.push( + languages.registerHoverProvider( + languageId, + new languageFeatures.HoverAdapter(worker, defaults) + ) + ); + } } registerProviders(); diff --git a/website/src/languages/index.ts b/website/src/languages/index.ts index 5f64c296..35e0e9ac 100644 --- a/website/src/languages/index.ts +++ b/website/src/languages/index.ts @@ -1,7 +1,9 @@ import 'monaco-sql-languages/esm/all.contributions.js'; import './languageWorker'; import './theme'; -import { setupLanguageFeatures, LanguageIdEnum } from 'monaco-sql-languages/esm/main.js'; + +import { LanguageIdEnum, setupLanguageFeatures } from 'monaco-sql-languages/esm/main.js'; + import { completionService } from './helpers/completionService'; /** @@ -65,6 +67,9 @@ setupLanguageFeatures(LanguageIdEnum.FLINK, { enable: true, completionService }, + references: true, + definitions: true, + hover: true, preprocessCode }); @@ -73,6 +78,9 @@ setupLanguageFeatures(LanguageIdEnum.SPARK, { enable: true, completionService }, + references: true, + definitions: true, + hover: true, preprocessCode }); @@ -81,6 +89,9 @@ setupLanguageFeatures(LanguageIdEnum.HIVE, { enable: true, completionService }, + references: true, + definitions: true, + hover: true, preprocessCode: (code: string) => preprocessCodeHive(code, '`') }); @@ -89,6 +100,9 @@ setupLanguageFeatures(LanguageIdEnum.MYSQL, { enable: true, completionService }, + references: true, + definitions: true, + hover: true, preprocessCode }); @@ -97,6 +111,9 @@ setupLanguageFeatures(LanguageIdEnum.TRINO, { enable: true, completionService }, + references: true, + definitions: true, + hover: true, preprocessCode }); @@ -105,6 +122,9 @@ setupLanguageFeatures(LanguageIdEnum.PG, { enable: true, completionService }, + references: true, + definitions: true, + hover: true, preprocessCode }); @@ -113,5 +133,8 @@ setupLanguageFeatures(LanguageIdEnum.IMPALA, { enable: true, completionService }, + references: true, + definitions: true, + hover: true, preprocessCode });