@@ -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.", "7"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "8"]
|
[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": [
|
"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.", "0"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
[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": [
|
"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.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
|
|||||||
@@ -254,6 +254,7 @@
|
|||||||
"@grafana/aws-sdk": "0.0.37",
|
"@grafana/aws-sdk": "0.0.37",
|
||||||
"@grafana/data": "workspace:*",
|
"@grafana/data": "workspace:*",
|
||||||
"@grafana/e2e-selectors": "workspace:*",
|
"@grafana/e2e-selectors": "workspace:*",
|
||||||
|
"@grafana/experimental": "^0.0.2-canary.36",
|
||||||
"@grafana/google-sdk": "0.0.3",
|
"@grafana/google-sdk": "0.0.3",
|
||||||
"@grafana/lezer-logql": "0.0.14",
|
"@grafana/lezer-logql": "0.0.14",
|
||||||
"@grafana/runtime": "workspace:*",
|
"@grafana/runtime": "workspace:*",
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
export * from './query-editor-raw';
|
|
||||||
export * from './visual-query-builder';
|
export * from './visual-query-builder';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
import { SQLEditor } from '@grafana/experimental';
|
||||||
|
|
||||||
import { LanguageCompletionProvider, SQLQuery } from '../../types';
|
import { LanguageCompletionProvider, SQLQuery } from '../../types';
|
||||||
import { formatSQL } from '../../utils/formatSQL';
|
import { formatSQL } from '../../utils/formatSQL';
|
||||||
|
|
||||||
import { SQLEditor } from './SQLEditor';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
query: SQLQuery;
|
query: SQLQuery;
|
||||||
onChange: (value: SQLQuery, processQuery: boolean) => void;
|
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 './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,
|
TimeRange,
|
||||||
toOption as toOptionFromData,
|
toOption as toOptionFromData,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { Monaco, monacoTypes } from '@grafana/ui';
|
import { CompletionItemKind, LanguageCompletionProvider } from '@grafana/experimental';
|
||||||
|
|
||||||
import { QueryWithDefaults } from './defaults';
|
import { QueryWithDefaults } from './defaults';
|
||||||
import {
|
import {
|
||||||
@@ -17,8 +17,6 @@ import {
|
|||||||
QueryEditorGroupByExpression,
|
QueryEditorGroupByExpression,
|
||||||
QueryEditorPropertyExpression,
|
QueryEditorPropertyExpression,
|
||||||
} from './expressions';
|
} from './expressions';
|
||||||
import { StatementPositionResolver, SuggestionsResolver } from './standardSql/types';
|
|
||||||
import { LinkedToken } from './utils/LinkedToken';
|
|
||||||
|
|
||||||
export interface SqlQueryForInterpolation {
|
export interface SqlQueryForInterpolation {
|
||||||
dataset?: string;
|
dataset?: string;
|
||||||
@@ -182,249 +180,17 @@ export interface MetaDefinition {
|
|||||||
kind: CompletionItemKind;
|
kind: CompletionItemKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export {
|
||||||
* Provides a context for suggestions resolver
|
CompletionItemKind,
|
||||||
* @alpha
|
LanguageCompletionProvider,
|
||||||
*/
|
LinkedToken,
|
||||||
export interface PositionContext {
|
ColumnDefinition,
|
||||||
position: monacoTypes.IPosition;
|
CompletionItemPriority,
|
||||||
kind: SuggestionKind[];
|
StatementPlacementProvider,
|
||||||
statementPosition: StatementPosition[];
|
SuggestionKindProvider,
|
||||||
currentToken: LinkedToken | null;
|
TableDefinition,
|
||||||
range: monacoTypes.IRange;
|
TokenType,
|
||||||
}
|
OperatorType,
|
||||||
|
StatementPosition,
|
||||||
export type CustomSuggestion = Partial<monacoTypes.languages.CompletionItem> & { label: string };
|
PositionContext,
|
||||||
|
} from '@grafana/experimental';
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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 { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
|
||||||
import {
|
import {
|
||||||
ColumnDefinition,
|
ColumnDefinition,
|
||||||
@@ -6,6 +5,7 @@ import {
|
|||||||
CompletionItemPriority,
|
CompletionItemPriority,
|
||||||
DB,
|
DB,
|
||||||
LanguageCompletionProvider,
|
LanguageCompletionProvider,
|
||||||
|
LinkedToken,
|
||||||
SQLQuery,
|
SQLQuery,
|
||||||
StatementPlacementProvider,
|
StatementPlacementProvider,
|
||||||
SuggestionKindProvider,
|
SuggestionKindProvider,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { LinkedToken } from 'app/features/plugins/sql';
|
|
||||||
import { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
|
import { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
|
||||||
import {
|
import {
|
||||||
Aggregate,
|
Aggregate,
|
||||||
@@ -7,6 +6,7 @@ import {
|
|||||||
CompletionItemPriority,
|
CompletionItemPriority,
|
||||||
DB,
|
DB,
|
||||||
LanguageCompletionProvider,
|
LanguageCompletionProvider,
|
||||||
|
LinkedToken,
|
||||||
MetaDefinition,
|
MetaDefinition,
|
||||||
PositionContext,
|
PositionContext,
|
||||||
SQLQuery,
|
SQLQuery,
|
||||||
|
|||||||
@@ -3799,6 +3799,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"@grafana/google-sdk@npm:0.0.3":
|
||||||
version: 0.0.3
|
version: 0.0.3
|
||||||
resolution: "@grafana/google-sdk@npm:0.0.3"
|
resolution: "@grafana/google-sdk@npm:0.0.3"
|
||||||
@@ -10983,7 +10997,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/uuid@npm:8.3.4":
|
"@types/uuid@npm:8.3.4, @types/uuid@npm:^8.3.3":
|
||||||
version: 8.3.4
|
version: 8.3.4
|
||||||
resolution: "@types/uuid@npm:8.3.4"
|
resolution: "@types/uuid@npm:8.3.4"
|
||||||
checksum: 6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f
|
checksum: 6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f
|
||||||
@@ -20233,6 +20247,7 @@ __metadata:
|
|||||||
"@grafana/e2e": "workspace:*"
|
"@grafana/e2e": "workspace:*"
|
||||||
"@grafana/e2e-selectors": "workspace:*"
|
"@grafana/e2e-selectors": "workspace:*"
|
||||||
"@grafana/eslint-config": 4.0.0
|
"@grafana/eslint-config": 4.0.0
|
||||||
|
"@grafana/experimental": ^0.0.2-canary.36
|
||||||
"@grafana/google-sdk": 0.0.3
|
"@grafana/google-sdk": 0.0.3
|
||||||
"@grafana/lezer-logql": 0.0.14
|
"@grafana/lezer-logql": 0.0.14
|
||||||
"@grafana/runtime": "workspace:*"
|
"@grafana/runtime": "workspace:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user