Make code editor work

This commit is contained in:
Zoltán Bedi
2022-07-11 17:21:34 +02:00
parent bd9ab7e3cc
commit 36d7d86e90
3 changed files with 27 additions and 99 deletions
@@ -8,7 +8,7 @@ import { getSchema, showDatabases, showTables } from './MSSqlMetaQuery';
import { MSSqlQueryModel } from './MSSqlQueryModel';
import { MSSqlResponseParser } from './response_parser';
import { fetchColumns, fetchTables, getSqlCompletionProvider } from './sqlCompletionProvider';
import { getIcon, getRAQBType, toRawSql } from './sqlUtil';
import { getIcon, getRAQBType, SCHEMA_NAME, toRawSql } from './sqlUtil';
export class MssqlDatasource extends SqlDatasource {
completionProvider: LanguageCompletionProvider | undefined = undefined;
@@ -72,7 +72,7 @@ export class MssqlDatasource extends SqlDatasource {
lookup: async (path?: string) => {
if (!path) {
const datasets = await this.fetchDatasets();
return datasets.map((d) => ({ name: d, completion: `${d}.` }));
return datasets.map((d) => ({ name: d, completion: `${d}.${SCHEMA_NAME}.` }));
} else {
const parts = path.split('.').filter((s: string) => s);
if (parts.length > 2) {
@@ -80,7 +80,7 @@ export class MssqlDatasource extends SqlDatasource {
}
if (parts.length === 1) {
const tables = await this.fetchTables(parts[0]);
return tables.map((t) => ({ name: t, completion: `${t}\`` }));
return tables.map((t) => ({ name: t, completion: `${t}` }));
} else {
return [];
}
@@ -7,7 +7,6 @@ import {
LanguageCompletionProvider,
LinkedToken,
StatementPlacementProvider,
// StatementPosition,
SuggestionKindProvider,
TableDefinition,
TokenType,
@@ -15,10 +14,11 @@ import {
import { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
import { DB, SQLQuery } from 'app/features/plugins/sql/types';
import { SCHEMA_NAME } from './sqlUtil';
interface CompletionProviderGetterArgs {
getColumns: React.MutableRefObject<(t: SQLQuery) => Promise<ColumnDefinition[]>>;
getTables: React.MutableRefObject<(d?: string) => Promise<TableDefinition[]>>;
// getTableSchema: React.MutableRefObject<(p: string, d: string, t: string) => Promise<TableSchema | null>>;
}
export const getSqlCompletionProvider: (args: CompletionProviderGetterArgs) => LanguageCompletionProvider =
@@ -33,20 +33,19 @@ export const getSqlCompletionProvider: (args: CompletionProviderGetterArgs) => L
let processedToken = token;
let tablePath = processedToken.value;
while (processedToken.next) {
while (processedToken.next && processedToken.next.type !== TokenType.Whitespace) {
tablePath += processedToken.next.value;
processedToken = processedToken.next;
}
return tablePath;
const tableName = tablePath.split('.').pop();
return tableName || tablePath;
},
},
columns: {
resolve: async (t: string) => {
// TODO - seems like a limitation in experimental since we may need database and table to get columns
// use . as delimiter?
// const cols = await getColumns({ table: t } as SQLQuery);
return await getColumns.current({ table: t, refId: 'A' });
},
},
@@ -57,58 +56,36 @@ export const getSqlCompletionProvider: (args: CompletionProviderGetterArgs) => L
});
export enum CustomStatementPlacement {
AfterDataset = 'afterDataset',
AfterDatabase = 'afterDatabase',
}
export enum CustomSuggestionKind {
TablesWithinDataset = 'tablesWithinDataset',
Partition = 'partition',
TablesWithinDatabase = 'tablesWithinDatabase',
}
export const customStatementPlacement: StatementPlacementProvider = () => [
{
id: CustomStatementPlacement.AfterDataset,
id: CustomStatementPlacement.AfterDatabase,
resolve: (currentToken, previousKeyword) => {
return Boolean(
currentToken?.is(TokenType.Delimiter, '.') ||
(currentToken?.is(TokenType.Whitespace) && currentToken?.previous?.is(TokenType.Delimiter, '.')) ||
// (currentToken?.value === '`' && currentToken?.previous?.is(TokenType.Delimiter, '.')) ||
(currentToken?.isNumber() && currentToken.value.endsWith('.')) // number with dot at the end like "projectname-21342."
// (currentToken?.value === '`' && isTypingTableIn(currentToken))
(currentToken?.isNumber() && currentToken.value.endsWith('.'))
);
},
},
// TODO - remove - frin big query
// Overriding default behaviour of AfterFrom resolver
// {
// id: StatementPosition.AfterFrom,
// overrideDefault: true,
// resolve: (currentToken) => {
// const untilFrom = currentToken?.getPreviousUntil(TokenType.Keyword, [], 'from');
// if (!untilFrom) {
// return false;
// }
// let q = '';
// for (let i = untilFrom?.length - 1; i >= 0; i--) {
// q += untilFrom[i].value;
// }
// return q.startsWith('`') && q.endsWith('`');
// },
// },
];
export const customSuggestionKinds: (
getTables: CompletionProviderGetterArgs['getTables'],
getFields: CompletionProviderGetterArgs['getColumns']
// getTableSchema: CompletionProviderGetterArgs['getTableSchema']
) => SuggestionKindProvider = (getTables) => () =>
[
{
id: CustomSuggestionKind.TablesWithinDataset,
applyTo: [CustomStatementPlacement.AfterDataset],
id: CustomSuggestionKind.TablesWithinDatabase,
applyTo: [CustomStatementPlacement.AfterDatabase],
suggestionsResolver: async (ctx) => {
const tablePath = ctx.currentToken ? getTablePath(ctx.currentToken) : '';
const tablePath = ctx.currentToken ? getDatabaseName(ctx.currentToken) : '';
const t = await getTables.current(tablePath);
return t.map((table) => ({
@@ -125,76 +102,25 @@ export const customSuggestionKinds: (
}));
},
},
// {
// id: CustomSuggestionKind.TablesWithinDataset,
// applyTo: [StatementPosition.AfterFrom],
// suggestionsResolver: async (ctx) => {
// console.log('after from');
// const tablePath = ctx.currentToken ? getTablePath(ctx.currentToken) : '';
// const t = await getTables.current(tablePath);
// return t.map((table) => ({
// label: table.name,
// insertText: table.completion ?? table.name,
// command: { id: 'editor.action.triggerSuggest', title: '' },
// kind: CompletionItemKind.Field,
// sortText: CompletionItemPriority.High,
// range: {
// ...ctx.range,
// startColumn: ctx.range.endColumn,
// endColumn: ctx.range.endColumn,
// },
// }));
// }
// }
];
export function getTablePath(token: LinkedToken) {
export function getDatabaseName(token: LinkedToken) {
let processedToken = token;
let tablePath = '';
let database = '';
while (processedToken?.previous && !processedToken.previous.isWhiteSpace()) {
processedToken = processedToken.previous;
tablePath = processedToken.value + tablePath;
database = processedToken.value + database;
}
tablePath = tablePath.trim();
if (database.includes(SCHEMA_NAME)) {
database = database.replace(SCHEMA_NAME, '');
}
// TODO - remove - frin big query
database = database.trim();
// if (tablePath.startsWith('`')) {
// tablePath = tablePath.slice(1);
// }
// if (tablePath.endsWith('`')) {
// tablePath = tablePath.slice(0, -1);
// }
return tablePath;
return database;
}
// TODO - not sure if we need this
// function isTypingTableIn(token: LinkedToken | null, l?: boolean) {
// if (!token) {
// return false;
// }
// const tokens = token.getPreviousUntil(TokenType.Keyword, [], 'from');
// if (!tokens) {
// return false;
// }
// let path = '';
// for (let i = tokens.length - 1; i >= 0; i--) {
// path += tokens[i].value;
// }
// // TODO - remove - frin big query
// // if (path.startsWith('`')) {
// // path = path.slice(1);
// // }
// return path.split('.').length === 2;
// }
export async function fetchColumns(db: DB, q: SQLQuery) {
const cols = await db.fields(q);
if (cols.length > 0) {
@@ -79,6 +79,8 @@ export function getRAQBType(type: string): RAQBFieldTypes {
}
}
export const SCHEMA_NAME = 'dbo';
export function toRawSql({ sql, dataset, table }: SQLQuery): string {
let rawQuery = '';
@@ -90,7 +92,7 @@ export function toRawSql({ sql, dataset, table }: SQLQuery): string {
rawQuery += createSelectClause(sql.columns, sql.limit);
if (dataset && table) {
rawQuery += `FROM ${dataset}.dbo.${table} `;
rawQuery += `FROM ${dataset}.${SCHEMA_NAME}.${table} `;
}
if (sql.whereString) {