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