Skip to content

Commit be7fc5d

Browse files
committed
fix: simplify interface
1 parent 8a7d934 commit be7fc5d

File tree

5 files changed

+205
-189
lines changed

5 files changed

+205
-189
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
2+
3+
import {getSuggestionsGetter} from './yqlAutocomplete';
4+
import type {YQLAutocompleteConfig} from './yqlAutocomplete';
5+
6+
const completionProvider = new Map<string, monaco.IDisposable>();
7+
8+
function disableCodeSuggestions(langId: string): void {
9+
const provider = completionProvider.get(langId);
10+
if (provider) {
11+
provider.dispose();
12+
}
13+
}
14+
15+
export function registerCompletionItemProvider(
16+
langId: string,
17+
triggerCharacters: string[],
18+
config: YQLAutocompleteConfig,
19+
) {
20+
disableCodeSuggestions(langId);
21+
const provider = monaco.languages.registerCompletionItemProvider(langId, {
22+
triggerCharacters: triggerCharacters,
23+
provideCompletionItems: getSuggestionsGetter(config),
24+
});
25+
completionProvider.set(langId, provider);
26+
}

src/utils/monaco/yql/autocomplete/types.ts

-16
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,3 @@ export type FetchedEntity = {
1111
};
1212

1313
export type FetchedColumn = {name: string; detail?: string; parent: string};
14-
15-
type ConstantsGetter = () => string[] | Promise<string[]>;
16-
17-
export type AutocompleteConstant =
18-
| 'types'
19-
| 'windowFunctions'
20-
| 'udfs'
21-
| 'pragmas'
22-
| 'simpleFunctions'
23-
| 'tableFunctions'
24-
| 'aggregateFunctions';
25-
26-
export type AutocompleteConstantsInit = Partial<
27-
Record<AutocompleteConstant, string[] | ConstantsGetter>
28-
>;
29-
export type AutocompleteConstants = Partial<Record<AutocompleteConstant, string[]>>;

src/utils/monaco/yql/autocomplete/yqlAutocomplete.ts

+105-55
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,7 @@ import {
1717
generateVariableSuggestion,
1818
generateWindowFunctionsSuggestion,
1919
} from './generateSuggestions';
20-
import type {
21-
AutocompleteConstant,
22-
AutocompleteConstants,
23-
AutocompleteConstantsInit,
24-
CursorPosition,
25-
FetchedColumn,
26-
FetchedEntity,
27-
} from './types';
20+
import type {CursorPosition, FetchedColumn, FetchedEntity} from './types';
2821
import {getEntitiesToFetchColumns} from './utils';
2922

3023
function getCursorPosition(input: string, offset: number): CursorPosition {
@@ -61,33 +54,67 @@ function getRangeToInsertSuggestion(input: string, offset: number) {
6154
};
6255
}
6356

64-
/**
65-
* YQLAutocomplete class provides autocompletion suggestions for YQL queries.
66-
* It uses a set of constants to generate suggestions for various YQL elements.
67-
* The class is abstract and needs to be extended to provide implementation for
68-
* fetching entities and columns.
69-
*/
70-
export abstract class YQLAutocomplete {
71-
private readonly constants: AutocompleteConstants = {};
72-
73-
async init(constants: AutocompleteConstantsInit) {
74-
const constantsArray = Object.keys(constants) as AutocompleteConstant[];
75-
const promises: (string[] | Promise<string[]>)[] = [];
76-
constantsArray.forEach((c) => {
77-
const value = constants[c];
78-
if (typeof value === 'function') {
79-
promises.push(value());
80-
} else if (value) {
81-
promises.push(value);
82-
}
83-
});
84-
const result = await Promise.allSettled(promises);
85-
result.forEach((item, index) => {
86-
if (item.status === 'fulfilled') {
87-
const constantName = constantsArray[index];
88-
this.constants[constantName] = item.value;
89-
}
90-
});
57+
export type YQLAutocompleteConfig = {
58+
getQueryParser?: YQLAutocomplete['getQueryParser'];
59+
getUdfs?: YQLAutocomplete['getUdfs'];
60+
getSimpleTypes?: YQLAutocomplete['getSimpleTypes'];
61+
getPragmas?: YQLAutocomplete['getPragmas'];
62+
getWindowFunctions?: YQLAutocomplete['getWindowFunctions'];
63+
getTableFunctions?: YQLAutocomplete['getTableFunctions'];
64+
getAggregateFunctions?: YQLAutocomplete['getAggregateFunctions'];
65+
getSimpleFunctions?: YQLAutocomplete['getSimpleFunctions'];
66+
getEntitySettings?: YQLAutocomplete['getEntitySettings'];
67+
fetchEntities?: YQLAutocomplete['fetchEntities'];
68+
fetchEntityColumns?: YQLAutocomplete['fetchEntityColumns'];
69+
};
70+
71+
export class YQLAutocomplete {
72+
constructor({
73+
getQueryParser,
74+
getUdfs,
75+
getSimpleTypes,
76+
getPragmas,
77+
getWindowFunctions,
78+
getTableFunctions,
79+
getAggregateFunctions,
80+
getSimpleFunctions,
81+
getEntitySettings,
82+
fetchEntities,
83+
fetchEntityColumns,
84+
}: YQLAutocompleteConfig) {
85+
if (getQueryParser) {
86+
this.getQueryParser = getQueryParser;
87+
}
88+
if (getUdfs) {
89+
this.getUdfs = getUdfs;
90+
}
91+
if (getSimpleTypes) {
92+
this.getSimpleTypes = getSimpleTypes;
93+
}
94+
if (getPragmas) {
95+
this.getPragmas = getPragmas;
96+
}
97+
if (getWindowFunctions) {
98+
this.getWindowFunctions = getWindowFunctions;
99+
}
100+
if (getTableFunctions) {
101+
this.getTableFunctions = getTableFunctions;
102+
}
103+
if (getAggregateFunctions) {
104+
this.getAggregateFunctions = getAggregateFunctions;
105+
}
106+
if (getSimpleFunctions) {
107+
this.getSimpleFunctions = getSimpleFunctions;
108+
}
109+
if (getEntitySettings) {
110+
this.getEntitySettings = getEntitySettings;
111+
}
112+
if (fetchEntities) {
113+
this.fetchEntities = fetchEntities;
114+
}
115+
if (fetchEntityColumns) {
116+
this.fetchEntityColumns = fetchEntityColumns;
117+
}
91118
}
92119

93120
async getSuggestions(input: string, offset: number) {
@@ -235,52 +262,75 @@ export abstract class YQLAutocomplete {
235262
return parseQuery;
236263
}
237264

238-
async getSimpleTypes(_prefix?: string) {
239-
return this.constants.types;
265+
async getSimpleTypes(_prefix?: string): Promise<string[]> {
266+
return [];
240267
}
241268

242-
async getUdfs(_prefix?: string) {
243-
return this.constants.udfs;
269+
async getUdfs(_prefix?: string): Promise<string[]> {
270+
return [];
244271
}
245272

246-
async getPragmas(_prefix?: string) {
247-
return this.constants.pragmas;
273+
async getPragmas(_prefix?: string): Promise<string[]> {
274+
return [];
248275
}
249-
async getWindowFunctions(_prefix?: string) {
250-
return this.constants.windowFunctions;
276+
async getWindowFunctions(_prefix?: string): Promise<string[]> {
277+
return [];
251278
}
252279

253-
async getTableFunctions(_prefix?: string) {
254-
return this.constants.tableFunctions;
280+
async getTableFunctions(_prefix?: string): Promise<string[]> {
281+
return [];
255282
}
256-
async getAggregateFunctions(_prefix?: string) {
257-
return this.constants.aggregateFunctions;
283+
async getAggregateFunctions(_prefix?: string): Promise<string[]> {
284+
return [];
258285
}
259286

260-
async getSimpleFunctions(_prefix?: string) {
261-
return this.constants.simpleFunctions;
287+
async getSimpleFunctions(_prefix?: string): Promise<string[]> {
288+
return [];
262289
}
263290

264291
/**
265292
* Fetches entity settings based on provided entity type.
266293
* @param entityType
267294
* @returns A promise that resolves to an array of entity settings.
268295
*/
269-
abstract getEntitySettings(entityType: YQLEntity): Promise<string[]>;
296+
async getEntitySettings(_entityType: YQLEntity): Promise<string[]> {
297+
return [];
298+
}
270299
/**
271300
* Fetches entities based on the provided prefix and needed entity types.
272301
* @param prefix - The prefix to filter entities.
273302
* @param neededEntityTypes - An array of entity types to fetch.
274303
* @returns A promise that resolves to an array of fetched entities.
275304
*/
276-
abstract fetchEntities(
277-
prefix: string,
278-
neededEntityTypes: YQLEntity[],
279-
): Promise<FetchedEntity[]>;
305+
async fetchEntities(
306+
_prefix: string,
307+
_neededEntityTypes: YQLEntity[],
308+
): Promise<FetchedEntity[]> {
309+
return [];
310+
}
280311
/**
281312
* Fetches columns for the specified entities.
282313
* @param entityNames - An array of entity names to fetch columns for.
283314
* @returns A promise that resolves to an array of fetched columns.
284315
*/
285-
abstract fetchEntityColumns(entityNames: string[]): Promise<FetchedColumn[]>;
316+
async fetchEntityColumns(_entityNames: string[]): Promise<FetchedColumn[]> {
317+
return [];
318+
}
319+
}
320+
321+
export function getSuggestionsGetter(config: YQLAutocompleteConfig = {}) {
322+
const autocompleteInstance = new YQLAutocomplete(config);
323+
return async (
324+
model: Monaco.editor.ITextModel,
325+
monacoCursorPosition: Monaco.Position,
326+
_context: Monaco.languages.CompletionContext,
327+
_token: Monaco.CancellationToken,
328+
) => {
329+
const suggestions = await autocompleteInstance.getSuggestions(
330+
model.getValue(),
331+
model.getOffsetAt(monacoCursorPosition),
332+
);
333+
334+
return {suggestions};
335+
};
286336
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,81 @@
1-
import * as monaco from 'monaco-editor';
1+
import type {YQLEntity} from '@gravity-ui/websql-autocomplete/yql';
22
import {LANGUAGE_ID} from 'monaco-yql-languages/build/yql/yql.contribution';
33

4-
import {createProvideSuggestionsFunction} from './yqlSuggestions';
4+
import {isAutocompleteColumn} from '../../../types/api/autocomplete';
5+
import type {TAutocompleteEntity} from '../../../types/api/autocomplete';
56

6-
let completionProvider: monaco.IDisposable | undefined;
7-
8-
function disableCodeSuggestions(): void {
9-
if (completionProvider) {
10-
completionProvider.dispose();
11-
}
12-
}
7+
import {registerCompletionItemProvider} from './autocomplete/registerProvider';
8+
import type {FetchedColumn} from './autocomplete/types';
9+
import {removeBackticks} from './autocomplete/utils';
10+
import {
11+
checkIsDirectory,
12+
filterAutocompleteEntities,
13+
getAggregateFunctions,
14+
getColumnDetails,
15+
getEntitySettings,
16+
getPragmas,
17+
getSimpleFunctions,
18+
getSimpleTypes,
19+
getTableFunctions,
20+
getUdfs,
21+
getWindowFunctions,
22+
normalizeEntityNames,
23+
} from './generateSuggestions';
1324

1425
export function registerYQLCompletionItemProvider(database: string) {
15-
disableCodeSuggestions();
16-
completionProvider = monaco.languages.registerCompletionItemProvider(LANGUAGE_ID, {
17-
triggerCharacters: [' ', '.', '`', '(', '/'],
18-
provideCompletionItems: createProvideSuggestionsFunction(database),
26+
const fetchEntities = async (prefix: string, neededTypes: YQLEntity[]) => {
27+
const data = await window.api.viewer.autocomplete({
28+
database,
29+
prefix: removeBackticks(prefix),
30+
limit: 1000,
31+
});
32+
if (!data.Success || !data.Result.Entities) {
33+
return [];
34+
}
35+
const filteredEntities = filterAutocompleteEntities(data.Result.Entities, neededTypes);
36+
return (
37+
filteredEntities?.map(({Name, Type}) => ({
38+
value: Name,
39+
detail: Type,
40+
isDir: checkIsDirectory(Type),
41+
})) ?? []
42+
);
43+
};
44+
45+
const fetchEntityColumns = async (entityNames: string[]) => {
46+
let autocompleteEntities: TAutocompleteEntity[] = [];
47+
const normalizedNames = normalizeEntityNames(entityNames, database);
48+
const autocompleteResponse = await window.api.viewer.autocomplete({
49+
database,
50+
table: normalizedNames,
51+
limit: 1000,
52+
});
53+
if (autocompleteResponse.Success) {
54+
autocompleteEntities = autocompleteResponse.Result.Entities ?? [];
55+
}
56+
const result: FetchedColumn[] = [];
57+
autocompleteEntities.forEach((entity) => {
58+
if (isAutocompleteColumn(entity)) {
59+
result.push({
60+
name: entity.Name,
61+
detail: getColumnDetails(entity),
62+
parent: entity.Parent,
63+
});
64+
}
65+
});
66+
return result;
67+
};
68+
69+
registerCompletionItemProvider(LANGUAGE_ID, [' ', '.', '`', '(', '/'], {
70+
fetchEntities,
71+
fetchEntityColumns,
72+
getEntitySettings,
73+
getPragmas,
74+
getSimpleFunctions,
75+
getAggregateFunctions,
76+
getTableFunctions,
77+
getWindowFunctions,
78+
getUdfs,
79+
getSimpleTypes,
1980
});
2081
}

0 commit comments

Comments
 (0)