make completions smarter (#13830)

This commit is contained in:
Vadim Ogievetsky 2023-02-23 10:17:10 -08:00 committed by GitHub
parent 70f9052f1d
commit e4e6c7ed01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 89 additions and 49 deletions

View File

@ -72,9 +72,10 @@ export interface FlexibleQueryInputProps {
export interface FlexibleQueryInputState {
// For reasons (https://github.com/securingsincity/react-ace/issues/415) react ace editor needs an explicit height
// Since this component will grown and shrink dynamically we will measure its height and then set it.
// Since this component will grow and shrink dynamically we will measure its height and then set it.
editorHeight: number;
completions: any[];
quotedCompletions: Ace.Completion[];
unquotedCompletions: Ace.Completion[];
prevColumnMetadata?: readonly ColumnMetadata[];
prevCurrentTable?: string;
prevCurrentSchema?: string;
@ -89,7 +90,7 @@ export class FlexibleQueryInput extends React.PureComponent<
static replaceDefaultAutoCompleter(): void {
if (!langTools) return;
const keywordList = ([] as any[]).concat(
const keywordList = ([] as Ace.Completion[]).concat(
SQL_KEYWORDS.map(v => ({ name: v, value: v, score: 0, meta: 'keyword' })),
SQL_EXPRESSION_PARTS.map(v => ({ name: v, value: v, score: 0, meta: 'keyword' })),
SQL_CONSTANTS.map(v => ({ name: v, value: v, score: 0, meta: 'constant' })),
@ -108,7 +109,13 @@ export class FlexibleQueryInput extends React.PureComponent<
langTools.snippetCompleter,
langTools.textCompleter,
{
getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => {
getCompletions: (
_state: string,
_session: Ace.EditSession,
_pos: Ace.Point,
_prefix: string,
callback: any,
) => {
return callback(null, keywordList);
},
getDocTooltip: (item: any) => {
@ -123,17 +130,19 @@ export class FlexibleQueryInput extends React.PureComponent<
static addFunctionAutoCompleter(): void {
if (!langTools) return;
const functionList: any[] = Object.entries(SQL_FUNCTIONS).flatMap(([name, versions]) => {
return versions.map(([args, description]) => ({
name: name,
value: versions.length > 1 ? `${name}(${args})` : name,
score: 1100, // Use a high score to appear over the 'local' suggestions that have a score of 1000
meta: 'function',
syntax: `${name}(${args})`,
description,
completer: COMPLETER,
}));
});
const functionList: Ace.Completion[] = Object.entries(SQL_FUNCTIONS).flatMap(
([name, versions]) => {
return versions.map(([args, description]) => ({
name: name,
value: versions.length > 1 ? `${name}(${args})` : name,
score: 1100, // Use a high score to appear over the 'local' suggestions that have a score of 1000
meta: 'function',
syntax: `${name}(${args})`,
description,
completer: COMPLETER,
}));
},
);
langTools.addCompleter({
getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => {
@ -154,6 +163,43 @@ export class FlexibleQueryInput extends React.PureComponent<
<div class="doc-description">${item.description}</div>`;
}
static getCompletions(
columnMetadata: readonly ColumnMetadata[],
currentSchema: string | undefined,
currentTable: string | undefined,
quote: boolean,
): Ace.Completion[] {
return ([] as Ace.Completion[]).concat(
uniq(columnMetadata.map(d => d.TABLE_SCHEMA)).map(v => ({
value: quote ? String(T(v)) : v,
score: 10,
meta: 'schema',
})),
uniq(
columnMetadata
.filter(d => (currentSchema ? d.TABLE_SCHEMA === currentSchema : true))
.map(d => d.TABLE_NAME),
).map(v => ({
value: quote ? String(T(v)) : v,
score: 49,
meta: 'datasource',
})),
uniq(
columnMetadata
.filter(d =>
currentTable && currentSchema
? d.TABLE_NAME === currentTable && d.TABLE_SCHEMA === currentSchema
: true,
)
.map(d => d.COLUMN_NAME),
).map(v => ({
value: quote ? String(C(v)) : v,
score: 50,
meta: 'column',
})),
);
}
static getDerivedStateFromProps(props: FlexibleQueryInputProps, state: FlexibleQueryInputState) {
const { columnMetadata, currentSchema, currentTable } = props;
@ -163,38 +209,19 @@ export class FlexibleQueryInput extends React.PureComponent<
currentSchema !== state.prevCurrentSchema ||
currentTable !== state.prevCurrentTable)
) {
const completions = ([] as any[]).concat(
uniq(columnMetadata.map(d => d.TABLE_SCHEMA)).map(v => ({
value: String(T(v)),
score: 10,
meta: 'schema',
})),
uniq(
columnMetadata
.filter(d => (currentSchema ? d.TABLE_SCHEMA === currentSchema : true))
.map(d => d.TABLE_NAME),
).map(v => ({
value: String(T(v)),
score: 49,
meta: 'datasource',
})),
uniq(
columnMetadata
.filter(d =>
currentTable && currentSchema
? d.TABLE_NAME === currentTable && d.TABLE_SCHEMA === currentSchema
: true,
)
.map(d => d.COLUMN_NAME),
).map(v => ({
value: String(C(v)),
score: 50,
meta: 'column',
})),
);
return {
completions,
quotedCompletions: FlexibleQueryInput.getCompletions(
columnMetadata,
currentSchema,
currentTable,
true,
),
unquotedCompletions: FlexibleQueryInput.getCompletions(
columnMetadata,
currentSchema,
currentTable,
false,
),
prevColumnMetadata: columnMetadata,
prevCurrentSchema: currentSchema,
prevCurrentTable: currentTable,
@ -207,7 +234,8 @@ export class FlexibleQueryInput extends React.PureComponent<
super(props, context);
this.state = {
editorHeight: 200,
completions: [],
quotedCompletions: [],
unquotedCompletions: [],
};
}
@ -216,8 +244,20 @@ export class FlexibleQueryInput extends React.PureComponent<
FlexibleQueryInput.addFunctionAutoCompleter();
if (langTools) {
langTools.addCompleter({
getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => {
callback(null, this.state.completions);
getCompletions: (
_state: string,
session: Ace.EditSession,
pos: Ace.Point,
prefix: string,
callback: any,
) => {
const charBeforePrefix = session.getLine(pos.row)[pos.column - prefix.length - 1];
callback(
null,
charBeforePrefix === '"'
? this.state.unquotedCompletions
: this.state.quotedCompletions,
);
},
});
}