feat: add types, base components

This commit is contained in:
Yunus M 2025-04-26 23:53:30 +05:30 committed by ahrefabhi
parent a37a98b0ae
commit b856cdadf7
13 changed files with 2743 additions and 3 deletions

View File

@ -1,5 +1,11 @@
import QueryWhereClause from './WhereClause/WhereClause';
function QueryBuilderV2(): JSX.Element {
return <div className="query-builder-v2">QueryBuilderV2</div>;
return (
<div className="query-builder-v2">
<QueryWhereClause />
</div>
);
}
export default QueryBuilderV2;

View File

@ -0,0 +1,88 @@
.where-clause {
width: 100%;
border: 1px solid #d9d9d9;
border-radius: 2px;
background-color: #fff;
&-header {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
}
&-content {
padding: 12px;
.query-input {
width: 100%;
margin-bottom: 8px;
font-family: monospace;
&.error {
border-color: #ff4d4f;
}
&.valid {
border-color: #52c41a;
}
}
.error-alert,
.success-alert {
margin-bottom: 8px;
}
.query-examples {
margin-top: 8px;
ul {
margin: 8px 0;
padding-left: 0;
list-style-type: none;
li {
margin: 4px 0;
}
}
}
}
}
.condition-builder {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.conditions-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.condition-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px;
background: #f5f5f5;
color: #000;
border-radius: 4px;
}
.where-clause-content {
.query-examples {
ul {
li {
margin: 4px 0;
padding-left: 0;
list-style-type: none;
color: #000;
code {
color: #000;
}
}
}
}
}

View File

@ -0,0 +1,138 @@
/* eslint-disable no-nested-ternary */
import './WhereClause.styles.scss';
import { Input, Typography } from 'antd';
import { useCallback, useEffect, useState } from 'react';
import { IQueryContext, IValidationResult } from 'types/antlrQueryTypes';
import { getQueryContextAtCursor, validateQuery } from 'utils/antlrQueryUtils';
const { Text } = Typography;
function QueryWhereClause(): JSX.Element {
const [query, setQuery] = useState<string>('');
const [cursorPosition, setCursorPosition] = useState<number>(0);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [queryContext, setQueryContext] = useState<IQueryContext | null>(null);
const [validation, setValidation] = useState<IValidationResult>({
isValid: false,
message: '',
errors: [],
});
console.log({
cursorPosition,
queryContext,
validation,
isLoading,
});
const handleQueryChange = useCallback(async (newQuery: string) => {
setIsLoading(true);
setQuery(newQuery);
try {
const validationResponse = validateQuery(newQuery);
setValidation(validationResponse);
} catch (error) {
setValidation({
isValid: false,
message: 'Failed to process query',
errors: [error instanceof Error ? error.message : 'Unknown error'],
});
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
if (query) {
const context = getQueryContextAtCursor(query, cursorPosition);
setQueryContext(context as IQueryContext);
}
}, [query, cursorPosition]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
const { value, selectionStart } = e.target;
setQuery(value);
setCursorPosition(selectionStart || 0);
handleQueryChange(value);
};
const handleCursorMove = (e: React.SyntheticEvent<HTMLInputElement>): void => {
const { selectionStart } = e.currentTarget;
setCursorPosition(selectionStart || 0);
};
return (
<div className="where-clause">
<div className="where-clause-header">
<Text strong>Where</Text>
</div>
<div className="where-clause-content">
<Input
value={query}
onChange={handleChange}
onSelect={handleCursorMove}
onKeyUp={handleCursorMove}
placeholder="Enter your query (e.g., status = 'error' AND service = 'frontend')"
/>
{queryContext && (
<div className="query-context">
<h3>Current Context</h3>
<div className="context-details">
<p>
<strong>Token:</strong> {queryContext.currentToken}
</p>
<p>
<strong>Type:</strong> {queryContext.tokenType}
</p>
<p>
<strong>Context:</strong>{' '}
{queryContext.isInValue
? 'Value'
: queryContext.isInKey
? 'Key'
: queryContext.isInOperator
? 'Operator'
: queryContext.isInFunction
? 'Function'
: 'Unknown'}
</p>
</div>
</div>
)}
<div className="query-examples">
<Text type="secondary">Examples:</Text>
<ul>
<li>
<Text code>status = &apos;error&apos;</Text>
</li>
<li>
<Text code>
service = &apos;frontend&apos; AND level = &apos;error&apos;
</Text>
</li>
<li>
<Text code>message LIKE &apos;%timeout%&apos;</Text>
</li>
<li>
<Text code>duration {'>'} 1000</Text>
</li>
<li>
<Text code>tags IN [&apos;prod&apos;, &apos;frontend&apos;]</Text>
</li>
<li>
<Text code>
NOT (status = &apos;error&apos; OR level = &apos;error&apos;)
</Text>
</li>
</ul>
</div>
</div>
</div>
);
}
export default QueryWhereClause;

View File

@ -0,0 +1,11 @@
import QueryBuilderV2 from 'components/QueryBuilderV2/QueryBuilderV2';
function Home2(): JSX.Element {
return (
<div>
<QueryBuilderV2 />
</div>
);
}
export default Home2;

View File

@ -1,7 +1,7 @@
import Home from 'container/Home';
import Home2 from 'container/Home/Home2';
function HomePage(): JSX.Element {
return <Home />;
return <Home2 />;
}
export default HomePage;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,49 @@
LPAREN=1
RPAREN=2
LBRACK=3
RBRACK=4
COMMA=5
EQUALS=6
NOT_EQUALS=7
NEQ=8
LT=9
LE=10
GT=11
GE=12
LIKE=13
NOT_LIKE=14
ILIKE=15
NOT_ILIKE=16
BETWEEN=17
NOT_BETWEEN=18
EXISTS=19
NOT_EXISTS=20
REGEXP=21
NOT_REGEXP=22
CONTAINS=23
NOT_CONTAINS=24
IN=25
NOT_IN=26
NOT=27
AND=28
OR=29
HAS=30
HASANY=31
HASALL=32
HASNONE=33
BOOL=34
NUMBER=35
QUOTED_TEXT=36
KEY=37
WS=38
'('=1
')'=2
'['=3
']'=4
','=5
'!='=7
'<>'=8
'<'=9
'<='=10
'>'=11
'>='=12

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,371 @@
// Generated from /Users/younix/Documents/SigNoz-Repos/signoz/frontend/src/query-grammar/FilterQuery.g4 by ANTLR 4.13.1
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"})
public class FilterQueryLexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
LPAREN=1, RPAREN=2, LBRACK=3, RBRACK=4, COMMA=5, EQUALS=6, NOT_EQUALS=7,
NEQ=8, LT=9, LE=10, GT=11, GE=12, LIKE=13, NOT_LIKE=14, ILIKE=15, NOT_ILIKE=16,
BETWEEN=17, NOT_BETWEEN=18, EXISTS=19, NOT_EXISTS=20, REGEXP=21, NOT_REGEXP=22,
CONTAINS=23, NOT_CONTAINS=24, IN=25, NOT_IN=26, NOT=27, AND=28, OR=29,
HAS=30, HASANY=31, HASALL=32, HASNONE=33, BOOL=34, NUMBER=35, QUOTED_TEXT=36,
KEY=37, WS=38;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE"
};
private static String[] makeRuleNames() {
return new String[] {
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "NOT_LIKE", "ILIKE", "NOT_ILIKE",
"BETWEEN", "NOT_BETWEEN", "EXISTS", "NOT_EXISTS", "REGEXP", "NOT_REGEXP",
"CONTAINS", "NOT_CONTAINS", "IN", "NOT_IN", "NOT", "AND", "OR", "HAS",
"HASANY", "HASALL", "HASNONE", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY",
"WS", "DIGIT"
};
}
public static final String[] ruleNames = makeRuleNames();
private static String[] makeLiteralNames() {
return new String[] {
null, "'('", "')'", "'['", "']'", "','", null, "'!='", "'<>'", "'<'",
"'<='", "'>'", "'>='"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static String[] makeSymbolicNames() {
return new String[] {
null, "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "NOT_LIKE", "ILIKE", "NOT_ILIKE",
"BETWEEN", "NOT_BETWEEN", "EXISTS", "NOT_EXISTS", "REGEXP", "NOT_REGEXP",
"CONTAINS", "NOT_CONTAINS", "IN", "NOT_IN", "NOT", "AND", "OR", "HAS",
"HASANY", "HASALL", "HASNONE", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY",
"WS"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
/**
* @deprecated Use {@link #VOCABULARY} instead.
*/
@Deprecated
public static final String[] tokenNames;
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
public FilterQueryLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
@Override
public String getGrammarFileName() { return "FilterQuery.g4"; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\u0004\u0000&\u0167\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+
"\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+
"\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+
"\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+
"\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+
"\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+
"\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+
"\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+
"\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+
"\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+
"\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+
"!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007"+
"&\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002"+
"\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005"+
"\u0001\u0005\u0003\u0005]\b\u0005\u0001\u0006\u0001\u0006\u0001\u0006"+
"\u0001\u0007\u0001\u0007\u0001\u0007\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+
"\t\u0001\n\u0001\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\f\u0001\f"+
"\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\r\u0004\rx\b\r"+
"\u000b\r\f\ry\u0001\r\u0001\r\u0001\r\u0001\r\u0001\r\u0001\u000e\u0001"+
"\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001"+
"\u000f\u0001\u000f\u0001\u000f\u0004\u000f\u008b\b\u000f\u000b\u000f\f"+
"\u000f\u008c\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f"+
"\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010"+
"\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0011\u0001\u0011\u0001\u0011"+
"\u0001\u0011\u0004\u0011\u00a1\b\u0011\u000b\u0011\f\u0011\u00a2\u0001"+
"\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001"+
"\u0011\u0001\u0011\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001"+
"\u0012\u0001\u0012\u0003\u0012\u00b3\b\u0012\u0001\u0013\u0001\u0013\u0001"+
"\u0013\u0001\u0013\u0004\u0013\u00b9\b\u0013\u000b\u0013\f\u0013\u00ba"+
"\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013"+
"\u0003\u0013\u00c3\b\u0013\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014"+
"\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0015\u0001\u0015\u0001\u0015"+
"\u0001\u0015\u0004\u0015\u00d0\b\u0015\u000b\u0015\f\u0015\u00d1\u0001"+
"\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001"+
"\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0003\u0016\u00e3\b\u0016\u0001\u0017\u0001"+
"\u0017\u0001\u0017\u0001\u0017\u0004\u0017\u00e9\b\u0017\u000b\u0017\f"+
"\u0017\u00ea\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017"+
"\u0001\u0017\u0001\u0017\u0001\u0017\u0003\u0017\u00f5\b\u0017\u0001\u0018"+
"\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u0019"+
"\u0004\u0019\u00fe\b\u0019\u000b\u0019\f\u0019\u00ff\u0001\u0019\u0001"+
"\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001"+
"\u001b\u0001\u001b\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001"+
"\u001c\u0001\u001d\u0001\u001d\u0001\u001d\u0001\u001d\u0001\u001e\u0001"+
"\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001"+
"\u001f\u0001\u001f\u0001\u001f\u0001\u001f\u0001\u001f\u0001\u001f\u0001"+
"\u001f\u0001 \u0001 \u0001 \u0001 \u0001 \u0001 \u0001 \u0001 \u0001!"+
"\u0001!\u0001!\u0001!\u0001!\u0001!\u0001!\u0001!\u0001!\u0003!\u0133"+
"\b!\u0001\"\u0004\"\u0136\b\"\u000b\"\f\"\u0137\u0001\"\u0001\"\u0004"+
"\"\u013c\b\"\u000b\"\f\"\u013d\u0003\"\u0140\b\"\u0001#\u0001#\u0001#"+
"\u0001#\u0005#\u0146\b#\n#\f#\u0149\t#\u0001#\u0001#\u0001#\u0001#\u0001"+
"#\u0005#\u0150\b#\n#\f#\u0153\t#\u0001#\u0003#\u0156\b#\u0001$\u0001$"+
"\u0005$\u015a\b$\n$\f$\u015d\t$\u0001%\u0004%\u0160\b%\u000b%\f%\u0161"+
"\u0001%\u0001%\u0001&\u0001&\u0000\u0000\'\u0001\u0001\u0003\u0002\u0005"+
"\u0003\u0007\u0004\t\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n"+
"\u0015\u000b\u0017\f\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011"+
"#\u0012%\u0013\'\u0014)\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b"+
"7\u001c9\u001d;\u001e=\u001f? A!C\"E#G$I%K&M\u0000\u0001\u0000\u001c\u0002"+
"\u0000LLll\u0002\u0000IIii\u0002\u0000KKkk\u0002\u0000EEee\u0002\u0000"+
"NNnn\u0002\u0000OOoo\u0002\u0000TTtt\u0002\u0000\t\t \u0002\u0000BBb"+
"b\u0002\u0000WWww\u0002\u0000XXxx\u0002\u0000SSss\u0002\u0000RRrr\u0002"+
"\u0000GGgg\u0002\u0000PPpp\u0002\u0000CCcc\u0002\u0000AAaa\u0002\u0000"+
"DDdd\u0002\u0000HHhh\u0002\u0000YYyy\u0002\u0000UUuu\u0002\u0000FFff\u0002"+
"\u0000\"\"\\\\\u0002\u0000\'\'\\\\\u0004\u000009AZ__az\u0006\u0000..0"+
"9A[]]__az\u0003\u0000\t\n\r\r \u0001\u000009\u017c\u0000\u0001\u0001"+
"\u0000\u0000\u0000\u0000\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001"+
"\u0000\u0000\u0000\u0000\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000"+
"\u0000\u0000\u0000\u000b\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000"+
"\u0000\u0000\u000f\u0001\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000"+
"\u0000\u0000\u0013\u0001\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000"+
"\u0000\u0000\u0017\u0001\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000"+
"\u0000\u0000\u001b\u0001\u0000\u0000\u0000\u0000\u001d\u0001\u0000\u0000"+
"\u0000\u0000\u001f\u0001\u0000\u0000\u0000\u0000!\u0001\u0000\u0000\u0000"+
"\u0000#\u0001\u0000\u0000\u0000\u0000%\u0001\u0000\u0000\u0000\u0000\'"+
"\u0001\u0000\u0000\u0000\u0000)\u0001\u0000\u0000\u0000\u0000+\u0001\u0000"+
"\u0000\u0000\u0000-\u0001\u0000\u0000\u0000\u0000/\u0001\u0000\u0000\u0000"+
"\u00001\u0001\u0000\u0000\u0000\u00003\u0001\u0000\u0000\u0000\u00005"+
"\u0001\u0000\u0000\u0000\u00007\u0001\u0000\u0000\u0000\u00009\u0001\u0000"+
"\u0000\u0000\u0000;\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000"+
"\u0000?\u0001\u0000\u0000\u0000\u0000A\u0001\u0000\u0000\u0000\u0000C"+
"\u0001\u0000\u0000\u0000\u0000E\u0001\u0000\u0000\u0000\u0000G\u0001\u0000"+
"\u0000\u0000\u0000I\u0001\u0000\u0000\u0000\u0000K\u0001\u0000\u0000\u0000"+
"\u0001O\u0001\u0000\u0000\u0000\u0003Q\u0001\u0000\u0000\u0000\u0005S"+
"\u0001\u0000\u0000\u0000\u0007U\u0001\u0000\u0000\u0000\tW\u0001\u0000"+
"\u0000\u0000\u000b\\\u0001\u0000\u0000\u0000\r^\u0001\u0000\u0000\u0000"+
"\u000fa\u0001\u0000\u0000\u0000\u0011d\u0001\u0000\u0000\u0000\u0013f"+
"\u0001\u0000\u0000\u0000\u0015i\u0001\u0000\u0000\u0000\u0017k\u0001\u0000"+
"\u0000\u0000\u0019n\u0001\u0000\u0000\u0000\u001bs\u0001\u0000\u0000\u0000"+
"\u001d\u0080\u0001\u0000\u0000\u0000\u001f\u0086\u0001\u0000\u0000\u0000"+
"!\u0094\u0001\u0000\u0000\u0000#\u009c\u0001\u0000\u0000\u0000%\u00ac"+
"\u0001\u0000\u0000\u0000\'\u00b4\u0001\u0000\u0000\u0000)\u00c4\u0001"+
"\u0000\u0000\u0000+\u00cb\u0001\u0000\u0000\u0000-\u00da\u0001\u0000\u0000"+
"\u0000/\u00e4\u0001\u0000\u0000\u00001\u00f6\u0001\u0000\u0000\u00003"+
"\u00f9\u0001\u0000\u0000\u00005\u0104\u0001\u0000\u0000\u00007\u0108\u0001"+
"\u0000\u0000\u00009\u010c\u0001\u0000\u0000\u0000;\u010f\u0001\u0000\u0000"+
"\u0000=\u0113\u0001\u0000\u0000\u0000?\u011a\u0001\u0000\u0000\u0000A"+
"\u0121\u0001\u0000\u0000\u0000C\u0132\u0001\u0000\u0000\u0000E\u0135\u0001"+
"\u0000\u0000\u0000G\u0155\u0001\u0000\u0000\u0000I\u0157\u0001\u0000\u0000"+
"\u0000K\u015f\u0001\u0000\u0000\u0000M\u0165\u0001\u0000\u0000\u0000O"+
"P\u0005(\u0000\u0000P\u0002\u0001\u0000\u0000\u0000QR\u0005)\u0000\u0000"+
"R\u0004\u0001\u0000\u0000\u0000ST\u0005[\u0000\u0000T\u0006\u0001\u0000"+
"\u0000\u0000UV\u0005]\u0000\u0000V\b\u0001\u0000\u0000\u0000WX\u0005,"+
"\u0000\u0000X\n\u0001\u0000\u0000\u0000Y]\u0005=\u0000\u0000Z[\u0005="+
"\u0000\u0000[]\u0005=\u0000\u0000\\Y\u0001\u0000\u0000\u0000\\Z\u0001"+
"\u0000\u0000\u0000]\f\u0001\u0000\u0000\u0000^_\u0005!\u0000\u0000_`\u0005"+
"=\u0000\u0000`\u000e\u0001\u0000\u0000\u0000ab\u0005<\u0000\u0000bc\u0005"+
">\u0000\u0000c\u0010\u0001\u0000\u0000\u0000de\u0005<\u0000\u0000e\u0012"+
"\u0001\u0000\u0000\u0000fg\u0005<\u0000\u0000gh\u0005=\u0000\u0000h\u0014"+
"\u0001\u0000\u0000\u0000ij\u0005>\u0000\u0000j\u0016\u0001\u0000\u0000"+
"\u0000kl\u0005>\u0000\u0000lm\u0005=\u0000\u0000m\u0018\u0001\u0000\u0000"+
"\u0000no\u0007\u0000\u0000\u0000op\u0007\u0001\u0000\u0000pq\u0007\u0002"+
"\u0000\u0000qr\u0007\u0003\u0000\u0000r\u001a\u0001\u0000\u0000\u0000"+
"st\u0007\u0004\u0000\u0000tu\u0007\u0005\u0000\u0000uw\u0007\u0006\u0000"+
"\u0000vx\u0007\u0007\u0000\u0000wv\u0001\u0000\u0000\u0000xy\u0001\u0000"+
"\u0000\u0000yw\u0001\u0000\u0000\u0000yz\u0001\u0000\u0000\u0000z{\u0001"+
"\u0000\u0000\u0000{|\u0007\u0000\u0000\u0000|}\u0007\u0001\u0000\u0000"+
"}~\u0007\u0002\u0000\u0000~\u007f\u0007\u0003\u0000\u0000\u007f\u001c"+
"\u0001\u0000\u0000\u0000\u0080\u0081\u0007\u0001\u0000\u0000\u0081\u0082"+
"\u0007\u0000\u0000\u0000\u0082\u0083\u0007\u0001\u0000\u0000\u0083\u0084"+
"\u0007\u0002\u0000\u0000\u0084\u0085\u0007\u0003\u0000\u0000\u0085\u001e"+
"\u0001\u0000\u0000\u0000\u0086\u0087\u0007\u0004\u0000\u0000\u0087\u0088"+
"\u0007\u0005\u0000\u0000\u0088\u008a\u0007\u0006\u0000\u0000\u0089\u008b"+
"\u0007\u0007\u0000\u0000\u008a\u0089\u0001\u0000\u0000\u0000\u008b\u008c"+
"\u0001\u0000\u0000\u0000\u008c\u008a\u0001\u0000\u0000\u0000\u008c\u008d"+
"\u0001\u0000\u0000\u0000\u008d\u008e\u0001\u0000\u0000\u0000\u008e\u008f"+
"\u0007\u0001\u0000\u0000\u008f\u0090\u0007\u0000\u0000\u0000\u0090\u0091"+
"\u0007\u0001\u0000\u0000\u0091\u0092\u0007\u0002\u0000\u0000\u0092\u0093"+
"\u0007\u0003\u0000\u0000\u0093 \u0001\u0000\u0000\u0000\u0094\u0095\u0007"+
"\b\u0000\u0000\u0095\u0096\u0007\u0003\u0000\u0000\u0096\u0097\u0007\u0006"+
"\u0000\u0000\u0097\u0098\u0007\t\u0000\u0000\u0098\u0099\u0007\u0003\u0000"+
"\u0000\u0099\u009a\u0007\u0003\u0000\u0000\u009a\u009b\u0007\u0004\u0000"+
"\u0000\u009b\"\u0001\u0000\u0000\u0000\u009c\u009d\u0007\u0004\u0000\u0000"+
"\u009d\u009e\u0007\u0005\u0000\u0000\u009e\u00a0\u0007\u0006\u0000\u0000"+
"\u009f\u00a1\u0007\u0007\u0000\u0000\u00a0\u009f\u0001\u0000\u0000\u0000"+
"\u00a1\u00a2\u0001\u0000\u0000\u0000\u00a2\u00a0\u0001\u0000\u0000\u0000"+
"\u00a2\u00a3\u0001\u0000\u0000\u0000\u00a3\u00a4\u0001\u0000\u0000\u0000"+
"\u00a4\u00a5\u0007\b\u0000\u0000\u00a5\u00a6\u0007\u0003\u0000\u0000\u00a6"+
"\u00a7\u0007\u0006\u0000\u0000\u00a7\u00a8\u0007\t\u0000\u0000\u00a8\u00a9"+
"\u0007\u0003\u0000\u0000\u00a9\u00aa\u0007\u0003\u0000\u0000\u00aa\u00ab"+
"\u0007\u0004\u0000\u0000\u00ab$\u0001\u0000\u0000\u0000\u00ac\u00ad\u0007"+
"\u0003\u0000\u0000\u00ad\u00ae\u0007\n\u0000\u0000\u00ae\u00af\u0007\u0001"+
"\u0000\u0000\u00af\u00b0\u0007\u000b\u0000\u0000\u00b0\u00b2\u0007\u0006"+
"\u0000\u0000\u00b1\u00b3\u0007\u000b\u0000\u0000\u00b2\u00b1\u0001\u0000"+
"\u0000\u0000\u00b2\u00b3\u0001\u0000\u0000\u0000\u00b3&\u0001\u0000\u0000"+
"\u0000\u00b4\u00b5\u0007\u0004\u0000\u0000\u00b5\u00b6\u0007\u0005\u0000"+
"\u0000\u00b6\u00b8\u0007\u0006\u0000\u0000\u00b7\u00b9\u0007\u0007\u0000"+
"\u0000\u00b8\u00b7\u0001\u0000\u0000\u0000\u00b9\u00ba\u0001\u0000\u0000"+
"\u0000\u00ba\u00b8\u0001\u0000\u0000\u0000\u00ba\u00bb\u0001\u0000\u0000"+
"\u0000\u00bb\u00bc\u0001\u0000\u0000\u0000\u00bc\u00bd\u0007\u0003\u0000"+
"\u0000\u00bd\u00be\u0007\n\u0000\u0000\u00be\u00bf\u0007\u0001\u0000\u0000"+
"\u00bf\u00c0\u0007\u000b\u0000\u0000\u00c0\u00c2\u0007\u0006\u0000\u0000"+
"\u00c1\u00c3\u0007\u000b\u0000\u0000\u00c2\u00c1\u0001\u0000\u0000\u0000"+
"\u00c2\u00c3\u0001\u0000\u0000\u0000\u00c3(\u0001\u0000\u0000\u0000\u00c4"+
"\u00c5\u0007\f\u0000\u0000\u00c5\u00c6\u0007\u0003\u0000\u0000\u00c6\u00c7"+
"\u0007\r\u0000\u0000\u00c7\u00c8\u0007\u0003\u0000\u0000\u00c8\u00c9\u0007"+
"\n\u0000\u0000\u00c9\u00ca\u0007\u000e\u0000\u0000\u00ca*\u0001\u0000"+
"\u0000\u0000\u00cb\u00cc\u0007\u0004\u0000\u0000\u00cc\u00cd\u0007\u0005"+
"\u0000\u0000\u00cd\u00cf\u0007\u0006\u0000\u0000\u00ce\u00d0\u0007\u0007"+
"\u0000\u0000\u00cf\u00ce\u0001\u0000\u0000\u0000\u00d0\u00d1\u0001\u0000"+
"\u0000\u0000\u00d1\u00cf\u0001\u0000\u0000\u0000\u00d1\u00d2\u0001\u0000"+
"\u0000\u0000\u00d2\u00d3\u0001\u0000\u0000\u0000\u00d3\u00d4\u0007\f\u0000"+
"\u0000\u00d4\u00d5\u0007\u0003\u0000\u0000\u00d5\u00d6\u0007\r\u0000\u0000"+
"\u00d6\u00d7\u0007\u0003\u0000\u0000\u00d7\u00d8\u0007\n\u0000\u0000\u00d8"+
"\u00d9\u0007\u000e\u0000\u0000\u00d9,\u0001\u0000\u0000\u0000\u00da\u00db"+
"\u0007\u000f\u0000\u0000\u00db\u00dc\u0007\u0005\u0000\u0000\u00dc\u00dd"+
"\u0007\u0004\u0000\u0000\u00dd\u00de\u0007\u0006\u0000\u0000\u00de\u00df"+
"\u0007\u0010\u0000\u0000\u00df\u00e0\u0007\u0001\u0000\u0000\u00e0\u00e2"+
"\u0007\u0004\u0000\u0000\u00e1\u00e3\u0007\u000b\u0000\u0000\u00e2\u00e1"+
"\u0001\u0000\u0000\u0000\u00e2\u00e3\u0001\u0000\u0000\u0000\u00e3.\u0001"+
"\u0000\u0000\u0000\u00e4\u00e5\u0007\u0004\u0000\u0000\u00e5\u00e6\u0007"+
"\u0005\u0000\u0000\u00e6\u00e8\u0007\u0006\u0000\u0000\u00e7\u00e9\u0007"+
"\u0007\u0000\u0000\u00e8\u00e7\u0001\u0000\u0000\u0000\u00e9\u00ea\u0001"+
"\u0000\u0000\u0000\u00ea\u00e8\u0001\u0000\u0000\u0000\u00ea\u00eb\u0001"+
"\u0000\u0000\u0000\u00eb\u00ec\u0001\u0000\u0000\u0000\u00ec\u00ed\u0007"+
"\u000f\u0000\u0000\u00ed\u00ee\u0007\u0005\u0000\u0000\u00ee\u00ef\u0007"+
"\u0004\u0000\u0000\u00ef\u00f0\u0007\u0006\u0000\u0000\u00f0\u00f1\u0007"+
"\u0010\u0000\u0000\u00f1\u00f2\u0007\u0001\u0000\u0000\u00f2\u00f4\u0007"+
"\u0004\u0000\u0000\u00f3\u00f5\u0007\u000b\u0000\u0000\u00f4\u00f3\u0001"+
"\u0000\u0000\u0000\u00f4\u00f5\u0001\u0000\u0000\u0000\u00f50\u0001\u0000"+
"\u0000\u0000\u00f6\u00f7\u0007\u0001\u0000\u0000\u00f7\u00f8\u0007\u0004"+
"\u0000\u0000\u00f82\u0001\u0000\u0000\u0000\u00f9\u00fa\u0007\u0004\u0000"+
"\u0000\u00fa\u00fb\u0007\u0005\u0000\u0000\u00fb\u00fd\u0007\u0006\u0000"+
"\u0000\u00fc\u00fe\u0007\u0007\u0000\u0000\u00fd\u00fc\u0001\u0000\u0000"+
"\u0000\u00fe\u00ff\u0001\u0000\u0000\u0000\u00ff\u00fd\u0001\u0000\u0000"+
"\u0000\u00ff\u0100\u0001\u0000\u0000\u0000\u0100\u0101\u0001\u0000\u0000"+
"\u0000\u0101\u0102\u0007\u0001\u0000\u0000\u0102\u0103\u0007\u0004\u0000"+
"\u0000\u01034\u0001\u0000\u0000\u0000\u0104\u0105\u0007\u0004\u0000\u0000"+
"\u0105\u0106\u0007\u0005\u0000\u0000\u0106\u0107\u0007\u0006\u0000\u0000"+
"\u01076\u0001\u0000\u0000\u0000\u0108\u0109\u0007\u0010\u0000\u0000\u0109"+
"\u010a\u0007\u0004\u0000\u0000\u010a\u010b\u0007\u0011\u0000\u0000\u010b"+
"8\u0001\u0000\u0000\u0000\u010c\u010d\u0007\u0005\u0000\u0000\u010d\u010e"+
"\u0007\f\u0000\u0000\u010e:\u0001\u0000\u0000\u0000\u010f\u0110\u0007"+
"\u0012\u0000\u0000\u0110\u0111\u0007\u0010\u0000\u0000\u0111\u0112\u0007"+
"\u000b\u0000\u0000\u0112<\u0001\u0000\u0000\u0000\u0113\u0114\u0007\u0012"+
"\u0000\u0000\u0114\u0115\u0007\u0010\u0000\u0000\u0115\u0116\u0007\u000b"+
"\u0000\u0000\u0116\u0117\u0007\u0010\u0000\u0000\u0117\u0118\u0007\u0004"+
"\u0000\u0000\u0118\u0119\u0007\u0013\u0000\u0000\u0119>\u0001\u0000\u0000"+
"\u0000\u011a\u011b\u0007\u0012\u0000\u0000\u011b\u011c\u0007\u0010\u0000"+
"\u0000\u011c\u011d\u0007\u000b\u0000\u0000\u011d\u011e\u0007\u0010\u0000"+
"\u0000\u011e\u011f\u0007\u0000\u0000\u0000\u011f\u0120\u0007\u0000\u0000"+
"\u0000\u0120@\u0001\u0000\u0000\u0000\u0121\u0122\u0007\u0012\u0000\u0000"+
"\u0122\u0123\u0007\u0010\u0000\u0000\u0123\u0124\u0007\u000b\u0000\u0000"+
"\u0124\u0125\u0007\u0004\u0000\u0000\u0125\u0126\u0007\u0005\u0000\u0000"+
"\u0126\u0127\u0007\u0004\u0000\u0000\u0127\u0128\u0007\u0003\u0000\u0000"+
"\u0128B\u0001\u0000\u0000\u0000\u0129\u012a\u0007\u0006\u0000\u0000\u012a"+
"\u012b\u0007\f\u0000\u0000\u012b\u012c\u0007\u0014\u0000\u0000\u012c\u0133"+
"\u0007\u0003\u0000\u0000\u012d\u012e\u0007\u0015\u0000\u0000\u012e\u012f"+
"\u0007\u0010\u0000\u0000\u012f\u0130\u0007\u0000\u0000\u0000\u0130\u0131"+
"\u0007\u000b\u0000\u0000\u0131\u0133\u0007\u0003\u0000\u0000\u0132\u0129"+
"\u0001\u0000\u0000\u0000\u0132\u012d\u0001\u0000\u0000\u0000\u0133D\u0001"+
"\u0000\u0000\u0000\u0134\u0136\u0003M&\u0000\u0135\u0134\u0001\u0000\u0000"+
"\u0000\u0136\u0137\u0001\u0000\u0000\u0000\u0137\u0135\u0001\u0000\u0000"+
"\u0000\u0137\u0138\u0001\u0000\u0000\u0000\u0138\u013f\u0001\u0000\u0000"+
"\u0000\u0139\u013b\u0005.\u0000\u0000\u013a\u013c\u0003M&\u0000\u013b"+
"\u013a\u0001\u0000\u0000\u0000\u013c\u013d\u0001\u0000\u0000\u0000\u013d"+
"\u013b\u0001\u0000\u0000\u0000\u013d\u013e\u0001\u0000\u0000\u0000\u013e"+
"\u0140\u0001\u0000\u0000\u0000\u013f\u0139\u0001\u0000\u0000\u0000\u013f"+
"\u0140\u0001\u0000\u0000\u0000\u0140F\u0001\u0000\u0000\u0000\u0141\u0147"+
"\u0005\"\u0000\u0000\u0142\u0146\b\u0016\u0000\u0000\u0143\u0144\u0005"+
"\\\u0000\u0000\u0144\u0146\t\u0000\u0000\u0000\u0145\u0142\u0001\u0000"+
"\u0000\u0000\u0145\u0143\u0001\u0000\u0000\u0000\u0146\u0149\u0001\u0000"+
"\u0000\u0000\u0147\u0145\u0001\u0000\u0000\u0000\u0147\u0148\u0001\u0000"+
"\u0000\u0000\u0148\u014a\u0001\u0000\u0000\u0000\u0149\u0147\u0001\u0000"+
"\u0000\u0000\u014a\u0156\u0005\"\u0000\u0000\u014b\u0151\u0005\'\u0000"+
"\u0000\u014c\u0150\b\u0017\u0000\u0000\u014d\u014e\u0005\\\u0000\u0000"+
"\u014e\u0150\t\u0000\u0000\u0000\u014f\u014c\u0001\u0000\u0000\u0000\u014f"+
"\u014d\u0001\u0000\u0000\u0000\u0150\u0153\u0001\u0000\u0000\u0000\u0151"+
"\u014f\u0001\u0000\u0000\u0000\u0151\u0152\u0001\u0000\u0000\u0000\u0152"+
"\u0154\u0001\u0000\u0000\u0000\u0153\u0151\u0001\u0000\u0000\u0000\u0154"+
"\u0156\u0005\'\u0000\u0000\u0155\u0141\u0001\u0000\u0000\u0000\u0155\u014b"+
"\u0001\u0000\u0000\u0000\u0156H\u0001\u0000\u0000\u0000\u0157\u015b\u0007"+
"\u0018\u0000\u0000\u0158\u015a\u0007\u0019\u0000\u0000\u0159\u0158\u0001"+
"\u0000\u0000\u0000\u015a\u015d\u0001\u0000\u0000\u0000\u015b\u0159\u0001"+
"\u0000\u0000\u0000\u015b\u015c\u0001\u0000\u0000\u0000\u015cJ\u0001\u0000"+
"\u0000\u0000\u015d\u015b\u0001\u0000\u0000\u0000\u015e\u0160\u0007\u001a"+
"\u0000\u0000\u015f\u015e\u0001\u0000\u0000\u0000\u0160\u0161\u0001\u0000"+
"\u0000\u0000\u0161\u015f\u0001\u0000\u0000\u0000\u0161\u0162\u0001\u0000"+
"\u0000\u0000\u0162\u0163\u0001\u0000\u0000\u0000\u0163\u0164\u0006%\u0000"+
"\u0000\u0164L\u0001\u0000\u0000\u0000\u0165\u0166\u0007\u001b\u0000\u0000"+
"\u0166N\u0001\u0000\u0000\u0000\u0018\u0000\\y\u008c\u00a2\u00b2\u00ba"+
"\u00c2\u00d1\u00e2\u00ea\u00f4\u00ff\u0132\u0137\u013d\u013f\u0145\u0147"+
"\u014f\u0151\u0155\u015b\u0161\u0001\u0006\u0000\u0000";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}

View File

@ -0,0 +1,49 @@
LPAREN=1
RPAREN=2
LBRACK=3
RBRACK=4
COMMA=5
EQUALS=6
NOT_EQUALS=7
NEQ=8
LT=9
LE=10
GT=11
GE=12
LIKE=13
NOT_LIKE=14
ILIKE=15
NOT_ILIKE=16
BETWEEN=17
NOT_BETWEEN=18
EXISTS=19
NOT_EXISTS=20
REGEXP=21
NOT_REGEXP=22
CONTAINS=23
NOT_CONTAINS=24
IN=25
NOT_IN=26
NOT=27
AND=28
OR=29
HAS=30
HASANY=31
HASALL=32
HASNONE=33
BOOL=34
NUMBER=35
QUOTED_TEXT=36
KEY=37
WS=38
'('=1
')'=2
'['=3
']'=4
','=5
'!='=7
'<>'=8
'<'=9
'<='=10
'>'=11
'>='=12

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
export interface IValidationResult {
isValid: boolean;
message: string;
errors: string[];
}
export interface IToken {
type: number;
text: string;
start: number;
stop: number;
channel?: number;
}
export interface IQueryContext {
tokenType: number;
text: string;
start: number;
stop: number;
currentToken: string;
isInValue: boolean;
isInKey: boolean;
isInOperator: boolean;
isInFunction: boolean;
}
export interface IDetailedError {
message: string;
line: number;
column: number;
offendingSymbol?: string;
expectedTokens?: string[];
}
export interface ASTNode {
type: string;
value?: string;
children?: ASTNode[];
}

View File

@ -0,0 +1,340 @@
/* eslint-disable sonarjs/no-collapsible-if */
/* eslint-disable no-continue */
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable max-classes-per-file */
import { CharStreams, CommonTokenStream } from 'antlr4';
import FilterQueryLexer from 'parser/FilterQueryLexer';
import FilterQueryParser from 'parser/FilterQueryParser';
import {
IDetailedError,
IQueryContext,
IToken,
IValidationResult,
} from 'types/antlrQueryTypes';
// Custom error listener to capture ANTLR errors
class QueryErrorListener {
private errors: IDetailedError[] = [];
syntaxError(
_recognizer: any,
offendingSymbol: any,
line: number,
column: number,
msg: string,
): void {
// For unterminated quotes, we only want to show one error
if (this.hasUnterminatedQuoteError() && msg.includes('expecting')) {
return;
}
const error: IDetailedError = {
message: msg,
line,
column,
offendingSymbol: offendingSymbol?.text || String(offendingSymbol),
};
// Extract expected tokens if available
if (msg.includes('expecting')) {
const expectedTokens = msg
.split('expecting')[1]
.trim()
.split(',')
.map((token) => token.trim());
error.expectedTokens = expectedTokens;
}
// Check if this is a duplicate error (same location and similar message)
const isDuplicate = this.errors.some(
(e) =>
e.line === line &&
e.column === column &&
this.isSimilarError(e.message, msg),
);
if (!isDuplicate) {
this.errors.push(error);
}
}
private hasUnterminatedQuoteError(): boolean {
return this.errors.some(
(error) =>
error.message.includes('unterminated') ||
(error.message.includes('missing') && error.message.includes("'")),
);
}
private isSimilarError = (msg1: string, msg2: string): boolean => {
// Consider errors similar if they're for the same core issue
const normalize = (msg: string): string =>
msg.toLowerCase().replace(/['"`]/g, 'quote').replace(/\s+/g, ' ').trim();
return normalize(msg1) === normalize(msg2);
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
reportAmbiguity = (): void => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
reportAttemptingFullContext = (): void => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
reportContextSensitivity = (): void => {};
getErrors(): IDetailedError[] {
return this.errors;
}
hasErrors(): boolean {
return this.errors.length > 0;
}
getFormattedErrors(): string[] {
return this.errors.map((error) => {
let message = `Line ${error.line}:${error.column} - ${error.message}`;
if (error.offendingSymbol && error.offendingSymbol !== 'undefined') {
message += `\nOffending symbol: '${error.offendingSymbol}'`;
}
if (error.expectedTokens && error.expectedTokens.length > 0) {
message += `\nExpected: ${error.expectedTokens.join(', ')}`;
}
return message;
});
}
}
export const validateQuery = (query: string): IValidationResult => {
// Empty query is considered invalid
if (!query.trim()) {
return {
isValid: false,
message: 'Query cannot be empty',
errors: ['Query cannot be empty'],
};
}
try {
const errorListener = new QueryErrorListener();
const inputStream = CharStreams.fromString(query);
// Setup lexer
const lexer = new FilterQueryLexer(inputStream);
lexer.removeErrorListeners(); // Remove default error listeners
lexer.addErrorListener(errorListener);
// Setup parser
const tokenStream = new CommonTokenStream(lexer);
const parser = new FilterQueryParser(tokenStream);
parser.removeErrorListeners(); // Remove default error listeners
parser.addErrorListener(errorListener);
// Try parsing
const parsedTree = parser.query();
console.log('parsedTree', parsedTree);
// Check if any errors were captured
if (errorListener.hasErrors()) {
return {
isValid: false,
message: 'Query syntax error',
errors: errorListener.getFormattedErrors(),
};
}
return {
isValid: true,
message: 'Query is valid!',
errors: [],
};
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'Invalid query syntax';
return {
isValid: false,
message: 'Invalid query syntax',
errors: [errorMessage],
};
}
};
export function getQueryContextAtCursor(
query: string,
cursorIndex: number,
): IQueryContext {
try {
// Create input stream and lexer
const input = query || '';
const chars = CharStreams.fromString(input);
const lexer = new FilterQueryLexer(chars);
// Create token stream and force token generation
const tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
// Get all tokens including whitespace
const allTokens = tokenStream.tokens as IToken[];
// Find exact token at cursor, including whitespace
let exactToken: IToken | null = null;
let previousToken: IToken | null = null;
let nextToken: IToken | null = null;
// Handle cursor at the very end of input
if (cursorIndex === input.length && allTokens.length > 0) {
const lastRealToken = allTokens
.filter((t) => t.type !== FilterQueryLexer.EOF)
.pop();
if (lastRealToken) {
exactToken = lastRealToken;
previousToken =
allTokens.filter((t) => t.stop < lastRealToken.start).pop() || null;
}
} else {
// Normal token search
for (let i = 0; i < allTokens.length; i++) {
const token = allTokens[i];
// Skip EOF token in normal search
if (token.type === FilterQueryLexer.EOF) {
continue;
}
// Check if cursor is within token bounds (inclusive)
if (token.start <= cursorIndex && cursorIndex <= token.stop + 1) {
exactToken = token;
previousToken = i > 0 ? allTokens[i - 1] : null;
nextToken = i < allTokens.length - 1 ? allTokens[i + 1] : null;
break;
}
}
// If cursor is between tokens, find surrounding tokens
if (!exactToken) {
for (let i = 0; i < allTokens.length - 1; i++) {
const current = allTokens[i];
const next = allTokens[i + 1];
if (current.type === FilterQueryLexer.EOF) {
continue;
}
if (next.type === FilterQueryLexer.EOF) {
continue;
}
if (current.stop + 1 < cursorIndex && cursorIndex < next.start) {
previousToken = current;
nextToken = next;
break;
}
}
}
}
console.log('Cursor context:', {
cursorIndex,
query,
exact: exactToken,
prev: previousToken,
next: nextToken,
});
// Determine the context based on cursor position and surrounding tokens
let currentToken: IToken | null = null;
if (exactToken) {
// If cursor is in a non-whitespace token, use that
if (exactToken.channel === 0) {
currentToken = exactToken;
} else {
// If in whitespace, use the previous non-whitespace token
currentToken = previousToken?.channel === 0 ? previousToken : nextToken;
}
} else if (previousToken?.channel === 0) {
// If between tokens, prefer the previous non-whitespace token
currentToken = previousToken;
} else if (nextToken?.channel === 0) {
// Otherwise use the next non-whitespace token
currentToken = nextToken;
}
// If still no token (empty query or all whitespace), return default context
if (!currentToken) {
return {
tokenType: -1,
text: '',
start: cursorIndex,
stop: cursorIndex,
currentToken: '',
isInValue: false,
isInKey: false,
isInOperator: false,
isInFunction: false,
};
}
// Determine the context based on the token type
const isInValue = [
FilterQueryLexer.QUOTED_TEXT,
FilterQueryLexer.NUMBER,
FilterQueryLexer.BOOL,
].includes(currentToken.type);
const isInKey = currentToken.type === FilterQueryLexer.KEY;
const isInOperator = [
FilterQueryLexer.EQUALS,
FilterQueryLexer.NOT_EQUALS,
FilterQueryLexer.NEQ,
FilterQueryLexer.LT,
FilterQueryLexer.LE,
FilterQueryLexer.GT,
FilterQueryLexer.GE,
FilterQueryLexer.LIKE,
FilterQueryLexer.NOT_LIKE,
FilterQueryLexer.ILIKE,
FilterQueryLexer.NOT_ILIKE,
FilterQueryLexer.BETWEEN,
FilterQueryLexer.EXISTS,
FilterQueryLexer.REGEXP,
FilterQueryLexer.CONTAINS,
FilterQueryLexer.IN,
FilterQueryLexer.NOT,
].includes(currentToken.type);
const isInFunction = [
FilterQueryLexer.HAS,
FilterQueryLexer.HASANY,
FilterQueryLexer.HASALL,
FilterQueryLexer.HASNONE,
].includes(currentToken.type);
return {
tokenType: currentToken.type,
text: currentToken.text,
start: currentToken.start,
stop: currentToken.stop,
currentToken: currentToken.text,
isInValue,
isInKey,
isInOperator,
isInFunction,
};
} catch (error) {
console.error('Error in getQueryContextAtCursor:', error);
return {
tokenType: -1,
text: '',
start: cursorIndex,
stop: cursorIndex,
currentToken: '',
isInValue: false,
isInKey: false,
isInOperator: false,
isInFunction: false,
};
}
}