Initial code commit
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
export class CosmosDBSqlKeywords{
|
||||
|
||||
private static readonly KeywordTypeHintPrefix : string = "KeywordTypeHint:";
|
||||
|
||||
public static keywordsRegisteredForCompletion : { [key : string] : string } =
|
||||
{
|
||||
"AND": "AND",
|
||||
"ARRAY": "ARRAY",
|
||||
"AS": "AS",
|
||||
"ASC": "ASC",
|
||||
"BETWEEN": "BETWEEN",
|
||||
"BY": "BY",
|
||||
"CASE": "CASE",
|
||||
"CAST": "CAST",
|
||||
"CONVERT": "CONVERT",
|
||||
"CROSS": "CROSS",
|
||||
"DESC": "DESC",
|
||||
"DISTINCT": "DISTINCT",
|
||||
"ELSE": "ELSE",
|
||||
"END": "END",
|
||||
"ESCAPE": "ESCAPE",
|
||||
"EXISTS": "EXISTS",
|
||||
"K_false": "false",
|
||||
"FOR": "FOR",
|
||||
"FROM": "FROM",
|
||||
"GROUP": "GROUP",
|
||||
"HAVING": "HAVING",
|
||||
"IN": "IN",
|
||||
"INNER": "INNER",
|
||||
"INSERT": "INSERT",
|
||||
"INTO": "INTO",
|
||||
"IS": "IS",
|
||||
"JOIN": "JOIN",
|
||||
"LEFT": "LEFT",
|
||||
"LIKE": "LIKE",
|
||||
"LIMIT": "LIMIT",
|
||||
"NOT": "NOT",
|
||||
"K_null": "null",
|
||||
"OFFSET": "OFFSET",
|
||||
"ON": "ON",
|
||||
"OR": "OR",
|
||||
"ORDER": "ORDER",
|
||||
"OUTER": "OUTER",
|
||||
"OVER": "OVER",
|
||||
"RIGHT": "RIGHT",
|
||||
"SELECT": "SELECT",
|
||||
"SET": "SET",
|
||||
"THEN": "THEN",
|
||||
"TOP": "TOP",
|
||||
"K_true": "true",
|
||||
"K_udf": "udf",
|
||||
"K_undefined": "undefined",
|
||||
"UPDATE": "UPDATE",
|
||||
"VALUE": "VALUE",
|
||||
"WHEN": "WHEN",
|
||||
"WHERE": "WHERE",
|
||||
"WITH": "WITH",
|
||||
"Infinity": "Infinity",
|
||||
"NaN": "NaN",
|
||||
|
||||
"ABS": "ABS",
|
||||
"ACOS": "ACOS",
|
||||
"ARRAY_CONCAT": "ARRAY_CONCAT",
|
||||
"ARRAY_CONTAINS": "ARRAY_CONTAINS",
|
||||
"ARRAY_LENGTH": "ARRAY_LENGTH",
|
||||
"ARRAY_SLICE": "ARRAY_SLICE",
|
||||
"ASIN": "ASIN",
|
||||
"ATAN": "ATAN",
|
||||
"ATN2": "ATN2",
|
||||
"AVG": "AVG",
|
||||
"CEILING": "CEILING",
|
||||
"CONCAT": "CONCAT",
|
||||
"CONTAINS": "CONTAINS",
|
||||
"COS": "COS",
|
||||
"COT": "COT",
|
||||
"COUNT": "COUNT",
|
||||
"DEGREES": "DEGREES",
|
||||
"ENDSWITH": "ENDSWITH",
|
||||
"EXP": "EXP",
|
||||
"FLOOR": "FLOOR",
|
||||
"INDEX_OF": "INDEX_OF",
|
||||
"S_ARRAY": "S_ARRAY",
|
||||
"IS_BOOL": "IS_BOOL",
|
||||
"IS_DEFINED": "IS_DEFINED",
|
||||
"IS_FINITE_NUMBER": "IS_FINITE_NUMBER",
|
||||
"IS_NULL": "IS_NULL",
|
||||
"IS_NUMBER": "IS_NUMBER",
|
||||
"IS_OBJECT": "IS_OBJECT",
|
||||
"IS_PRIMITIVE": "IS_PRIMITIVE",
|
||||
"IS_STRING": "IS_STRING",
|
||||
"LENGTH": "LENGTH",
|
||||
"LOG10": "LOG10",
|
||||
"LOWER": "LOWER",
|
||||
"LTRIM": "LTRIM",
|
||||
"MAX": "MAX",
|
||||
"MIN": "MIN",
|
||||
"PI": "PI",
|
||||
"POWER": "POWER",
|
||||
"RADIANS": "RADIANS",
|
||||
"RAND": "RAND",
|
||||
"REPLACE": "REPLACE",
|
||||
"REPLICATE": "REPLICATE",
|
||||
"REVERSE": "REVERSE",
|
||||
"ROUND": "ROUND",
|
||||
"RTRIM": "RTRIM",
|
||||
"SIGN": "SIGN",
|
||||
"SIN": "SIN",
|
||||
"SQRT": "SQRT",
|
||||
"SQUARE": "SQUARE",
|
||||
"ST_DISTANCE": "ST_DISTANCE",
|
||||
"ST_INTERSECTS": "ST_INTERSECTS",
|
||||
"ST_ISVALID": "ST_ISVALID",
|
||||
"ST_ISVALIDDETAILED": "ST_ISVALIDDETAILED",
|
||||
"ST_WITHIN": "ST_WITHIN",
|
||||
"STARTSWITH": "STARTSWITH",
|
||||
"SUBSTRING": "SUBSTRING",
|
||||
"SUM": "SUM",
|
||||
"TAN": "TAN",
|
||||
"TRUNC": "TRUNC",
|
||||
"UPPER": "UPPER",
|
||||
"ID": CosmosDBSqlKeywords.KeywordTypeHintPrefix + "ID",
|
||||
"NUMBER": CosmosDBSqlKeywords.KeywordTypeHintPrefix + "NUMBER"
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
lexer grammar CosmosDBSqlLexer;
|
||||
|
||||
// keywords
|
||||
|
||||
AND: A N D;
|
||||
ARRAY: A R R A Y;
|
||||
AS: A S;
|
||||
ASC: A S C;
|
||||
BETWEEN: B E T W E E N;
|
||||
BY: B Y;
|
||||
CASE: C A S E;
|
||||
CAST: C A S T;
|
||||
CONVERT: C O N V E R T;
|
||||
CROSS: C R O S S;
|
||||
DESC: D E S C;
|
||||
DISTINCT: D I S T I N C T;
|
||||
ELSE: E L S E;
|
||||
END: E N D;
|
||||
ESCAPE: E S C A P E;
|
||||
EXISTS: E X I S T S;
|
||||
K_false: 'false'; // case sensitive
|
||||
FOR: F O R;
|
||||
FROM: F R O M ;
|
||||
GROUP: G R O U P;
|
||||
HAVING: H A V I N G;
|
||||
IN: I N;
|
||||
INNER: I N N E R;
|
||||
INSERT: I N S E R T;
|
||||
INTO: I N T O;
|
||||
IS: I S;
|
||||
JOIN: J O I N;
|
||||
LEFT: L E F T;
|
||||
LIKE: L I K E;
|
||||
LIMIT: L I M I T;
|
||||
NOT: N O T;
|
||||
K_null: 'null'; // case sensitive
|
||||
OFFSET: O F F S E T;
|
||||
ON: O N;
|
||||
OR: O R;
|
||||
ORDER: O R D E R;
|
||||
OUTER: O U T E R;
|
||||
OVER: O V E R;
|
||||
RIGHT: R I G H T;
|
||||
SELECT: S E L E C T;
|
||||
SET: S E T;
|
||||
THEN: T H E N;
|
||||
TOP: T O P;
|
||||
K_true: 'true'; // case sensitive
|
||||
K_udf: 'udf'; // case sensitive
|
||||
K_undefined: 'undefined'; // case sensitive
|
||||
UPDATE: U P D A T E;
|
||||
VALUE: V A L U E;
|
||||
WHEN: W H E N;
|
||||
WHERE: W H E R E;
|
||||
WITH: W I T H;
|
||||
Infinity: 'Infinity'; // case sensitive
|
||||
NaN: 'NaN'; // case sensitive
|
||||
|
||||
// build-in functions
|
||||
|
||||
ABS: A B S;
|
||||
ACOS: A C O S;
|
||||
ARRAY_CONCAT: A R R A Y '_' C O N C A T;
|
||||
ARRAY_CONTAINS: A R R A Y '_' C O N T A I N S;
|
||||
ARRAY_LENGTH: A R R A Y '_' L E N G T H;
|
||||
ARRAY_SLICE: A R R A Y '_' S L I C E;
|
||||
ASIN: A S I N;
|
||||
ATAN: A T A N;
|
||||
ATN2: A T N '2';
|
||||
AVG: A V G;
|
||||
CEILING: C E I L I N G;
|
||||
CONCAT: C O N C A T;
|
||||
CONTAINS: C O N T A I N S;
|
||||
COS: C O S;
|
||||
COT: C O T;
|
||||
COUNT: C O U N T;
|
||||
DEGREES: D E G R E E S;
|
||||
ENDSWITH: E N D S W I T H;
|
||||
EXP: E X P;
|
||||
FLOOR: F L O O R;
|
||||
INDEX_OF: I N D E X '_' O F;
|
||||
S_ARRAY: I S '_' A R R A Y;
|
||||
IS_BOOL: I S '_' B O O L;
|
||||
IS_DEFINED: I S '_' D E F I N E D;
|
||||
IS_FINITE_NUMBER: I S '_' F I N I T E '_' N U M B E R;
|
||||
IS_NULL: I S '_' N U L L;
|
||||
IS_NUMBER: I S '_' N U M B E R;
|
||||
IS_OBJECT: I S '_' O B J E C T;
|
||||
IS_PRIMITIVE: I S '_' P R I M I T I V E;
|
||||
IS_STRING: I S '_' S T R I N G;
|
||||
LENGTH: L E N G T H;
|
||||
LOG: L O G;
|
||||
LOG10: L O G '1' '0';
|
||||
LOWER: L O W E R;
|
||||
LTRIM: L T R I M;
|
||||
MAX: M A X;
|
||||
MIN: M I N;
|
||||
PI: P I;
|
||||
POWER: P O W E R;
|
||||
RADIANS: R A D I A N S;
|
||||
RAND: R A N D;
|
||||
REPLACE: R E P L A C E;
|
||||
REPLICATE: R E P L I C A T E;
|
||||
REVERSE: R E V E R S E;
|
||||
ROUND: R O U N D;
|
||||
RTRIM: R T R I M;
|
||||
SIGN: S I G N;
|
||||
SIN: S I N;
|
||||
SQRT: S Q R T;
|
||||
SQUARE: S Q U A R E;
|
||||
ST_DISTANCE: S T '_' D I S T A N C E;
|
||||
ST_INTERSECTS: S T '_' I N T E R S E C T S;
|
||||
ST_ISVALID: S T '_' I S V A L I D;
|
||||
ST_ISVALIDDETAILED: S T '_' I S V A L I D D E T A I L E D;
|
||||
ST_WITHIN: S T '_' W I T H I N;
|
||||
STARTSWITH: S T A R T S W I T H;
|
||||
SUBSTRING: S U B S T R I N G;
|
||||
SUM: S U M;
|
||||
TAN: T A N;
|
||||
TRUNC: T R U N C;
|
||||
UPPER: U P P E R;
|
||||
|
||||
// others
|
||||
|
||||
SPACE: [ \t\r\n]+ -> skip;
|
||||
COMMENTS: '-' '-' ~[\t\r\n]+ [\t\r\n] -> skip;
|
||||
|
||||
// keywords type groups
|
||||
ID: [a-zA-Z_][a-zA-Z_0-9]*;
|
||||
NUMBER: [1-9][0-9]*;
|
||||
|
||||
// operators
|
||||
COL: 'C';
|
||||
COMMA: ',';
|
||||
DOT: '.';
|
||||
ADD: '+';
|
||||
SUB: '-';
|
||||
MUL: '*';
|
||||
DIV: '/';
|
||||
MOD: '%';
|
||||
COLON: ':';
|
||||
EQUAL: '=';
|
||||
GREATER: '>';
|
||||
LESS: '<';
|
||||
BIT_NOT_OP: '~';
|
||||
BIT_OR_OP: '|';
|
||||
BIT_AND_OP: '&';
|
||||
BIT_XOR_OP: '^';
|
||||
QUEST: '?';
|
||||
LEFT_BRACE: '{';
|
||||
RIGHT_BRACE: '}';
|
||||
LEFT_BRACKET: '[';
|
||||
RIGHT_BRACKET: ']';
|
||||
LEFT_PARENTHESIS: '(';
|
||||
RIGHT_PARENTHESIS: ')';
|
||||
QUOTE: '"'|'\'';
|
||||
|
||||
|
||||
fragment A : [aA];
|
||||
fragment B : [bB];
|
||||
fragment C : [cC];
|
||||
fragment D : [dD];
|
||||
fragment E : [eE];
|
||||
fragment F : [fF];
|
||||
fragment G : [gG];
|
||||
fragment H : [hH];
|
||||
fragment I : [iI];
|
||||
fragment J : [jJ];
|
||||
fragment K : [kK];
|
||||
fragment L : [lL];
|
||||
fragment M : [mM];
|
||||
fragment N : [nN];
|
||||
fragment O : [oO];
|
||||
fragment P : [pP];
|
||||
fragment Q : [qQ];
|
||||
fragment R : [rR];
|
||||
fragment S : [sS];
|
||||
fragment T : [tT];
|
||||
fragment U : [uU];
|
||||
fragment V : [vV];
|
||||
fragment W : [wW];
|
||||
fragment X : [xX];
|
||||
fragment Y : [yY];
|
||||
fragment Z : [zZ];
|
||||
@@ -0,0 +1,250 @@
|
||||
parser grammar CosmosDBSqlParser;
|
||||
|
||||
options {
|
||||
tokenVocab = CosmosDBSqlLexer;
|
||||
}
|
||||
|
||||
root
|
||||
: sql_query
|
||||
;
|
||||
|
||||
sql_query
|
||||
: select_clause from_clause? where_clause? orderby_clause?
|
||||
;
|
||||
|
||||
select_clause
|
||||
: SELECT top_spec? selection
|
||||
;
|
||||
|
||||
top_spec
|
||||
: TOP NUMBER
|
||||
;
|
||||
|
||||
from_clause
|
||||
: FROM from_specification
|
||||
;
|
||||
|
||||
where_clause
|
||||
: WHERE scalar_expression
|
||||
;
|
||||
|
||||
orderby_clause
|
||||
: ORDER BY orderby_item_list
|
||||
;
|
||||
|
||||
selection
|
||||
: select_list
|
||||
| select_value_spec
|
||||
| MUL // FIXME 'SELECT *<EOF>' is not supported actually
|
||||
;
|
||||
|
||||
select_value_spec
|
||||
: VALUE scalar_expression
|
||||
;
|
||||
|
||||
select_list
|
||||
: select_item
|
||||
| select_list COMMA select_item
|
||||
;
|
||||
|
||||
select_item
|
||||
: scalar_expression
|
||||
| scalar_expression select_alias
|
||||
;
|
||||
|
||||
select_alias
|
||||
: ID
|
||||
| AS ID
|
||||
;
|
||||
|
||||
orderby_item_list
|
||||
: orderby_item
|
||||
| orderby_item_list COMMA orderby_item
|
||||
;
|
||||
|
||||
orderby_item:
|
||||
scalar_expression
|
||||
| scalar_expression ASC
|
||||
| scalar_expression DESC
|
||||
;
|
||||
|
||||
from_specification
|
||||
: primary_from_specification
|
||||
| from_specification JOIN primary_from_specification
|
||||
;
|
||||
|
||||
primary_from_specification
|
||||
: input_collection
|
||||
| input_collection input_alias
|
||||
| ID IN input_collection
|
||||
;
|
||||
|
||||
input_alias
|
||||
: ID
|
||||
| AS ID
|
||||
;
|
||||
|
||||
input_collection
|
||||
: relative_path
|
||||
| LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
|
||||
;
|
||||
|
||||
relative_path
|
||||
: relative_path_segment
|
||||
| relative_path DOT relative_path_segment
|
||||
| relative_path LEFT_BRACKET NUMBER RIGHT_BRACKET
|
||||
| relative_path LEFT_BRACKET QUOTE relative_path_segment QUOTE RIGHT_BRACKET
|
||||
;
|
||||
|
||||
relative_path_segment
|
||||
: ID
|
||||
;
|
||||
|
||||
array_item_list
|
||||
: scalar_expression
|
||||
| array_item_list COMMA scalar_expression
|
||||
;
|
||||
|
||||
array_create_expression
|
||||
: LEFT_BRACKET array_item_list? RIGHT_BRACKET
|
||||
;
|
||||
|
||||
property_name
|
||||
: ID
|
||||
;
|
||||
|
||||
object_property
|
||||
: property_name COLON scalar_expression
|
||||
;
|
||||
|
||||
object_property_list:
|
||||
object_property
|
||||
| object_property_list COMMA object_property
|
||||
;
|
||||
|
||||
object_create_expression
|
||||
: LEFT_BRACE object_property_list? RIGHT_BRACE
|
||||
;
|
||||
|
||||
function_arg_list:
|
||||
scalar_expression
|
||||
| function_arg_list COMMA scalar_expression
|
||||
;
|
||||
|
||||
sys_function_name
|
||||
: ID
|
||||
;
|
||||
|
||||
udf_function_name
|
||||
: ID
|
||||
;
|
||||
|
||||
function_call_expression
|
||||
: sys_function_name LEFT_PARENTHESIS function_arg_list? RIGHT_PARENTHESIS
|
||||
| K_udf DOT udf_function_name LEFT_PARENTHESIS function_arg_list? RIGHT_PARENTHESIS
|
||||
;
|
||||
|
||||
scalar_expression
|
||||
: logical_scalar_expression
|
||||
| between_scalar_expression
|
||||
;
|
||||
|
||||
logical_scalar_expression
|
||||
: binary_expression
|
||||
| in_scalar_expression
|
||||
| logical_scalar_expression AND logical_scalar_expression
|
||||
| logical_scalar_expression OR logical_scalar_expression
|
||||
;
|
||||
|
||||
between_scalar_expression
|
||||
: binary_expression BETWEEN binary_expression AND binary_expression
|
||||
| binary_expression NOT BETWEEN binary_expression AND binary_expression
|
||||
;
|
||||
|
||||
in_scalar_expression
|
||||
: binary_expression IN LEFT_PARENTHESIS in_scalar_expression_item_list RIGHT_PARENTHESIS
|
||||
| binary_expression NOT IN LEFT_PARENTHESIS in_scalar_expression_item_list RIGHT_PARENTHESIS
|
||||
;
|
||||
|
||||
exists_scalar_expression
|
||||
: EXISTS LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
|
||||
;
|
||||
|
||||
array_scalar_expression
|
||||
: ARRAY LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
|
||||
;
|
||||
|
||||
in_scalar_expression_item_list
|
||||
: scalar_expression
|
||||
| in_scalar_expression_item_list COMMA scalar_expression
|
||||
;
|
||||
|
||||
binary_expression:
|
||||
unary_expression
|
||||
| binary_expression ADD binary_expression
|
||||
| binary_expression SUB binary_expression
|
||||
| binary_expression MUL binary_expression
|
||||
| binary_expression DIV binary_expression
|
||||
| binary_expression MOD binary_expression
|
||||
| binary_expression EQUAL binary_expression
|
||||
| binary_expression LESS binary_expression
|
||||
| binary_expression GREATER binary_expression
|
||||
| binary_expression BIT_AND_OP binary_expression
|
||||
| binary_expression BIT_OR_OP binary_expression
|
||||
| binary_expression BIT_XOR_OP binary_expression
|
||||
;
|
||||
|
||||
unary_expression:
|
||||
primary_expression
|
||||
| SUB unary_expression
|
||||
| ADD unary_expression
|
||||
| BIT_NOT_OP unary_expression
|
||||
| NOT unary_expression;
|
||||
|
||||
primary_expression
|
||||
: constant
|
||||
| input_alias
|
||||
// parameter_name: Represents a value of the specified parameter name. Parameter names must have a single @ as the first character.
|
||||
| array_create_expression
|
||||
| object_create_expression
|
||||
| function_call_expression
|
||||
| LEFT_PARENTHESIS scalar_expression RIGHT_PARENTHESIS
|
||||
| LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
|
||||
| primary_expression DOT property_name
|
||||
| primary_expression LEFT_BRACKET scalar_expression RIGHT_BRACKET
|
||||
| exists_scalar_expression
|
||||
| array_scalar_expression
|
||||
;
|
||||
|
||||
constant
|
||||
: K_undefined
|
||||
| K_null
|
||||
| K_true
|
||||
| K_false
|
||||
| NUMBER
|
||||
| QUOTE ID QUOTE
|
||||
| array_constant
|
||||
| object_constant
|
||||
;
|
||||
|
||||
array_constant
|
||||
: LEFT_BRACKET array_constant_list? RIGHT_BRACKET
|
||||
;
|
||||
|
||||
array_constant_list
|
||||
: constant
|
||||
| array_constant_list COMMA constant
|
||||
;
|
||||
|
||||
object_constant
|
||||
: LEFT_BRACE object_constant_items? RIGHT_BRACE
|
||||
;
|
||||
|
||||
object_constant_item
|
||||
: property_name COLON constant
|
||||
;
|
||||
|
||||
object_constant_items
|
||||
: object_constant_item
|
||||
| object_constant_items COMMA object_constant_item
|
||||
;
|
||||
@@ -0,0 +1,79 @@
|
||||
import * as Q from "q";
|
||||
import {editor, languages, MarkerSeverity} from "monaco-editor";
|
||||
|
||||
export enum ParseReason {
|
||||
GetCompletionWords = 1,
|
||||
GetErrors = 2
|
||||
}
|
||||
|
||||
export class LanguageServiceFacade {
|
||||
private static readonly timeout : number = 2000;
|
||||
|
||||
private static workingWorker : Worker = null;
|
||||
|
||||
public static GetLanguageServiceParseResult(str : string, parseReason : ParseReason) : Q.Promise<any[]> {
|
||||
const timeExceeded = Q.Promise<any[]>((resolve : any, reject : any) => {
|
||||
const wait = setTimeout(() => {
|
||||
const words : any = [];
|
||||
resolve(words);
|
||||
}, LanguageServiceFacade.timeout);
|
||||
});
|
||||
|
||||
const result = LanguageServiceFacade.GetParseResult(str, parseReason);
|
||||
return Q.race([timeExceeded, result]).then(function(words) {
|
||||
LanguageServiceFacade.workingWorker.terminate();
|
||||
return words;
|
||||
});
|
||||
}
|
||||
|
||||
private static GetParseResult = (str : string, parseReason : ParseReason) : Q.Promise<any[]> => {
|
||||
return Q.Promise((resolve : any) => {
|
||||
|
||||
if (LanguageServiceFacade.workingWorker != null) {
|
||||
LanguageServiceFacade.workingWorker.terminate();
|
||||
}
|
||||
|
||||
const currentUrlWithoutQueryParamsAndHashRoute: string = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;
|
||||
let url = currentUrlWithoutQueryParamsAndHashRoute.replace(/\/[^\/]*$/, '/node_modules/cosmosdb-language-service/dist/worker/dist/LanguageServiceWorker.js');
|
||||
LanguageServiceFacade.workingWorker = new Worker(url);
|
||||
|
||||
LanguageServiceFacade.workingWorker.onmessage = (ev : MessageEvent) => {
|
||||
var processedResults: any = [];
|
||||
|
||||
var results : any[] = ev.data;
|
||||
|
||||
if (parseReason === ParseReason.GetCompletionWords) {
|
||||
results.forEach((label: string) => {
|
||||
if (!!label) {
|
||||
processedResults.push({
|
||||
label: label,
|
||||
kind: languages.CompletionItemKind.Keyword
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (parseReason === ParseReason.GetErrors) {
|
||||
results.forEach((err: any) => {
|
||||
const mark: editor.IMarkerData = {
|
||||
severity: MarkerSeverity.Error,
|
||||
message: err.Message,
|
||||
startLineNumber: err.line,
|
||||
startColumn: err.column,
|
||||
endLineNumber: err.line,
|
||||
endColumn: err.column
|
||||
};
|
||||
|
||||
processedResults.push(mark)
|
||||
});
|
||||
}
|
||||
|
||||
resolve(processedResults);
|
||||
}
|
||||
|
||||
const source = {
|
||||
code : str,
|
||||
reason : parseReason
|
||||
};
|
||||
LanguageServiceFacade.workingWorker.postMessage(source);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
import { CommonTokenStream } from "antlr4/CommonTokenStream";
|
||||
import { Lexer } from "antlr4/Lexer";
|
||||
import { Token } from "antlr4/Token";
|
||||
|
||||
export class LSCommonTokenStream extends CommonTokenStream {
|
||||
public EofListener;
|
||||
|
||||
constructor(tokenSource : Lexer) {
|
||||
super(tokenSource);
|
||||
}
|
||||
|
||||
public LA(i : number) : number {
|
||||
let token : number = super.LA(i);
|
||||
|
||||
if (token != null && token == Token.EOF && this.EofListener != undefined) {
|
||||
this.EofListener();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
public LT(i : number) : any {
|
||||
let token = super.LT(i);
|
||||
|
||||
if (token != null && token.type == Token.EOF && this.EofListener != undefined) {
|
||||
this.EofListener();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
import { ErrorListener } from "antlr4/error/ErrorListener";
|
||||
|
||||
export class LSErrorListener extends ErrorListener {
|
||||
private AddSyntaxError : (msg : string, line : number, column : number) => any;
|
||||
|
||||
constructor(AddSyntaxError : (msg : string, line : number, column : number) => any) {
|
||||
super();
|
||||
this.AddSyntaxError = AddSyntaxError;
|
||||
}
|
||||
|
||||
public syntaxError(recognizer: any, offendingSymbol: any,line: number, column: number, msg: string, e: any): void {
|
||||
this.AddSyntaxError(msg, line, column);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
import * as ATNState from "antlr4/atn/ATNState";
|
||||
import * as Transition from "antlr4/atn/Transition";
|
||||
import { ATN } from "antlr4/atn/ATN";
|
||||
import { CommonTokenStream } from "antlr4/CommonTokenStream";
|
||||
import { DFA } from "antlr4/dfa/DFA";
|
||||
import { LanguageService } from "./LanguageService";
|
||||
import { NoViableAltException } from "antlr4/error/Errors";
|
||||
import { Parser } from "antlr4/Parser";
|
||||
import { ParserATNSimulator } from "antlr4/atn/ParserATNSimulator";
|
||||
import { PredictionContextCache } from "antlr4/PredictionContext";
|
||||
import { PredictionMode } from "antlr4/atn/PredictionMode";
|
||||
import { RuleContext } from "antlr4/RuleContext";
|
||||
import { Token } from "antlr4/Token";
|
||||
import { Utils } from "./Utils";
|
||||
|
||||
interface StateWithTransitionPath {
|
||||
state : ATNState.ATNState,
|
||||
transitionStates : ATNState.ATNState[]
|
||||
}
|
||||
|
||||
export class LSParserATNSimulator extends ParserATNSimulator {
|
||||
private predictionMode = PredictionMode.LL;
|
||||
|
||||
private parser : Parser;
|
||||
|
||||
private atn : ATN;
|
||||
|
||||
private languageService : LanguageService;
|
||||
|
||||
constructor(parser : Parser, atn : ATN, decisionToDFA : Array<DFA>, sharedContextCache : PredictionContextCache, languageService : LanguageService) {
|
||||
super(parser, atn, decisionToDFA, sharedContextCache);
|
||||
this.parser = parser;
|
||||
this.atn = atn;
|
||||
this.languageService = languageService;
|
||||
}
|
||||
|
||||
public adaptivePredict(input : CommonTokenStream, decision : number, outerContext : RuleContext) {
|
||||
let tokensLeft : number = -1;
|
||||
|
||||
try {
|
||||
this.languageService.IsInPredict = true;
|
||||
this.languageService.EofReachedInPredict = false;
|
||||
|
||||
if (decision >= 0) {
|
||||
return super.adaptivePredict(input, decision, outerContext);
|
||||
}
|
||||
}
|
||||
catch(error) {
|
||||
if (error instanceof NoViableAltException && error.offendingToken.type === Token.EOF) {
|
||||
tokensLeft = error.offendingToken.tokenIndex - this.parser.getCurrentToken().tokenIndex;
|
||||
return 1;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (this.languageService.EofReachedInPredict) {
|
||||
if (tokensLeft < 0) {
|
||||
tokensLeft = 0;
|
||||
while (input.LA(tokensLeft + 1) != Token.EOF) {
|
||||
tokensLeft++;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokensLeft > 0) {
|
||||
let states = this.CalculateValidStates(input, tokensLeft);
|
||||
this.languageService.RecordErrorStatesBeforeEof(states);
|
||||
}
|
||||
}
|
||||
|
||||
this.languageService.IsInPredict = false;
|
||||
}
|
||||
}
|
||||
|
||||
private CalculateValidStates(input : CommonTokenStream, tokensLeft : number): ATNState.ATNState[] {
|
||||
let state = this.atn.states[this.parser.state];
|
||||
let states : StateWithTransitionPath[] = [ {
|
||||
state : state,
|
||||
transitionStates : []
|
||||
} ];
|
||||
let validStates : StateWithTransitionPath[] = [];
|
||||
|
||||
// one step each time. Consume a single token each time.
|
||||
for (let index = 1; index <= tokensLeft; index++) {
|
||||
let _states : StateWithTransitionPath[] = [];
|
||||
let nextToken : number = input.LA(index);
|
||||
|
||||
states.forEach(s => { _states = _states.concat(this.ConsumeSingleTokenAhead(s, nextToken)).filter(Utils.notDuplicate); });
|
||||
states = _states.filter(Utils.notDuplicate);
|
||||
}
|
||||
|
||||
states.forEach(s => { validStates = validStates.concat(this.SearchValidStates(s)); });
|
||||
return validStates.map(s => s.state).filter(Utils.notDuplicate);
|
||||
}
|
||||
|
||||
private ConsumeSingleTokenAhead(stateWithTransitionPath : StateWithTransitionPath, matchToken : Token) : StateWithTransitionPath[] {
|
||||
let validStates : StateWithTransitionPath[] = [];
|
||||
let currentState = stateWithTransitionPath.state;
|
||||
let nextStateWithTransitionPath : StateWithTransitionPath = {
|
||||
state : null, // Temporary null
|
||||
transitionStates : stateWithTransitionPath.transitionStates.slice()
|
||||
};
|
||||
|
||||
if(nextStateWithTransitionPath.transitionStates.length > 0 &&
|
||||
nextStateWithTransitionPath.transitionStates[nextStateWithTransitionPath.transitionStates.length - 1].ruleIndex === currentState.ruleIndex) {
|
||||
nextStateWithTransitionPath.transitionStates.pop();
|
||||
}
|
||||
|
||||
nextStateWithTransitionPath.transitionStates.push(currentState);
|
||||
|
||||
if (!(currentState instanceof ATNState.RuleStopState)) {
|
||||
for (let index = 0;index < currentState.transitions.length;index++) {
|
||||
let transition = currentState.transitions[index];
|
||||
let destinationChildState = transition.target;
|
||||
nextStateWithTransitionPath.state = destinationChildState;
|
||||
|
||||
if (!transition.isEpsilon) {
|
||||
if (transition.label != null && transition.label.contains(matchToken)) {
|
||||
validStates = validStates.concat(this.SearchValidStates(nextStateWithTransitionPath));
|
||||
}
|
||||
} else {
|
||||
validStates = validStates.concat(this.ConsumeSingleTokenAhead(nextStateWithTransitionPath, matchToken)).filter(Utils.notDuplicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validStates.filter(Utils.notEmpty);
|
||||
}
|
||||
|
||||
private SearchValidStates(stateWithTransitionPath : StateWithTransitionPath) : StateWithTransitionPath[] {
|
||||
let validStates : StateWithTransitionPath[] = [];
|
||||
|
||||
if (!this.IsLastStateBeforeRuleStopState(stateWithTransitionPath.state)) {
|
||||
validStates.push(stateWithTransitionPath);
|
||||
} else {
|
||||
validStates = this.BackTracingAndFindActiveStates(stateWithTransitionPath).filter(Utils.notDuplicate);
|
||||
|
||||
if (this.HasActiveChildrenState(stateWithTransitionPath.state)) {
|
||||
validStates.push(stateWithTransitionPath);
|
||||
}
|
||||
}
|
||||
return validStates;
|
||||
}
|
||||
|
||||
private BackTracingAndFindActiveStates(stateWithTransitionPath : StateWithTransitionPath) : StateWithTransitionPath[] {
|
||||
let validStates : StateWithTransitionPath[] = [];
|
||||
let completedRuleIndex = stateWithTransitionPath.state.ruleIndex;
|
||||
let statesStack = this.GetLastStateInDifferentRulesFomStatesStack(stateWithTransitionPath.transitionStates, completedRuleIndex);
|
||||
let currentStateIndex = statesStack.length - 1;
|
||||
let keepBackTracing : boolean = true;
|
||||
|
||||
while (keepBackTracing && currentStateIndex >= 0) {
|
||||
let currentState = statesStack[currentStateIndex];
|
||||
keepBackTracing = false;
|
||||
let followingStates = this.GetRuleFollowingState(currentState, completedRuleIndex);
|
||||
|
||||
for (let index = 0;index < followingStates.length; index++) {
|
||||
let lastStateBeforeRuleStopState : boolean = false;
|
||||
let haveActiveChildrenStatesInCurrentRule : boolean = false;
|
||||
let transitions = followingStates[index].transitions;
|
||||
while (transitions.length > 0){
|
||||
let epsilonTrans = [];
|
||||
for (let tIndex = 0;tIndex < transitions.length; tIndex++) {
|
||||
if (transitions[tIndex].isEpsilon) {
|
||||
if (transitions[tIndex] instanceof Transition.RuleTransition) {
|
||||
haveActiveChildrenStatesInCurrentRule = true;
|
||||
} else if (transitions[tIndex].target instanceof ATNState.RuleStopState) {
|
||||
lastStateBeforeRuleStopState = true;
|
||||
} else {
|
||||
epsilonTrans = epsilonTrans.concat(transitions[tIndex].target.transitions);
|
||||
}
|
||||
} else {
|
||||
haveActiveChildrenStatesInCurrentRule = true;
|
||||
}
|
||||
}
|
||||
|
||||
transitions = epsilonTrans;
|
||||
if (lastStateBeforeRuleStopState && haveActiveChildrenStatesInCurrentRule) {
|
||||
// We can jump out of loop ahead of schedule.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastStateBeforeRuleStopState) {
|
||||
keepBackTracing = true;
|
||||
}
|
||||
|
||||
if (haveActiveChildrenStatesInCurrentRule) {
|
||||
//validStates.push(followingStates[index]);
|
||||
let newValidState : StateWithTransitionPath = {
|
||||
state : followingStates[index],
|
||||
transitionStates : statesStack.slice(0, currentStateIndex + 1)
|
||||
};
|
||||
validStates.push(newValidState);
|
||||
}
|
||||
}
|
||||
|
||||
currentStateIndex--;
|
||||
|
||||
if(keepBackTracing) {
|
||||
completedRuleIndex = followingStates[0].ruleIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return validStates.filter(Utils.notEmpty);
|
||||
}
|
||||
|
||||
private GetLastStateInDifferentRulesFomStatesStack(statesStack : ATNState.ATNState[], lastMatchedRuleIndex : number) : ATNState.ATNState[] {
|
||||
let lastStates : ATNState.ATNState[] = [];
|
||||
let matchedRuleIndex = lastMatchedRuleIndex;
|
||||
for(let currentStateIndex = statesStack.length - 1; currentStateIndex >= 0; currentStateIndex--) {
|
||||
if(statesStack[currentStateIndex].ruleIndex === matchedRuleIndex) {
|
||||
continue;
|
||||
} else {
|
||||
lastStates.push(statesStack[currentStateIndex]);
|
||||
matchedRuleIndex = statesStack[currentStateIndex].ruleIndex;
|
||||
}
|
||||
}
|
||||
|
||||
lastStates.reverse();
|
||||
return lastStates.filter(Utils.notEmpty);
|
||||
}
|
||||
|
||||
private GetRuleFollowingState(state : ATNState.ATNState, ruleIndex : number) : ATNState.ATNState[] {
|
||||
let followingStates : ATNState.ATNState[] = [];
|
||||
|
||||
if(state instanceof ATNState.RuleStopState) {
|
||||
return followingStates;
|
||||
}
|
||||
|
||||
let transitions = state.transitions;
|
||||
|
||||
while(transitions.length > 0) {
|
||||
let epsilonTrans = [];
|
||||
for (let index = 0;index < transitions.length;index++) {
|
||||
if (transitions[index].isEpsilon) {
|
||||
if (transitions[index] instanceof Transition.RuleTransition) {
|
||||
if (transitions[index].ruleIndex === ruleIndex) {
|
||||
followingStates.push(transitions[index].followState);
|
||||
}
|
||||
} else if (!(transitions[index].target instanceof ATNState.RuleStopState)) {
|
||||
epsilonTrans = epsilonTrans.concat(transitions[index].target.transitions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transitions = epsilonTrans;
|
||||
}
|
||||
|
||||
return followingStates.filter(Utils.notEmpty);
|
||||
}
|
||||
|
||||
// Means with this state, parser can make up a complete rule.
|
||||
private IsLastStateBeforeRuleStopState(state : ATNState.ATNState) {
|
||||
let transitions = state.transitions;
|
||||
|
||||
while(transitions.length > 0) {
|
||||
let epsilonTrans = [];
|
||||
for (let index = 0;index < transitions.length;index++) {
|
||||
if (transitions[index].isEpsilon) {
|
||||
if (transitions[index].target instanceof ATNState.RuleStopState) {
|
||||
return true;
|
||||
} else if (!(transitions[index] instanceof Transition.RuleTransition)) {
|
||||
epsilonTrans = epsilonTrans.concat(transitions[index].target.transitions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transitions = epsilonTrans;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private HasActiveChildrenState(state : ATNState.ATNState) : boolean {
|
||||
let transitions = state.transitions;
|
||||
|
||||
while(transitions.length > 0) {
|
||||
let epsilonTrans = [];
|
||||
for (let index = 0;index < transitions.length;index++) {
|
||||
if (transitions[index].isEpsilon) {
|
||||
if (transitions[index] instanceof Transition.RuleTransition) {
|
||||
return true;
|
||||
} else if (!(transitions[index].target instanceof ATNState.RuleStopState)) {
|
||||
epsilonTrans = epsilonTrans.concat(transitions[index].target.transitions);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
transitions = epsilonTrans;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
import * as antlr4 from "antlr4";
|
||||
import * as ATNState from "antlr4/atn/ATNState";
|
||||
import { InputStream } from "antlr4/InputStream";
|
||||
import { IntervalSet } from "antlr4/IntervalSet";
|
||||
import { Lexer } from "antlr4/Lexer";
|
||||
import { LSCommonTokenStream } from "./LSCommonTokenStream";
|
||||
import { LSErrorListener } from "./LSErrorListener";
|
||||
import { LSParserATNSimulator } from "./LSParserATNSimulator";
|
||||
import { Parser } from "antlr4/Parser";
|
||||
import { Utils } from "./Utils";
|
||||
|
||||
interface ErrorMarkItem {
|
||||
line: number;
|
||||
column: number;
|
||||
Message: string;
|
||||
}
|
||||
|
||||
interface StateContextDict {
|
||||
[key : number] : StateContext
|
||||
}
|
||||
|
||||
class StateContext {
|
||||
public State : number;
|
||||
|
||||
public ExpectedTokens : IntervalSet;
|
||||
|
||||
public RuleIndex : number;
|
||||
|
||||
public RuleStack : string[];
|
||||
|
||||
constructor(state : number, ruleIndex : number, expectedTokens : IntervalSet, ruleStack : string[]) {
|
||||
this.State = state;
|
||||
this.RuleIndex = ruleIndex;
|
||||
this.ExpectedTokens = expectedTokens;
|
||||
this.RuleStack = ruleStack;
|
||||
}
|
||||
}
|
||||
|
||||
export class LanguageService {
|
||||
private _lexerCtr : any;
|
||||
private _parserCtr : any;
|
||||
private _lexer : Lexer = null;
|
||||
private _parser : Parser = null;
|
||||
|
||||
private _keywordsDict: { [key : string] : string } = null;
|
||||
|
||||
public StatesBeforeEof : StateContextDict = {};
|
||||
|
||||
public SyntaxErrors : ErrorMarkItem[] = [];
|
||||
|
||||
private EofReached : boolean = false;
|
||||
|
||||
public EofReachedInPredict : boolean = false;
|
||||
|
||||
private ExThrownAfterEofReached : boolean = false;
|
||||
|
||||
public IsInPredict : boolean = false;
|
||||
|
||||
constructor(lexerCtr : Lexer, parserCtr : Parser, keywordsDict : { [key : string] : string }) {
|
||||
this._lexerCtr = lexerCtr;
|
||||
this._parserCtr = parserCtr;
|
||||
this._keywordsDict = keywordsDict;
|
||||
}
|
||||
|
||||
private _parse(input : string) {
|
||||
this.PrepareParse();
|
||||
this._lexer = new this._lexerCtr(new InputStream(input));
|
||||
this._parser = new this._parserCtr(new LSCommonTokenStream(this._lexer));
|
||||
|
||||
this._parser.getTokenStream().EofListener = () => {
|
||||
this.RecordStateBeforeEof();
|
||||
};
|
||||
|
||||
this._parser.removeErrorListeners();
|
||||
this._parser.addErrorListener(new LSErrorListener(
|
||||
(msg, line, column) => {
|
||||
this.AddSyntaxError(msg, line, column);
|
||||
}
|
||||
));
|
||||
|
||||
let decisionsToDFA = this._parser.atn.decisionToState.map((ds, index) => { return new antlr4.dfa.DFA(ds, index);});
|
||||
this._parser._interp = new LSParserATNSimulator(this._parser, this._parser.atn, decisionsToDFA, new antlr4.PredictionContextCache(), this);
|
||||
this._parser.root();
|
||||
}
|
||||
|
||||
public GetExpectedTokenStrs = function() : string[] {
|
||||
let intervalSets = new IntervalSet();
|
||||
for (var key in this.StatesBeforeEof) {
|
||||
if (this.StatesBeforeEof.hasOwnProperty(key)) {
|
||||
intervalSets.addSet(this.StatesBeforeEof[key].ExpectedTokens);
|
||||
}
|
||||
}
|
||||
|
||||
var expectedStrings = [];
|
||||
if (intervalSets.intervals === null) {
|
||||
return expectedStrings;
|
||||
}
|
||||
|
||||
for (var i = 0; i < intervalSets.intervals.length; i++) {
|
||||
var v = intervalSets.intervals[i];
|
||||
if (v.start < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var j = v.start; j < v.stop; j++) {
|
||||
var tokenString = this._parser._input.tokenSource.symbolicNames[j];
|
||||
if (tokenString != null) {
|
||||
let keyword = this._keywordsDict[tokenString.replace(/^\'|\'$/gi, "")];
|
||||
if (keyword != null) {
|
||||
expectedStrings.push(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectedStrings.filter(Utils.notDuplicate);
|
||||
}
|
||||
|
||||
public RecordStateBeforeEof = function() {
|
||||
if (!this.IsInPredict) {
|
||||
this.EofReached = true;
|
||||
if (!this.ExThrownAfterEofReached) {
|
||||
if (this.StatesBeforeEof[this._parser.state] == undefined || this.StatesBeforeEof[this._parser.state] == null) {
|
||||
this.StatesBeforeEof[this._parser.state] = new StateContext(this._parser.state, this._parser._ctx.ruleIndex, this._parser.getExpectedTokens(), this._parser.getRuleInvocationStack());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.EofReachedInPredict = true;
|
||||
}
|
||||
}
|
||||
|
||||
public RecordErrorStatesBeforeEof = function(states : ATNState.ATNState[]) {
|
||||
if (states.length > 0) {
|
||||
states.forEach(state => {
|
||||
if (state != null) {
|
||||
if (this.StatesBeforeEof[state.stateNumber] == undefined || this.StatesBeforeEof[state.stateNumber] == null) {
|
||||
this.StatesBeforeEof[state.stateNumber] = new StateContext(state.stateNumber, state.ruleIndex, this._parser._interp.atn.nextTokens(state), this._parser.getRuleInvocationStack());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public AddSyntaxError = (msg : string, line : number, column : number) : any => {
|
||||
let error : ErrorMarkItem = {
|
||||
line : line,
|
||||
column : column,
|
||||
Message : msg
|
||||
};
|
||||
|
||||
this.SyntaxErrors.push(error);
|
||||
|
||||
if (this.EofReached) {
|
||||
this.ExThrownAfterEofReached = true;
|
||||
}
|
||||
}
|
||||
|
||||
public PrepareParse() : any {
|
||||
this.EofReached = false;
|
||||
this.EofReachedInPredict = false;
|
||||
this.ExThrownAfterEofReached = false;
|
||||
this.StatesBeforeEof = {};
|
||||
this.SyntaxErrors = [];
|
||||
}
|
||||
|
||||
public getCompletionWords(input : string) : string[] {
|
||||
this._parse(input);
|
||||
return this.GetExpectedTokenStrs();
|
||||
}
|
||||
|
||||
public getSyntaxErrors(input : string) : ErrorMarkItem[] {
|
||||
this._parse(input);
|
||||
return this.SyntaxErrors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
export class Utils {
|
||||
public static notEmpty<TValue>(value : TValue | null | undefined) : value is TValue {
|
||||
return value !== null && value !== undefined;
|
||||
}
|
||||
|
||||
public static notDuplicate(item, pos, self) {
|
||||
return self.indexOf(item) == pos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import * as Q from "q";
|
||||
import {editor} from "monaco-editor"
|
||||
import {LanguageServiceFacade, ParseReason} from "../facade/LanguageServiceFacade";
|
||||
|
||||
export class ErrorMarkProvider {
|
||||
public static getErrorMark(input: string): Q.Promise<editor.IMarkerData[]> {
|
||||
return LanguageServiceFacade.GetLanguageServiceParseResult(input, ParseReason.GetErrors);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {LanguageServiceFacade, ParseReason} from "../facade/LanguageServiceFacade";
|
||||
import {editor, Position, CancellationToken} from "monaco-editor";
|
||||
|
||||
export class SqlCompletionItemProvider {
|
||||
public triggerCharacters: string[] = [" ","."];
|
||||
|
||||
provideCompletionItems(model: editor.IReadOnlyModel, position: Position, token: CancellationToken) {
|
||||
const range = {
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: position.lineNumber,
|
||||
endColumn: position.column
|
||||
}
|
||||
|
||||
let text = model.getValueInRange(range);
|
||||
text = this.triggerCharacters.indexOf(text.charAt(text.length - 1)) < 0 ? text.substring(0, text.length - 1) : text;
|
||||
return LanguageServiceFacade.GetLanguageServiceParseResult(text, ParseReason.GetCompletionWords);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { LanguageService } from "../language-service/LanguageService";
|
||||
import { CosmosDBSqlLexer } from "../cosmosdb-sql/generated/CosmosDBSqlLexer";
|
||||
import { CosmosDBSqlParser } from "../cosmosdb-sql/generated/CosmosDBSqlParser";
|
||||
import { CosmosDBSqlKeywords } from "../cosmosdb-sql/grammar/CosmosDBSqlKeywords";
|
||||
|
||||
enum ParseReason {
|
||||
GetCompletionWords = 1,
|
||||
GetErrors = 2
|
||||
}
|
||||
|
||||
export module LanguageServiceWorker {
|
||||
// Respond to message from parent thread
|
||||
onmessage = (event: MessageEvent) => {
|
||||
const code: string = event.data.code;
|
||||
const reason : number = event.data.reason;
|
||||
|
||||
let parseResults = [];
|
||||
|
||||
let languageService = new LanguageService(CosmosDBSqlLexer, CosmosDBSqlParser, CosmosDBSqlKeywords.keywordsRegisteredForCompletion);
|
||||
|
||||
if (reason == ParseReason.GetCompletionWords) {
|
||||
parseResults = languageService.getCompletionWords(code);
|
||||
} else if (reason == ParseReason.GetErrors) {
|
||||
parseResults = languageService.getSyntaxErrors(code);
|
||||
}
|
||||
|
||||
postMessage(parseResults, undefined);
|
||||
close();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
LanguageServiceWorker: './LanguageServiceWorker.ts'
|
||||
},
|
||||
mode: 'production',
|
||||
target: 'web',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
output: {
|
||||
globalObject: 'this',
|
||||
path: __dirname + "/dist",
|
||||
filename: '[name].js',
|
||||
library: '[name]',
|
||||
libraryTarget: 'umd'
|
||||
},
|
||||
node: { fs: "empty" }
|
||||
};
|
||||
Reference in New Issue
Block a user