feat: add support for negation context in query processing

This commit is contained in:
ahrefabhi 2025-06-27 11:44:04 +05:30
parent 6f08aff58b
commit c783c5c7ee
2 changed files with 176 additions and 14 deletions

View File

@ -248,9 +248,9 @@ export function findKeyOperatorValueTriplet(
FilterQueryLexer.GT, FilterQueryLexer.GT,
FilterQueryLexer.GE, FilterQueryLexer.GE,
FilterQueryLexer.LIKE, FilterQueryLexer.LIKE,
FilterQueryLexer.NOT_LIKE, // FilterQueryLexer.NOT_LIKE,
FilterQueryLexer.ILIKE, FilterQueryLexer.ILIKE,
FilterQueryLexer.NOT_ILIKE, // FilterQueryLexer.NOT_ILIKE,
FilterQueryLexer.BETWEEN, FilterQueryLexer.BETWEEN,
FilterQueryLexer.EXISTS, FilterQueryLexer.EXISTS,
FilterQueryLexer.REGEXP, FilterQueryLexer.REGEXP,
@ -375,6 +375,7 @@ export function getQueryContextAtCursor(
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInKey: true, // Default to key context when input is empty isInKey: true, // Default to key context when input is empty
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -388,6 +389,7 @@ export function getQueryContextAtCursor(
stop: cursorIndex, stop: cursorIndex,
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInNegation: false,
isInKey: false, isInKey: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
@ -418,6 +420,8 @@ export function getQueryContextAtCursor(
const isInKey = currentToken.type === FilterQueryLexer.KEY; const isInKey = currentToken.type === FilterQueryLexer.KEY;
const isInNegation = currentToken.type === FilterQueryLexer.NOT;
const isInOperator = [ const isInOperator = [
FilterQueryLexer.EQUALS, FilterQueryLexer.EQUALS,
FilterQueryLexer.NOT_EQUALS, FilterQueryLexer.NOT_EQUALS,
@ -427,9 +431,9 @@ export function getQueryContextAtCursor(
FilterQueryLexer.GT, FilterQueryLexer.GT,
FilterQueryLexer.GE, FilterQueryLexer.GE,
FilterQueryLexer.LIKE, FilterQueryLexer.LIKE,
FilterQueryLexer.NOT_LIKE, // FilterQueryLexer.NOT_LIKE,
FilterQueryLexer.ILIKE, FilterQueryLexer.ILIKE,
FilterQueryLexer.NOT_ILIKE, // FilterQueryLexer.NOT_ILIKE,
FilterQueryLexer.BETWEEN, FilterQueryLexer.BETWEEN,
FilterQueryLexer.EXISTS, FilterQueryLexer.EXISTS,
FilterQueryLexer.REGEXP, FilterQueryLexer.REGEXP,
@ -442,7 +446,7 @@ export function getQueryContextAtCursor(
FilterQueryLexer.HAS, FilterQueryLexer.HAS,
FilterQueryLexer.HASANY, FilterQueryLexer.HASANY,
FilterQueryLexer.HASALL, FilterQueryLexer.HASALL,
FilterQueryLexer.HASNONE, // FilterQueryLexer.HASNONE,
].includes(currentToken.type); ].includes(currentToken.type);
// Get the context-related tokens (key, operator, value) // Get the context-related tokens (key, operator, value)
@ -473,6 +477,7 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: true, isInOperator: true,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -491,6 +496,7 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: true, isInValue: true,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -509,6 +515,7 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: true, isInConjunction: true,
@ -526,6 +533,7 @@ export function getQueryContextAtCursor(
stop: currentToken.stop, stop: currentToken.stop,
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInNegation: false,
isInKey: true, isInKey: true,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
@ -546,6 +554,7 @@ export function getQueryContextAtCursor(
stop: currentToken.stop, stop: currentToken.stop,
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInNegation: false,
isInKey: true, isInKey: true,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
@ -567,6 +576,7 @@ export function getQueryContextAtCursor(
stop: currentToken.stop, stop: currentToken.stop,
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInNegation: false,
isInKey: false, isInKey: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
@ -585,6 +595,7 @@ export function getQueryContextAtCursor(
stop: currentToken.stop, stop: currentToken.stop,
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: true, isInValue: true,
isInNegation: false,
isInKey: false, isInKey: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
@ -612,6 +623,25 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInKey: true, isInKey: true,
isInNegation: false,
isInOperator: false,
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
...relationTokens, // Include related tokens
};
}
if (isInNegation && nextToken.type === FilterQueryLexer.NOT) {
return {
tokenType: currentToken.type,
text: currentToken.text,
start: currentToken.start,
stop: currentToken.stop,
currentToken: currentToken.text,
isInValue: false,
isInKey: false,
isInNegation: true,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -636,6 +666,7 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: true, isInOperator: true,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -660,6 +691,7 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: true, isInValue: true,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -684,6 +716,7 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: true, isInConjunction: true,
@ -703,6 +736,25 @@ export function getQueryContextAtCursor(
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInKey: true, isInKey: true,
isInNegation: false,
isInOperator: false,
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
...relationTokens, // Include related tokens
};
}
if (nextToken.type === FilterQueryLexer.NOT) {
return {
tokenType: -1,
text: '',
start: cursorIndex,
stop: cursorIndex,
currentToken: '',
isInValue: false,
isInKey: false,
isInNegation: true,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -729,6 +781,7 @@ export function getQueryContextAtCursor(
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: true, isInOperator: true,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -750,6 +803,7 @@ export function getQueryContextAtCursor(
start: cursorIndex, start: cursorIndex,
stop: cursorIndex, stop: cursorIndex,
currentToken: '', currentToken: '',
isInNegation: false,
isInValue: true, isInValue: true,
isInKey: false, isInKey: false,
isInOperator: false, isInOperator: false,
@ -769,6 +823,7 @@ export function getQueryContextAtCursor(
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: true, isInConjunction: true,
@ -794,6 +849,7 @@ export function getQueryContextAtCursor(
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -812,6 +868,7 @@ export function getQueryContextAtCursor(
currentToken: currentToken.text, currentToken: currentToken.text,
isInValue, isInValue,
isInKey, isInKey,
isInNegation,
isInOperator, isInOperator,
isInFunction, isInFunction,
isInConjunction, isInConjunction,
@ -828,6 +885,7 @@ export function getQueryContextAtCursor(
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -836,6 +894,16 @@ export function getQueryContextAtCursor(
} }
} }
export const negationQueryOperatorSuggestions = [
{ label: 'LIKE', type: 'operator', info: 'Like' },
{ label: 'ILIKE', type: 'operator', info: 'Case insensitive like' },
{ label: 'EXISTS', type: 'operator', info: 'Exists' },
{ label: 'BETWEEN', type: 'operator', info: 'Between' },
{ label: 'IN', type: 'operator', info: 'In' },
{ label: 'REGEXP', type: 'operator', info: 'Regular expression' },
{ label: 'CONTAINS', type: 'operator', info: 'Contains' },
];
export const queryOperatorSuggestions = [ export const queryOperatorSuggestions = [
{ label: '=', type: 'operator', info: 'Equal to' }, { label: '=', type: 'operator', info: 'Equal to' },
{ label: '!=', type: 'operator', info: 'Not equal to' }, { label: '!=', type: 'operator', info: 'Not equal to' },
@ -843,14 +911,6 @@ export const queryOperatorSuggestions = [
{ label: '<', type: 'operator', info: 'Less than' }, { label: '<', type: 'operator', info: 'Less than' },
{ label: '>=', type: 'operator', info: 'Greater than or equal to' }, { label: '>=', type: 'operator', info: 'Greater than or equal to' },
{ label: '<=', type: 'operator', info: 'Less than or equal to' }, { label: '<=', type: 'operator', info: 'Less than or equal to' },
{ label: 'LIKE', type: 'operator', info: 'Like' },
{ label: 'ILIKE', type: 'operator', info: 'Case insensitive like' },
{ label: 'BETWEEN', type: 'operator', info: 'Between' },
{ label: 'EXISTS', type: 'operator', info: 'Exists' },
{ label: 'NOT_EXISTS', type: 'operator', info: 'Not Exists' },
{ label: 'REGEXP', type: 'operator', info: 'Regular expression' },
{ label: 'CONTAINS', type: 'operator', info: 'Contains' },
{ label: 'IN', type: 'operator', info: 'In' },
{ label: 'NOT', type: 'operator', info: 'Not' }, { label: 'NOT', type: 'operator', info: 'Not' },
{ label: 'NOT_LIKE', type: 'operator', info: 'Not like' }, ...negationQueryOperatorSuggestions,
]; ];

View File

@ -51,6 +51,7 @@ function normalizeSpaces(query: string): string {
export function createContext( export function createContext(
token: Token, token: Token,
isInKey: boolean, isInKey: boolean,
isInNegation: boolean,
isInOperator: boolean, isInOperator: boolean,
isInValue: boolean, isInValue: boolean,
keyToken?: string, keyToken?: string,
@ -66,6 +67,7 @@ export function createContext(
stop: token.stop, stop: token.stop,
currentToken: token.text || '', currentToken: token.text || '',
isInKey, isInKey,
isInNegation,
isInOperator, isInOperator,
isInValue, isInValue,
isInFunction: false, isInFunction: false,
@ -85,6 +87,7 @@ function determineTokenContext(
query: string, query: string,
): { ): {
isInKey: boolean; isInKey: boolean;
isInNegation: boolean;
isInOperator: boolean; isInOperator: boolean;
isInValue: boolean; isInValue: boolean;
isInFunction: boolean; isInFunction: boolean;
@ -92,6 +95,7 @@ function determineTokenContext(
isInParenthesis: boolean; isInParenthesis: boolean;
} { } {
let isInKey: boolean = false; let isInKey: boolean = false;
let isInNegation: boolean = false;
let isInOperator: boolean = false; let isInOperator: boolean = false;
let isInValue: boolean = false; let isInValue: boolean = false;
let isInFunction: boolean = false; let isInFunction: boolean = false;
@ -126,6 +130,9 @@ function determineTokenContext(
} }
} }
// Negation context
isInNegation = tokenType === FilterQueryLexer.NOT;
// Function context // Function context
isInFunction = isFunctionToken(tokenType); isInFunction = isFunctionToken(tokenType);
@ -137,6 +144,7 @@ function determineTokenContext(
return { return {
isInKey, isInKey,
isInNegation,
isInOperator, isInOperator,
isInValue, isInValue,
isInFunction, isInFunction,
@ -156,6 +164,7 @@ function determineContextBoundaries(
operatorContext: { start: number; end: number } | null; operatorContext: { start: number; end: number } | null;
valueContext: { start: number; end: number } | null; valueContext: { start: number; end: number } | null;
conjunctionContext: { start: number; end: number } | null; conjunctionContext: { start: number; end: number } | null;
negationContext: { start: number; end: number } | null;
bracketContext: { start: number; end: number; isForList: boolean } | null; bracketContext: { start: number; end: number; isForList: boolean } | null;
} { } {
// Find the current query pair based on cursor position // Find the current query pair based on cursor position
@ -343,6 +352,12 @@ function determineContextBoundaries(
if (currentPair) { if (currentPair) {
const { position } = currentPair; const { position } = currentPair;
// Negation context: from negationStart to negationEnd
const negationContext = {
start: position.negationStart ?? 0,
end: position.negationEnd ?? 0,
};
// Key context: from keyStart to keyEnd // Key context: from keyStart to keyEnd
const keyContext = { const keyContext = {
start: position.keyStart, start: position.keyStart,
@ -412,6 +427,7 @@ function determineContextBoundaries(
return { return {
keyContext, keyContext,
negationContext,
operatorContext, operatorContext,
valueContext, valueContext,
conjunctionContext, conjunctionContext,
@ -433,6 +449,18 @@ function determineContextBoundaries(
if (tokenAtCursor.type === FilterQueryLexer.KEY) { if (tokenAtCursor.type === FilterQueryLexer.KEY) {
return { return {
keyContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop }, keyContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop },
negationContext: null,
operatorContext: null,
valueContext: null,
conjunctionContext: null,
bracketContext,
};
}
if (tokenAtCursor.type === FilterQueryLexer.NOT) {
return {
keyContext: null,
negationContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop },
operatorContext: null, operatorContext: null,
valueContext: null, valueContext: null,
conjunctionContext: null, conjunctionContext: null,
@ -443,6 +471,7 @@ function determineContextBoundaries(
if (isOperatorToken(tokenAtCursor.type)) { if (isOperatorToken(tokenAtCursor.type)) {
return { return {
keyContext: null, keyContext: null,
negationContext: null,
operatorContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop }, operatorContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop },
valueContext: null, valueContext: null,
conjunctionContext: null, conjunctionContext: null,
@ -453,6 +482,7 @@ function determineContextBoundaries(
if (isValueToken(tokenAtCursor.type)) { if (isValueToken(tokenAtCursor.type)) {
return { return {
keyContext: null, keyContext: null,
negationContext: null,
operatorContext: null, operatorContext: null,
valueContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop }, valueContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop },
conjunctionContext: null, conjunctionContext: null,
@ -463,6 +493,7 @@ function determineContextBoundaries(
if (isConjunctionToken(tokenAtCursor.type)) { if (isConjunctionToken(tokenAtCursor.type)) {
return { return {
keyContext: null, keyContext: null,
negationContext: null,
operatorContext: null, operatorContext: null,
valueContext: null, valueContext: null,
conjunctionContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop }, conjunctionContext: { start: tokenAtCursor.start, end: tokenAtCursor.stop },
@ -474,6 +505,7 @@ function determineContextBoundaries(
// If no current pair, return null for all contexts except possibly bracket context // If no current pair, return null for all contexts except possibly bracket context
return { return {
keyContext: null, keyContext: null,
negationContext: null,
operatorContext: null, operatorContext: null,
valueContext: null, valueContext: null,
conjunctionContext: null, conjunctionContext: null,
@ -515,6 +547,7 @@ export function getQueryContextAtCursor(
stop: cursorIndex, stop: cursorIndex,
currentToken: '', currentToken: '',
isInKey: true, isInKey: true,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -660,6 +693,12 @@ export function getQueryContextAtCursor(
adjustedCursorIndex <= contextBoundaries.keyContext.end) || adjustedCursorIndex <= contextBoundaries.keyContext.end) ||
adjustedCursorIndex === contextBoundaries.keyContext.end + 1); adjustedCursorIndex === contextBoundaries.keyContext.end + 1);
const isInNegationBoundary =
contextBoundaries.negationContext &&
((adjustedCursorIndex >= contextBoundaries.negationContext.start &&
adjustedCursorIndex <= contextBoundaries.negationContext.end) ||
adjustedCursorIndex === contextBoundaries.negationContext.end + 1);
const isInOperatorBoundary = const isInOperatorBoundary =
contextBoundaries.operatorContext && contextBoundaries.operatorContext &&
((adjustedCursorIndex >= contextBoundaries.operatorContext.start && ((adjustedCursorIndex >= contextBoundaries.operatorContext.start &&
@ -703,6 +742,7 @@ export function getQueryContextAtCursor(
// If cursor is within a specific context boundary, this takes precedence // If cursor is within a specific context boundary, this takes precedence
if ( if (
isInKeyBoundary || isInKeyBoundary ||
isInNegationBoundary ||
isInOperatorBoundary || isInOperatorBoundary ||
isInValueBoundary || isInValueBoundary ||
isInConjunctionBoundary || isInConjunctionBoundary ||
@ -736,6 +776,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: '', currentToken: '',
isInKey: isInKeyBoundary || false, isInKey: isInKeyBoundary || false,
isInNegation: isInNegationBoundary || false,
isInOperator: isInOperatorBoundary || false, isInOperator: isInOperatorBoundary || false,
isInValue: finalIsInValue || false, isInValue: finalIsInValue || false,
isInConjunction: finalIsInConjunction || false, isInConjunction: finalIsInConjunction || false,
@ -824,6 +865,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: '', currentToken: '',
isInKey: true, // Default to key context when input is empty isInKey: true, // Default to key context when input is empty
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -853,6 +895,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: lastTokenBeforeCursor.text, currentToken: lastTokenBeforeCursor.text,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: true, // After key + space, should be operator context isInOperator: true, // After key + space, should be operator context
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -865,6 +908,27 @@ export function getQueryContextAtCursor(
}; };
} }
if (lastTokenContext.isInNegation) {
return {
tokenType: lastTokenBeforeCursor.type,
text: lastTokenBeforeCursor.text,
start: adjustedCursorIndex,
stop: adjustedCursorIndex,
currentToken: lastTokenBeforeCursor.text,
isInKey: false,
isInNegation: false,
isInOperator: true, // After key + space + NOT, should be operator context
isInValue: false,
isInFunction: false,
isInConjunction: false,
isInParenthesis: false,
isInBracketList: false,
keyToken: lastTokenBeforeCursor.text,
queryPairs: queryPairs,
currentPair: currentPair,
};
}
if (lastTokenContext.isInOperator) { if (lastTokenContext.isInOperator) {
// If we just typed an operator and then a space, we move to value context // If we just typed an operator and then a space, we move to value context
const keyFromPair = currentPair?.key || ''; const keyFromPair = currentPair?.key || '';
@ -876,6 +940,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: lastTokenBeforeCursor.text, currentToken: lastTokenBeforeCursor.text,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: !isNonValueToken, // After operator + space, should be value context isInValue: !isNonValueToken, // After operator + space, should be value context
isInFunction: false, isInFunction: false,
@ -900,6 +965,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: lastTokenBeforeCursor.text, currentToken: lastTokenBeforeCursor.text,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -923,6 +989,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: lastTokenBeforeCursor.text, currentToken: lastTokenBeforeCursor.text,
isInKey: true, // After conjunction + space, should be key context isInKey: true, // After conjunction + space, should be key context
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -1016,6 +1083,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: previousToken.text, currentToken: previousToken.text,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: true, // Always in value context after operator isInValue: true, // Always in value context after operator
isInFunction: false, isInFunction: false,
@ -1038,6 +1106,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: previousToken.text, currentToken: previousToken.text,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: true, // After key, progress to operator context isInOperator: true, // After key, progress to operator context
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -1058,6 +1127,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: previousToken.text, currentToken: previousToken.text,
isInKey: false, isInKey: false,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -1080,6 +1150,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: previousToken.text, currentToken: previousToken.text,
isInKey: true, // After conjunction, progress back to key context isInKey: true, // After conjunction, progress back to key context
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -1100,6 +1171,7 @@ export function getQueryContextAtCursor(
stop: adjustedCursorIndex, stop: adjustedCursorIndex,
currentToken: '', currentToken: '',
isInKey: true, isInKey: true,
isInNegation: false,
isInOperator: false, isInOperator: false,
isInValue: false, isInValue: false,
isInFunction: false, isInFunction: false,
@ -1119,6 +1191,7 @@ export function getQueryContextAtCursor(
currentToken: '', currentToken: '',
isInValue: false, isInValue: false,
isInKey: true, // Default to key context on error isInKey: true, // Default to key context on error
isInNegation: false,
isInOperator: false, isInOperator: false,
isInFunction: false, isInFunction: false,
isInConjunction: false, isInConjunction: false,
@ -1185,6 +1258,7 @@ export function extractQueryPairs(query: string): IQueryPair[] {
key: currentPair.key, key: currentPair.key,
operator: currentPair.operator || '', operator: currentPair.operator || '',
value: currentPair.value, value: currentPair.value,
hasNegation: currentPair.hasNegation || false,
position: { position: {
keyStart: currentPair.position?.keyStart || 0, keyStart: currentPair.position?.keyStart || 0,
keyEnd: currentPair.position?.keyEnd || 0, keyEnd: currentPair.position?.keyEnd || 0,
@ -1192,6 +1266,8 @@ export function extractQueryPairs(query: string): IQueryPair[] {
operatorEnd: currentPair.position?.operatorEnd || 0, operatorEnd: currentPair.position?.operatorEnd || 0,
valueStart: currentPair.position?.valueStart, valueStart: currentPair.position?.valueStart,
valueEnd: currentPair.position?.valueEnd, valueEnd: currentPair.position?.valueEnd,
negationStart: currentPair.position?.negationStart || 0,
negationEnd: currentPair.position?.negationEnd || 0,
}, },
isComplete: !!( isComplete: !!(
currentPair.key && currentPair.key &&
@ -1212,6 +1288,22 @@ export function extractQueryPairs(query: string): IQueryPair[] {
}, },
}; };
} }
// If NOT token comes set hasNegation to true
else if (token.type === FilterQueryLexer.NOT && currentPair) {
currentPair.hasNegation = true;
currentPair.position = {
keyStart: currentPair.position?.keyStart || 0,
keyEnd: currentPair.position?.keyEnd || 0,
operatorStart: currentPair.position?.operatorStart || 0,
operatorEnd: currentPair.position?.operatorEnd || 0,
valueStart: currentPair.position?.valueStart,
valueEnd: currentPair.position?.valueEnd,
negationStart: token.start,
negationEnd: token.stop,
};
}
// If token is an operator and we have a key, add the operator // If token is an operator and we have a key, add the operator
else if ( else if (
isOperatorToken(token.type) && isOperatorToken(token.type) &&
@ -1228,6 +1320,8 @@ export function extractQueryPairs(query: string): IQueryPair[] {
operatorEnd: token.stop, operatorEnd: token.stop,
valueStart: currentPair.position?.valueStart, valueStart: currentPair.position?.valueStart,
valueEnd: currentPair.position?.valueEnd, valueEnd: currentPair.position?.valueEnd,
negationStart: currentPair.position?.negationStart || 0,
negationEnd: currentPair.position?.negationEnd || 0,
}; };
} }
// If token is a value and we have a key and operator, add the value // If token is a value and we have a key and operator, add the value
@ -1247,6 +1341,8 @@ export function extractQueryPairs(query: string): IQueryPair[] {
operatorEnd: currentPair.position?.operatorEnd || 0, operatorEnd: currentPair.position?.operatorEnd || 0,
valueStart: token.start, valueStart: token.start,
valueEnd: token.stop, valueEnd: token.stop,
negationStart: currentPair.position?.negationStart || 0,
negationEnd: currentPair.position?.negationEnd || 0,
}; };
} }
// If token is a conjunction (AND/OR), finalize the current pair // If token is a conjunction (AND/OR), finalize the current pair
@ -1255,6 +1351,7 @@ export function extractQueryPairs(query: string): IQueryPair[] {
key: currentPair.key, key: currentPair.key,
operator: currentPair.operator || '', operator: currentPair.operator || '',
value: currentPair.value, value: currentPair.value,
hasNegation: currentPair.hasNegation || false,
position: { position: {
keyStart: currentPair.position?.keyStart || 0, keyStart: currentPair.position?.keyStart || 0,
keyEnd: currentPair.position?.keyEnd || 0, keyEnd: currentPair.position?.keyEnd || 0,
@ -1262,6 +1359,8 @@ export function extractQueryPairs(query: string): IQueryPair[] {
operatorEnd: currentPair.position?.operatorEnd || 0, operatorEnd: currentPair.position?.operatorEnd || 0,
valueStart: currentPair.position?.valueStart, valueStart: currentPair.position?.valueStart,
valueEnd: currentPair.position?.valueEnd, valueEnd: currentPair.position?.valueEnd,
negationStart: currentPair.position?.negationStart || 0,
negationEnd: currentPair.position?.negationEnd || 0,
}, },
isComplete: !!( isComplete: !!(
currentPair.key && currentPair.key &&
@ -1281,6 +1380,7 @@ export function extractQueryPairs(query: string): IQueryPair[] {
key: currentPair.key, key: currentPair.key,
operator: currentPair.operator || '', operator: currentPair.operator || '',
value: currentPair.value, value: currentPair.value,
hasNegation: currentPair.hasNegation || false,
position: { position: {
keyStart: currentPair.position?.keyStart || 0, keyStart: currentPair.position?.keyStart || 0,
keyEnd: currentPair.position?.keyEnd || 0, keyEnd: currentPair.position?.keyEnd || 0,
@ -1288,6 +1388,8 @@ export function extractQueryPairs(query: string): IQueryPair[] {
operatorEnd: currentPair.position?.operatorEnd || 0, operatorEnd: currentPair.position?.operatorEnd || 0,
valueStart: currentPair.position?.valueStart, valueStart: currentPair.position?.valueStart,
valueEnd: currentPair.position?.valueEnd, valueEnd: currentPair.position?.valueEnd,
negationStart: currentPair.position?.negationStart || 0,
negationEnd: currentPair.position?.negationEnd || 0,
}, },
isComplete: !!( isComplete: !!(
currentPair.key && currentPair.key &&