CloudWatch Logs: Add getStatementPosition function for Monaco editor (#70724)

* CloudWatch Logs: Add getStatementPosition function for Monaco editor

* Add test cases

* add command and function arg positions

* remove unneeded statement position

* remove debug stuff

* remove unneeded cases

* add filterarg

* remove editor changes

* fix tests

* fix lint

* add debug for testing

* Revert "add debug for testing"

This reverts commit 22dd0e7a77eb43dbed7c065c246da04cddfa1afc.
This commit is contained in:
Kevin Yu
2023-07-17 13:43:48 -07:00
committed by GitHub
parent dd41c7c262
commit 31e142633d
10 changed files with 581 additions and 0 deletions
@@ -0,0 +1,11 @@
import { monacoTypes } from '@grafana/ui';
import { LogsTokenTypes } from '../../language/logs/completion/types';
import { CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID } from '../../language/logs/definition';
export const commentOnlyQuery = {
query: `# comment ending with whitespace `,
tokens: [
[{ offset: 0, type: LogsTokenTypes.Comment, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID }],
] as monacoTypes.Token[][],
};
@@ -0,0 +1,5 @@
export const emptyQuery = {
query: '',
tokens: [],
position: { lineNumber: 1, column: 1 },
};
@@ -0,0 +1,5 @@
export { emptyQuery } from './empty';
export { whitespaceOnlyQuery } from './whitespaceQuery';
export { commentOnlyQuery } from './commentOnlyQuery';
export { singleLineFullQuery } from './singleLineFullQuery';
export { multiLineFullQuery } from './multiLineFullQuery';
@@ -0,0 +1,109 @@
import { monacoTypes } from '@grafana/ui';
import { LogsTokenTypes } from '../../language/logs/completion/types';
import { CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID } from '../../language/logs/definition';
export const multiLineFullQuery = {
query: `fields @timestamp, unmask(@message) as msg, @memorySize
| filter (@message like /error/ and bytes > 1000)
| parse @message /(?<NetworkInterface>eni-.*?)/
| stats count(NetworkInterface), max(@memorySize / 1000 / 1000) as provisonedMemoryMB by bin(1m)
# this is a comment with the next line left being intentionally blank
| limit 20`,
tokens: [
[
{ offset: 0, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 6, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 7, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 17, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 18, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 19, type: LogsTokenTypes.Function, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 25, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 26, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 34, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 35, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 36, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 38, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 39, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 42, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 43, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 44, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
],
[
{ offset: 0, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 1, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 2, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 8, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 9, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 10, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 18, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 19, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 26, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 27, type: LogsTokenTypes.Regexp, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 31, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 32, type: LogsTokenTypes.Operator, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 35, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 36, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 41, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 42, type: LogsTokenTypes.Operator, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 43, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 44, type: LogsTokenTypes.Number, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 48, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
],
[
{ offset: 0, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 1, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 2, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 7, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 8, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 16, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 17, type: LogsTokenTypes.Regexp, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
],
[
{ offset: 0, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 1, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 2, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 7, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 8, type: LogsTokenTypes.Function, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 13, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 14, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 30, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 31, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 32, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 33, type: LogsTokenTypes.Function, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 36, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 37, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 48, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 49, type: LogsTokenTypes.Operator, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 50, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 51, type: LogsTokenTypes.Number, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 55, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 56, type: LogsTokenTypes.Operator, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 57, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 58, type: LogsTokenTypes.Number, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 62, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 63, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 64, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 66, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 67, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 85, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 86, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 88, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 89, type: LogsTokenTypes.Function, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 92, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 93, type: LogsTokenTypes.Number, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 94, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 95, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
],
[{ offset: 0, type: LogsTokenTypes.Comment, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID }],
[{ offset: 0, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID }],
[
{ offset: 0, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 1, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 2, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 7, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 8, type: LogsTokenTypes.Number, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
],
] as monacoTypes.Token[][],
};
@@ -0,0 +1,26 @@
import { monacoTypes } from '@grafana/ui';
import { LogsTokenTypes } from '../../language/logs/completion/types';
import { CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID } from '../../language/logs/definition';
export const singleLineFullQuery = {
query: `fields @timestamp, @message | limit 20 # this is a comment`,
tokens: [
[
{ offset: 0, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 6, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 7, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 17, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 18, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 19, type: LogsTokenTypes.Identifier, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 27, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 28, type: LogsTokenTypes.Delimiter, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 29, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 30, type: LogsTokenTypes.Keyword, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 35, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 36, type: LogsTokenTypes.Number, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 38, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
{ offset: 39, type: LogsTokenTypes.Comment, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID },
],
] as monacoTypes.Token[][],
};
@@ -0,0 +1,11 @@
import { monacoTypes } from '@grafana/ui';
import { LogsTokenTypes } from '../../language/logs/completion/types';
import { CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID } from '../../language/logs/definition';
export const whitespaceOnlyQuery = {
query: ` `,
tokens: [
[{ offset: 0, type: LogsTokenTypes.Whitespace, language: CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID }],
] as monacoTypes.Token[][],
};
@@ -1,6 +1,7 @@
import { monacoTypes } from '@grafana/ui';
import { Monaco } from '../../language/monarch/types';
import * as CloudwatchLogsTestData from '../cloudwatch-logs-test-data';
import * as SQLTestData from '../cloudwatch-sql-test-data';
import * as DynamicLabelTestData from '../dynamic-label-test-data';
import * as MetricMathTestData from '../metric-math-test-data';
@@ -39,6 +40,16 @@ const MonacoMock: Monaco = {
};
return TestData[value];
}
if (languageId === 'cloudwatch-logs') {
const TestData = {
[CloudwatchLogsTestData.emptyQuery.query]: CloudwatchLogsTestData.emptyQuery.tokens,
[CloudwatchLogsTestData.whitespaceOnlyQuery.query]: CloudwatchLogsTestData.whitespaceOnlyQuery.tokens,
[CloudwatchLogsTestData.commentOnlyQuery.query]: CloudwatchLogsTestData.commentOnlyQuery.tokens,
[CloudwatchLogsTestData.singleLineFullQuery.query]: CloudwatchLogsTestData.singleLineFullQuery.tokens,
[CloudwatchLogsTestData.multiLineFullQuery.query]: CloudwatchLogsTestData.multiLineFullQuery.tokens,
};
return TestData[value];
}
return [];
},
},
@@ -0,0 +1,157 @@
import { monacoTypes } from '@grafana/ui';
import {
emptyQuery,
whitespaceOnlyQuery,
commentOnlyQuery,
singleLineFullQuery,
multiLineFullQuery,
} from '../../../__mocks__/cloudwatch-logs-test-data';
import MonacoMock from '../../../__mocks__/monarch/Monaco';
import TextModel from '../../../__mocks__/monarch/TextModel';
import { linkedTokenBuilder } from '../../monarch/linkedTokenBuilder';
import { StatementPosition } from '../../monarch/types';
import cloudWatchLogsLanguageDefinition from '../definition';
import { getStatementPosition } from './statementPosition';
import { LogsTokenTypes } from './types';
function generateToken(query: string, position: monacoTypes.IPosition) {
const testModel = TextModel(query);
return linkedTokenBuilder(
MonacoMock,
cloudWatchLogsLanguageDefinition,
testModel as monacoTypes.editor.ITextModel,
position,
LogsTokenTypes
);
}
describe('getStatementPosition', () => {
it('should return StatementPosition.NewCommand the current token is null', () => {
expect(getStatementPosition(null)).toEqual(StatementPosition.NewCommand);
});
it('should return StatementPosition.NewCommand for an empty query', () => {
expect(getStatementPosition(generateToken(emptyQuery.query, { lineNumber: 1, column: 1 }))).toEqual(
StatementPosition.NewCommand
);
});
it('should return StatementPosition.NewCommand for a query that is only whitespace', () => {
expect(getStatementPosition(generateToken(whitespaceOnlyQuery.query, { lineNumber: 1, column: 1 }))).toEqual(
StatementPosition.NewCommand
);
});
it('should return StatementPosition.Comment for a query with only a comment', () => {
expect(getStatementPosition(generateToken(commentOnlyQuery.query, { lineNumber: 1, column: 1 }))).toEqual(
StatementPosition.Comment
);
});
it('should return StatementPosition.NewCommand after a `|`', () => {
expect(getStatementPosition(generateToken(singleLineFullQuery.query, { lineNumber: 1, column: 30 }))).toEqual(
StatementPosition.NewCommand
);
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 2, column: 2 }))).toEqual(
StatementPosition.NewCommand
);
});
it('should return StatementPosition.FieldsKeyword inside the `fields` keyword', () => {
expect(getStatementPosition(generateToken(singleLineFullQuery.query, { lineNumber: 1, column: 6 }))).toEqual(
StatementPosition.FieldsKeyword
);
});
it('should return StatementPosition.AfterFieldsKeyword after the `fields` keyword', () => {
expect(getStatementPosition(generateToken(singleLineFullQuery.query, { lineNumber: 1, column: 7 }))).toEqual(
StatementPosition.AfterFieldsKeyword
);
});
it('should return StatementPosition.CommandArg after a keyword', () => {
expect(getStatementPosition(generateToken(singleLineFullQuery.query, { lineNumber: 1, column: 8 }))).toEqual(
StatementPosition.CommandArg
);
});
it('should return StatementPosition.AfterCommand after a function', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 2, column: 49 }))).toEqual(
StatementPosition.AfterCommand
);
});
it('should return StatementPosition.Function within a function', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 4, column: 9 }))).toEqual(
StatementPosition.Function
);
});
it('should return StatementPosition.FunctionArg when providing arguments to a function', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 4, column: 16 }))).toEqual(
StatementPosition.FunctionArg
);
});
it('should return StatementPosition.AfterFunction after a function', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 4, column: 64 }))).toEqual(
StatementPosition.AfterFunction
);
});
it('should return StatementPosition.ArithmeticOperatorArg after an arithmetic operator', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 4, column: 51 }))).toEqual(
StatementPosition.ArithmeticOperatorArg
);
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 4, column: 58 }))).toEqual(
StatementPosition.ArithmeticOperatorArg
);
});
it('should return StatementPosition.BooleanOperatorArg after a boolean operator', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 2, column: 37 }))).toEqual(
StatementPosition.BooleanOperatorArg
);
});
it('should return StatementPosition.ComparisonOperatorArg after a comparison operator', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 2, column: 44 }))).toEqual(
StatementPosition.ComparisonOperatorArg
);
});
it('should return StatementPosition.ArithmeticOperator after an arithmetic operator', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 4, column: 50 }))).toEqual(
StatementPosition.ArithmeticOperator
);
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 4, column: 57 }))).toEqual(
StatementPosition.ArithmeticOperator
);
});
it('should return StatementPosition.BooleanOperator after a boolean operator', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 2, column: 35 }))).toEqual(
StatementPosition.BooleanOperator
);
});
it('should return StatementPosition.ComparisonOperator after a comparison operator', () => {
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 2, column: 43 }))).toEqual(
StatementPosition.ComparisonOperator
);
});
it('should return StatementPosition.Comment when token is a comment', () => {
expect(getStatementPosition(generateToken(singleLineFullQuery.query, { lineNumber: 1, column: 40 }))).toEqual(
StatementPosition.Comment
);
expect(getStatementPosition(generateToken(commentOnlyQuery.query, { lineNumber: 1, column: 35 }))).toEqual(
StatementPosition.Comment
);
expect(getStatementPosition(generateToken(multiLineFullQuery.query, { lineNumber: 5, column: 3 }))).toEqual(
StatementPosition.Comment
);
});
});
@@ -0,0 +1,203 @@
import { LinkedToken } from '../../monarch/LinkedToken';
import { StatementPosition } from '../../monarch/types';
import {
DISPLAY,
FIELDS,
FILTER,
STATS,
SORT,
LIMIT,
PARSE,
DEDUP,
LOGS_COMMANDS,
LOGS_FUNCTION_OPERATORS,
LOGS_LOGIC_OPERATORS,
} from '../language';
import { LogsTokenTypes } from './types';
export const getStatementPosition = (currentToken: LinkedToken | null): StatementPosition => {
const previousNonWhiteSpace = currentToken?.getPreviousNonWhiteSpaceToken();
const nextNonWhiteSpace = currentToken?.getNextNonWhiteSpaceToken();
const normalizedCurrentToken = currentToken?.value?.toLowerCase();
const normalizedPreviousNonWhiteSpace = previousNonWhiteSpace?.value?.toLowerCase();
if (currentToken?.is(LogsTokenTypes.Comment)) {
return StatementPosition.Comment;
}
if (currentToken?.isFunction()) {
return StatementPosition.Function;
}
if (
currentToken === null ||
(currentToken?.isWhiteSpace() && previousNonWhiteSpace === null && nextNonWhiteSpace === null) ||
(previousNonWhiteSpace?.is(LogsTokenTypes.Delimiter, '|') && currentToken?.isWhiteSpace()) ||
(currentToken?.isIdentifier() &&
(previousNonWhiteSpace?.is(LogsTokenTypes.Delimiter, '|') || previousNonWhiteSpace === null))
) {
return StatementPosition.NewCommand;
}
if (
currentToken?.is(LogsTokenTypes.Delimiter, ')') ||
(currentToken?.isWhiteSpace() && previousNonWhiteSpace?.is(LogsTokenTypes.Delimiter, ')'))
) {
const openingParenthesis = currentToken?.getPreviousOfType(LogsTokenTypes.Delimiter, '(');
const normalizedNonWhitespacePreceedingOpeningParenthesis = openingParenthesis
?.getPreviousNonWhiteSpaceToken()
?.value?.toLowerCase();
if (normalizedNonWhitespacePreceedingOpeningParenthesis) {
if (LOGS_COMMANDS.includes(normalizedNonWhitespacePreceedingOpeningParenthesis)) {
return StatementPosition.AfterCommand;
}
if (LOGS_FUNCTION_OPERATORS.includes(normalizedNonWhitespacePreceedingOpeningParenthesis)) {
return StatementPosition.AfterFunction;
}
}
}
if (currentToken?.isKeyword() && normalizedCurrentToken) {
switch (normalizedCurrentToken) {
case DEDUP:
return StatementPosition.DedupKeyword;
case DISPLAY:
return StatementPosition.DisplayKeyword;
case FIELDS:
return StatementPosition.FieldsKeyword;
case FILTER:
return StatementPosition.FilterKeyword;
case LIMIT:
return StatementPosition.LimitKeyword;
case PARSE:
return StatementPosition.ParseKeyword;
case STATS:
return StatementPosition.StatsKeyword;
case SORT:
return StatementPosition.SortKeyword;
case 'as':
return StatementPosition.AsKeyword;
case 'by':
return StatementPosition.ByKeyword;
case 'in':
return StatementPosition.InKeyword;
case 'like':
return StatementPosition.LikeKeyword;
}
}
if (currentToken?.isWhiteSpace() && previousNonWhiteSpace?.isKeyword && normalizedPreviousNonWhiteSpace) {
switch (normalizedPreviousNonWhiteSpace) {
case DEDUP:
return StatementPosition.AfterDedupKeyword;
case DISPLAY:
return StatementPosition.AfterDisplayKeyword;
case FIELDS:
return StatementPosition.AfterFieldsKeyword;
case FILTER:
return StatementPosition.AfterFilterKeyword;
case LIMIT:
return StatementPosition.AfterLimitKeyword;
case PARSE:
return StatementPosition.AfterParseKeyword;
case STATS:
return StatementPosition.AfterStatsKeyword;
case SORT:
return StatementPosition.AfterSortKeyword;
case 'as':
return StatementPosition.AfterAsKeyword;
case 'by':
return StatementPosition.AfterByKeyword;
case 'in':
return StatementPosition.AfterInKeyword;
case 'like':
return StatementPosition.AfterLikeKeyword;
}
}
if (currentToken?.is(LogsTokenTypes.Operator) && normalizedCurrentToken) {
if (['+', '-', '*', '/', '^', '%'].includes(normalizedCurrentToken)) {
return StatementPosition.ArithmeticOperator;
}
if (['=', '!=', '<', '>', '<=', '>='].includes(normalizedCurrentToken)) {
return StatementPosition.ComparisonOperator;
}
if (LOGS_LOGIC_OPERATORS.includes(normalizedCurrentToken)) {
return StatementPosition.BooleanOperator;
}
}
if (previousNonWhiteSpace?.is(LogsTokenTypes.Operator) && normalizedPreviousNonWhiteSpace) {
if (['+', '-', '*', '/', '^', '%'].includes(normalizedPreviousNonWhiteSpace)) {
return StatementPosition.ArithmeticOperatorArg;
}
if (['=', '!=', '<', '>', '<=', '>='].includes(normalizedPreviousNonWhiteSpace)) {
return StatementPosition.ComparisonOperatorArg;
}
if (LOGS_LOGIC_OPERATORS.includes(normalizedPreviousNonWhiteSpace)) {
return StatementPosition.BooleanOperatorArg;
}
}
if (
currentToken?.isIdentifier() ||
currentToken?.isNumber() ||
currentToken?.is(LogsTokenTypes.Parenthesis, '()') ||
currentToken?.is(LogsTokenTypes.Delimiter, ',') ||
currentToken?.is(LogsTokenTypes.Parenthesis, ')') ||
(currentToken?.isWhiteSpace() && previousNonWhiteSpace?.is(LogsTokenTypes.Delimiter, ',')) ||
(currentToken?.isWhiteSpace() && previousNonWhiteSpace?.isIdentifier()) ||
(currentToken?.isWhiteSpace() &&
previousNonWhiteSpace?.isKeyword() &&
normalizedPreviousNonWhiteSpace &&
LOGS_COMMANDS.includes(normalizedPreviousNonWhiteSpace))
) {
const nearestKeyword = currentToken?.getPreviousOfType(LogsTokenTypes.Keyword);
const nearestFunction = currentToken?.getPreviousOfType(LogsTokenTypes.Function);
if (nearestKeyword !== null && nearestFunction === null) {
if (nearestKeyword.value === SORT) {
return StatementPosition.SortArg;
}
if (nearestKeyword.value === FILTER) {
return StatementPosition.FilterArg;
}
return StatementPosition.CommandArg;
}
if (nearestFunction !== null && nearestKeyword === null) {
return StatementPosition.FunctionArg;
}
if (nearestKeyword !== null && nearestFunction !== null) {
if (
nearestKeyword.range.startLineNumber > nearestFunction.range.startLineNumber ||
nearestKeyword.range.endColumn > nearestFunction.range.endColumn
) {
if (nearestKeyword.value === SORT) {
return StatementPosition.SortArg;
}
if (nearestKeyword.value === FILTER) {
return StatementPosition.FilterArg;
}
return StatementPosition.CommandArg;
}
if (
nearestFunction.range.startLineNumber > nearestKeyword.range.startLineNumber ||
nearestFunction.range.endColumn > nearestKeyword.range.endColumn
) {
return StatementPosition.FunctionArg;
}
}
}
return StatementPosition.Unknown;
};
@@ -45,6 +45,49 @@ export enum StatementPosition {
PredefinedFuncSecondArg,
AfterFunction,
WithinString,
// logs
NewCommand,
Comment,
DedupKeyword,
AfterDedupKeyword,
DisplayKeyword,
AfterDisplayKeyword,
FieldsKeyword,
AfterFieldsKeyword,
FilterKeyword,
AfterFilterKeyword,
FilterArg,
LimitKeyword,
AfterLimitKeyword,
ParseKeyword,
AfterParseKeyword,
SortKeyword,
AfterSortKeyword,
SortArg,
StatsKeyword,
AfterStatsKeyword,
AsKeyword,
AfterAsKeyword,
ByKeyword,
AfterByKeyword,
InKeyword,
AfterInKeyword,
LikeKeyword,
AfterLikeKeyword,
Function,
FunctionArg,
CommandArg,
AfterCommand,
ArithmeticOperator,
ArithmeticOperatorArg,
BooleanOperator,
BooleanOperatorArg,
ComparisonOperator,
ComparisonOperatorArg,
}
export enum SuggestionKind {