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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 581 additions and 0 deletions

View File

@ -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[][],
};

View File

@ -0,0 +1,5 @@
export const emptyQuery = {
query: '',
tokens: [],
position: { lineNumber: 1, column: 1 },
};

View File

@ -0,0 +1,5 @@
export { emptyQuery } from './empty';
export { whitespaceOnlyQuery } from './whitespaceQuery';
export { commentOnlyQuery } from './commentOnlyQuery';
export { singleLineFullQuery } from './singleLineFullQuery';
export { multiLineFullQuery } from './multiLineFullQuery';

View File

@ -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[][],
};

View File

@ -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[][],
};

View File

@ -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[][],
};

View File

@ -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 [];
},
},

View File

@ -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
);
});
});

View File

@ -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;
};

View File

@ -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 {