diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index 2a9a869e..d649d8ce 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -1,16 +1,20 @@ +import type { ParseError } 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'; + +import { BaseSQLWorker } from './baseSQLWorker'; +import { debounce } from './common/utils'; import { + CancellationToken, editor, - Uri, IDisposable, - MarkerSeverity, - Range, languages, + MarkerSeverity, Position, - CancellationToken + Range, + Uri } from './fillers/monaco-editor-core'; -import { debounce } from './common/utils'; -import { BaseSQLWorker } from './baseSQLWorker'; -import type { ParseError } from 'dt-sql-parser'; import type { LanguageServiceDefaults } from './monaco.contribution'; export interface WorkerAccessor { @@ -197,3 +201,135 @@ export class CompletionAdapter }); } } + +export class DefinitionAdapter implements languages.DefinitionProvider { + constructor( + private readonly _worker: WorkerAccessor, + private readonly _defaults: LanguageServiceDefaults + ) {} + provideDefinition( + model: editor.IReadOnlyModel, + position: Position, + _token: CancellationToken + ): languages.ProviderResult { + const resource = model.uri; + const lineContent = model.getLineContent(position.lineNumber); + if (lineContent.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); + }) + .then((entities) => { + const word = model.getWordAtPosition(position); + let pos: WordPosition = { + line: -1, + startIndex: -1, + endIndex: -1, + startColumn: -1, + endColumn: -1 + }; + const curEntity = entities?.find((entity: EntityContext) => { + const entityPosition = entity.position; + if ( + entityPosition.startColumn === word?.startColumn && + entityPosition.endColumn === word?.endColumn && + entityPosition.line === position.lineNumber + ) { + return entity; + } + return null; + }); + if (curEntity) { + for (let k in entities) { + const entity = entities[Number(k)]; + if ( + entity.entityContextType.includes('Create') && + word?.word && + entity.text === word?.word && + entity.entityContextType.includes(curEntity.entityContextType) + ) { + pos = entity.position; + break; + } + } + } + if (pos && pos.line !== -1) { + return { + uri: model.uri, + range: new monaco.Range( + pos?.line, + pos?.startColumn, + pos?.line, + pos?.endColumn + ) + }; + } + }); + } +} + +export class ReferenceAdapter implements languages.ReferenceProvider { + constructor( + private readonly _worker: WorkerAccessor, + private readonly _defaults: LanguageServiceDefaults + ) {} + provideReferences( + model: editor.IReadOnlyModel, + position: Position, + _context: languages.ReferenceContext, + _token: CancellationToken + ): languages.ProviderResult { + const resource = model.uri; + const lineContent = model.getLineContent(position.lineNumber); + if (!lineContent.startsWith('CREATE')) return; + return this._worker(resource) + .then((worker) => { + let code = model?.getValue() || ''; + if (typeof this._defaults.preprocessCode === 'function') { + code = this._defaults.preprocessCode(code); + } + return worker.getAllEntities(model?.getValue()); + }) + .then((entities) => { + const word = model.getWordAtPosition(position); + const arr: languages.Location[] = []; + const curEntity = entities?.find((entity: EntityContext) => { + const entityPosition = entity.position; + if ( + entityPosition.startColumn === word?.startColumn && + entityPosition.endColumn === word?.endColumn && + entityPosition.line === position.lineNumber + ) { + return entity; + } + return null; + }); + if (curEntity) { + entities?.forEach((entity) => { + if ( + word?.word && + entity.text === word?.word && + curEntity.entityContextType.includes(entity.entityContextType) + ) { + let pos: WordPosition | null = null; + pos = entity.position; + arr.push({ + uri: model.uri, + range: new monaco.Range( + pos?.line, + pos?.startColumn, + pos?.line, + pos?.endColumn + ) + }); + } + }); + } + return arr; + }); + } +} diff --git a/src/languages/flink/flink.contribution.ts b/src/languages/flink/flink.contribution.ts index 5efc8c86..b43ed3fc 100644 --- a/src/languages/flink/flink.contribution.ts +++ b/src/languages/flink/flink.contribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { registerLanguage } from '../../_.contribution'; -import { setupLanguageFeatures } from '../../setupLanguageFeatures'; import { LanguageIdEnum } from '../../common/constants'; +import { setupLanguageFeatures } from '../../setupLanguageFeatures'; registerLanguage({ id: LanguageIdEnum.FLINK, @@ -16,5 +16,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.FLINK, { completionItems: true, - diagnostics: true + diagnostics: true, + references: true, + definitions: true }); diff --git a/src/languages/hive/hive.contribution.ts b/src/languages/hive/hive.contribution.ts index 1ec7650b..462a8378 100644 --- a/src/languages/hive/hive.contribution.ts +++ b/src/languages/hive/hive.contribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { registerLanguage } from '../../_.contribution'; -import { setupLanguageFeatures } from '../../setupLanguageFeatures'; import { LanguageIdEnum } from '../../common/constants'; +import { setupLanguageFeatures } from '../../setupLanguageFeatures'; registerLanguage({ id: LanguageIdEnum.HIVE, @@ -16,5 +16,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.HIVE, { completionItems: true, - diagnostics: true + diagnostics: true, + references: true, + definitions: true }); diff --git a/src/languages/impala/impala.contribution.ts b/src/languages/impala/impala.contribution.ts index 35a6b6f0..8ba80c7b 100644 --- a/src/languages/impala/impala.contribution.ts +++ b/src/languages/impala/impala.contribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { registerLanguage } from '../../_.contribution'; -import { setupLanguageFeatures } from '../../setupLanguageFeatures'; import { LanguageIdEnum } from '../../common/constants'; +import { setupLanguageFeatures } from '../../setupLanguageFeatures'; registerLanguage({ id: LanguageIdEnum.IMPALA, @@ -16,5 +16,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.IMPALA, { completionItems: true, - diagnostics: true + diagnostics: true, + references: true, + definitions: true }); diff --git a/src/languages/mysql/mysql.contribution.ts b/src/languages/mysql/mysql.contribution.ts index bd4c8be1..34d772bb 100644 --- a/src/languages/mysql/mysql.contribution.ts +++ b/src/languages/mysql/mysql.contribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { registerLanguage } from '../../_.contribution'; -import { setupLanguageFeatures } from '../../setupLanguageFeatures'; import { LanguageIdEnum } from '../../common/constants'; +import { setupLanguageFeatures } from '../../setupLanguageFeatures'; registerLanguage({ id: LanguageIdEnum.MYSQL, @@ -16,5 +16,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.MYSQL, { completionItems: true, - diagnostics: true + diagnostics: true, + references: true, + definitions: true }); diff --git a/src/languages/pgsql/pgsql.contribution.ts b/src/languages/pgsql/pgsql.contribution.ts index 6d6adc56..46978f43 100644 --- a/src/languages/pgsql/pgsql.contribution.ts +++ b/src/languages/pgsql/pgsql.contribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { registerLanguage } from '../../_.contribution'; -import { setupLanguageFeatures } from '../../setupLanguageFeatures'; import { LanguageIdEnum } from '../../common/constants'; +import { setupLanguageFeatures } from '../../setupLanguageFeatures'; registerLanguage({ id: LanguageIdEnum.PG, @@ -16,5 +16,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.PG, { completionItems: true, - diagnostics: true + diagnostics: true, + references: true, + definitions: true }); diff --git a/src/languages/spark/spark.contribution.ts b/src/languages/spark/spark.contribution.ts index 18c67757..8efd836e 100644 --- a/src/languages/spark/spark.contribution.ts +++ b/src/languages/spark/spark.contribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { registerLanguage } from '../../_.contribution'; -import { setupLanguageFeatures } from '../../setupLanguageFeatures'; import { LanguageIdEnum } from '../../common/constants'; +import { setupLanguageFeatures } from '../../setupLanguageFeatures'; registerLanguage({ id: LanguageIdEnum.SPARK, @@ -16,5 +16,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.SPARK, { completionItems: true, - diagnostics: true + diagnostics: true, + references: true, + definitions: true }); diff --git a/src/languages/trino/trino.contribution.ts b/src/languages/trino/trino.contribution.ts index c8971197..c1822d08 100644 --- a/src/languages/trino/trino.contribution.ts +++ b/src/languages/trino/trino.contribution.ts @@ -1,6 +1,6 @@ import { registerLanguage } from '../../_.contribution'; -import { setupLanguageFeatures } from '../../setupLanguageFeatures'; import { LanguageIdEnum } from '../../common/constants'; +import { setupLanguageFeatures } from '../../setupLanguageFeatures'; registerLanguage({ id: LanguageIdEnum.TRINO, @@ -11,5 +11,7 @@ registerLanguage({ setupLanguageFeatures(LanguageIdEnum.TRINO, { completionItems: true, - diagnostics: true + diagnostics: true, + references: true, + definitions: true }); diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index e655fc54..c1a2cdc2 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -1,6 +1,7 @@ -import { languages, Emitter, IEvent, editor, Position, IRange } from './fillers/monaco-editor-core'; import { EntityContext, Suggestions } from 'dt-sql-parser'; +import { editor, Emitter, IEvent, IRange, languages, Position } from './fillers/monaco-editor-core'; + /** * A completion item. * ICompletionItem is pretty much the same as {@link languages.CompletionItem}, @@ -65,7 +66,7 @@ export interface ModeConfiguration { /** * Defines whether the built-in definitions provider is enabled. */ - // readonly definitions?: boolean; + readonly definitions?: boolean; /** * Defines whether the built-in rename provider is enabled. @@ -75,7 +76,7 @@ export interface ModeConfiguration { /** * Defines whether the built-in references provider is enabled. */ - // readonly references?: boolean; + readonly references?: boolean; } /** @@ -171,5 +172,7 @@ export const modeConfigurationDefault: Required = { completionService: defaultCompletionService, triggerCharacters: ['.', ' '] }, - diagnostics: true + diagnostics: true, + definitions: true, + references: true }; diff --git a/src/setupLanguageFeatures.ts b/src/setupLanguageFeatures.ts index bdaeb0f6..44cb3f6e 100644 --- a/src/setupLanguageFeatures.ts +++ b/src/setupLanguageFeatures.ts @@ -1,13 +1,13 @@ +import { LanguageIdEnum } from './common/constants'; +import { IDisposable, languages } from './fillers/monaco-editor-core'; import { - PreprocessCode, + CompletionOptions, LanguageServiceDefaults, LanguageServiceDefaultsImpl, - modeConfigurationDefault, ModeConfiguration, - CompletionOptions + modeConfigurationDefault, + PreprocessCode } from './monaco.contribution'; -import { languages, IDisposable } from './fillers/monaco-editor-core'; -import { LanguageIdEnum } from './common/constants'; export interface FeatureConfiguration { /** @@ -20,6 +20,14 @@ export interface FeatureConfiguration { * Defaults to true. */ diagnostics?: boolean; + /** + * Defines whether the built-in definitions provider is enabled. + */ + definitions?: boolean; + /** + * Defines whether the built-in references provider is enabled. + */ + references?: boolean; /** * Define a function to preprocess code. * By default, do not something. @@ -110,6 +118,14 @@ function processConfiguration( ? configuration.completionItems!.triggerCharacters : (defaults?.modeConfiguration.completionItems.triggerCharacters ?? modeConfigurationDefault.completionItems.triggerCharacters); + const references = + typeof configuration.references === 'boolean' + ? configuration.references + : (defaults?.modeConfiguration.references ?? modeConfigurationDefault.references); + const definitions = + typeof configuration.definitions === 'boolean' + ? configuration.definitions + : (defaults?.modeConfiguration.definitions ?? modeConfigurationDefault.definitions); return { diagnostics, @@ -117,6 +133,8 @@ function processConfiguration( enable: completionEnable, completionService, triggerCharacters - } + }, + references, + definitions }; } diff --git a/src/setupLanguageMode.ts b/src/setupLanguageMode.ts index 03a13d8b..7c575393 100644 --- a/src/setupLanguageMode.ts +++ b/src/setupLanguageMode.ts @@ -1,8 +1,8 @@ -import { WorkerManager } from './workerManager'; -import { LanguageServiceDefaults } from './monaco.contribution'; -import * as languageFeatures from './languageFeatures'; -import { Uri, IDisposable, languages } from './fillers/monaco-editor-core'; import type { BaseSQLWorker } from './baseSQLWorker'; +import { IDisposable, languages, Uri } from './fillers/monaco-editor-core'; +import * as languageFeatures from './languageFeatures'; +import { LanguageServiceDefaults } from './monaco.contribution'; +import { WorkerManager } from './workerManager'; export function setupLanguageMode( defaults: LanguageServiceDefaults @@ -19,7 +19,6 @@ export function setupLanguageMode( function registerProviders(): void { const { languageId, modeConfiguration } = defaults; - disposeAll(providers); if (modeConfiguration.diagnostics) { @@ -34,6 +33,24 @@ export function setupLanguageMode( ) ); } + + if (modeConfiguration.references) { + providers.push( + languages.registerReferenceProvider( + languageId, + new languageFeatures.ReferenceAdapter(worker, defaults) + ) + ); + } + + if (modeConfiguration.definitions) { + providers.push( + languages.registerDefinitionProvider( + languageId, + new languageFeatures.DefinitionAdapter(worker, defaults) + ) + ); + } } registerProviders();