mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
parent
904d21dc93
commit
fe7add0bc6
@ -5377,39 +5377,11 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "7"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "8"]
|
||||
],
|
||||
"public/app/features/plugins/sql/components/query-editor-raw/SQLEditor.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "5"]
|
||||
],
|
||||
"public/app/features/plugins/sql/components/visual-query-builder/AwesomeQueryBuilder.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"public/app/features/plugins/sql/mocks/Monaco.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/plugins/sql/mocks/queries/singleLineFullQuery.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/plugins/sql/standardSql/definition.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/plugins/sql/test-utils/statementPosition.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"]
|
||||
],
|
||||
"public/app/features/plugins/sql/utils/debugger.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"public/app/features/plugins/tests/datasource_srv.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
|
@ -254,6 +254,7 @@
|
||||
"@grafana/aws-sdk": "0.0.37",
|
||||
"@grafana/data": "workspace:*",
|
||||
"@grafana/e2e-selectors": "workspace:*",
|
||||
"@grafana/experimental": "^0.0.2-canary.36",
|
||||
"@grafana/google-sdk": "0.0.3",
|
||||
"@grafana/lezer-logql": "0.0.14",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
|
@ -1,2 +1 @@
|
||||
export * from './query-editor-raw';
|
||||
export * from './visual-query-builder';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { SQLEditor } from '@grafana/experimental';
|
||||
|
||||
import { LanguageCompletionProvider, SQLQuery } from '../../types';
|
||||
import { formatSQL } from '../../utils/formatSQL';
|
||||
|
||||
import { SQLEditor } from './SQLEditor';
|
||||
|
||||
type Props = {
|
||||
query: SQLQuery;
|
||||
onChange: (value: SQLQuery, processQuery: boolean) => void;
|
||||
|
@ -1,405 +0,0 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { Registry } from '@grafana/data';
|
||||
import { CodeEditor, Monaco, monacoTypes } from '@grafana/ui';
|
||||
|
||||
import standardSQLLanguageDefinition from '../../standardSql/definition';
|
||||
import { getStandardSuggestions } from '../../standardSql/getStandardSuggestions';
|
||||
import { getStatementPosition } from '../../standardSql/getStatementPosition';
|
||||
import {
|
||||
initFunctionsRegistry,
|
||||
initMacrosRegistry,
|
||||
initOperatorsRegistry,
|
||||
initStandardSuggestions,
|
||||
} from '../../standardSql/standardSuggestionsRegistry';
|
||||
import { initStatementPositionResolvers } from '../../standardSql/statementPositionResolversRegistry';
|
||||
import { initSuggestionsKindRegistry, SuggestionKindRegistryItem } from '../../standardSql/suggestionsKindRegistry';
|
||||
import {
|
||||
FunctionsRegistryItem,
|
||||
MacrosRegistryItem,
|
||||
OperatorsRegistryItem,
|
||||
SQLMonarchLanguage,
|
||||
StatementPositionResolversRegistryItem,
|
||||
SuggestionsRegistryItem,
|
||||
} from '../../standardSql/types';
|
||||
import {
|
||||
CompletionItemKind,
|
||||
CompletionItemPriority,
|
||||
CustomSuggestion,
|
||||
PositionContext,
|
||||
SQLCompletionItemProvider,
|
||||
StatementPosition,
|
||||
SuggestionKind,
|
||||
} from '../../types';
|
||||
import { LinkedToken } from '../../utils/LinkedToken';
|
||||
import { TRIGGER_SUGGEST } from '../../utils/commands';
|
||||
import { sqlEditorLog } from '../../utils/debugger';
|
||||
import { getSuggestionKinds } from '../../utils/getSuggestionKind';
|
||||
import { linkedTokenBuilder } from '../../utils/linkedTokenBuilder';
|
||||
import { getTableToken } from '../../utils/tokenUtils';
|
||||
|
||||
const STANDARD_SQL_LANGUAGE = 'sql';
|
||||
|
||||
export interface LanguageDefinition extends monacoTypes.languages.ILanguageExtensionPoint {
|
||||
loader?: (module: any) => Promise<{
|
||||
language: SQLMonarchLanguage;
|
||||
conf: monacoTypes.languages.LanguageConfiguration;
|
||||
}>;
|
||||
// Provides API for customizing the autocomplete
|
||||
completionProvider?: (m: Monaco) => SQLCompletionItemProvider;
|
||||
// Function that returns a formatted query
|
||||
formatter?: (q: string) => string;
|
||||
}
|
||||
|
||||
interface SQLEditorProps {
|
||||
query: string;
|
||||
/**
|
||||
* Use for inspecting the query as it changes. I.e. for validation.
|
||||
*/
|
||||
onChange?: (q: string, processQuery: boolean) => void;
|
||||
language?: LanguageDefinition;
|
||||
children?: (props: { formatQuery: () => void }) => React.ReactNode;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
const defaultTableNameParser = (t: LinkedToken) => t.value;
|
||||
|
||||
interface LanguageRegistries {
|
||||
functions: Registry<FunctionsRegistryItem>;
|
||||
operators: Registry<OperatorsRegistryItem>;
|
||||
suggestionKinds: Registry<SuggestionKindRegistryItem>;
|
||||
positionResolvers: Registry<StatementPositionResolversRegistryItem>;
|
||||
macros: Registry<MacrosRegistryItem>;
|
||||
}
|
||||
|
||||
const LANGUAGES_CACHE = new Map<string, LanguageRegistries>();
|
||||
const INSTANCE_CACHE = new Map<string, Registry<SuggestionsRegistryItem>>();
|
||||
|
||||
export const SQLEditor: React.FC<SQLEditorProps> = ({
|
||||
children,
|
||||
onChange,
|
||||
query,
|
||||
language = { id: STANDARD_SQL_LANGUAGE },
|
||||
width,
|
||||
height,
|
||||
}) => {
|
||||
const monacoRef = useRef<monacoTypes.editor.IStandaloneCodeEditor>(null);
|
||||
const langUid = useRef<string>();
|
||||
// create unique language id for each SQLEditor instance
|
||||
const id = useMemo(() => {
|
||||
const uid = v4();
|
||||
const id = `${language.id}-${uid}`;
|
||||
langUid.current = id;
|
||||
return id;
|
||||
}, [language.id]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
INSTANCE_CACHE.delete(langUid.current!);
|
||||
sqlEditorLog(`Removing instance cache ${langUid.current}`, false, INSTANCE_CACHE);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const formatQuery = useCallback(() => {
|
||||
if (monacoRef.current) {
|
||||
monacoRef.current.getAction('editor.action.formatDocument').run();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ width }}>
|
||||
<CodeEditor
|
||||
height={height || '240px'}
|
||||
// -2px to compensate for borders width
|
||||
width={width ? `${width - 2}px` : undefined}
|
||||
language={id}
|
||||
value={query}
|
||||
onBlur={(v) => onChange && onChange(v, false)}
|
||||
showMiniMap={false}
|
||||
showLineNumbers={true}
|
||||
// Using onEditorDidMount instead of onBeforeEditorMount to support Grafana < 8.2.x
|
||||
onEditorDidMount={(editor, m) => {
|
||||
// TODO - says its read only but this worked in experimental
|
||||
// monacoRef.current = editor;
|
||||
editor.onDidChangeModelContent((e) => {
|
||||
const text = editor.getValue();
|
||||
if (onChange) {
|
||||
onChange(text, false);
|
||||
}
|
||||
});
|
||||
|
||||
editor.addCommand(m.KeyMod.CtrlCmd | m.KeyCode.Enter, () => {
|
||||
const text = editor.getValue();
|
||||
if (onChange) {
|
||||
onChange(text, true);
|
||||
}
|
||||
});
|
||||
registerLanguageAndSuggestions(m, language, id);
|
||||
}}
|
||||
/>
|
||||
{children && children({ formatQuery })}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// There's three ways to define Monaco language:
|
||||
// 1. Leave language.id empty or set it to 'sql'. This will load a standard sql language definition, including syntax highlighting and tokenization for
|
||||
// common Grafana entities such as macros and template variables
|
||||
// 2. Provide a custom language and load it via the async LanguageDefinition.loader callback
|
||||
// 3. Specify a language.id that exists in the Monaco language registry. See available languages here: https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages
|
||||
// If a custom language is specified, its LanguageDefinition will be merged with the LanguageDefinition for standard SQL. This allows the consumer to only
|
||||
// override parts of the LanguageDefinition, such as for example the completion item provider.
|
||||
const resolveLanguage = (monaco: Monaco, languageDefinitionProp: LanguageDefinition): LanguageDefinition => {
|
||||
if (languageDefinitionProp?.id !== STANDARD_SQL_LANGUAGE && !languageDefinitionProp.loader) {
|
||||
sqlEditorLog(`Loading language '${languageDefinitionProp?.id}' from Monaco registry`, false);
|
||||
const allLangs = monaco.languages.getLanguages();
|
||||
const custom = allLangs.find(({ id }) => id === languageDefinitionProp?.id);
|
||||
if (!custom) {
|
||||
throw Error(`Unknown Monaco language ${languageDefinitionProp?.id}`);
|
||||
}
|
||||
return custom;
|
||||
}
|
||||
|
||||
return {
|
||||
...standardSQLLanguageDefinition,
|
||||
...languageDefinitionProp,
|
||||
};
|
||||
};
|
||||
|
||||
export const registerLanguageAndSuggestions = async (monaco: Monaco, l: LanguageDefinition, lid: string) => {
|
||||
const languageDefinition = resolveLanguage(monaco, l);
|
||||
const { language, conf } = await languageDefinition.loader!(monaco);
|
||||
monaco.languages.register({ id: lid });
|
||||
monaco.languages.setMonarchTokensProvider(lid, { ...language });
|
||||
monaco.languages.setLanguageConfiguration(lid, { ...conf });
|
||||
|
||||
if (languageDefinition.formatter) {
|
||||
monaco.languages.registerDocumentFormattingEditProvider(lid, {
|
||||
provideDocumentFormattingEdits: (model) => {
|
||||
var formatted = l.formatter!(model.getValue());
|
||||
return [
|
||||
{
|
||||
range: model.getFullModelRange(),
|
||||
text: formatted,
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (languageDefinition.completionProvider) {
|
||||
const customProvider = l.completionProvider!(monaco);
|
||||
extendStandardRegistries(l.id, lid, customProvider);
|
||||
const languageSuggestionsRegistries = LANGUAGES_CACHE.get(l.id)!;
|
||||
const instanceSuggestionsRegistry = INSTANCE_CACHE.get(lid)!;
|
||||
|
||||
const completionProvider: monacoTypes.languages.CompletionItemProvider['provideCompletionItems'] = async (
|
||||
model,
|
||||
position,
|
||||
context,
|
||||
token
|
||||
) => {
|
||||
const currentToken = linkedTokenBuilder(monaco, model, position, 'sql');
|
||||
const statementPosition = getStatementPosition(currentToken, languageSuggestionsRegistries.positionResolvers);
|
||||
const kind = getSuggestionKinds(statementPosition, languageSuggestionsRegistries.suggestionKinds);
|
||||
|
||||
sqlEditorLog('Statement position', false, statementPosition);
|
||||
sqlEditorLog('Suggestion kinds', false, kind);
|
||||
|
||||
const ctx: PositionContext = {
|
||||
position,
|
||||
currentToken,
|
||||
statementPosition,
|
||||
kind,
|
||||
range: monaco.Range.fromPositions(position),
|
||||
};
|
||||
|
||||
// // Completely custom suggestions - hope this won't we needed
|
||||
// let ci;
|
||||
// if (customProvider.provideCompletionItems) {
|
||||
// ci = customProvider.provideCompletionItems(model, position, context, token, ctx);
|
||||
// }
|
||||
|
||||
const stdSuggestions = await getStandardSuggestions(monaco, currentToken, kind, ctx, instanceSuggestionsRegistry);
|
||||
|
||||
return {
|
||||
// ...ci,
|
||||
suggestions: stdSuggestions,
|
||||
};
|
||||
};
|
||||
|
||||
monaco.languages.registerCompletionItemProvider(lid, {
|
||||
...customProvider,
|
||||
provideCompletionItems: completionProvider,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function extendStandardRegistries(id: string, lid: string, customProvider: SQLCompletionItemProvider) {
|
||||
if (!LANGUAGES_CACHE.has(id)) {
|
||||
initializeLanguageRegistries(id);
|
||||
}
|
||||
|
||||
const languageRegistries = LANGUAGES_CACHE.get(id)!;
|
||||
|
||||
if (!INSTANCE_CACHE.has(lid)) {
|
||||
INSTANCE_CACHE.set(
|
||||
lid,
|
||||
new Registry(
|
||||
initStandardSuggestions(languageRegistries.functions, languageRegistries.operators, languageRegistries.macros)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const instanceSuggestionsRegistry = INSTANCE_CACHE.get(lid)!;
|
||||
|
||||
if (customProvider.supportedFunctions) {
|
||||
for (const func of customProvider.supportedFunctions()) {
|
||||
const exists = languageRegistries.functions.getIfExists(func.id);
|
||||
if (!exists) {
|
||||
languageRegistries.functions.register(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (customProvider.supportedOperators) {
|
||||
for (const op of customProvider.supportedOperators()) {
|
||||
const exists = languageRegistries.operators.getIfExists(op.id);
|
||||
if (!exists) {
|
||||
languageRegistries.operators.register({ ...op, name: op.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (customProvider.supportedMacros) {
|
||||
for (const macro of customProvider.supportedMacros()) {
|
||||
const exists = languageRegistries.macros.getIfExists(macro.id);
|
||||
if (!exists) {
|
||||
languageRegistries.macros.register({ ...macro, name: macro.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (customProvider.customStatementPlacement) {
|
||||
for (const placement of customProvider.customStatementPlacement()) {
|
||||
const exists = languageRegistries.positionResolvers.getIfExists(placement.id);
|
||||
if (!exists) {
|
||||
languageRegistries.positionResolvers.register({
|
||||
...placement,
|
||||
id: placement.id as StatementPosition,
|
||||
name: placement.id,
|
||||
});
|
||||
languageRegistries.suggestionKinds.register({
|
||||
id: placement.id as StatementPosition,
|
||||
name: placement.id,
|
||||
kind: [],
|
||||
});
|
||||
} else {
|
||||
// Allow extension to the built-in placement resolvers
|
||||
const origResolve = exists.resolve;
|
||||
exists.resolve = (...args) => {
|
||||
const ext = placement.resolve(...args);
|
||||
if (placement.overrideDefault) {
|
||||
return ext;
|
||||
}
|
||||
const orig = origResolve(...args);
|
||||
return orig || ext;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (customProvider.customSuggestionKinds) {
|
||||
for (const kind of customProvider.customSuggestionKinds()) {
|
||||
kind.applyTo?.forEach((applyTo) => {
|
||||
const exists = languageRegistries.suggestionKinds.getIfExists(applyTo);
|
||||
if (exists) {
|
||||
// avoid duplicates
|
||||
if (exists.kind.indexOf(kind.id as SuggestionKind) === -1) {
|
||||
exists.kind.push(kind.id as SuggestionKind);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (kind.overrideDefault) {
|
||||
const stbBehaviour = instanceSuggestionsRegistry.get(kind.id);
|
||||
if (stbBehaviour !== undefined) {
|
||||
stbBehaviour.suggestions = kind.suggestionsResolver;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
instanceSuggestionsRegistry.register({
|
||||
id: kind.id as SuggestionKind,
|
||||
name: kind.id,
|
||||
suggestions: kind.suggestionsResolver,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (customProvider.tables) {
|
||||
const stbBehaviour = instanceSuggestionsRegistry.get(SuggestionKind.Tables);
|
||||
const s = stbBehaviour!.suggestions;
|
||||
stbBehaviour!.suggestions = async (ctx, m) => {
|
||||
const o = await s(ctx, m);
|
||||
const oo = (await customProvider.tables!.resolve!()).map((x) => ({
|
||||
label: x.name,
|
||||
insertText: x.completion ?? x.name,
|
||||
command: TRIGGER_SUGGEST,
|
||||
kind: CompletionItemKind.Field,
|
||||
sortText: CompletionItemPriority.High,
|
||||
}));
|
||||
return [...o, ...oo];
|
||||
};
|
||||
}
|
||||
|
||||
if (customProvider.columns) {
|
||||
const stbBehaviour = instanceSuggestionsRegistry.get(SuggestionKind.Columns);
|
||||
const s = stbBehaviour!.suggestions;
|
||||
stbBehaviour!.suggestions = async (ctx, m) => {
|
||||
const o = await s(ctx, m);
|
||||
const tableToken = getTableToken(ctx.currentToken);
|
||||
let table = '';
|
||||
const tableNameParser = customProvider.tables?.parseName ?? defaultTableNameParser;
|
||||
|
||||
if (tableToken && tableToken.value) {
|
||||
table = tableNameParser(tableToken).trim();
|
||||
}
|
||||
|
||||
let oo: CustomSuggestion[] = [];
|
||||
if (table) {
|
||||
const columns = await customProvider.columns?.resolve!(table);
|
||||
oo = columns
|
||||
? columns.map<CustomSuggestion>((x) => ({
|
||||
label: x.name,
|
||||
insertText: x.completion ?? x.name,
|
||||
kind: CompletionItemKind.Field,
|
||||
sortText: CompletionItemPriority.High,
|
||||
detail: x.type,
|
||||
documentation: x.description,
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
return [...o, ...oo];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes language specific registries that are treated as singletons
|
||||
*/
|
||||
function initializeLanguageRegistries(id: string) {
|
||||
if (!LANGUAGES_CACHE.has(id)) {
|
||||
LANGUAGES_CACHE.set(id, {
|
||||
functions: new Registry(initFunctionsRegistry),
|
||||
operators: new Registry(initOperatorsRegistry),
|
||||
suggestionKinds: new Registry(initSuggestionsKindRegistry),
|
||||
positionResolvers: new Registry(initStatementPositionResolvers),
|
||||
macros: new Registry(initMacrosRegistry),
|
||||
});
|
||||
}
|
||||
|
||||
return LANGUAGES_CACHE.get(id)!;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { SQLEditor, LanguageDefinition } from './SQLEditor';
|
@ -1,22 +1,2 @@
|
||||
export { SQLEditorTestUtils, TestQueryModel } from './test-utils';
|
||||
export { LinkedToken } from './utils/LinkedToken';
|
||||
export { language as grafanaStandardSQLLanguage, conf as grafanaStandardSQLLanguageConf } from './standardSql/language';
|
||||
export { SQLMonarchLanguage } from './standardSql/types';
|
||||
|
||||
export {
|
||||
TableDefinition,
|
||||
ColumnDefinition,
|
||||
StatementPlacementProvider,
|
||||
SuggestionKindProvider,
|
||||
LanguageCompletionProvider,
|
||||
OperatorType,
|
||||
MacroType,
|
||||
TokenType,
|
||||
StatementPosition,
|
||||
SuggestionKind,
|
||||
CompletionItemKind,
|
||||
CompletionItemPriority,
|
||||
CompletionItemInsertTextRule,
|
||||
} from './types';
|
||||
|
||||
export * from './components';
|
||||
export * from './types';
|
||||
|
@ -1,26 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
// Stub for the Monaco instance. Only implements the parts that are used in cloudwatch sql
|
||||
const getMonacoMock: (
|
||||
testData: Map<string, Array<Array<Pick<monacoTypes.Token, 'language' | 'offset' | 'type'>>>>
|
||||
) => any = (testData) => ({
|
||||
editor: {
|
||||
tokenize: (value: string, languageId: string) => testData.get(value),
|
||||
},
|
||||
Range: {
|
||||
containsPosition: (range: monacoTypes.IRange, position: monacoTypes.IPosition) => {
|
||||
return (
|
||||
position.lineNumber >= range.startLineNumber &&
|
||||
position.lineNumber <= range.endLineNumber &&
|
||||
position.column >= range.startColumn &&
|
||||
position.column <= range.endColumn
|
||||
);
|
||||
},
|
||||
},
|
||||
languages: {
|
||||
CompletionItemKind: { Snippet: 2, Function: 1, Keyword: 3 },
|
||||
CompletionItemInsertTextRule: { InsertAsSnippet: 2 },
|
||||
},
|
||||
});
|
||||
|
||||
export { getMonacoMock };
|
@ -1,21 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
// Stub for monacoTypes.editor.ITextModel
|
||||
function TextModel(value: string) {
|
||||
return {
|
||||
getValue: function (eol?: monacoTypes.editor.EndOfLinePreference, preserveBOM?: boolean): string {
|
||||
return value;
|
||||
},
|
||||
getValueInRange: function (range: monacoTypes.IRange, eol?: monacoTypes.editor.EndOfLinePreference): string {
|
||||
const lines = value.split('\n');
|
||||
const line = lines[range.startLineNumber - 1];
|
||||
return line.trim().slice(range.startColumn === 0 ? 0 : range.startColumn - 1, range.endColumn - 1);
|
||||
},
|
||||
getLineLength: function (lineNumber: number): number {
|
||||
const lines = value.split('\n');
|
||||
return lines[lineNumber - 1].trim().length;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { TextModel };
|
@ -1,214 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const multiLineFullQuery: TestQueryModel = {
|
||||
query: `SELECT column1,
|
||||
FROM table1
|
||||
|
||||
WHERE column1 = "value1"
|
||||
GROUP BY column1 ORDER BY column1 DESC
|
||||
LIMIT 10;`,
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 14,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 15,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 4,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 11,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 14,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 15,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 17,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 24,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 8,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 9,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 17,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 25,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 26,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 33,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 34,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 38,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 8,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
@ -1,229 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const multiLineFullQueryWithAggregation: TestQueryModel = {
|
||||
query: `SELECT count(column1),
|
||||
FROM table1
|
||||
|
||||
WHERE column1 = "value1"
|
||||
GROUP BY column1 ORDER BY column1 DESC
|
||||
LIMIT 10;`,
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 12,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 20,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 21,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 4,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 11,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 14,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 15,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 17,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 24,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 8,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 9,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 17,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 25,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 26,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 33,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 34,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 38,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 8,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
@ -1,269 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const multiLineMultipleColumns: TestQueryModel = {
|
||||
query: `SELECT count(column1), column2
|
||||
FROM table1
|
||||
|
||||
WHERE column1 = "value1"
|
||||
GROUP BY column1 ORDER BY column1, avg(column2) DESC
|
||||
LIMIT 10;`,
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 12,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 20,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 21,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 30,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 4,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 11,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 14,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 15,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 17,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 24,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 8,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 9,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 17,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 25,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 26,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 33,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 34,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 35,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 38,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 39,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 46,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 47,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 48,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 52,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 5,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 8,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const singleLineEmptyQuery: TestQueryModel = {
|
||||
query: '',
|
||||
tokens: [],
|
||||
};
|
@ -1,196 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const singleLineFullQuery: TestQueryModel = {
|
||||
query: `SELECT column1, FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10`,
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 14,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 15,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 20,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 21,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 27,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 28,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 33,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 34,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 41,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 42,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 43,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 44,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 45,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 51,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 52,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 53,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 58,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 59,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 61,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 62,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 69,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 70,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 75,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 76,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 78,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 79,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 86,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 87,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 91,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 92,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 97,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 98,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 100,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
] as monacoTypes.Token[][],
|
||||
};
|
@ -1,209 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const singleLineFullQueryWithAggregation: TestQueryModel = {
|
||||
query: 'SELECT count(column1), FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10;',
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 12,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 20,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 21,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 27,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 28,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 34,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 35,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 40,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 41,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 48,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 49,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 50,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 51,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 52,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 58,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 59,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 60,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 65,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 66,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 68,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 69,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 76,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 77,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 82,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 83,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 85,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 86,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 93,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 94,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 98,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 99,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 104,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 105,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 107,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
@ -1,250 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const singleLineMultipleColumns: TestQueryModel = {
|
||||
query:
|
||||
'SELECT count(column1), column2 FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1, avg(column2) DESC LIMIT 10;',
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 12,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 20,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 21,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 30,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 31,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 35,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 36,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 42,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 43,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 48,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 49,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 56,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 57,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 58,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 59,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 60,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 66,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 67,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 68,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 73,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 74,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 76,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 77,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 84,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 85,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 90,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 91,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 93,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 94,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 101,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 102,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 103,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 106,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 107,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 114,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 115,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 116,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 120,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 121,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 126,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 127,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 129,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
@ -1,385 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const singleLineTwoQueries: TestQueryModel = {
|
||||
query:
|
||||
'SELECT column1, FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10; SELECT column2, FROM table2 WHERE column2 = "value2" GROUP BY column1 ORDER BY column2 DESC LIMIT 10;',
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 14,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 15,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 16,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 20,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 21,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 27,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 28,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 33,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 34,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 41,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 42,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 43,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 44,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 45,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 51,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 52,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 53,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 58,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 59,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 61,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 62,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 69,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 70,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 75,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 76,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 78,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 79,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 86,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 87,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 91,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 92,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 97,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 98,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 100,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 101,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 102,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 108,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 109,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 116,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 117,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 118,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 122,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 123,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 129,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 130,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 135,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 136,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 143,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 144,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 145,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 146,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 147,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 153,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 154,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 155,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 160,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 161,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 163,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 164,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 171,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 172,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 177,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 178,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 180,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 181,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 188,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 189,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 193,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 194,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 199,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 200,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 202,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
@ -1,415 +0,0 @@
|
||||
import { TestQueryModel } from '../../test-utils/types';
|
||||
|
||||
export const singleLineTwoQueriesWithAggregation: TestQueryModel = {
|
||||
query:
|
||||
'SELECT count(column1), FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10; SELECT count(column2), FROM table2 WHERE column2 = "value2" GROUP BY column1 ORDER BY column2 DESC LIMIT 10;',
|
||||
tokens: [
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 6,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 7,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 12,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 13,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 20,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 21,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 22,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 23,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 27,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 28,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 34,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 35,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 40,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 41,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 48,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 49,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 50,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 51,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 52,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 58,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 59,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 60,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 65,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 66,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 68,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 69,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 76,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 77,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 82,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 83,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 85,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 86,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 93,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 94,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 98,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 99,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 104,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 105,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 107,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 108,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 109,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 115,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 116,
|
||||
type: 'predefined.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 121,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 122,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 129,
|
||||
type: 'delimiter.parenthesis.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 130,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 131,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 132,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 136,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 137,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 143,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 144,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 149,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 150,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 157,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 158,
|
||||
type: 'operator.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 159,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 160,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 161,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 167,
|
||||
type: 'identifier.quote.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 168,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 169,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 174,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 175,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 177,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 178,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 185,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 186,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 191,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 192,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 194,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 195,
|
||||
type: 'identifier.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 202,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 203,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 207,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 208,
|
||||
type: 'keyword.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 213,
|
||||
type: 'white.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 214,
|
||||
type: 'number.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
{
|
||||
offset: 216,
|
||||
type: 'delimiter.sql',
|
||||
language: 'sql',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
export { singleLineFullQuery } from './queries/singleLineFullQuery';
|
||||
export { singleLineFullQueryWithAggregation } from './queries/singleLineFullQueryWithAggregation';
|
||||
export { multiLineFullQuery } from './queries/multiLineFullQuery';
|
||||
export { multiLineFullQueryWithAggregation } from './queries/multiLineFullQueryWithAggregation';
|
||||
export { singleLineEmptyQuery } from './queries/singleLineEmptyQuery';
|
||||
export { singleLineTwoQueries } from './queries/singleLineTwoQueries';
|
||||
export { singleLineTwoQueriesWithAggregation } from './queries/singleLineTwoQueriesWithAggregation';
|
||||
export { singleLineMultipleColumns } from './queries/singleLineMultipleColumns';
|
||||
export { multiLineMultipleColumns } from './queries/multiLineMultipleColumns';
|
@ -1,24 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { SQLMonarchLanguage } from './types';
|
||||
|
||||
export type LanguageDefinition = {
|
||||
id: string;
|
||||
extensions: string[];
|
||||
aliases: string[];
|
||||
mimetypes: string[];
|
||||
loader: (monaco: any) => Promise<{
|
||||
language: SQLMonarchLanguage;
|
||||
conf: monacoTypes.languages.LanguageConfiguration;
|
||||
}>;
|
||||
};
|
||||
|
||||
const standardSQLLanguageDefinition: LanguageDefinition = {
|
||||
id: 'standardSql',
|
||||
extensions: ['.sql'],
|
||||
aliases: ['sql'],
|
||||
mimetypes: [],
|
||||
loader: () => import('./language'),
|
||||
};
|
||||
|
||||
export default standardSQLLanguageDefinition;
|
@ -1,273 +0,0 @@
|
||||
import { Registry } from '@grafana/data';
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { getMonacoMock } from '../mocks/Monaco';
|
||||
import { TextModel } from '../mocks/TextModel';
|
||||
import { singleLineFullQuery } from '../mocks/testData';
|
||||
import { OperatorType, SuggestionKind, CustomSuggestion, PositionContext, MacroType } from '../types';
|
||||
import { linkedTokenBuilder } from '../utils/linkedTokenBuilder';
|
||||
|
||||
import { getStandardSuggestions } from './getStandardSuggestions';
|
||||
import { initStandardSuggestions } from './standardSuggestionsRegistry';
|
||||
import { FunctionsRegistryItem, MacrosRegistryItem, OperatorsRegistryItem, SuggestionsRegistryItem } from './types';
|
||||
|
||||
describe('getStandardSuggestions', () => {
|
||||
const mockQueries = new Map<string, Array<Array<Pick<monacoTypes.Token, 'language' | 'offset' | 'type'>>>>();
|
||||
const cases = [{ query: singleLineFullQuery, position: { line: 1, column: 0 } }];
|
||||
cases.forEach((c) => mockQueries.set(c.query.query, c.query.tokens));
|
||||
const MonacoMock = getMonacoMock(mockQueries);
|
||||
const token = linkedTokenBuilder(MonacoMock, TextModel(singleLineFullQuery.query) as monacoTypes.editor.ITextModel, {
|
||||
lineNumber: 1,
|
||||
column: 0,
|
||||
});
|
||||
const posContextMock = {};
|
||||
|
||||
it('calls the resolvers', async () => {
|
||||
const suggestionMock: CustomSuggestion = { label: 'customSuggest' };
|
||||
const resolveFunctionSpy = jest.fn().mockReturnValue([suggestionMock]);
|
||||
const kind = 'customSuggestionItemKind' as SuggestionKind;
|
||||
const suggestionsRegistry = new Registry<SuggestionsRegistryItem>(() => {
|
||||
return [
|
||||
{
|
||||
id: kind,
|
||||
name: 'customSuggestionItemKind',
|
||||
suggestions: resolveFunctionSpy,
|
||||
},
|
||||
];
|
||||
});
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[kind],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(resolveFunctionSpy).toBeCalledTimes(1);
|
||||
expect(resolveFunctionSpy).toBeCalledWith({ range: token!.range }, MonacoMock);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].label).toEqual(suggestionMock.label);
|
||||
});
|
||||
|
||||
it('suggests custom functions with arguments from the registry', async () => {
|
||||
const customFunction = {
|
||||
name: 'customFunction',
|
||||
id: 'customFunction',
|
||||
};
|
||||
|
||||
const suggestionsRegistry = new Registry(
|
||||
initStandardSuggestions(
|
||||
new Registry<FunctionsRegistryItem>(() => [customFunction]),
|
||||
new Registry<OperatorsRegistryItem>(() => []),
|
||||
new Registry<MacrosRegistryItem>(() => [])
|
||||
)
|
||||
);
|
||||
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[SuggestionKind.FunctionsWithArguments],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].label).toEqual(customFunction.name);
|
||||
});
|
||||
|
||||
it('suggests custom functions without arguments from the registry', async () => {
|
||||
const customFunction = {
|
||||
name: 'customFunction',
|
||||
id: 'customFunction',
|
||||
};
|
||||
|
||||
const suggestionsRegistry = new Registry(
|
||||
initStandardSuggestions(
|
||||
new Registry<FunctionsRegistryItem>(() => [customFunction]),
|
||||
new Registry<OperatorsRegistryItem>(() => []),
|
||||
new Registry<MacrosRegistryItem>(() => [])
|
||||
)
|
||||
);
|
||||
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[SuggestionKind.FunctionsWithoutArguments],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].label).toEqual(customFunction.name);
|
||||
});
|
||||
|
||||
it('suggests custom logical operators from the registry', async () => {
|
||||
const customLogicalOperator = {
|
||||
type: OperatorType.Logical,
|
||||
name: 'customOperator',
|
||||
id: 'customOperator',
|
||||
operator: '½',
|
||||
};
|
||||
|
||||
const suggestionsRegistry = new Registry(
|
||||
initStandardSuggestions(
|
||||
new Registry<FunctionsRegistryItem>(() => []),
|
||||
new Registry<OperatorsRegistryItem>(() => [customLogicalOperator]),
|
||||
new Registry<MacrosRegistryItem>(() => [])
|
||||
)
|
||||
);
|
||||
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[SuggestionKind.LogicalOperators],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].label).toEqual(customLogicalOperator.operator);
|
||||
});
|
||||
|
||||
it('suggests custom comparison operators from the registry', async () => {
|
||||
const customComparisonOperator = {
|
||||
type: OperatorType.Comparison,
|
||||
name: 'customOperator',
|
||||
id: 'customOperator',
|
||||
operator: '§',
|
||||
};
|
||||
|
||||
const suggestionsRegistry = new Registry(
|
||||
initStandardSuggestions(
|
||||
new Registry<FunctionsRegistryItem>(() => []),
|
||||
new Registry<OperatorsRegistryItem>(() => [customComparisonOperator]),
|
||||
new Registry<MacrosRegistryItem>(() => [])
|
||||
)
|
||||
);
|
||||
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[SuggestionKind.ComparisonOperators],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(5);
|
||||
expect(result[0].label).toEqual(customComparisonOperator.operator);
|
||||
});
|
||||
|
||||
it('does not suggest logical operators when asked for comparison operators', async () => {
|
||||
const customLogicalOperator = {
|
||||
type: OperatorType.Logical,
|
||||
name: 'customOperator',
|
||||
id: 'customOperator',
|
||||
operator: '§',
|
||||
};
|
||||
|
||||
const suggestionsRegistry = new Registry(
|
||||
initStandardSuggestions(
|
||||
new Registry<FunctionsRegistryItem>(() => []),
|
||||
new Registry<OperatorsRegistryItem>(() => [customLogicalOperator]),
|
||||
new Registry<MacrosRegistryItem>(() => [])
|
||||
)
|
||||
);
|
||||
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[SuggestionKind.ComparisonOperators],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('suggests $__time(dateColumn) macro when in column position', async () => {
|
||||
const customMacro: MacrosRegistryItem = {
|
||||
name: '$__time',
|
||||
id: '$__time',
|
||||
text: '$__time',
|
||||
type: MacroType.Value,
|
||||
};
|
||||
|
||||
const suggestionsRegistry = new Registry(
|
||||
initStandardSuggestions(
|
||||
new Registry<FunctionsRegistryItem>(() => []),
|
||||
new Registry<OperatorsRegistryItem>(() => []),
|
||||
new Registry<MacrosRegistryItem>(() => [customMacro])
|
||||
)
|
||||
);
|
||||
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[SuggestionKind.SelectMacro],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].label).toEqual('$__time');
|
||||
});
|
||||
|
||||
it('suggests SELECT and SELECT FROM from the standard registry', async () => {
|
||||
const suggestionsRegistry = new Registry(
|
||||
initStandardSuggestions(
|
||||
new Registry<FunctionsRegistryItem>(() => []),
|
||||
new Registry<OperatorsRegistryItem>(() => []),
|
||||
new Registry<MacrosRegistryItem>(() => [])
|
||||
)
|
||||
);
|
||||
|
||||
const result = await getStandardSuggestions(
|
||||
MonacoMock,
|
||||
token,
|
||||
[SuggestionKind.SelectKeyword],
|
||||
posContextMock as PositionContext,
|
||||
suggestionsRegistry
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"command": Object {
|
||||
"id": "editor.action.triggerSuggest",
|
||||
"title": "",
|
||||
},
|
||||
"insertText": "SELECT $0",
|
||||
"insertTextRules": 4,
|
||||
"kind": 27,
|
||||
"label": "SELECT <column>",
|
||||
"range": Object {
|
||||
"endColumn": 7,
|
||||
"endLineNumber": 1,
|
||||
"startColumn": 0,
|
||||
"startLineNumber": 1,
|
||||
},
|
||||
"sortText": "g",
|
||||
},
|
||||
Object {
|
||||
"command": Object {
|
||||
"id": "editor.action.triggerSuggest",
|
||||
"title": "",
|
||||
},
|
||||
"insertText": "SELECT $2 FROM $1",
|
||||
"insertTextRules": 4,
|
||||
"kind": 27,
|
||||
"label": "SELECT <column> FROM <table>",
|
||||
"range": Object {
|
||||
"endColumn": 7,
|
||||
"endLineNumber": 1,
|
||||
"startColumn": 0,
|
||||
"startLineNumber": 1,
|
||||
},
|
||||
"sortText": "g",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
@ -1,34 +0,0 @@
|
||||
import { Registry } from '@grafana/data';
|
||||
import { Monaco, monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { PositionContext, SuggestionKind } from '../types';
|
||||
import { LinkedToken } from '../utils/LinkedToken';
|
||||
import { toCompletionItem } from '../utils/toCompletionItem';
|
||||
|
||||
import { SuggestionsRegistryItem } from './types';
|
||||
|
||||
// Given standard and custom registered suggestions and kinds of suggestion expected, return a list of completion items
|
||||
export const getStandardSuggestions = async (
|
||||
monaco: Monaco,
|
||||
currentToken: LinkedToken | null,
|
||||
suggestionKinds: SuggestionKind[],
|
||||
positionContext: PositionContext,
|
||||
suggestionsRegistry: Registry<SuggestionsRegistryItem>
|
||||
): Promise<monacoTypes.languages.CompletionItem[]> => {
|
||||
let suggestions: monacoTypes.languages.CompletionItem[] = [];
|
||||
const invalidRangeToken = currentToken?.isWhiteSpace() || currentToken?.isParenthesis();
|
||||
const range =
|
||||
invalidRangeToken || !currentToken?.range
|
||||
? monaco.Range.fromPositions(positionContext.position)
|
||||
: currentToken?.range;
|
||||
|
||||
// iterating over Set to deduplicate
|
||||
for (const suggestion of [...new Set(suggestionKinds)]) {
|
||||
const registeredSuggestions = suggestionsRegistry.getIfExists(suggestion);
|
||||
if (registeredSuggestions) {
|
||||
const su = await registeredSuggestions.suggestions({ ...positionContext, range }, monaco);
|
||||
suggestions = [...suggestions, ...su.map((s) => toCompletionItem(s.label, range, { kind: s.kind, ...s }))];
|
||||
}
|
||||
}
|
||||
return Promise.resolve(suggestions);
|
||||
};
|
@ -1,184 +0,0 @@
|
||||
import {
|
||||
multiLineFullQuery,
|
||||
multiLineFullQueryWithAggregation,
|
||||
multiLineMultipleColumns,
|
||||
singleLineEmptyQuery,
|
||||
singleLineFullQuery,
|
||||
singleLineFullQueryWithAggregation,
|
||||
singleLineMultipleColumns,
|
||||
singleLineTwoQueries,
|
||||
singleLineTwoQueriesWithAggregation,
|
||||
} from '../mocks/testData';
|
||||
import { testStatementPosition } from '../test-utils/statementPosition';
|
||||
import { StatementPosition } from '../types';
|
||||
|
||||
import { initStatementPositionResolvers } from './statementPositionResolversRegistry';
|
||||
|
||||
const templateSrvMock = { replace: jest.fn(), getVariables: () => [], getAdhocFilters: jest.fn() };
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...(jest.requireActual('@grafana/runtime') as unknown as object),
|
||||
getTemplateSrv: () => templateSrvMock,
|
||||
}));
|
||||
|
||||
describe('statementPosition', () => {
|
||||
testStatementPosition(
|
||||
StatementPosition.SelectKeyword,
|
||||
[
|
||||
{ query: singleLineEmptyQuery, position: { line: 1, column: 0 } },
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 0 } },
|
||||
{ query: multiLineFullQuery, position: { line: 1, column: 0 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 103 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterSelectKeyword,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 7 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 109 } },
|
||||
{ query: multiLineFullQuery, position: { line: 1, column: 7 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterSelectArguments,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 16 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 16 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 118 } },
|
||||
{ query: multiLineFullQuery, position: { line: 1, column: 16 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterSelectFuncFirstArgument,
|
||||
[
|
||||
{ query: singleLineFullQueryWithAggregation, position: { line: 1, column: 14 } },
|
||||
{ query: multiLineFullQueryWithAggregation, position: { line: 1, column: 14 } },
|
||||
{ query: singleLineTwoQueriesWithAggregation, position: { line: 1, column: 128 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.FromKeyword,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 17 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 119 } },
|
||||
{ query: multiLineFullQuery, position: { line: 2, column: 0 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterFromKeyword,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 21 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 123 } },
|
||||
{ query: multiLineFullQuery, position: { line: 2, column: 5 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterFrom,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 28 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 130 } },
|
||||
{ query: multiLineFullQuery, position: { line: 2, column: 12 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.WhereKeyword,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 34 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 136 } },
|
||||
{ query: multiLineFullQuery, position: { line: 4, column: 6 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.WhereComparisonOperator,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 43 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 145 } },
|
||||
{ query: multiLineFullQuery, position: { line: 4, column: 15 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.WhereValue,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 44 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 146 } },
|
||||
{ query: multiLineFullQuery, position: { line: 4, column: 16 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterWhereValue,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 53 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 155 } },
|
||||
{ query: multiLineFullQuery, position: { line: 4, column: 25 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterGroupByKeywords,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 63 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 167 } },
|
||||
{ query: multiLineFullQuery, position: { line: 5, column: 11 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterGroupBy,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 71 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 173 } },
|
||||
{ query: multiLineFullQuery, position: { line: 5, column: 18 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterOrderByKeywords,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 80 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 181 } },
|
||||
{ query: multiLineFullQuery, position: { line: 5, column: 26 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterOrderByFunction,
|
||||
[
|
||||
{ query: singleLineMultipleColumns, position: { line: 1, column: 108 } },
|
||||
{ query: multiLineMultipleColumns, position: { line: 5, column: 40 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
|
||||
testStatementPosition(
|
||||
StatementPosition.AfterOrderByDirection,
|
||||
[
|
||||
{ query: singleLineFullQuery, position: { line: 1, column: 92 } },
|
||||
{ query: singleLineTwoQueries, position: { line: 1, column: 196 } },
|
||||
{ query: multiLineFullQuery, position: { line: 5, column: 39 } },
|
||||
],
|
||||
initStatementPositionResolvers
|
||||
);
|
||||
});
|
@ -1,31 +0,0 @@
|
||||
import { Registry } from '@grafana/data';
|
||||
|
||||
import { StatementPosition, TokenType } from '../types';
|
||||
import { LinkedToken } from '../utils/LinkedToken';
|
||||
|
||||
import { StatementPositionResolversRegistryItem } from './types';
|
||||
|
||||
// Given current cursor position in the SQL editor, returns the statement position.
|
||||
export function getStatementPosition(
|
||||
currentToken: LinkedToken | null,
|
||||
statementPositionResolversRegistry: Registry<StatementPositionResolversRegistryItem>
|
||||
): StatementPosition[] {
|
||||
const previousNonWhiteSpace = currentToken?.getPreviousNonWhiteSpaceToken();
|
||||
const previousKeyword = currentToken?.getPreviousKeyword();
|
||||
const previousIsSlash = currentToken?.getPreviousNonWhiteSpaceToken()?.is(TokenType.Operator, '/');
|
||||
const resolvers = statementPositionResolversRegistry.list();
|
||||
const positions = [];
|
||||
|
||||
for (const resolver of resolvers) {
|
||||
if (
|
||||
resolver.resolve(currentToken, previousKeyword ?? null, previousNonWhiteSpace ?? null, Boolean(previousIsSlash))
|
||||
) {
|
||||
positions.push(resolver.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (positions.length === 0) {
|
||||
return [StatementPosition.Unknown];
|
||||
}
|
||||
return positions;
|
||||
}
|
@ -1,880 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { SQLMonarchLanguage } from './types';
|
||||
|
||||
// STD basic SQL
|
||||
export const SELECT = 'select';
|
||||
export const FROM = 'from';
|
||||
export const WHERE = 'where';
|
||||
export const GROUP = 'group';
|
||||
export const ORDER = 'order';
|
||||
export const BY = 'by';
|
||||
export const DESC = 'desc';
|
||||
export const ASC = 'asc';
|
||||
export const LIMIT = 'limit';
|
||||
export const WITH = 'with';
|
||||
export const AS = 'as';
|
||||
export const SCHEMA = 'schema';
|
||||
|
||||
export const STD_STATS = ['AVG', 'COUNT', 'MAX', 'MIN', 'SUM'];
|
||||
|
||||
export const AND = 'AND';
|
||||
export const OR = 'OR';
|
||||
export const LOGICAL_OPERATORS = [AND, OR];
|
||||
|
||||
export const EQUALS = '=';
|
||||
export const NOT_EQUALS = '!=';
|
||||
export const COMPARISON_OPERATORS = [EQUALS, NOT_EQUALS];
|
||||
|
||||
export const STD_OPERATORS = [...COMPARISON_OPERATORS];
|
||||
|
||||
export const conf: monacoTypes.languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
lineComment: '--',
|
||||
blockComment: ['/*', '*/'],
|
||||
},
|
||||
brackets: [
|
||||
['{', '}'],
|
||||
['[', ']'],
|
||||
['(', ')'],
|
||||
],
|
||||
autoClosingPairs: [
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '"', close: '"' },
|
||||
{ open: "'", close: "'" },
|
||||
],
|
||||
surroundingPairs: [
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '"', close: '"' },
|
||||
{ open: "'", close: "'" },
|
||||
],
|
||||
};
|
||||
|
||||
// based on https://github.com/microsoft/monaco-editor/blob/main/src/basic-languages/sql/sql.ts
|
||||
export const language: SQLMonarchLanguage = {
|
||||
defaultToken: '',
|
||||
tokenPostfix: '.sql',
|
||||
ignoreCase: true,
|
||||
|
||||
brackets: [
|
||||
{ open: '[', close: ']', token: 'delimiter.square' },
|
||||
{ open: '(', close: ')', token: 'delimiter.parenthesis' },
|
||||
],
|
||||
|
||||
keywords: [
|
||||
'ABORT',
|
||||
'ABSOLUTE',
|
||||
'ACTION',
|
||||
'ADA',
|
||||
'ADD',
|
||||
'AFTER',
|
||||
'ALL',
|
||||
'ALLOCATE',
|
||||
'ALTER',
|
||||
'ALWAYS',
|
||||
'ANALYZE',
|
||||
'AND',
|
||||
'ANY',
|
||||
'ARE',
|
||||
'AS',
|
||||
'ASC',
|
||||
'ASSERTION',
|
||||
'AT',
|
||||
'ATTACH',
|
||||
'AUTHORIZATION',
|
||||
'AUTOINCREMENT',
|
||||
'AVG',
|
||||
'BACKUP',
|
||||
'BEFORE',
|
||||
'BEGIN',
|
||||
'BETWEEN',
|
||||
'BIT',
|
||||
'BIT_LENGTH',
|
||||
'BOTH',
|
||||
'BREAK',
|
||||
'BROWSE',
|
||||
'BULK',
|
||||
'BY',
|
||||
'CASCADE',
|
||||
'CASCADED',
|
||||
'CASE',
|
||||
'CAST',
|
||||
'CATALOG',
|
||||
'CHAR',
|
||||
'CHARACTER',
|
||||
'CHARACTER_LENGTH',
|
||||
'CHAR_LENGTH',
|
||||
'CHECK',
|
||||
'CHECKPOINT',
|
||||
'CLOSE',
|
||||
'CLUSTERED',
|
||||
'COALESCE',
|
||||
'COLLATE',
|
||||
'COLLATION',
|
||||
'COLUMN',
|
||||
'COMMIT',
|
||||
'COMPUTE',
|
||||
'CONFLICT',
|
||||
'CONNECT',
|
||||
'CONNECTION',
|
||||
'CONSTRAINT',
|
||||
'CONSTRAINTS',
|
||||
'CONTAINS',
|
||||
'CONTAINSTABLE',
|
||||
'CONTINUE',
|
||||
'CONVERT',
|
||||
'CORRESPONDING',
|
||||
'COUNT',
|
||||
'CREATE',
|
||||
'CROSS',
|
||||
'CURRENT',
|
||||
'CURRENT_DATE',
|
||||
'CURRENT_TIME',
|
||||
'CURRENT_TIMESTAMP',
|
||||
'CURRENT_USER',
|
||||
'CURSOR',
|
||||
'DATABASE',
|
||||
'DATE',
|
||||
'DAY',
|
||||
'DBCC',
|
||||
'DEALLOCATE',
|
||||
'DEC',
|
||||
'DECIMAL',
|
||||
'DECLARE',
|
||||
'DEFAULT',
|
||||
'DEFERRABLE',
|
||||
'DEFERRED',
|
||||
'DELETE',
|
||||
'DENY',
|
||||
'DESC',
|
||||
'DESCRIBE',
|
||||
'DESCRIPTOR',
|
||||
'DETACH',
|
||||
'DIAGNOSTICS',
|
||||
'DISCONNECT',
|
||||
'DISK',
|
||||
'DISTINCT',
|
||||
'DISTRIBUTED',
|
||||
'DO',
|
||||
'DOMAIN',
|
||||
'DOUBLE',
|
||||
'DROP',
|
||||
'DUMP',
|
||||
'EACH',
|
||||
'ELSE',
|
||||
'END',
|
||||
'END-EXEC',
|
||||
'ERRLVL',
|
||||
'ESCAPE',
|
||||
'EXCEPT',
|
||||
'EXCEPTION',
|
||||
'EXCLUDE',
|
||||
'EXCLUSIVE',
|
||||
'EXEC',
|
||||
'EXECUTE',
|
||||
'EXISTS',
|
||||
'EXIT',
|
||||
'EXPLAIN',
|
||||
'EXTERNAL',
|
||||
'EXTRACT',
|
||||
'FAIL',
|
||||
'FALSE',
|
||||
'FETCH',
|
||||
'FILE',
|
||||
'FILLFACTOR',
|
||||
'FILTER',
|
||||
'FIRST',
|
||||
'FLOAT',
|
||||
'FOLLOWING',
|
||||
'FOR',
|
||||
'FOREIGN',
|
||||
'FORTRAN',
|
||||
'FOUND',
|
||||
'FREETEXT',
|
||||
'FREETEXTTABLE',
|
||||
'FROM',
|
||||
'FULL',
|
||||
'FUNCTION',
|
||||
'GENERATED',
|
||||
'GET',
|
||||
'GLOB',
|
||||
'GLOBAL',
|
||||
'GO',
|
||||
'GOTO',
|
||||
'GRANT',
|
||||
'GROUP',
|
||||
'GROUPS',
|
||||
'HAVING',
|
||||
'HOLDLOCK',
|
||||
'HOUR',
|
||||
'IDENTITY',
|
||||
'IDENTITYCOL',
|
||||
'IDENTITY_INSERT',
|
||||
'IF',
|
||||
'IGNORE',
|
||||
'IMMEDIATE',
|
||||
'IN',
|
||||
'INCLUDE',
|
||||
'INDEX',
|
||||
'INDEXED',
|
||||
'INDICATOR',
|
||||
'INITIALLY',
|
||||
'INNER',
|
||||
'INPUT',
|
||||
'INSENSITIVE',
|
||||
'INSERT',
|
||||
'INSTEAD',
|
||||
'INT',
|
||||
'INTEGER',
|
||||
'INTERSECT',
|
||||
'INTERVAL',
|
||||
'INTO',
|
||||
'IS',
|
||||
'ISNULL',
|
||||
'ISOLATION',
|
||||
'JOIN',
|
||||
'KEY',
|
||||
'KILL',
|
||||
'LANGUAGE',
|
||||
'LAST',
|
||||
'LEADING',
|
||||
'LEFT',
|
||||
'LEVEL',
|
||||
'LIKE',
|
||||
'LIMIT',
|
||||
'LINENO',
|
||||
'LOAD',
|
||||
'LOCAL',
|
||||
'LOWER',
|
||||
'MATCH',
|
||||
'MATERIALIZED',
|
||||
'MAX',
|
||||
'MERGE',
|
||||
'MIN',
|
||||
'MINUTE',
|
||||
'MODULE',
|
||||
'MONTH',
|
||||
'NAMES',
|
||||
'NATIONAL',
|
||||
'NATURAL',
|
||||
'NCHAR',
|
||||
'NEXT',
|
||||
'NO',
|
||||
'NOCHECK',
|
||||
'NONCLUSTERED',
|
||||
'NONE',
|
||||
'NOT',
|
||||
'NOTHING',
|
||||
'NOTNULL',
|
||||
'NULL',
|
||||
'NULLIF',
|
||||
'NULLS',
|
||||
'NUMERIC',
|
||||
'OCTET_LENGTH',
|
||||
'OF',
|
||||
'OFF',
|
||||
'OFFSET',
|
||||
'OFFSETS',
|
||||
'ON',
|
||||
'ONLY',
|
||||
'OPEN',
|
||||
'OPENDATASOURCE',
|
||||
'OPENQUERY',
|
||||
'OPENROWSET',
|
||||
'OPENXML',
|
||||
'OPTION',
|
||||
'OR',
|
||||
'ORDER',
|
||||
'OTHERS',
|
||||
'OUTER',
|
||||
'OUTPUT',
|
||||
'OVER',
|
||||
'OVERLAPS',
|
||||
'PAD',
|
||||
'PARTIAL',
|
||||
'PARTITION',
|
||||
'PASCAL',
|
||||
'PERCENT',
|
||||
'PIVOT',
|
||||
'PLAN',
|
||||
'POSITION',
|
||||
'PRAGMA',
|
||||
'PRECEDING',
|
||||
'PRECISION',
|
||||
'PREPARE',
|
||||
'PRESERVE',
|
||||
'PRIMARY',
|
||||
'PRINT',
|
||||
'PRIOR',
|
||||
'PRIVILEGES',
|
||||
'PROC',
|
||||
'PROCEDURE',
|
||||
'PUBLIC',
|
||||
'QUERY',
|
||||
'RAISE',
|
||||
'RAISERROR',
|
||||
'RANGE',
|
||||
'READ',
|
||||
'READTEXT',
|
||||
'REAL',
|
||||
'RECONFIGURE',
|
||||
'RECURSIVE',
|
||||
'REFERENCES',
|
||||
'REGEXP',
|
||||
'REINDEX',
|
||||
'RELATIVE',
|
||||
'RELEASE',
|
||||
'RENAME',
|
||||
'REPLACE',
|
||||
'REPLICATION',
|
||||
'RESTORE',
|
||||
'RESTRICT',
|
||||
'RETURN',
|
||||
'RETURNING',
|
||||
'REVERT',
|
||||
'REVOKE',
|
||||
'RIGHT',
|
||||
'ROLLBACK',
|
||||
'ROW',
|
||||
'ROWCOUNT',
|
||||
'ROWGUIDCOL',
|
||||
'ROWS',
|
||||
'RULE',
|
||||
'SAVE',
|
||||
'SAVEPOINT',
|
||||
'SCHEMA',
|
||||
'SCROLL',
|
||||
'SECOND',
|
||||
'SECTION',
|
||||
'SECURITYAUDIT',
|
||||
'SELECT',
|
||||
'SEMANTICKEYPHRASETABLE',
|
||||
'SEMANTICSIMILARITYDETAILSTABLE',
|
||||
'SEMANTICSIMILARITYTABLE',
|
||||
'SESSION',
|
||||
'SESSION_USER',
|
||||
'SET',
|
||||
'SETUSER',
|
||||
'SHUTDOWN',
|
||||
'SIZE',
|
||||
'SMALLINT',
|
||||
'SOME',
|
||||
'SPACE',
|
||||
'SQL',
|
||||
'SQLCA',
|
||||
'SQLCODE',
|
||||
'SQLERROR',
|
||||
'SQLSTATE',
|
||||
'SQLWARNING',
|
||||
'STATISTICS',
|
||||
'SUBSTRING',
|
||||
'SUM',
|
||||
'SYSTEM_USER',
|
||||
'TABLE',
|
||||
'TABLESAMPLE',
|
||||
'TEMP',
|
||||
'TEMPORARY',
|
||||
'TEXTSIZE',
|
||||
'THEN',
|
||||
'TIES',
|
||||
'TIME',
|
||||
'TIMESTAMP',
|
||||
'TIMEZONE_HOUR',
|
||||
'TIMEZONE_MINUTE',
|
||||
'TO',
|
||||
'TOP',
|
||||
'TRAILING',
|
||||
'TRAN',
|
||||
'TRANSACTION',
|
||||
'TRANSLATE',
|
||||
'TRANSLATION',
|
||||
'TRIGGER',
|
||||
'TRIM',
|
||||
'TRUE',
|
||||
'TRUNCATE',
|
||||
'TRY_CONVERT',
|
||||
'TSEQUAL',
|
||||
'UNBOUNDED',
|
||||
'UNION',
|
||||
'UNIQUE',
|
||||
'UNKNOWN',
|
||||
'UNPIVOT',
|
||||
'UPDATE',
|
||||
'UPDATETEXT',
|
||||
'UPPER',
|
||||
'USAGE',
|
||||
'USE',
|
||||
'USER',
|
||||
'USING',
|
||||
'VACUUM',
|
||||
'VALUE',
|
||||
'VALUES',
|
||||
'VARCHAR',
|
||||
'VARYING',
|
||||
'VIEW',
|
||||
'VIRTUAL',
|
||||
'WAITFOR',
|
||||
'WHEN',
|
||||
'WHENEVER',
|
||||
'WHERE',
|
||||
'WHILE',
|
||||
'WINDOW',
|
||||
'WITH',
|
||||
'WITHIN GROUP',
|
||||
'WITHOUT',
|
||||
'WORK',
|
||||
'WRITE',
|
||||
'WRITETEXT',
|
||||
'YEAR',
|
||||
'ZONE',
|
||||
],
|
||||
operators: [
|
||||
// Set
|
||||
'EXCEPT',
|
||||
'INTERSECT',
|
||||
'UNION',
|
||||
// Join
|
||||
'APPLY',
|
||||
'CROSS',
|
||||
'FULL',
|
||||
'INNER',
|
||||
'JOIN',
|
||||
'LEFT',
|
||||
'OUTER',
|
||||
'RIGHT',
|
||||
// Predicates
|
||||
'CONTAINS',
|
||||
'FREETEXT',
|
||||
'IS',
|
||||
'NULL',
|
||||
// Pivoting
|
||||
'PIVOT',
|
||||
'UNPIVOT',
|
||||
// Merging
|
||||
'MATCHED',
|
||||
],
|
||||
logicalOperators: ['ALL', 'AND', 'ANY', 'BETWEEN', 'EXISTS', 'IN', 'LIKE', 'NOT', 'OR', 'SOME'],
|
||||
comparisonOperators: ['<>', '>', '<', '>=', '<=', '=', '!=', '&', '~', '^', '%'],
|
||||
|
||||
builtinFunctions: [
|
||||
// Aggregate
|
||||
'AVG',
|
||||
'CHECKSUM_AGG',
|
||||
'COUNT',
|
||||
'COUNT_BIG',
|
||||
'GROUPING',
|
||||
'GROUPING_ID',
|
||||
'MAX',
|
||||
'MIN',
|
||||
'SUM',
|
||||
'STDEV',
|
||||
'STDEVP',
|
||||
'VAR',
|
||||
'VARP',
|
||||
// Analytic
|
||||
'CUME_DIST',
|
||||
'FIRST_VALUE',
|
||||
'LAG',
|
||||
'LAST_VALUE',
|
||||
'LEAD',
|
||||
'PERCENTILE_CONT',
|
||||
'PERCENTILE_DISC',
|
||||
'PERCENT_RANK',
|
||||
// Collation
|
||||
'COLLATE',
|
||||
'COLLATIONPROPERTY',
|
||||
'TERTIARY_WEIGHTS',
|
||||
// Azure
|
||||
'FEDERATION_FILTERING_VALUE',
|
||||
// Conversion
|
||||
'CAST',
|
||||
'CONVERT',
|
||||
'PARSE',
|
||||
'TRY_CAST',
|
||||
'TRY_CONVERT',
|
||||
'TRY_PARSE',
|
||||
// Cryptographic
|
||||
'ASYMKEY_ID',
|
||||
'ASYMKEYPROPERTY',
|
||||
'CERTPROPERTY',
|
||||
'CERT_ID',
|
||||
'CRYPT_GEN_RANDOM',
|
||||
'DECRYPTBYASYMKEY',
|
||||
'DECRYPTBYCERT',
|
||||
'DECRYPTBYKEY',
|
||||
'DECRYPTBYKEYAUTOASYMKEY',
|
||||
'DECRYPTBYKEYAUTOCERT',
|
||||
'DECRYPTBYPASSPHRASE',
|
||||
'ENCRYPTBYASYMKEY',
|
||||
'ENCRYPTBYCERT',
|
||||
'ENCRYPTBYKEY',
|
||||
'ENCRYPTBYPASSPHRASE',
|
||||
'HASHBYTES',
|
||||
'IS_OBJECTSIGNED',
|
||||
'KEY_GUID',
|
||||
'KEY_ID',
|
||||
'KEY_NAME',
|
||||
'SIGNBYASYMKEY',
|
||||
'SIGNBYCERT',
|
||||
'SYMKEYPROPERTY',
|
||||
'VERIFYSIGNEDBYCERT',
|
||||
'VERIFYSIGNEDBYASYMKEY',
|
||||
// Cursor
|
||||
'CURSOR_STATUS',
|
||||
// Datatype
|
||||
'DATALENGTH',
|
||||
'IDENT_CURRENT',
|
||||
'IDENT_INCR',
|
||||
'IDENT_SEED',
|
||||
'IDENTITY',
|
||||
'SQL_VARIANT_PROPERTY',
|
||||
// Datetime
|
||||
'CURRENT_TIMESTAMP',
|
||||
'DATEADD',
|
||||
'DATEDIFF',
|
||||
'DATEFROMPARTS',
|
||||
'DATENAME',
|
||||
'DATEPART',
|
||||
'DATETIME2FROMPARTS',
|
||||
'DATETIMEFROMPARTS',
|
||||
'DATETIMEOFFSETFROMPARTS',
|
||||
'DAY',
|
||||
'EOMONTH',
|
||||
'GETDATE',
|
||||
'GETUTCDATE',
|
||||
'ISDATE',
|
||||
'MONTH',
|
||||
'SMALLDATETIMEFROMPARTS',
|
||||
'SWITCHOFFSET',
|
||||
'SYSDATETIME',
|
||||
'SYSDATETIMEOFFSET',
|
||||
'SYSUTCDATETIME',
|
||||
'TIMEFROMPARTS',
|
||||
'TODATETIMEOFFSET',
|
||||
'YEAR',
|
||||
// Logical
|
||||
'CHOOSE',
|
||||
'COALESCE',
|
||||
'IIF',
|
||||
'NULLIF',
|
||||
// Mathematical
|
||||
'ABS',
|
||||
'ACOS',
|
||||
'ASIN',
|
||||
'ATAN',
|
||||
'ATN2',
|
||||
'CEILING',
|
||||
'COS',
|
||||
'COT',
|
||||
'DEGREES',
|
||||
'EXP',
|
||||
'FLOOR',
|
||||
'LOG',
|
||||
'LOG10',
|
||||
'PI',
|
||||
'POWER',
|
||||
'RADIANS',
|
||||
'RAND',
|
||||
'ROUND',
|
||||
'SIGN',
|
||||
'SIN',
|
||||
'SQRT',
|
||||
'SQUARE',
|
||||
'TAN',
|
||||
// Metadata
|
||||
'APP_NAME',
|
||||
'APPLOCK_MODE',
|
||||
'APPLOCK_TEST',
|
||||
'ASSEMBLYPROPERTY',
|
||||
'COL_LENGTH',
|
||||
'COL_NAME',
|
||||
'COLUMNPROPERTY',
|
||||
'DATABASE_PRINCIPAL_ID',
|
||||
'DATABASEPROPERTYEX',
|
||||
'DB_ID',
|
||||
'DB_NAME',
|
||||
'FILE_ID',
|
||||
'FILE_IDEX',
|
||||
'FILE_NAME',
|
||||
'FILEGROUP_ID',
|
||||
'FILEGROUP_NAME',
|
||||
'FILEGROUPPROPERTY',
|
||||
'FILEPROPERTY',
|
||||
'FULLTEXTCATALOGPROPERTY',
|
||||
'FULLTEXTSERVICEPROPERTY',
|
||||
'INDEX_COL',
|
||||
'INDEXKEY_PROPERTY',
|
||||
'INDEXPROPERTY',
|
||||
'OBJECT_DEFINITION',
|
||||
'OBJECT_ID',
|
||||
'OBJECT_NAME',
|
||||
'OBJECT_SCHEMA_NAME',
|
||||
'OBJECTPROPERTY',
|
||||
'OBJECTPROPERTYEX',
|
||||
'ORIGINAL_DB_NAME',
|
||||
'PARSENAME',
|
||||
'SCHEMA_ID',
|
||||
'SCHEMA_NAME',
|
||||
'SCOPE_IDENTITY',
|
||||
'SERVERPROPERTY',
|
||||
'STATS_DATE',
|
||||
'TYPE_ID',
|
||||
'TYPE_NAME',
|
||||
'TYPEPROPERTY',
|
||||
// Ranking
|
||||
'DENSE_RANK',
|
||||
'NTILE',
|
||||
'RANK',
|
||||
'ROW_NUMBER',
|
||||
// Replication
|
||||
'PUBLISHINGSERVERNAME',
|
||||
// Rowset
|
||||
'OPENDATASOURCE',
|
||||
'OPENQUERY',
|
||||
'OPENROWSET',
|
||||
'OPENXML',
|
||||
// Security
|
||||
'CERTENCODED',
|
||||
'CERTPRIVATEKEY',
|
||||
'CURRENT_USER',
|
||||
'HAS_DBACCESS',
|
||||
'HAS_PERMS_BY_NAME',
|
||||
'IS_MEMBER',
|
||||
'IS_ROLEMEMBER',
|
||||
'IS_SRVROLEMEMBER',
|
||||
'LOGINPROPERTY',
|
||||
'ORIGINAL_LOGIN',
|
||||
'PERMISSIONS',
|
||||
'PWDENCRYPT',
|
||||
'PWDCOMPARE',
|
||||
'SESSION_USER',
|
||||
'SESSIONPROPERTY',
|
||||
'SUSER_ID',
|
||||
'SUSER_NAME',
|
||||
'SUSER_SID',
|
||||
'SUSER_SNAME',
|
||||
'SYSTEM_USER',
|
||||
'USER',
|
||||
'USER_ID',
|
||||
'USER_NAME',
|
||||
// String
|
||||
'ASCII',
|
||||
'CHAR',
|
||||
'CHARINDEX',
|
||||
'CONCAT',
|
||||
'DIFFERENCE',
|
||||
'FORMAT',
|
||||
'LEFT',
|
||||
'LEN',
|
||||
'LOWER',
|
||||
'LTRIM',
|
||||
'NCHAR',
|
||||
'PATINDEX',
|
||||
'QUOTENAME',
|
||||
'REPLACE',
|
||||
'REPLICATE',
|
||||
'REVERSE',
|
||||
'RIGHT',
|
||||
'RTRIM',
|
||||
'SOUNDEX',
|
||||
'SPACE',
|
||||
'STR',
|
||||
'STUFF',
|
||||
'SUBSTRING',
|
||||
'UNICODE',
|
||||
'UPPER',
|
||||
// System
|
||||
'BINARY_CHECKSUM',
|
||||
'CHECKSUM',
|
||||
'CONNECTIONPROPERTY',
|
||||
'CONTEXT_INFO',
|
||||
'CURRENT_REQUEST_ID',
|
||||
'ERROR_LINE',
|
||||
'ERROR_NUMBER',
|
||||
'ERROR_MESSAGE',
|
||||
'ERROR_PROCEDURE',
|
||||
'ERROR_SEVERITY',
|
||||
'ERROR_STATE',
|
||||
'FORMATMESSAGE',
|
||||
'GETANSINULL',
|
||||
'GET_FILESTREAM_TRANSACTION_CONTEXT',
|
||||
'HOST_ID',
|
||||
'HOST_NAME',
|
||||
'ISNULL',
|
||||
'ISNUMERIC',
|
||||
'MIN_ACTIVE_ROWVERSION',
|
||||
'NEWID',
|
||||
'NEWSEQUENTIALID',
|
||||
'ROWCOUNT_BIG',
|
||||
'XACT_STATE',
|
||||
// TextImage
|
||||
'TEXTPTR',
|
||||
'TEXTVALID',
|
||||
// Trigger
|
||||
'COLUMNS_UPDATED',
|
||||
'EVENTDATA',
|
||||
'TRIGGER_NESTLEVEL',
|
||||
'UPDATE',
|
||||
// ChangeTracking
|
||||
'CHANGETABLE',
|
||||
'CHANGE_TRACKING_CONTEXT',
|
||||
'CHANGE_TRACKING_CURRENT_VERSION',
|
||||
'CHANGE_TRACKING_IS_COLUMN_IN_MASK',
|
||||
'CHANGE_TRACKING_MIN_VALID_VERSION',
|
||||
// FullTextSearch
|
||||
'CONTAINSTABLE',
|
||||
'FREETEXTTABLE',
|
||||
// SemanticTextSearch
|
||||
'SEMANTICKEYPHRASETABLE',
|
||||
'SEMANTICSIMILARITYDETAILSTABLE',
|
||||
'SEMANTICSIMILARITYTABLE',
|
||||
// FileStream
|
||||
'FILETABLEROOTPATH',
|
||||
'GETFILENAMESPACEPATH',
|
||||
'GETPATHLOCATOR',
|
||||
'PATHNAME',
|
||||
// ServiceBroker
|
||||
'GET_TRANSMISSION_STATUS',
|
||||
],
|
||||
builtinVariables: [
|
||||
// Configuration
|
||||
'@@DATEFIRST',
|
||||
'@@DBTS',
|
||||
'@@LANGID',
|
||||
'@@LANGUAGE',
|
||||
'@@LOCK_TIMEOUT',
|
||||
'@@MAX_CONNECTIONS',
|
||||
'@@MAX_PRECISION',
|
||||
'@@NESTLEVEL',
|
||||
'@@OPTIONS',
|
||||
'@@REMSERVER',
|
||||
'@@SERVERNAME',
|
||||
'@@SERVICENAME',
|
||||
'@@SPID',
|
||||
'@@TEXTSIZE',
|
||||
'@@VERSION',
|
||||
// Cursor
|
||||
'@@CURSOR_ROWS',
|
||||
'@@FETCH_STATUS',
|
||||
// Datetime
|
||||
'@@DATEFIRST',
|
||||
// Metadata
|
||||
'@@PROCID',
|
||||
// System
|
||||
'@@ERROR',
|
||||
'@@IDENTITY',
|
||||
'@@ROWCOUNT',
|
||||
'@@TRANCOUNT',
|
||||
// Stats
|
||||
'@@CONNECTIONS',
|
||||
'@@CPU_BUSY',
|
||||
'@@IDLE',
|
||||
'@@IO_BUSY',
|
||||
'@@PACKET_ERRORS',
|
||||
'@@PACK_RECEIVED',
|
||||
'@@PACK_SENT',
|
||||
'@@TIMETICKS',
|
||||
'@@TOTAL_ERRORS',
|
||||
'@@TOTAL_READ',
|
||||
'@@TOTAL_WRITE',
|
||||
],
|
||||
pseudoColumns: ['$ACTION', '$IDENTITY', '$ROWGUID', '$PARTITION'],
|
||||
tokenizer: {
|
||||
root: [
|
||||
{ include: '@templateVariables' },
|
||||
{ include: '@macros' },
|
||||
{ include: '@comments' },
|
||||
{ include: '@whitespace' },
|
||||
{ include: '@pseudoColumns' },
|
||||
{ include: '@numbers' },
|
||||
{ include: '@strings' },
|
||||
{ include: '@complexIdentifiers' },
|
||||
{ include: '@scopes' },
|
||||
[/[;,.]/, 'delimiter'],
|
||||
[/[()]/, '@brackets'],
|
||||
[
|
||||
/[\w@#$|<|>|=|!|%|&|+|\|-|*|/|~|^]+/,
|
||||
{
|
||||
cases: {
|
||||
'@operators': 'operator',
|
||||
'@comparisonOperators': 'operator',
|
||||
'@logicalOperators': 'operator',
|
||||
'@builtinVariables': 'predefined',
|
||||
'@builtinFunctions': 'predefined',
|
||||
'@keywords': 'keyword',
|
||||
'@default': 'identifier',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
templateVariables: [[/\$[a-zA-Z0-9]+/, 'variable']],
|
||||
macros: [[/\$__[a-zA-Z0-9-_]+/, 'type']],
|
||||
whitespace: [[/\s+/, 'white']],
|
||||
comments: [
|
||||
[/--+.*/, 'comment'],
|
||||
[/\/\*/, { token: 'comment.quote', next: '@comment' }],
|
||||
],
|
||||
comment: [
|
||||
[/[^*/]+/, 'comment'],
|
||||
// Not supporting nested comments, as nested comments seem to not be standard?
|
||||
// i.e. http://stackoverflow.com/questions/728172/are-there-multiline-comment-delimiters-in-sql-that-are-vendor-agnostic
|
||||
// [/\/\*/, { token: 'comment.quote', next: '@push' }], // nested comment not allowed :-(
|
||||
[/\*\//, { token: 'comment.quote', next: '@pop' }],
|
||||
[/./, 'comment'],
|
||||
],
|
||||
pseudoColumns: [
|
||||
[
|
||||
/[$][A-Za-z_][\w@#$]*/,
|
||||
{
|
||||
cases: {
|
||||
'@pseudoColumns': 'predefined',
|
||||
'@default': 'identifier',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
numbers: [
|
||||
[/0[xX][0-9a-fA-F]*/, 'number'],
|
||||
[/[$][+-]*\d*(\.\d*)?/, 'number'],
|
||||
[/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, 'number'],
|
||||
],
|
||||
strings: [
|
||||
[/N'/, { token: 'string', next: '@string' }],
|
||||
[/'/, { token: 'string', next: '@string' }],
|
||||
],
|
||||
string: [
|
||||
[/[^']+/, 'string'],
|
||||
[/''/, 'string'],
|
||||
[/'/, { token: 'string', next: '@pop' }],
|
||||
],
|
||||
complexIdentifiers: [
|
||||
[/\[/, { token: 'identifier.quote', next: '@bracketedIdentifier' }],
|
||||
[/"/, { token: 'identifier.quote', next: '@quotedIdentifier' }],
|
||||
],
|
||||
bracketedIdentifier: [
|
||||
[/[^\]]+/, 'identifier'],
|
||||
[/]]/, 'identifier'],
|
||||
[/]/, { token: 'identifier.quote', next: '@pop' }],
|
||||
],
|
||||
quotedIdentifier: [
|
||||
[/[^"]+/, 'identifier'],
|
||||
[/""/, 'identifier'],
|
||||
[/"/, { token: 'identifier.quote', next: '@pop' }],
|
||||
],
|
||||
scopes: [
|
||||
[/BEGIN\s+(DISTRIBUTED\s+)?TRAN(SACTION)?\b/i, 'keyword'],
|
||||
[/BEGIN\s+TRY\b/i, { token: 'keyword.try' }],
|
||||
[/END\s+TRY\b/i, { token: 'keyword.try' }],
|
||||
[/BEGIN\s+CATCH\b/i, { token: 'keyword.catch' }],
|
||||
[/END\s+CATCH\b/i, { token: 'keyword.catch' }],
|
||||
[/(BEGIN|CASE)\b/i, { token: 'keyword.block' }],
|
||||
[/END\b/i, { token: 'keyword.block' }],
|
||||
[/WHEN\b/i, { token: 'keyword.choice' }],
|
||||
[/THEN\b/i, { token: 'keyword.choice' }],
|
||||
],
|
||||
},
|
||||
};
|
@ -1,67 +0,0 @@
|
||||
import { MacrosRegistryItem } from './types';
|
||||
|
||||
const COLUMN = 'column',
|
||||
RELATIVE_TIME_STRING = "'5m'";
|
||||
|
||||
export enum MacroType {
|
||||
Value,
|
||||
Filter,
|
||||
Group,
|
||||
Column,
|
||||
Table,
|
||||
}
|
||||
|
||||
export const MACROS: MacrosRegistryItem[] = [
|
||||
{
|
||||
id: '$__timeFilter(dateColumn)',
|
||||
name: '$__timeFilter(dateColumn)',
|
||||
text: '$__timeFilter',
|
||||
args: [COLUMN],
|
||||
type: MacroType.Filter,
|
||||
description:
|
||||
'Will be replaced by a time range filter using the specified column name. For example, dateColumn BETWEEN FROM_UNIXTIME(1494410783) AND FROM_UNIXTIME(1494410983)',
|
||||
},
|
||||
{
|
||||
id: '$__timeFrom()',
|
||||
name: '$__timeFrom()',
|
||||
text: '$__timeFrom',
|
||||
args: [],
|
||||
type: MacroType.Filter,
|
||||
description:
|
||||
'Will be replaced by the start of the currently active time selection. For example, FROM_UNIXTIME(1494410783)',
|
||||
},
|
||||
{
|
||||
id: '$__timeTo()',
|
||||
name: '$__timeTo()',
|
||||
text: '$__timeTo',
|
||||
args: [],
|
||||
type: MacroType.Filter,
|
||||
description:
|
||||
'Will be replaced by the end of the currently active time selection. For example, FROM_UNIXTIME(1494410983)',
|
||||
},
|
||||
{
|
||||
id: "$__timeGroup(dateColumn, '5m')",
|
||||
name: "$__timeGroup(dateColumn, '5m')",
|
||||
text: '$__timeGroup',
|
||||
args: [COLUMN, RELATIVE_TIME_STRING],
|
||||
type: MacroType.Value,
|
||||
description:
|
||||
'Will be replaced by an expression usable in GROUP BY clause. For example, *cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) as signed)*300 as signed),*',
|
||||
},
|
||||
{
|
||||
id: '$__table',
|
||||
name: '$__table',
|
||||
text: '$__table',
|
||||
args: [],
|
||||
type: MacroType.Table,
|
||||
description: 'Will be replaced by the query table.',
|
||||
},
|
||||
{
|
||||
id: '$__column',
|
||||
name: '$__column',
|
||||
text: '$__column',
|
||||
args: [],
|
||||
type: MacroType.Column,
|
||||
description: 'Will be replaced by the query column.',
|
||||
},
|
||||
];
|
@ -1,425 +0,0 @@
|
||||
import { Registry } from '@grafana/data';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import {
|
||||
CompletionItemInsertTextRule,
|
||||
CompletionItemKind,
|
||||
CompletionItemPriority,
|
||||
MacroType,
|
||||
OperatorType,
|
||||
SuggestionKind,
|
||||
} from '../types';
|
||||
import { TRIGGER_SUGGEST } from '../utils/commands';
|
||||
|
||||
import { ASC, DESC, LOGICAL_OPERATORS, STD_OPERATORS, STD_STATS } from './language';
|
||||
import { MACROS } from './macros';
|
||||
import { FunctionsRegistryItem, MacrosRegistryItem, OperatorsRegistryItem, SuggestionsRegistryItem } from './types';
|
||||
|
||||
/**
|
||||
* This registry glues particular SuggestionKind with an async function that provides completion items for it.
|
||||
* To add a new suggestion kind, SQLEditor should be configured with a provider that implements customSuggestionKinds.
|
||||
*/
|
||||
|
||||
export const initStandardSuggestions =
|
||||
(
|
||||
functions: Registry<FunctionsRegistryItem>,
|
||||
operators: Registry<OperatorsRegistryItem>,
|
||||
macros: Registry<MacrosRegistryItem>
|
||||
) =>
|
||||
(): SuggestionsRegistryItem[] =>
|
||||
[
|
||||
{
|
||||
id: SuggestionKind.SelectKeyword,
|
||||
name: SuggestionKind.SelectKeyword,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: `SELECT <column>`,
|
||||
insertText: `SELECT $0`,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
kind: CompletionItemKind.Snippet,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
},
|
||||
{
|
||||
label: `SELECT <column> FROM <table>`,
|
||||
insertText: `SELECT $2 FROM $1`,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
kind: CompletionItemKind.Snippet,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.TemplateVariables,
|
||||
name: SuggestionKind.TemplateVariables,
|
||||
suggestions: (_, m) => {
|
||||
const templateSrv = getTemplateSrv();
|
||||
if (!templateSrv) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
return Promise.resolve(
|
||||
templateSrv.getVariables().map((variable) => {
|
||||
const label = `\$${variable.name}`;
|
||||
const val = templateSrv.replace(label);
|
||||
return {
|
||||
label,
|
||||
detail: `(Template Variable) ${val}`,
|
||||
kind: CompletionItemKind.Snippet,
|
||||
documentation: `(Template Variable) ${val}`,
|
||||
insertText: `\\$${variable.name} `,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
command: TRIGGER_SUGGEST,
|
||||
};
|
||||
})
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.SelectMacro,
|
||||
name: SuggestionKind.SelectMacro,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
...macros
|
||||
.list()
|
||||
.filter((m) => m.type === MacroType.Value || m.type === MacroType.Column)
|
||||
.map(createMacroSuggestionItem),
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.TableMacro,
|
||||
name: SuggestionKind.TableMacro,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
...macros
|
||||
.list()
|
||||
.filter((m) => m.type === MacroType.Table)
|
||||
.map(createMacroSuggestionItem),
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.GroupMacro,
|
||||
name: SuggestionKind.GroupMacro,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
...macros
|
||||
.list()
|
||||
.filter((m) => m.type === MacroType.Group)
|
||||
.map(createMacroSuggestionItem),
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.FilterMacro,
|
||||
name: SuggestionKind.FilterMacro,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
...macros
|
||||
.list()
|
||||
.filter((m) => m.type === MacroType.Filter)
|
||||
.map(createMacroSuggestionItem),
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.WithKeyword,
|
||||
name: SuggestionKind.WithKeyword,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: `WITH <alias> AS ( ... )`,
|
||||
insertText: `WITH $1 AS ( $2 )`,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
kind: CompletionItemKind.Snippet,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.FunctionsWithArguments,
|
||||
name: SuggestionKind.FunctionsWithArguments,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
...functions.list().map((f) => ({
|
||||
label: f.name,
|
||||
insertText: `${f.name}($0)`,
|
||||
documentation: f.description,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
kind: CompletionItemKind.Function,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumHigh,
|
||||
})),
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.FunctionsWithoutArguments,
|
||||
name: SuggestionKind.FunctionsWithoutArguments,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
...functions.list().map((f) => ({
|
||||
label: f.name,
|
||||
insertText: `${f.name}()`,
|
||||
documentation: f.description,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
kind: CompletionItemKind.Function,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumHigh,
|
||||
})),
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.FromKeyword,
|
||||
name: SuggestionKind.FromKeyword,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: 'FROM',
|
||||
insertText: `FROM $0`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.Tables,
|
||||
name: SuggestionKind.Tables,
|
||||
suggestions: (_, m) => Promise.resolve([]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.Columns,
|
||||
name: SuggestionKind.Columns,
|
||||
suggestions: (_, m) => Promise.resolve([]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.LogicalOperators,
|
||||
name: SuggestionKind.LogicalOperators,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve(
|
||||
operators
|
||||
.list()
|
||||
.filter((o) => o.type === OperatorType.Logical)
|
||||
.map((o) => ({
|
||||
label: o.operator,
|
||||
insertText: `${o.operator} `,
|
||||
documentation: o.description,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumHigh,
|
||||
kind: CompletionItemKind.Operator,
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.WhereKeyword,
|
||||
name: SuggestionKind.WhereKeyword,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: 'WHERE',
|
||||
insertText: `WHERE `,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumHigh,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.ComparisonOperators,
|
||||
name: SuggestionKind.ComparisonOperators,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
...operators
|
||||
.list()
|
||||
.filter((o) => o.type === OperatorType.Comparison)
|
||||
.map((o) => ({
|
||||
label: o.operator,
|
||||
insertText: `${o.operator} `,
|
||||
documentation: o.description,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumHigh,
|
||||
kind: CompletionItemKind.Operator,
|
||||
})),
|
||||
{
|
||||
label: 'IN (...)',
|
||||
insertText: `IN ( $0 )`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
kind: CompletionItemKind.Operator,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
},
|
||||
{
|
||||
label: 'NOT IN (...)',
|
||||
insertText: `NOT IN ( $0 )`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
kind: CompletionItemKind.Operator,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
},
|
||||
{
|
||||
label: 'IS',
|
||||
insertText: `IS`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
kind: CompletionItemKind.Operator,
|
||||
},
|
||||
{
|
||||
label: 'IS NOT',
|
||||
insertText: `IS NOT`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
kind: CompletionItemKind.Operator,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.GroupByKeywords,
|
||||
name: SuggestionKind.GroupByKeywords,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: 'GROUP BY',
|
||||
insertText: `GROUP BY `,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumHigh,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.OrderByKeywords,
|
||||
name: SuggestionKind.OrderByKeywords,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: 'ORDER BY',
|
||||
insertText: `ORDER BY `,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
},
|
||||
{
|
||||
label: 'ORDER BY(ascending)',
|
||||
insertText: `ORDER BY $1 ASC `,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumLow,
|
||||
kind: CompletionItemKind.Snippet,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
},
|
||||
{
|
||||
label: 'ORDER BY(descending)',
|
||||
insertText: `ORDER BY $1 DESC`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumLow,
|
||||
kind: CompletionItemKind.Snippet,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.LimitKeyword,
|
||||
name: SuggestionKind.LimitKeyword,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: 'LIMIT',
|
||||
insertText: `LIMIT `,
|
||||
command: TRIGGER_SUGGEST,
|
||||
sortText: CompletionItemPriority.MediumLow,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.SortOrderDirectionKeyword,
|
||||
name: SuggestionKind.SortOrderDirectionKeyword,
|
||||
suggestions: (_, m) =>
|
||||
Promise.resolve(
|
||||
[ASC, DESC].map((o) => ({
|
||||
label: o,
|
||||
insertText: `${o} `,
|
||||
command: TRIGGER_SUGGEST,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.NotKeyword,
|
||||
name: SuggestionKind.NotKeyword,
|
||||
suggestions: () =>
|
||||
Promise.resolve([
|
||||
{
|
||||
label: 'NOT',
|
||||
insertText: 'NOT',
|
||||
command: TRIGGER_SUGGEST,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
sortText: CompletionItemPriority.High,
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.BoolValues,
|
||||
name: SuggestionKind.BoolValues,
|
||||
suggestions: () =>
|
||||
Promise.resolve(
|
||||
['TRUE', 'FALSE'].map((o) => ({
|
||||
label: o,
|
||||
insertText: `${o}`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SuggestionKind.NullValue,
|
||||
name: SuggestionKind.NullValue,
|
||||
suggestions: () =>
|
||||
Promise.resolve(
|
||||
['NULL'].map((o) => ({
|
||||
label: o,
|
||||
insertText: `${o}`,
|
||||
command: TRIGGER_SUGGEST,
|
||||
kind: CompletionItemKind.Keyword,
|
||||
sortText: CompletionItemPriority.Low,
|
||||
}))
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export const initFunctionsRegistry = (): FunctionsRegistryItem[] => [
|
||||
...STD_STATS.map((s) => ({
|
||||
id: s,
|
||||
name: s,
|
||||
})),
|
||||
];
|
||||
|
||||
export const initMacrosRegistry = (): MacrosRegistryItem[] => [...MACROS];
|
||||
|
||||
export const initOperatorsRegistry = (): OperatorsRegistryItem[] => [
|
||||
...STD_OPERATORS.map((o) => ({
|
||||
id: o,
|
||||
name: o,
|
||||
operator: o,
|
||||
type: OperatorType.Comparison,
|
||||
})),
|
||||
...LOGICAL_OPERATORS.map((o) => ({ id: o, name: o.toUpperCase(), operator: o, type: OperatorType.Logical })),
|
||||
];
|
||||
|
||||
function createMacroSuggestionItem(m: MacrosRegistryItem) {
|
||||
return {
|
||||
label: m.name,
|
||||
insertText: `${'\\' + m.text}${argsString(m.args)} `,
|
||||
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
kind: CompletionItemKind.Snippet,
|
||||
documentation: m.description,
|
||||
command: TRIGGER_SUGGEST,
|
||||
};
|
||||
}
|
||||
|
||||
function argsString(args?: string[]): string {
|
||||
if (!args) {
|
||||
return '()';
|
||||
}
|
||||
return '('.concat(args.map((t, i) => `\${${i}:${t}}`).join(', ')).concat(')');
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
import { StatementPosition, TokenType } from '../types';
|
||||
|
||||
import { AND, AS, ASC, BY, DESC, FROM, GROUP, ORDER, SELECT, WHERE, WITH } from './language';
|
||||
import { StatementPositionResolversRegistryItem } from './types';
|
||||
|
||||
export function initStatementPositionResolvers(): StatementPositionResolversRegistryItem[] {
|
||||
return [
|
||||
{
|
||||
id: StatementPosition.SelectKeyword,
|
||||
name: StatementPosition.SelectKeyword,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
currentToken === null ||
|
||||
(currentToken.isWhiteSpace() && currentToken.previous === null) ||
|
||||
currentToken.is(TokenType.Keyword, SELECT) ||
|
||||
(currentToken.is(TokenType.Keyword, SELECT) && currentToken.previous === null) ||
|
||||
previousIsSlash ||
|
||||
(currentToken.isIdentifier() && (previousIsSlash || currentToken?.previous === null)) ||
|
||||
(currentToken.isIdentifier() && SELECT.startsWith(currentToken.value.toLowerCase()))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WithKeyword,
|
||||
name: StatementPosition.WithKeyword,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
currentToken === null ||
|
||||
(currentToken.isWhiteSpace() && currentToken.previous === null) ||
|
||||
(currentToken.is(TokenType.Keyword, WITH) && currentToken.previous === null) ||
|
||||
(currentToken.isIdentifier() && WITH.toLowerCase().startsWith(currentToken.value.toLowerCase()))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterSelectKeyword,
|
||||
name: StatementPosition.AfterSelectKeyword,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(previousNonWhiteSpace?.value.toLowerCase() === SELECT),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterSelectArguments,
|
||||
name: StatementPosition.AfterSelectArguments,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(previousKeyword?.value.toLowerCase() === SELECT && previousNonWhiteSpace?.value === ',');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterSelectFuncFirstArgument,
|
||||
name: StatementPosition.AfterSelectFuncFirstArgument,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(
|
||||
(previousKeyword?.value.toLowerCase() === SELECT || previousKeyword?.value.toLowerCase() === AS) &&
|
||||
(previousNonWhiteSpace?.is(TokenType.Parenthesis, '(') || currentToken?.is(TokenType.Parenthesis, '()'))
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterWhereFunctionArgument,
|
||||
name: StatementPosition.AfterWhereFunctionArgument,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(
|
||||
previousKeyword?.is(TokenType.Keyword, WHERE) &&
|
||||
(previousNonWhiteSpace?.is(TokenType.Parenthesis, '(') || currentToken?.is(TokenType.Parenthesis, '()'))
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterGroupBy,
|
||||
name: StatementPosition.AfterGroupBy,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
previousKeyword?.is(TokenType.Keyword, BY) &&
|
||||
previousKeyword?.getPreviousKeyword()?.is(TokenType.Keyword, GROUP) &&
|
||||
(previousNonWhiteSpace?.isIdentifier() ||
|
||||
previousNonWhiteSpace?.isDoubleQuotedString() ||
|
||||
previousNonWhiteSpace?.is(TokenType.Parenthesis, ')') ||
|
||||
previousNonWhiteSpace?.is(TokenType.Parenthesis, '()'))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.SelectAlias,
|
||||
name: StatementPosition.SelectAlias,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
if (previousNonWhiteSpace?.value === ',' && previousKeyword?.value.toLowerCase() === AS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: StatementPosition.FromKeyword,
|
||||
name: StatementPosition.FromKeyword,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(
|
||||
(previousKeyword?.value.toLowerCase() === SELECT && previousNonWhiteSpace?.value !== ',') ||
|
||||
((currentToken?.isKeyword() || currentToken?.isIdentifier()) &&
|
||||
FROM.toLowerCase().startsWith(currentToken.value.toLowerCase()))
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterFromKeyword,
|
||||
name: StatementPosition.AfterFromKeyword,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(previousNonWhiteSpace?.value.toLowerCase() === FROM),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterFrom,
|
||||
name: StatementPosition.AfterFrom,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
(previousKeyword?.value.toLowerCase() === FROM && previousNonWhiteSpace?.isDoubleQuotedString()) ||
|
||||
(previousKeyword?.value.toLowerCase() === FROM && previousNonWhiteSpace?.isIdentifier()) ||
|
||||
(previousKeyword?.value.toLowerCase() === FROM && previousNonWhiteSpace?.isVariable())
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterTable,
|
||||
name: StatementPosition.AfterTable,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(
|
||||
previousKeyword?.value.toLowerCase() === FROM &&
|
||||
(previousNonWhiteSpace?.isVariable() || previousNonWhiteSpace?.value !== '')
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WhereKeyword,
|
||||
name: StatementPosition.WhereKeyword,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
previousKeyword?.value.toLowerCase() === WHERE &&
|
||||
(previousNonWhiteSpace?.isKeyword() ||
|
||||
previousNonWhiteSpace?.is(TokenType.Parenthesis, '(') ||
|
||||
previousNonWhiteSpace?.is(TokenType.Operator, AND))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WhereComparisonOperator,
|
||||
name: StatementPosition.WhereComparisonOperator,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
previousKeyword?.value.toLowerCase() === WHERE &&
|
||||
!previousNonWhiteSpace?.getPreviousNonWhiteSpaceToken()?.isOperator() &&
|
||||
!currentToken?.is(TokenType.Delimiter, '.') &&
|
||||
!currentToken?.isParenthesis() &&
|
||||
(previousNonWhiteSpace?.isIdentifier() || previousNonWhiteSpace?.isDoubleQuotedString())
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WhereValue,
|
||||
name: StatementPosition.WhereValue,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(previousKeyword?.value.toLowerCase() === WHERE && previousNonWhiteSpace?.isOperator()),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterWhereValue,
|
||||
name: StatementPosition.AfterWhereValue,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(
|
||||
previousKeyword?.value.toLowerCase() === WHERE &&
|
||||
(previousNonWhiteSpace?.is(TokenType.Operator, 'and') ||
|
||||
previousNonWhiteSpace?.is(TokenType.Operator, 'or') ||
|
||||
previousNonWhiteSpace?.isString() ||
|
||||
previousNonWhiteSpace?.isNumber() ||
|
||||
previousNonWhiteSpace?.is(TokenType.Parenthesis, ')') ||
|
||||
previousNonWhiteSpace?.is(TokenType.Parenthesis, '()') ||
|
||||
previousNonWhiteSpace?.isTemplateVariable() ||
|
||||
(previousNonWhiteSpace?.is(TokenType.IdentifierQuote) &&
|
||||
previousNonWhiteSpace.getPreviousNonWhiteSpaceToken()?.is(TokenType.Identifier) &&
|
||||
previousNonWhiteSpace
|
||||
?.getPreviousNonWhiteSpaceToken()
|
||||
?.getPreviousNonWhiteSpaceToken()
|
||||
?.is(TokenType.IdentifierQuote)))
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterGroupByKeywords,
|
||||
name: StatementPosition.AfterGroupByKeywords,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
previousKeyword?.is(TokenType.Keyword, BY) &&
|
||||
previousKeyword?.getPreviousKeyword()?.is(TokenType.Keyword, GROUP) &&
|
||||
(previousNonWhiteSpace?.is(TokenType.Keyword, BY) || previousNonWhiteSpace?.is(TokenType.Delimiter, ','))
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterGroupByFunctionArgument,
|
||||
name: StatementPosition.AfterGroupByFunctionArgument,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(
|
||||
previousKeyword?.is(TokenType.Keyword, BY) &&
|
||||
previousKeyword?.getPreviousKeyword()?.is(TokenType.Keyword, GROUP) &&
|
||||
(previousNonWhiteSpace?.is(TokenType.Parenthesis, '(') || currentToken?.is(TokenType.Parenthesis, '()'))
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterOrderByKeywords,
|
||||
name: StatementPosition.AfterOrderByKeywords,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
previousNonWhiteSpace?.is(TokenType.Keyword, BY) &&
|
||||
previousNonWhiteSpace?.getPreviousKeyword()?.is(TokenType.Keyword, ORDER)
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterOrderByFunction,
|
||||
name: StatementPosition.AfterOrderByFunction,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(
|
||||
previousKeyword?.is(TokenType.Keyword, BY) &&
|
||||
previousKeyword?.getPreviousKeyword()?.is(TokenType.Keyword, ORDER) &&
|
||||
previousNonWhiteSpace?.is(TokenType.Parenthesis) &&
|
||||
previousNonWhiteSpace?.getPreviousNonWhiteSpaceToken()?.is(TokenType.Function)
|
||||
),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterOrderByDirection,
|
||||
name: StatementPosition.AfterOrderByDirection,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) =>
|
||||
Boolean(previousKeyword?.is(TokenType.Keyword, DESC) || previousKeyword?.is(TokenType.Keyword, ASC)),
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterIsOperator,
|
||||
name: StatementPosition.AfterIsOperator,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(previousNonWhiteSpace?.is(TokenType.Operator, 'IS'));
|
||||
},
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterIsNotOperator,
|
||||
name: StatementPosition.AfterIsNotOperator,
|
||||
resolve: (currentToken, previousKeyword, previousNonWhiteSpace, previousIsSlash) => {
|
||||
return Boolean(
|
||||
previousNonWhiteSpace?.is(TokenType.Operator, 'NOT') &&
|
||||
previousNonWhiteSpace.getPreviousNonWhiteSpaceToken()?.is(TokenType.Operator, 'IS')
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
import { RegistryItem } from '@grafana/data';
|
||||
|
||||
import { StatementPosition, SuggestionKind } from '../types';
|
||||
|
||||
export interface SuggestionKindRegistryItem extends RegistryItem {
|
||||
id: StatementPosition;
|
||||
kind: SuggestionKind[];
|
||||
}
|
||||
|
||||
// Registry of possible suggestions for the given statement position
|
||||
export const initSuggestionsKindRegistry = (): SuggestionKindRegistryItem[] => {
|
||||
return [
|
||||
{
|
||||
id: StatementPosition.SelectKeyword,
|
||||
name: StatementPosition.SelectKeyword,
|
||||
kind: [SuggestionKind.SelectKeyword],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WithKeyword,
|
||||
name: StatementPosition.WithKeyword,
|
||||
kind: [SuggestionKind.WithKeyword],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterSelectKeyword,
|
||||
name: StatementPosition.AfterSelectKeyword,
|
||||
kind: [SuggestionKind.FunctionsWithArguments, SuggestionKind.Columns, SuggestionKind.SelectMacro],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterSelectFuncFirstArgument,
|
||||
name: StatementPosition.AfterSelectFuncFirstArgument,
|
||||
kind: [SuggestionKind.Columns],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterGroupByFunctionArgument,
|
||||
name: StatementPosition.AfterGroupByFunctionArgument,
|
||||
kind: [SuggestionKind.Columns],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterWhereFunctionArgument,
|
||||
name: StatementPosition.AfterWhereFunctionArgument,
|
||||
kind: [SuggestionKind.Columns],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterSelectArguments,
|
||||
name: StatementPosition.AfterSelectArguments,
|
||||
kind: [SuggestionKind.Columns],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterFromKeyword,
|
||||
name: StatementPosition.AfterFromKeyword,
|
||||
kind: [SuggestionKind.Tables, SuggestionKind.TableMacro],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.SelectAlias,
|
||||
name: StatementPosition.SelectAlias,
|
||||
kind: [SuggestionKind.Columns, SuggestionKind.FunctionsWithArguments],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.FromKeyword,
|
||||
name: StatementPosition.FromKeyword,
|
||||
kind: [SuggestionKind.FromKeyword],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterFrom,
|
||||
name: StatementPosition.AfterFrom,
|
||||
kind: [
|
||||
SuggestionKind.WhereKeyword,
|
||||
SuggestionKind.GroupByKeywords,
|
||||
SuggestionKind.OrderByKeywords,
|
||||
SuggestionKind.LimitKeyword,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterTable,
|
||||
name: StatementPosition.AfterTable,
|
||||
kind: [
|
||||
SuggestionKind.WhereKeyword,
|
||||
SuggestionKind.GroupByKeywords,
|
||||
SuggestionKind.OrderByKeywords,
|
||||
SuggestionKind.LimitKeyword,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WhereKeyword,
|
||||
name: StatementPosition.WhereKeyword,
|
||||
kind: [SuggestionKind.Columns, SuggestionKind.FilterMacro, SuggestionKind.TemplateVariables],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WhereComparisonOperator,
|
||||
name: StatementPosition.WhereComparisonOperator,
|
||||
kind: [SuggestionKind.ComparisonOperators],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.WhereValue,
|
||||
name: StatementPosition.WhereValue,
|
||||
kind: [SuggestionKind.Columns, SuggestionKind.FilterMacro, SuggestionKind.TemplateVariables],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterWhereValue,
|
||||
name: StatementPosition.AfterWhereValue,
|
||||
kind: [
|
||||
SuggestionKind.LogicalOperators,
|
||||
SuggestionKind.GroupByKeywords,
|
||||
SuggestionKind.OrderByKeywords,
|
||||
SuggestionKind.LimitKeyword,
|
||||
SuggestionKind.Columns,
|
||||
SuggestionKind.TemplateVariables,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterGroupByKeywords,
|
||||
name: StatementPosition.AfterGroupByKeywords,
|
||||
kind: [SuggestionKind.GroupMacro],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterGroupBy,
|
||||
name: StatementPosition.AfterGroupBy,
|
||||
kind: [SuggestionKind.OrderByKeywords, SuggestionKind.LimitKeyword],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterOrderByKeywords,
|
||||
name: StatementPosition.AfterOrderByKeywords,
|
||||
kind: [SuggestionKind.Columns],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterOrderByFunction,
|
||||
name: StatementPosition.AfterOrderByFunction,
|
||||
kind: [SuggestionKind.SortOrderDirectionKeyword, SuggestionKind.LimitKeyword],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterOrderByDirection,
|
||||
name: StatementPosition.AfterOrderByDirection,
|
||||
kind: [SuggestionKind.LimitKeyword],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterIsOperator,
|
||||
name: StatementPosition.AfterOrderByDirection,
|
||||
kind: [SuggestionKind.NotKeyword, SuggestionKind.NullValue, SuggestionKind.BoolValues],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterIsNotOperator,
|
||||
name: StatementPosition.AfterOrderByDirection,
|
||||
kind: [SuggestionKind.NullValue, SuggestionKind.BoolValues],
|
||||
},
|
||||
];
|
||||
};
|
@ -1,52 +0,0 @@
|
||||
import { RegistryItem } from '@grafana/data';
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import {
|
||||
CustomSuggestion,
|
||||
MacroType,
|
||||
OperatorType,
|
||||
PositionContext,
|
||||
StatementPosition,
|
||||
SuggestionKind,
|
||||
} from '../types';
|
||||
import { LinkedToken } from '../utils/LinkedToken';
|
||||
|
||||
export interface SuggestionsRegistryItem extends RegistryItem {
|
||||
id: SuggestionKind;
|
||||
suggestions: (position: PositionContext, m: typeof monacoTypes) => Promise<CustomSuggestion[]>;
|
||||
}
|
||||
|
||||
export interface MacrosRegistryItem extends RegistryItem {
|
||||
type: MacroType;
|
||||
text: string;
|
||||
args?: string[];
|
||||
}
|
||||
|
||||
export interface FunctionsRegistryItem extends RegistryItem {}
|
||||
export interface OperatorsRegistryItem extends RegistryItem {
|
||||
operator: string;
|
||||
type: OperatorType;
|
||||
}
|
||||
|
||||
export type StatementPositionResolver = (
|
||||
currentToken: LinkedToken | null,
|
||||
previousKeyword: LinkedToken | null,
|
||||
previousNonWhiteSpace: LinkedToken | null,
|
||||
previousIsSlash: Boolean
|
||||
) => Boolean;
|
||||
|
||||
export interface StatementPositionResolversRegistryItem extends RegistryItem {
|
||||
id: StatementPosition;
|
||||
resolve: StatementPositionResolver;
|
||||
}
|
||||
|
||||
export type SuggestionsResolver = <T extends PositionContext = PositionContext>(
|
||||
positionContext: T
|
||||
) => Promise<CustomSuggestion[]>;
|
||||
|
||||
export interface SQLMonarchLanguage extends monacoTypes.languages.IMonarchLanguage {
|
||||
keywords?: string[];
|
||||
builtinFunctions?: string[];
|
||||
logicalOperators?: string[];
|
||||
comparisonOperators?: string[];
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import * as testData from '../mocks/testData';
|
||||
|
||||
import { testStatementPosition } from './statementPosition';
|
||||
import { TestQueryModel } from './types';
|
||||
|
||||
export const SQLEditorTestUtils = {
|
||||
testData,
|
||||
testStatementPosition,
|
||||
};
|
||||
|
||||
export { TestQueryModel };
|
@ -1,63 +0,0 @@
|
||||
import { Registry } from '@grafana/data';
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { getMonacoMock } from '../mocks/Monaco';
|
||||
import { TextModel } from '../mocks/TextModel';
|
||||
import { getStatementPosition } from '../standardSql/getStatementPosition';
|
||||
import { StatementPositionResolversRegistryItem } from '../standardSql/types';
|
||||
import { CustomStatementPlacement, StatementPosition } from '../types';
|
||||
import { linkedTokenBuilder } from '../utils/linkedTokenBuilder';
|
||||
|
||||
import { StatementPositionResolverTestCase } from './types';
|
||||
|
||||
function assertPosition(
|
||||
query: string,
|
||||
position: monacoTypes.IPosition,
|
||||
expected: StatementPosition | string,
|
||||
monacoMock: any,
|
||||
resolversRegistry: Registry<StatementPositionResolversRegistryItem>
|
||||
) {
|
||||
const testModel = TextModel(query);
|
||||
const current = linkedTokenBuilder(monacoMock, testModel as monacoTypes.editor.ITextModel, position);
|
||||
const statementPosition = getStatementPosition(current, resolversRegistry);
|
||||
|
||||
expect(statementPosition).toContain(expected);
|
||||
}
|
||||
|
||||
export const testStatementPosition = (
|
||||
expected: StatementPosition | string,
|
||||
cases: StatementPositionResolverTestCase[],
|
||||
resolvers: () => CustomStatementPlacement[]
|
||||
) => {
|
||||
describe(`${expected}`, () => {
|
||||
let MonacoMock: any;
|
||||
let statementPositionResolversRegistry: Registry<StatementPositionResolversRegistryItem>;
|
||||
|
||||
beforeEach(() => {
|
||||
const mockQueries = new Map<string, Array<Array<Pick<monacoTypes.Token, 'language' | 'offset' | 'type'>>>>();
|
||||
cases.forEach((c) => mockQueries.set(c.query.query, c.query.tokens));
|
||||
|
||||
MonacoMock = getMonacoMock(mockQueries);
|
||||
statementPositionResolversRegistry = new Registry(() => {
|
||||
return resolvers().map((r) => ({
|
||||
id: r.id as StatementPosition,
|
||||
name: r.name || r.id,
|
||||
resolve: r.resolve,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// using forEach here rather than test.each as been struggling to get the arguments intepolated in test name string
|
||||
cases.forEach((c) => {
|
||||
test(`${c.query.query}`, () => {
|
||||
assertPosition(
|
||||
c.query.query,
|
||||
{ lineNumber: c.position.line, column: c.position.column },
|
||||
expected,
|
||||
MonacoMock,
|
||||
statementPositionResolversRegistry
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
SELECT column1, FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10
|
||||
SELECT column1, FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10; SELECT column2, FROM table2 WHERE column2 = "value2" GROUP BY column1 ORDER BY column2 DESC LIMIT 10;
|
||||
SELECT count(column1), FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10;
|
||||
SELECT count(column1), FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1 DESC LIMIT 10; SELECT count(column2), FROM table2 WHERE column2 = "value2" GROUP BY column1 ORDER BY column2 DESC LIMIT 10;
|
||||
|
||||
|
||||
SELECT column1,
|
||||
FROM table1
|
||||
|
||||
WHERE column1 = "value1"
|
||||
GROUP BY column1 ORDER BY column1 DESC
|
||||
LIMIT 10;
|
||||
|
||||
SELECT count(column1), column2 FROM table1 WHERE column1 = "value1" GROUP BY column1 ORDER BY column1, avg(column2) DESC LIMIT 10;
|
||||
|
||||
SELECT count(column1), column2
|
||||
FROM table1
|
||||
|
||||
WHERE column1 = "value1"
|
||||
GROUP BY column1 ORDER BY column1, avg(column2) DESC
|
||||
LIMIT 10;
|
@ -1,11 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
export interface TestQueryModel {
|
||||
query: string;
|
||||
tokens: Array<Array<Pick<monacoTypes.Token, 'language' | 'offset' | 'type'>>>;
|
||||
}
|
||||
|
||||
export interface StatementPositionResolverTestCase {
|
||||
query: TestQueryModel;
|
||||
position: { line: number; column: number };
|
||||
}
|
@ -9,7 +9,7 @@ import {
|
||||
TimeRange,
|
||||
toOption as toOptionFromData,
|
||||
} from '@grafana/data';
|
||||
import { Monaco, monacoTypes } from '@grafana/ui';
|
||||
import { CompletionItemKind, LanguageCompletionProvider } from '@grafana/experimental';
|
||||
|
||||
import { QueryWithDefaults } from './defaults';
|
||||
import {
|
||||
@ -17,8 +17,6 @@ import {
|
||||
QueryEditorGroupByExpression,
|
||||
QueryEditorPropertyExpression,
|
||||
} from './expressions';
|
||||
import { StatementPositionResolver, SuggestionsResolver } from './standardSql/types';
|
||||
import { LinkedToken } from './utils/LinkedToken';
|
||||
|
||||
export interface SqlQueryForInterpolation {
|
||||
dataset?: string;
|
||||
@ -182,249 +180,17 @@ export interface MetaDefinition {
|
||||
kind: CompletionItemKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a context for suggestions resolver
|
||||
* @alpha
|
||||
*/
|
||||
export interface PositionContext {
|
||||
position: monacoTypes.IPosition;
|
||||
kind: SuggestionKind[];
|
||||
statementPosition: StatementPosition[];
|
||||
currentToken: LinkedToken | null;
|
||||
range: monacoTypes.IRange;
|
||||
}
|
||||
|
||||
export type CustomSuggestion = Partial<monacoTypes.languages.CompletionItem> & { label: string };
|
||||
|
||||
export interface CustomSuggestionKind {
|
||||
id: string;
|
||||
suggestionsResolver: SuggestionsResolver;
|
||||
applyTo?: Array<StatementPosition | string>;
|
||||
overrideDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface CustomStatementPlacement {
|
||||
id: string;
|
||||
name?: string;
|
||||
resolve: StatementPositionResolver;
|
||||
overrideDefault?: boolean;
|
||||
}
|
||||
export type StatementPlacementProvider = () => CustomStatementPlacement[];
|
||||
export type SuggestionKindProvider = () => CustomSuggestionKind[];
|
||||
|
||||
export interface ColumnDefinition {
|
||||
name: string;
|
||||
type?: string;
|
||||
description?: string;
|
||||
// Text used for automplete. If not provided name is used.
|
||||
completion?: string;
|
||||
}
|
||||
export interface TableDefinition {
|
||||
name: string;
|
||||
// Text used for automplete. If not provided name is used.
|
||||
completion?: string;
|
||||
}
|
||||
|
||||
export interface SQLCompletionItemProvider
|
||||
extends Omit<monacoTypes.languages.CompletionItemProvider, 'provideCompletionItems'> {
|
||||
/**
|
||||
* Allows dialect specific functions to be added to the completion list.
|
||||
* @alpha
|
||||
*/
|
||||
supportedFunctions?: () => Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Allows dialect specific operators to be added to the completion list.
|
||||
* @alpha
|
||||
*/
|
||||
supportedOperators?: () => Array<{
|
||||
id: string;
|
||||
operator: string;
|
||||
type: OperatorType;
|
||||
description?: string;
|
||||
}>;
|
||||
|
||||
supportedMacros?: () => Array<{
|
||||
id: string;
|
||||
text: string;
|
||||
type: MacroType;
|
||||
args: string[];
|
||||
description?: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Allows custom suggestion kinds to be defined and correlate them with <Custom>StatementPosition.
|
||||
* @alpha
|
||||
*/
|
||||
customSuggestionKinds?: SuggestionKindProvider;
|
||||
|
||||
/**
|
||||
* Allows custom statement placement definition.
|
||||
* @alpha
|
||||
*/
|
||||
customStatementPlacement?: StatementPlacementProvider;
|
||||
|
||||
/**
|
||||
* Allows providing a custom function for resolving db tables.
|
||||
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloades loaded).
|
||||
* @alpha
|
||||
*/
|
||||
tables?: {
|
||||
resolve: () => Promise<TableDefinition[]>;
|
||||
// Allows providing a custom function for calculating the table name from the query. If not specified a default implemnentation is used.
|
||||
parseName?: (t: LinkedToken) => string;
|
||||
};
|
||||
/**
|
||||
* Allows providing a custom function for resolving table.
|
||||
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloades loaded).
|
||||
* @alpha
|
||||
*/
|
||||
columns?: {
|
||||
resolve: (table: string) => Promise<ColumnDefinition[]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Not sure whether or not we need this. Would like to avoid this kind of flexibility.
|
||||
* @alpha
|
||||
*/
|
||||
provideCompletionItems?: (
|
||||
model: monacoTypes.editor.ITextModel,
|
||||
position: monacoTypes.Position,
|
||||
context: monacoTypes.languages.CompletionContext,
|
||||
token: monacoTypes.CancellationToken,
|
||||
positionContext: PositionContext // Decorates original provideCompletionItems function with our custom statement position context
|
||||
) => monacoTypes.languages.CompletionList;
|
||||
}
|
||||
|
||||
export type LanguageCompletionProvider = (m: Monaco) => SQLCompletionItemProvider;
|
||||
|
||||
export enum OperatorType {
|
||||
Comparison,
|
||||
Logical,
|
||||
}
|
||||
|
||||
export enum MacroType {
|
||||
Value,
|
||||
Filter,
|
||||
Group,
|
||||
Column,
|
||||
Table,
|
||||
}
|
||||
|
||||
export enum TokenType {
|
||||
Parenthesis = 'delimiter.parenthesis.sql',
|
||||
Whitespace = 'white.sql',
|
||||
Keyword = 'keyword.sql',
|
||||
Delimiter = 'delimiter.sql',
|
||||
Operator = 'operator.sql',
|
||||
Identifier = 'identifier.sql',
|
||||
IdentifierQuote = 'identifier.quote.sql',
|
||||
Type = 'type.sql',
|
||||
Function = 'predefined.sql',
|
||||
Number = 'number.sql',
|
||||
String = 'string.sql',
|
||||
Variable = 'variable.sql',
|
||||
}
|
||||
|
||||
export enum StatementPosition {
|
||||
Unknown = 'unknown',
|
||||
SelectKeyword = 'selectKeyword',
|
||||
WithKeyword = 'withKeyword',
|
||||
AfterSelectKeyword = 'afterSelectKeyword',
|
||||
AfterSelectArguments = 'afterSelectArguments',
|
||||
AfterSelectFuncFirstArgument = 'afterSelectFuncFirstArgument',
|
||||
SelectAlias = 'selectAlias',
|
||||
AfterFromKeyword = 'afterFromKeyword',
|
||||
AfterTable = 'afterTable',
|
||||
SchemaFuncFirstArgument = 'schemaFuncFirstArgument',
|
||||
SchemaFuncExtraArgument = 'schemaFuncExtraArgument',
|
||||
FromKeyword = 'fromKeyword',
|
||||
AfterFrom = 'afterFrom',
|
||||
WhereKeyword = 'whereKeyword',
|
||||
WhereComparisonOperator = 'whereComparisonOperator',
|
||||
WhereValue = 'whereValue',
|
||||
AfterWhereFunctionArgument = 'afterWhereFunctionArgument',
|
||||
AfterGroupByFunctionArgument = 'afterGroupByFunctionArgument',
|
||||
AfterWhereValue = 'afterWhereValue',
|
||||
AfterGroupByKeywords = 'afterGroupByKeywords',
|
||||
AfterGroupBy = 'afterGroupBy',
|
||||
AfterOrderByKeywords = 'afterOrderByKeywords',
|
||||
AfterOrderByFunction = 'afterOrderByFunction',
|
||||
AfterOrderByDirection = 'afterOrderByDirection',
|
||||
AfterIsOperator = 'afterIsOperator',
|
||||
AfterIsNotOperator = 'afterIsNotOperator',
|
||||
}
|
||||
|
||||
export enum SuggestionKind {
|
||||
Tables = 'tables',
|
||||
Columns = 'columns',
|
||||
SelectKeyword = 'selectKeyword',
|
||||
WithKeyword = 'withKeyword',
|
||||
FunctionsWithArguments = 'functionsWithArguments',
|
||||
FromKeyword = 'fromKeyword',
|
||||
WhereKeyword = 'whereKeyword',
|
||||
GroupByKeywords = 'groupByKeywords',
|
||||
OrderByKeywords = 'orderByKeywords',
|
||||
FunctionsWithoutArguments = 'functionsWithoutArguments',
|
||||
LimitKeyword = 'limitKeyword',
|
||||
SortOrderDirectionKeyword = 'sortOrderDirectionKeyword',
|
||||
ComparisonOperators = 'comparisonOperators',
|
||||
LogicalOperators = 'logicalOperators',
|
||||
SelectMacro = 'selectMacro',
|
||||
TableMacro = 'tableMacro',
|
||||
FilterMacro = 'filterMacro',
|
||||
GroupMacro = 'groupMacro',
|
||||
BoolValues = 'boolValues',
|
||||
NullValue = 'nullValue',
|
||||
NotKeyword = 'notKeyword',
|
||||
TemplateVariables = 'templateVariables',
|
||||
}
|
||||
|
||||
// TODO: export from grafana/ui
|
||||
export enum CompletionItemPriority {
|
||||
High = 'a',
|
||||
MediumHigh = 'd',
|
||||
Medium = 'g',
|
||||
MediumLow = 'k',
|
||||
Low = 'q',
|
||||
}
|
||||
|
||||
export enum CompletionItemKind {
|
||||
Method = 0,
|
||||
Function = 1,
|
||||
Constructor = 2,
|
||||
Field = 3,
|
||||
Variable = 4,
|
||||
Class = 5,
|
||||
Struct = 6,
|
||||
Interface = 7,
|
||||
Module = 8,
|
||||
Property = 9,
|
||||
Event = 10,
|
||||
Operator = 11,
|
||||
Unit = 12,
|
||||
Value = 13,
|
||||
Constant = 14,
|
||||
Enum = 15,
|
||||
EnumMember = 16,
|
||||
Keyword = 17,
|
||||
Text = 18,
|
||||
Color = 19,
|
||||
File = 20,
|
||||
Reference = 21,
|
||||
Customcolor = 22,
|
||||
Folder = 23,
|
||||
TypeParameter = 24,
|
||||
User = 25,
|
||||
Issue = 26,
|
||||
Snippet = 27,
|
||||
}
|
||||
|
||||
export enum CompletionItemInsertTextRule {
|
||||
KeepWhitespace = 1,
|
||||
InsertAsSnippet = 4,
|
||||
}
|
||||
export {
|
||||
CompletionItemKind,
|
||||
LanguageCompletionProvider,
|
||||
LinkedToken,
|
||||
ColumnDefinition,
|
||||
CompletionItemPriority,
|
||||
StatementPlacementProvider,
|
||||
SuggestionKindProvider,
|
||||
TableDefinition,
|
||||
TokenType,
|
||||
OperatorType,
|
||||
StatementPosition,
|
||||
PositionContext,
|
||||
} from '@grafana/experimental';
|
||||
|
@ -1,176 +0,0 @@
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { TokenType } from '../types';
|
||||
|
||||
export class LinkedToken {
|
||||
constructor(
|
||||
public type: string,
|
||||
public value: string,
|
||||
public range: monacoTypes.IRange,
|
||||
public previous: LinkedToken | null,
|
||||
public next: LinkedToken | null
|
||||
) {}
|
||||
|
||||
isKeyword(): boolean {
|
||||
return this.type === TokenType.Keyword;
|
||||
}
|
||||
|
||||
isWhiteSpace(): boolean {
|
||||
return this.type === TokenType.Whitespace;
|
||||
}
|
||||
|
||||
isParenthesis(): boolean {
|
||||
return this.type === TokenType.Parenthesis;
|
||||
}
|
||||
|
||||
isIdentifier(): boolean {
|
||||
return this.type === TokenType.Identifier;
|
||||
}
|
||||
|
||||
isString(): boolean {
|
||||
return this.type === TokenType.String;
|
||||
}
|
||||
|
||||
isNumber(): boolean {
|
||||
return this.type === TokenType.Number;
|
||||
}
|
||||
|
||||
isDoubleQuotedString(): boolean {
|
||||
return this.type === TokenType.Type;
|
||||
}
|
||||
|
||||
isVariable(): boolean {
|
||||
return this.type === TokenType.Variable;
|
||||
}
|
||||
|
||||
isFunction(): boolean {
|
||||
return this.type === TokenType.Function;
|
||||
}
|
||||
|
||||
isOperator(): boolean {
|
||||
return this.type === TokenType.Operator;
|
||||
}
|
||||
|
||||
isTemplateVariable(): boolean {
|
||||
const variables = getTemplateSrv()?.getVariables();
|
||||
return variables.find((v) => '$' + v.name === this.value) !== undefined;
|
||||
}
|
||||
|
||||
is(type: TokenType, value?: string | number | boolean): boolean {
|
||||
const isType = this.type === type;
|
||||
|
||||
return value !== undefined ? isType && compareTokenWithValue(type, this, value) : isType;
|
||||
}
|
||||
|
||||
getPreviousNonWhiteSpaceToken(): LinkedToken | null {
|
||||
let curr = this.previous;
|
||||
while (curr != null) {
|
||||
if (!curr.isWhiteSpace()) {
|
||||
return curr;
|
||||
}
|
||||
curr = curr.previous;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPreviousOfType(type: TokenType, value?: string): LinkedToken | null {
|
||||
let curr = this.previous;
|
||||
while (curr != null) {
|
||||
const isType = curr.type === type;
|
||||
|
||||
if (value !== undefined ? isType && compareTokenWithValue(type, curr, value) : isType) {
|
||||
return curr;
|
||||
}
|
||||
curr = curr.previous;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPreviousUntil(type: TokenType, ignoreTypes: TokenType[], value?: string): LinkedToken[] | null {
|
||||
let tokens: LinkedToken[] = [];
|
||||
let curr = this.previous;
|
||||
while (curr != null) {
|
||||
if (ignoreTypes.some((t) => t === curr?.type)) {
|
||||
curr = curr.previous;
|
||||
continue;
|
||||
}
|
||||
|
||||
const isType = curr.type === type;
|
||||
|
||||
if (value !== undefined ? isType && compareTokenWithValue(type, curr, value) : isType) {
|
||||
return tokens;
|
||||
}
|
||||
if (!curr.isWhiteSpace()) {
|
||||
tokens.push(curr);
|
||||
}
|
||||
curr = curr.previous;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
getNextUntil(type: TokenType, ignoreTypes: TokenType[], value?: string): LinkedToken[] | null {
|
||||
let tokens: LinkedToken[] = [];
|
||||
let curr = this.next;
|
||||
while (curr != null) {
|
||||
if (ignoreTypes.some((t) => t === curr?.type)) {
|
||||
curr = curr.next;
|
||||
continue;
|
||||
}
|
||||
|
||||
const isType = curr.type === type;
|
||||
|
||||
if (value !== undefined ? isType && compareTokenWithValue(type, curr, value) : isType) {
|
||||
return tokens;
|
||||
}
|
||||
if (!curr.isWhiteSpace()) {
|
||||
tokens.push(curr);
|
||||
}
|
||||
curr = curr.next;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
getPreviousKeyword(): LinkedToken | null {
|
||||
let curr = this.previous;
|
||||
while (curr != null) {
|
||||
if (curr.isKeyword()) {
|
||||
return curr;
|
||||
}
|
||||
curr = curr.previous;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getNextNonWhiteSpaceToken(): LinkedToken | null {
|
||||
let curr = this.next;
|
||||
while (curr != null) {
|
||||
if (!curr.isWhiteSpace()) {
|
||||
return curr;
|
||||
}
|
||||
curr = curr.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getNextOfType(type: TokenType, value?: string): LinkedToken | null {
|
||||
let curr = this.next;
|
||||
while (curr != null) {
|
||||
const isType = curr.type === type;
|
||||
|
||||
if (value !== undefined ? isType && compareTokenWithValue(type, curr, value) : isType) {
|
||||
return curr;
|
||||
}
|
||||
curr = curr.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function compareTokenWithValue(type: TokenType, token: LinkedToken, value: string | number | boolean) {
|
||||
return type === TokenType.Keyword || type === TokenType.Operator
|
||||
? token.value.toLowerCase() === value.toString().toLowerCase()
|
||||
: token.value === value;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
export const TRIGGER_SUGGEST = {
|
||||
id: 'editor.action.triggerSuggest',
|
||||
title: '',
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
import { attachDebugger, createLogger } from '@grafana/ui';
|
||||
|
||||
let sqlEditorLogger = { logger: () => {} };
|
||||
let sqlEditorLog: (...t: any[]) => void = () => {};
|
||||
|
||||
if (attachDebugger) {
|
||||
sqlEditorLogger = createLogger('SQLEditor');
|
||||
sqlEditorLog = sqlEditorLogger.logger;
|
||||
attachDebugger('sqleditor', undefined, sqlEditorLogger as any);
|
||||
}
|
||||
|
||||
export { sqlEditorLog, sqlEditorLogger };
|
@ -1,31 +0,0 @@
|
||||
import { Registry } from '@grafana/data';
|
||||
|
||||
import { SuggestionKindRegistryItem } from '../standardSql/suggestionsKindRegistry';
|
||||
import { StatementPosition, SuggestionKind } from '../types';
|
||||
|
||||
import { getSuggestionKinds } from './getSuggestionKind';
|
||||
|
||||
describe('getSuggestionKind', () => {
|
||||
const registry = new Registry((): SuggestionKindRegistryItem[] => {
|
||||
return [
|
||||
{
|
||||
id: StatementPosition.SelectKeyword,
|
||||
name: StatementPosition.SelectKeyword,
|
||||
kind: [SuggestionKind.SelectKeyword],
|
||||
},
|
||||
{
|
||||
id: StatementPosition.AfterSelectArguments,
|
||||
name: StatementPosition.AfterSelectArguments,
|
||||
kind: [SuggestionKind.Columns],
|
||||
},
|
||||
];
|
||||
});
|
||||
it('should return select kind when given select keyword as position', () => {
|
||||
const pos = [StatementPosition.SelectKeyword];
|
||||
expect([SuggestionKind.SelectKeyword]).toEqual(getSuggestionKinds(pos, registry));
|
||||
});
|
||||
it('should return column kind when given AfterSelectArguments as position', () => {
|
||||
const pos = [StatementPosition.AfterSelectArguments];
|
||||
expect([SuggestionKind.Columns]).toEqual(getSuggestionKinds(pos, registry));
|
||||
});
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
import { Registry } from '@grafana/data';
|
||||
|
||||
import { SuggestionKindRegistryItem } from '../standardSql/suggestionsKindRegistry';
|
||||
import { StatementPosition, SuggestionKind } from '../types';
|
||||
|
||||
/**
|
||||
* Given statement positions, returns list of suggestion kinds that apply to those positions.
|
||||
*/
|
||||
export function getSuggestionKinds(
|
||||
statementPosition: StatementPosition[],
|
||||
suggestionsKindRegistry: Registry<SuggestionKindRegistryItem>
|
||||
): SuggestionKind[] {
|
||||
let result: SuggestionKind[] = [];
|
||||
for (let i = 0; i < statementPosition.length; i++) {
|
||||
const exists = suggestionsKindRegistry.getIfExists(statementPosition[i]);
|
||||
if (exists) {
|
||||
result = result.concat(exists.kind);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { getMonacoMock } from '../mocks/Monaco';
|
||||
import { TextModel } from '../mocks/TextModel';
|
||||
import { multiLineFullQuery, singleLineFullQuery } from '../mocks/testData';
|
||||
import { DESC, LIMIT, SELECT } from '../standardSql/language';
|
||||
import { TokenType } from '../types';
|
||||
|
||||
import { linkedTokenBuilder } from './linkedTokenBuilder';
|
||||
|
||||
describe('linkedTokenBuilder', () => {
|
||||
describe('singleLineFullQuery', () => {
|
||||
const testModel = TextModel(singleLineFullQuery.query);
|
||||
const queriesMock = new Map();
|
||||
queriesMock.set(singleLineFullQuery.query, singleLineFullQuery.tokens);
|
||||
|
||||
const MonacoMock = getMonacoMock(queriesMock);
|
||||
|
||||
it('should add correct references to next LinkedToken', () => {
|
||||
const position: monacoTypes.IPosition = { lineNumber: 1, column: 0 };
|
||||
const current = linkedTokenBuilder(MonacoMock, testModel as monacoTypes.editor.ITextModel, position);
|
||||
|
||||
expect(current?.is(TokenType.Keyword, SELECT)).toBeTruthy();
|
||||
expect(current?.getNextNonWhiteSpaceToken()?.is(TokenType.Identifier, 'column1')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add correct references to previous LinkedToken', () => {
|
||||
const position: monacoTypes.IPosition = { lineNumber: 1, column: singleLineFullQuery.query.length };
|
||||
const current = linkedTokenBuilder(MonacoMock, testModel as monacoTypes.editor.ITextModel, position);
|
||||
expect(current?.is(TokenType.Number, '10')).toBeTruthy();
|
||||
expect(current?.getPreviousNonWhiteSpaceToken()?.is(TokenType.Keyword, 'LIMIT')).toBeTruthy();
|
||||
expect(
|
||||
current?.getPreviousNonWhiteSpaceToken()?.getPreviousNonWhiteSpaceToken()?.is(TokenType.Keyword, DESC)
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiLineFullQuery', () => {
|
||||
const testModel = TextModel(multiLineFullQuery.query);
|
||||
const queriesMock = new Map();
|
||||
queriesMock.set(multiLineFullQuery.query, multiLineFullQuery.tokens);
|
||||
|
||||
const MonacoMock = getMonacoMock(queriesMock);
|
||||
|
||||
it('should add LinkedToken with whitespace in case empty lines', () => {
|
||||
const position: monacoTypes.IPosition = { lineNumber: 3, column: 0 };
|
||||
const current = linkedTokenBuilder(MonacoMock, testModel as monacoTypes.editor.ITextModel, position);
|
||||
|
||||
expect(current).not.toBeNull();
|
||||
expect(current?.isWhiteSpace()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add correct references to next LinkedToken', () => {
|
||||
const position: monacoTypes.IPosition = { lineNumber: 1, column: 0 };
|
||||
const current = linkedTokenBuilder(MonacoMock, testModel as monacoTypes.editor.ITextModel, position);
|
||||
|
||||
expect(current?.is(TokenType.Keyword, SELECT)).toBeTruthy();
|
||||
expect(current?.getNextNonWhiteSpaceToken()?.is(TokenType.Identifier, 'column1')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add correct references to previous LinkedToken even when references spans over multiple lines', () => {
|
||||
const position: monacoTypes.IPosition = { lineNumber: 6, column: 7 };
|
||||
const current = linkedTokenBuilder(MonacoMock, testModel as monacoTypes.editor.ITextModel, position);
|
||||
|
||||
expect(current?.is(TokenType.Number, '10')).toBeTruthy();
|
||||
expect(current?.getPreviousNonWhiteSpaceToken()?.is(TokenType.Keyword, LIMIT)).toBeTruthy();
|
||||
expect(
|
||||
current?.getPreviousNonWhiteSpaceToken()?.getPreviousNonWhiteSpaceToken()?.is(TokenType.Keyword, DESC)
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,56 +0,0 @@
|
||||
import type { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { TokenType } from '../types';
|
||||
|
||||
import { LinkedToken } from './LinkedToken';
|
||||
import { Monaco } from './types';
|
||||
|
||||
export function linkedTokenBuilder(
|
||||
monaco: Monaco,
|
||||
model: monacoTypes.editor.ITextModel,
|
||||
position: monacoTypes.IPosition,
|
||||
languageId = 'sql'
|
||||
) {
|
||||
let current: LinkedToken | null = null;
|
||||
let previous: LinkedToken | null = null;
|
||||
const tokensPerLine = monaco.editor.tokenize(model.getValue() ?? '', languageId);
|
||||
for (let lineIndex = 0; lineIndex < tokensPerLine.length; lineIndex++) {
|
||||
const tokens = tokensPerLine[lineIndex];
|
||||
// In case position is first column in new line, add empty whitespace token so that links are not broken
|
||||
if (!tokens.length && previous) {
|
||||
const token: monacoTypes.Token = {
|
||||
offset: 0,
|
||||
type: TokenType.Whitespace,
|
||||
language: languageId,
|
||||
_tokenBrand: undefined,
|
||||
};
|
||||
tokens.push(token);
|
||||
}
|
||||
|
||||
for (let columnIndex = 0; columnIndex < tokens.length; columnIndex++) {
|
||||
const token = tokens[columnIndex];
|
||||
let endColumn =
|
||||
tokens.length > columnIndex + 1 ? tokens[columnIndex + 1].offset + 1 : model.getLineLength(lineIndex + 1) + 1;
|
||||
|
||||
const range: monacoTypes.IRange = {
|
||||
startLineNumber: lineIndex + 1,
|
||||
startColumn: token.offset === 0 ? 0 : token.offset + 1,
|
||||
endLineNumber: lineIndex + 1,
|
||||
endColumn,
|
||||
};
|
||||
|
||||
const value = model.getValueInRange(range);
|
||||
const sqlToken: LinkedToken = new LinkedToken(token.type, value, range, previous, null);
|
||||
|
||||
if (monaco.Range.containsPosition(range, position)) {
|
||||
current = sqlToken;
|
||||
}
|
||||
|
||||
if (previous) {
|
||||
previous.next = sqlToken;
|
||||
}
|
||||
previous = sqlToken;
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { CompletionItemKind, CompletionItemPriority } from '../types';
|
||||
|
||||
export const toCompletionItem = (
|
||||
value: string,
|
||||
range: monacoTypes.IRange,
|
||||
rest: Partial<monacoTypes.languages.CompletionItem> = {}
|
||||
) => {
|
||||
const item: monacoTypes.languages.CompletionItem = {
|
||||
label: value,
|
||||
insertText: value,
|
||||
kind: CompletionItemKind.Field,
|
||||
sortText: CompletionItemPriority.Medium,
|
||||
range,
|
||||
...rest,
|
||||
};
|
||||
return item;
|
||||
};
|
@ -1,57 +0,0 @@
|
||||
import { FROM, SCHEMA, SELECT } from '../standardSql/language';
|
||||
import { TokenType } from '../types';
|
||||
|
||||
import { LinkedToken } from './LinkedToken';
|
||||
|
||||
export const getSelectToken = (currentToken: LinkedToken | null) =>
|
||||
currentToken?.getPreviousOfType(TokenType.Keyword, SELECT) ?? null;
|
||||
|
||||
export const getSelectStatisticToken = (currentToken: LinkedToken | null) => {
|
||||
const assumedStatisticToken = getSelectToken(currentToken)?.getNextNonWhiteSpaceToken();
|
||||
return assumedStatisticToken?.isVariable() || assumedStatisticToken?.isFunction() ? assumedStatisticToken : null;
|
||||
};
|
||||
|
||||
export const getMetricNameToken = (currentToken: LinkedToken | null) => {
|
||||
// statistic function is followed by `(` and then an argument
|
||||
const assumedMetricNameToken = getSelectStatisticToken(currentToken)?.next?.next;
|
||||
return assumedMetricNameToken?.isVariable() || assumedMetricNameToken?.isIdentifier() ? assumedMetricNameToken : null;
|
||||
};
|
||||
|
||||
export const getFromKeywordToken = (currentToken: LinkedToken | null) => {
|
||||
const selectToken = getSelectToken(currentToken);
|
||||
return selectToken?.getNextOfType(TokenType.Keyword, FROM);
|
||||
};
|
||||
|
||||
export const getNamespaceToken = (currentToken: LinkedToken | null) => {
|
||||
const fromToken = getFromKeywordToken(currentToken);
|
||||
const nextNonWhiteSpace = fromToken?.getNextNonWhiteSpaceToken();
|
||||
|
||||
if (
|
||||
nextNonWhiteSpace?.isDoubleQuotedString() ||
|
||||
(nextNonWhiteSpace?.isVariable() && nextNonWhiteSpace?.value.toUpperCase() !== SCHEMA)
|
||||
) {
|
||||
// schema is not used
|
||||
return nextNonWhiteSpace;
|
||||
} else if (nextNonWhiteSpace?.isKeyword() && nextNonWhiteSpace.next?.is(TokenType.Parenthesis, '(')) {
|
||||
// schema is specified
|
||||
const assumedNamespaceToken = nextNonWhiteSpace.next?.next;
|
||||
if (assumedNamespaceToken?.isDoubleQuotedString() || assumedNamespaceToken?.isVariable()) {
|
||||
return assumedNamespaceToken;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getTableToken = (currentToken: LinkedToken | null) => {
|
||||
const fromToken = getFromKeywordToken(currentToken);
|
||||
const nextNonWhiteSpace = fromToken?.getNextNonWhiteSpaceToken();
|
||||
if (nextNonWhiteSpace?.isVariable()) {
|
||||
// TODO: resolve column from variable?
|
||||
return null;
|
||||
} else if (nextNonWhiteSpace?.isKeyword() && nextNonWhiteSpace.next?.is(TokenType.Parenthesis, '(')) {
|
||||
return null;
|
||||
} else {
|
||||
return nextNonWhiteSpace;
|
||||
}
|
||||
return null;
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
import { monacoTypes } from '@grafana/ui';
|
||||
|
||||
export interface Editor {
|
||||
tokenize: (value: string, languageId: string) => monacoTypes.Token[][];
|
||||
}
|
||||
|
||||
export interface Range {
|
||||
containsPosition: (range: monacoTypes.IRange, position: monacoTypes.IPosition) => boolean;
|
||||
}
|
||||
|
||||
export interface Monaco {
|
||||
editor: Editor;
|
||||
Range: Range;
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import { LinkedToken } from 'app/features/plugins/sql';
|
||||
import { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
|
||||
import {
|
||||
ColumnDefinition,
|
||||
@ -6,6 +5,7 @@ import {
|
||||
CompletionItemPriority,
|
||||
DB,
|
||||
LanguageCompletionProvider,
|
||||
LinkedToken,
|
||||
SQLQuery,
|
||||
StatementPlacementProvider,
|
||||
SuggestionKindProvider,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { LinkedToken } from 'app/features/plugins/sql';
|
||||
import { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
|
||||
import {
|
||||
Aggregate,
|
||||
@ -7,6 +6,7 @@ import {
|
||||
CompletionItemPriority,
|
||||
DB,
|
||||
LanguageCompletionProvider,
|
||||
LinkedToken,
|
||||
MetaDefinition,
|
||||
PositionContext,
|
||||
SQLQuery,
|
||||
|
17
yarn.lock
17
yarn.lock
@ -3799,6 +3799,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/experimental@npm:^0.0.2-canary.36":
|
||||
version: 0.0.2-canary.36
|
||||
resolution: "@grafana/experimental@npm:0.0.2-canary.36"
|
||||
dependencies:
|
||||
"@types/uuid": ^8.3.3
|
||||
uuid: ^8.3.2
|
||||
peerDependencies:
|
||||
"@emotion/css": 11.1.3
|
||||
react: 17.0.1
|
||||
react-select: 5.2.1
|
||||
checksum: 2aea6c2cbed323f7212c82df1b529897296c5c2938345dc0e7866cc4576c763f731f4f89803e6344ec7a3eb3940e0e8bdc4a01739b67833a5513913434130aba
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/google-sdk@npm:0.0.3":
|
||||
version: 0.0.3
|
||||
resolution: "@grafana/google-sdk@npm:0.0.3"
|
||||
@ -10983,7 +10997,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/uuid@npm:8.3.4":
|
||||
"@types/uuid@npm:8.3.4, @types/uuid@npm:^8.3.3":
|
||||
version: 8.3.4
|
||||
resolution: "@types/uuid@npm:8.3.4"
|
||||
checksum: 6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f
|
||||
@ -20233,6 +20247,7 @@ __metadata:
|
||||
"@grafana/e2e": "workspace:*"
|
||||
"@grafana/e2e-selectors": "workspace:*"
|
||||
"@grafana/eslint-config": 4.0.0
|
||||
"@grafana/experimental": ^0.0.2-canary.36
|
||||
"@grafana/google-sdk": 0.0.3
|
||||
"@grafana/lezer-logql": 0.0.14
|
||||
"@grafana/runtime": "workspace:*"
|
||||
|
Loading…
Reference in New Issue
Block a user