Skip to content

Commit d8b2456

Browse files
committed
feat: complete after error syntax
1 parent b308d03 commit d8b2456

File tree

2 files changed

+102
-6
lines changed

2 files changed

+102
-6
lines changed

src/parser/common/basicSQL.ts

+101-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import type { EntityCollector } from './entityCollector';
2929
import { EntityContext } from './entityCollector';
3030
import SemanticContextCollector from './semanticContextCollector';
3131

32+
export const SQL_SPLIT_SYMBOL_TEXT = ';';
33+
3234
/**
3335
* Basic SQL class, every sql needs extends it.
3436
*/
@@ -286,7 +288,6 @@ export abstract class BasicSQL<
286288
if (errors.length || !this._parseTree) {
287289
return null;
288290
}
289-
290291
const splitListener = this.splitListener;
291292
this.listen(splitListener, this._parseTree);
292293

@@ -299,6 +300,78 @@ export abstract class BasicSQL<
299300
return res;
300301
}
301302

303+
/**
304+
* Get the smaller range of input
305+
* @param input string
306+
* @param allTokens all tokens from input
307+
* @param tokenIndexOffset offset of the tokenIndex in the range of input
308+
* @param caretTokenIndex tokenIndex of caretPosition
309+
* @returns inputSlice: string, caretTokenIndex: number
310+
*/
311+
private splitInputBySymbolText(
312+
input: string,
313+
allTokens: Token[],
314+
tokenIndexOffset: number,
315+
caretTokenIndex: number
316+
): { inputSlice: string; allTokens: Token[]; caretTokenIndex: number } {
317+
const tokens = allTokens.slice(tokenIndexOffset);
318+
/**
319+
* Set startToken
320+
*/
321+
let startToken: Token | null = null;
322+
for (let tokenIndex = caretTokenIndex - tokenIndexOffset; tokenIndex >= 0; tokenIndex--) {
323+
const token = tokens[tokenIndex];
324+
if (token?.text === SQL_SPLIT_SYMBOL_TEXT) {
325+
startToken = tokens[tokenIndex + 1];
326+
break;
327+
}
328+
}
329+
if (startToken === null) {
330+
startToken = tokens[0];
331+
}
332+
333+
/**
334+
* Set stopToken
335+
*/
336+
let stopToken: Token | null = null;
337+
for (
338+
let tokenIndex = caretTokenIndex - tokenIndexOffset;
339+
tokenIndex < tokens.length;
340+
tokenIndex++
341+
) {
342+
const token = tokens[tokenIndex];
343+
if (token?.text === SQL_SPLIT_SYMBOL_TEXT) {
344+
stopToken = token;
345+
break;
346+
}
347+
}
348+
if (stopToken === null) {
349+
stopToken = tokens[tokens.length - 1];
350+
}
351+
352+
const indexOffset = tokens[0].start;
353+
let startIndex = startToken.start - indexOffset;
354+
let stopIndex = stopToken.stop + 1 - indexOffset;
355+
356+
/**
357+
* Save offset of the tokenIndex in the range of input
358+
* compared to the tokenIndex in the whole input
359+
*/
360+
const _tokenIndexOffset = startToken.tokenIndex;
361+
const _caretTokenIndex = caretTokenIndex - _tokenIndexOffset;
362+
363+
/**
364+
* Get the smaller range of _input
365+
*/
366+
const _input = input.slice(startIndex, stopIndex);
367+
368+
return {
369+
inputSlice: _input,
370+
allTokens: allTokens.slice(_tokenIndexOffset),
371+
caretTokenIndex: _caretTokenIndex,
372+
};
373+
}
374+
302375
/**
303376
* Get the minimum input string that can be parsed successfully by c3.
304377
* @param input source string
@@ -448,10 +521,33 @@ export abstract class BasicSQL<
448521

449522
const inputInfo = this.getMinimumInputInfo(input, caretTokenIndex, this._parseTree);
450523
if (!inputInfo) return null;
451-
const { input: _input, tokenIndexOffset } = inputInfo;
452-
caretTokenIndex = caretTokenIndex - tokenIndexOffset;
524+
const { input: _input, tokenIndexOffset, statementCount } = inputInfo;
453525
let inputSlice = _input;
454526

527+
/**
528+
* Split the inputSlice by separator to get the smaller range of inputSlice.
529+
*/
530+
if (inputSlice.includes(SQL_SPLIT_SYMBOL_TEXT)) {
531+
const {
532+
inputSlice: _inputSlice,
533+
allTokens: _allTokens,
534+
caretTokenIndex: _caretTokenIndex,
535+
} = this.splitInputBySymbolText(
536+
inputSlice,
537+
allTokens,
538+
tokenIndexOffset,
539+
caretTokenIndex
540+
);
541+
542+
allTokens = _allTokens;
543+
caretTokenIndex = _caretTokenIndex;
544+
inputSlice = _inputSlice;
545+
} else {
546+
if (statementCount > 1) {
547+
caretTokenIndex = caretTokenIndex - tokenIndexOffset;
548+
}
549+
}
550+
455551
let sqlParserIns = this._parser;
456552
let parseTree = this._parseTree;
457553

@@ -475,7 +571,8 @@ export abstract class BasicSQL<
475571
candidates,
476572
allTokens,
477573
caretTokenIndex,
478-
tokenIndexOffset
574+
0
575+
// tokenIndexOffset
479576
);
480577

481578
const syntaxSuggestions: SyntaxSuggestion<WordRange>[] = originalSuggestions.syntax.map(

src/parser/common/semanticContextCollector.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import {
66
SemanticContext,
77
SqlSplitStrategy,
88
} from '../common/types';
9-
10-
export const SQL_SPLIT_SYMBOL_TEXT = ';';
9+
import { SQL_SPLIT_SYMBOL_TEXT } from './basicSQL';
1110

1211
abstract class SemanticContextCollector {
1312
constructor(

0 commit comments

Comments
 (0)