From 321227aeeb647097da983817f31a72591f31eec1 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Mon, 26 May 2025 20:45:42 +0200 Subject: [PATCH 01/19] Update autocomplete --- package.json | 6 +- .../codemirror-lang-typeql/complete.ts | 171 ++++ .../typeql.grammar.generated.terms.ts | 224 +++++ .../generated/typeql.grammar.generated.ts | 16 + .../codemirror-lang-typeql/index.cjs | 198 ----- .../codemirror-lang-typeql/index.d.cts | 5 - .../codemirror-lang-typeql/index.d.ts | 5 - src/framework/codemirror-lang-typeql/index.js | 192 ----- src/framework/codemirror-lang-typeql/index.ts | 133 +++ .../codemirror-lang-typeql/schema.ts | 191 +++++ .../codemirror-lang-typeql/typeql.grammar | 776 ++++++++++++++++++ .../typeql.grammar.d.ts | 3 + .../typeql_suggestions.ts | 178 ++++ src/module/query/query-tool.component.html | 2 +- src/module/query/query-tool.component.ts | 3 +- 15 files changed, 1700 insertions(+), 403 deletions(-) create mode 100644 src/framework/codemirror-lang-typeql/complete.ts create mode 100644 src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.terms.ts create mode 100644 src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.ts delete mode 100644 src/framework/codemirror-lang-typeql/index.cjs delete mode 100644 src/framework/codemirror-lang-typeql/index.d.cts delete mode 100644 src/framework/codemirror-lang-typeql/index.d.ts delete mode 100644 src/framework/codemirror-lang-typeql/index.js create mode 100644 src/framework/codemirror-lang-typeql/index.ts create mode 100644 src/framework/codemirror-lang-typeql/schema.ts create mode 100644 src/framework/codemirror-lang-typeql/typeql.grammar create mode 100644 src/framework/codemirror-lang-typeql/typeql.grammar.d.ts create mode 100644 src/framework/codemirror-lang-typeql/typeql_suggestions.ts diff --git a/package.json b/package.json index 13d7e317..85b45e9e 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "3.4.0-rc1", "scripts": { "ng": "ng", + "generate-grammar": "lezer-generator --typeScript src/framework/codemirror-lang-typeql/typeql.grammar -o src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated", "start": "ng serve", "build": "ng build", "watch": "ng build --watch --configuration development", @@ -23,9 +24,11 @@ "@angular/platform-browser-dynamic": "20.0.3", "@angular/router": "20.0.3", "@codemirror/lint": "6.8.5", + "@codemirror/autocomplete": "6.18.6", "@customerio/cdp-analytics-browser": "0.2.0", "@hhangular/resizable": "1.18.1", "@intercom/messenger-js-sdk": "0.0.14", + "@lezer/common": "^1.0.0", "@lezer/lr": "1.4.2", "@sigma/edge-curve": "3.1.0", "@sigma/node-square": "3.0.0", @@ -68,7 +71,8 @@ "netlify-plugin-discord": "0.0.2", "typescript": "5.8.3", "unplugin-lezer": "1.0.1", - "@tauri-apps/cli": "^2" + "@tauri-apps/cli": "^2", + "@lezer/generator": "^1.7.3" }, "browserslist": [ "defaults and fully supports es6-module" diff --git a/src/framework/codemirror-lang-typeql/complete.ts b/src/framework/codemirror-lang-typeql/complete.ts new file mode 100644 index 00000000..d4889a52 --- /dev/null +++ b/src/framework/codemirror-lang-typeql/complete.ts @@ -0,0 +1,171 @@ + +import { CompletionContext, Completion, CompletionResult } from "@codemirror/autocomplete"; +import { syntaxTree } from "@codemirror/language" +import { SyntaxNode, NodeType, Tree } from "@lezer/common" +import { SUGGESTION_MAP } from "./typeql_suggestions"; + +export interface SuggestionMap { + [key: number]: SuffixOfPrefixSuggestion[] +}; + + +export type SuffixCandidate = number[]; // A SuffixCandidate 's' "matches" a prefix if prefix[-s.length:] == s +export interface SuffixOfPrefixSuggestion { + suffixes: SuffixCandidate[], // If any of the suffix candidates match, the suggestions will be used. + suggestions: SuggestionFunction[] +}; + +export type SuggestionFunction = (context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], state: STATE) => Completion[] | null; + +export function suggest(type: string, label: string, boost: number = 0): Completion { + // type (docs): used to pick an icon to show for the completion. Icons are styled with a CSS class created by appending the type name to "cm-completionIcon-". + return { + label: label, + type: type, + apply: label, + info: type, + boost: boost, + }; +} + +interface NodePrefixAutoCompleteState { + mayUpdateFromEditorState(context: CompletionContext, tree: Tree): void; +} + +// See: https://codemirror.net/examples/autocompletion/ and maybe the SQL / HTML Example there. +export class NodePrefixAutoComplete { + suggestionMap: SuggestionMap; + suggestorState: STATE; + + constructor(suggestionMap: SuggestionMap, suggestorState: STATE) { + // This is where we would set up the autocompletion, but we do it in the index.ts file. + // See: https://codemirror.net/docs/ref/#autocomplete.autocompletion + this.suggestionMap = suggestionMap; + this.suggestorState = suggestorState; + } + + autocomplete(context: CompletionContext): CompletionResult | null { + let tree: Tree = syntaxTree(context.state); + this.suggestorState.mayUpdateFromEditorState(context, tree); + let currentNode: SyntaxNode = tree.resolveInner(context.pos, -1); // https://lezer.codemirror.net/docs/ref/#common.SyntaxNode + let options = this.getSuggestions(context, tree, currentNode); + if (options != null) { + // And once we figure out, we have to create a list of completion objects + // It may be worth changing the grammar to be able to do this more easily, rather than replicate the original TypeQL grammar. + // https://codemirror.net/docs/ref/#autocomplete.Completion + let from = findStartOfCompletion(context) + 1; + return { + from: from, + options: options, + // Docs: "regular expression that tells the extension that, as long as the updated input (the range between the result's from property and the completion point) matches that value, it can continue to use the list of completions." + validFor: /^([\w\$]+)?$/ + } + } else { + return null; + } + } + + getSuggestions(context: CompletionContext, tree: Tree, parseAt: SyntaxNode): Completion[] | null { + return this.climbTillWeRecogniseSomething(context, tree, parseAt, parseAt, collectPrecedingChildrenOf(context, parseAt)); + } + + + climbTillWeRecogniseSomething(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode | null, prefix: NodeType[]): Completion[] | null { + if (climbedTo == null) { + // this.logInterestingStuff(context, tree, parseAt, climbedTo, prefix); + return null; + } + let suggestionEither = this.suggestionMap[climbedTo.type.id]; + if (suggestionEither != null) { + for (var sops of (suggestionEither as SuffixOfPrefixSuggestion[])) { + if (prefixHasAnyOfSuffixes(prefix, sops.suffixes)) { + return this.combineSuggestions(context, tree, parseAt, climbedTo, prefix, sops.suggestions); + } + } + // None match? Fall through. + // console.log("Fell through!!!: ", climbedTo.type.name, "with prefix", prefix); + } + let newPrefix = collectSiblingsOf(climbedTo).concat(prefix); + return this.climbTillWeRecogniseSomething(context, tree, parseAt, climbedTo.parent, newPrefix); + } + + + combineSuggestions(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], suggestionFunctions: SuggestionFunction[]): Completion[] { + let suggestions = suggestionFunctions.map((f) => { + return f(context, tree, parseAt, climbedTo, prefix, this.suggestorState); + }).reduce((acc, curr) => { + return (curr == null) ? acc : acc!.concat(curr); + }, []); + // console.log("Matched:", climbedTo.type.name, "with prefix", prefix, ". Suggestions:", suggestions); + return suggestions!; + } + + logInterestingStuff(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode | null, prefix: NodeType[]) { + console.log("Current Node:", parseAt.name); + console.log("ClimbedTo Node:", climbedTo?.name); + + let at: SyntaxNode | null = parseAt; + let climbThrough = []; + while (at != null && at.name != climbedTo?.name) { + climbThrough.push(at.name); + at = at.parent; + } + climbThrough.push(at?.name); + console.log("Climbed through", climbThrough); + console.log("Prefix:", prefix); + } +} + +function isPartOfWord(s: string): boolean { + let matches = s.match(/^[A-Za-z0-9_\-\$]+/); + return matches != null && matches.length > 0; +} + +function findStartOfCompletion(context: CompletionContext): number { + let str = context.state.doc.sliceString(0, context.pos); + let at = context.pos - 1; + while (at >= 0 && isPartOfWord(str.charAt(at))) { + at -= 1; + } + return at; +} + +function collectSiblingsOf(node: SyntaxNode): NodeType[] { + let siblings = []; + let prev: SyntaxNode | null = node; + while (null != (prev = prev.prevSibling)) { + siblings.push(prev.type); + }; + return siblings.reverse(); +} + +function collectPrecedingChildrenOf(context: CompletionContext, node: SyntaxNode): NodeType[] { + let lastChild = node.childBefore(context.pos); + if (lastChild == null) { + return []; + } + let precedingChildren = collectSiblingsOf(lastChild); + precedingChildren.push(lastChild.type); + return precedingChildren; +} + +function prefixHasAnyOfSuffixes(prefix: NodeType[], suffixes: SuffixCandidate[]): boolean { + for (let i = 0; i < suffixes.length; i++) { + if (prefixHasSuffix(prefix, suffixes[i])) { + return true; + } + } + return false; +} + +function prefixHasSuffix(prefix: NodeType[], suffix: number[]): boolean { + if (prefix.length < suffix.length) { + return false; + } + for (let i = 0; i < suffix.length; i++) { + if (prefix[prefix.length - suffix.length + i].id != suffix[i]) { + return false; + } + } + return true; +} diff --git a/src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.terms.ts b/src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.terms.ts new file mode 100644 index 00000000..fd713919 --- /dev/null +++ b/src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.terms.ts @@ -0,0 +1,224 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + Query = 1, + QuerySchema = 2, + QueryDefine = 3, + DEFINE = 4, + Definables = 5, + Definable = 6, + DefinitionType = 7, + KIND = 8, + LABEL = 9, + COMMA = 10, + Annotations = 11, + Annotation = 12, + ANNOTATION_ABSTRACT = 13, + ANNOTATION_CASCADE = 14, + ANNOTATION_DISTINCT = 15, + ANNOTATION_INDEPENDENT = 16, + ANNOTATION_KEY = 17, + ANNOTATION_UNIQUE = 18, + ANNOTATION_CARD = 19, + PARENOPEN = 20, + Cardinality = 21, + INTEGERLITERAL = 22, + DOUBLE_DOT = 23, + PARENCLOSE = 24, + ANNOTATION_RANGE = 25, + Range = 26, + RangeBound = 27, + ValueLiteral = 28, + BOOLEANLITERAL = 29, + STRINGLITERAL = 30, + DOUBLELITERAL = 31, + ANNOTATION_REGEX = 32, + ANNOTATION_SUBKEY = 33, + IDENTIFIER = 34, + ANNOTATION_VALUES = 35, + TypeCapability = 36, + TypeCapabilityBase = 37, + SubDeclaration = 38, + SUB = 39, + ValueTypeDeclaration = 40, + VALUEKEYWORD = 41, + ValueType = 42, + BOOLEAN = 43, + INTEGER = 44, + DOUBLE = 45, + DECIMAL = 46, + DATETIMETZ = 47, + DATETIME = 48, + DATE = 49, + DURATION = 50, + STRING = 51, + OwnsDeclaration = 52, + OWNS = 53, + LabelList = 54, + SQBRACKETOPEN = 55, + SQBRACKETCLOSE = 56, + PlaysDeclaration = 57, + PLAYS = 58, + RelatesDeclaration = 59, + RELATES = 60, + AS = 61, + DefinitionFunction = 62, + FUN = 63, + FunctionSignature = 64, + FunctionArguments = 65, + FunctionArgument = 66, + VAR = 67, + COLON = 68, + NamedTypeAny = 69, + NamedTypeList = 70, + NamedType = 71, + ARROW = 72, + FunctionOutput = 73, + FunctionOutputStream = 74, + CURLYOPEN = 75, + CURLYCLOSE = 76, + FunctionOutputSingle = 77, + FunctionBlock = 78, + QueryStage = 79, + ClauseMatch = 80, + MATCH = 81, + Patterns = 82, + Pattern = 83, + Statement = 84, + StatementAssignment = 85, + LET = 86, + AssignmentLeft = 87, + VarsAssignment = 88, + VarAssignment = 89, + QUESTIONMARK = 90, + ASSIGN = 91, + IN = 92, + Expression = 93, + ExpressionValue = 94, + ExpressionParenthesis = 95, + FunctionCall = 96, + FunctionName = 97, + FunctionCallArguments = 98, + ExpressionOperator = 99, + POWER = 100, + TIMES = 101, + DIVIDE = 102, + MODULO = 103, + PLUS = 104, + MINUS = 105, + StatementType = 106, + TypeRef = 107, + TypeConstraint = 108, + TypeConstraintBase = 109, + SubConstraint = 110, + ValueTypeConstraint = 111, + LabelConstraint = 112, + LABELKEYWORD = 113, + OwnsConstraint = 114, + TypeRefList = 115, + RelatesConstraint = 116, + PlaysConstraint = 117, + StatementThing = 118, + ThingConstraintList = 119, + ThingConstraint = 120, + IsaConstraint = 121, + ISA = 122, + Comparison = 123, + ComparisonOperator = 124, + EQUAL = 125, + NOT_EQUAL = 126, + GREATER = 127, + GREATER_EQUAL = 128, + LESS = 129, + LESS_EQUAL = 130, + LIKE = 131, + CONTAINS = 132, + IidConstraint = 133, + IID = 134, + IID_VALUE = 135, + HasConstraint = 136, + HAS = 137, + LinksConstraint = 138, + LINKS = 139, + Relation = 140, + RolePlayer = 141, + PatternDisjunction = 142, + OR = 143, + PatternConjunction = 144, + PatternNegation = 145, + NOT = 146, + PatternTry = 147, + TRY = 148, + SEMICOLON = 149, + ClauseInsert = 150, + INSERT = 151, + ClausePut = 152, + PUT = 153, + ClauseUpdate = 154, + UPDATE = 155, + ClauseDelete = 156, + DELETE = 157, + StatementDeletable = 158, + OF = 159, + OperatorStream = 160, + OperatorSelect = 161, + SELECT = 162, + Vars = 163, + OperatorSort = 164, + SORT = 165, + VAROrder = 166, + ORDER = 167, + OperatorDistinct = 168, + DISTINCT = 169, + OperatorOffset = 170, + OFFSET = 171, + OperatorLimit = 172, + LIMIT = 173, + OperatorRequire = 174, + REQUIRE = 175, + OperatorReduce = 176, + REDUCE = 177, + ReduceAssign = 178, + Reducer = 179, + COUNT = 180, + MAX = 181, + MIN = 182, + MEAN = 183, + MEDIAN = 184, + STD = 185, + SUM = 186, + LIST = 187, + GROUPBY = 188, + ReturnStatement = 189, + RETURN = 190, + ReturnStream = 191, + ReturnSingle = 192, + ReturnSingleSelector = 193, + FIRST = 194, + LAST = 195, + ReturnReduce = 196, + DefinitionStruct = 197, + STRUCT = 198, + DefinitionStructFields = 199, + DefinitionStructField = 200, + StructFieldValueType = 201, + ValueTypeOptional = 202, + QueryUndefine = 203, + UNDEFINE = 204, + Undefinables = 205, + Undefinable = 206, + UndefineFrom = 207, + UndefineAnnotationFromCapability = 208, + AnnotationCategory = 209, + FROM = 210, + UndefineAnnotationFromType = 211, + UndefineCapability = 212, + UndefineSpecialise = 213, + UndefineStruct = 214, + QueryRedefine = 215, + REDEFINE = 216, + Redefinables = 217, + Redefinable = 218, + RedefinableType = 219, + QueryPipelinePreambled = 220, + Pipeline = 221, + END = 222 diff --git a/src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.ts b/src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.ts new file mode 100644 index 00000000..acf0573f --- /dev/null +++ b/src/framework/codemirror-lang-typeql/generated/typeql.grammar.generated.ts @@ -0,0 +1,16 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +export const parser = LRParser.deserialize({ + version: 14, + states: "!+`O!QQPOOO!_QQO'#EOO!vQPO'#FhO!vQPO'#FjO!vQPO'#FlO!{QPO'#FnO#ZQPO'#FsO#`QPO'#FvO#eQPO'#FzO#jQPO'#F|O#oQPO'#GOO#ZQPO'#GQO#tQPO'#GSOOQO'#Fr'#FrOOQO'#D}'#D}OOQO'#HZ'#HZO#yQPO'#HRO$TQQO'#C_O$cQPO'#GoO%pQQO'#G{OOQO'#C^'#C^OOQO'#HQ'#HQQOQPOOQ%{QPOOO&QQPO'#ETOOQO'#Ek'#EkO&hQPO'#EkO'gQPO'#EjO'nQQO'#EjOOQO'#ES'#ESO!_QQO'#FbO'vQPO'#FcO'{QPO'#FeOOQO'#ER'#ERO(QQPO'#H[O(VQQO'#EQOOQO,5:j,5:jO&VQPO'#EvO)]QPO'#HdO)bQPO,5O,5>OOOQO-E;b-E;bO7bQPO'#EkO7pQPO'#EsO7sQPO'#F_O7xQPO,5;xO8QQPO,5<[O8VQSO,5<[OOQO,5>P,5>POOQO-E;c-E;cO8[QPO,5PQQO,5=dO>UQPO,5=eOOQO,5=f,5=fOOQO,5=^,5=^OOQO,5>V,5>VOOQO-E;i-E;iO>ZQPO,5=kO>cQPO,5=kOOQO,5>W,5>WOOQO-E;j-E;jOOQO,5:s,5:sO>jQSO,5:rO>uQSO,5:rO?QQWO1G0ZO?iQWO,5;eOOQO,5;q,5;qO@]QPO,5;tO@tQPO,5;tOOQO,5;v,5;vOAVQPO,5;cOAhQPO,5;cOOQO1G0|1G0|OOQO,5;Y,5;YOOQO,5;Z,5;ZOOQO,5;[,5;[OApQPO,5;^OOQO,5;^,5;^OCiQPO,5;`OCsQPO,5;`OOQO,5;a,5;aOOQO,5;W,5;WOCzQPO1G0pODRQPO1G0pODRQPO1G0pODZQPO1G1hODcQPO1G1iODhQPO1G1kODmQPO,5;_ODrQPO,5;yODwQQO1G1dOESQPO1G1dOOQO1G1d1G1dOOQO1G1v1G1vOE[QPO1G1vOOQO,5>Q,5>QOEaQPO1G1{OOQO-E;d-E;dOOQO,5>R,5>ROOQO-E;e-E;eOOQO7+'h7+'hOElQPO'#GVOEzQPO'#GVOOQO1G2[1G2[OOQO,5>S,5>SOOQO-E;f-E;fOOQO7+'t7+'tO#ZQPO7+'tOFPQPO7+'tOFUQPO,59SOFZQPO,59SOFlQPO,59SO1jQQO,59SOFqQPO,59SOOQO-E;S-E;SOOQO,59l,59lOGPQPO1G.iOGeQPO1G.iOGeQPO1G.iOGmQPO1G.iOGtQPO,5:YOYQPO1G/rO1jQQO1G2oOG|QPO,5:OOHRQQO1G/oOHWQQO1G/oOH]QPO1G2}OOQO1G3O1G3OOHdQQO1G3POHiQPO1G3VOHpQPO1G3VOHpQPO1G3VOOQO,5=w,5=wOHxQSO1G0^OOQO-E;Z-E;ZOOQO'#Cx'#CxO?QQWO'#E_OOQO'#Ea'#EaOITQPO'#E`OOQO'#E^'#E^OInQQO'#E]OOQO7+%u7+%uOOQO'#E|'#E|O?QQWO'#E{OOQO1G1P1G1POOQO1G1`1G1`OOQO,5=|,5=|OI{QPO1G0}OOQO-E;`-E;`O'nQQO1G0zO'nQQO1G0zOOQO,5=z,5=zOJ^QPO7+&[OOQO-E;^-E;^OJeQPO7+&[OJmQPO'#HcOJrQPO7+'QOOQO7+'T7+'TOOQO7+'V7+'VOOQO1G0y1G0yOOQO1G1e1G1eOOQO,5={,5={OOQO7+'O7+'OOJzQQO7+'OOOQO-E;_-E;_OOQO7+'b7+'bPKVQPO'#HfOK[QPO,5U,5>UO!%qQQO1G2qOOQO-E;h-E;hO!%yQWO,5:|O!&QQPO,5:|OOQO7+&Q7+&QOOQO7+)T7+)TOOQOAN=`AN=`PFqQPO'#HVO!&YQPO,5:`PGwQPO'#HXO!&_QPO,5:dO!&gQQO,5:gO!&qQPO,5:gOOQOAN>fAN>fO!&yQPO,5<}OOQO,5=O,5=OO!'OQPO,5=SO!'VQPO,5=SOOQO,5=Y,5=YP1jQQO'#HjOOQO,5=x,5=xO!'_QWO1G0hOOQO-E;[-E;[OOQO1G/z1G/zO!'fQQO1G0OO!'pQPO1G0OOOQO1G0O1G0OOOQO,5=t,5=tO!'xQQO1G0ROOQO-E;W-E;WOOQO1G2i1G2iOOQO,5>T,5>TO!(SQPO1G2nOOQO-E;g-E;gP?QQWO'#H^OOQO7+%j7+%jO!(ZQQO7+%jP!!RQQO'#HYP8zQPO'#HiOOQO<T#V#WB[#W#XH}#X#Y!7Z#Y#Z!:|#Z#[!Cn#[#]!Gx#]#^!Ig#^#`5^#`#a#$u#a#b#1`#b#c#:k#c#d#<]#d#e#Bh#e#f5^#f#g#Fq#g#h$)t#h#i$7P#i#j$8w#j#k$@v#k#o5^#o#p$Cq#q#r$Cv~$pS&c~XY$kYZ$k]^$kpq$k~%PP!_!`%S~%XO#r~~%[VOr%Xrs%qs#O%X#O#P%v#P;'S%X;'S;=`&o<%lO%X~%vOn~~%yRO;'S%X;'S;=`&S;=`O%X~&VWOr%Xrs%qs#O%X#O#P%v#P;'S%X;'S;=`&o;=`<%l%X<%lO%X~&rP;=`<%l%X~&zS&d~OY&uZ;'S&u;'S;=`'W<%lO&u~'ZP;=`<%l&u~'aT}!O'p!Q!['p!c!}'p#R#S'p#T#o'p~'uT!e~}!O'p!Q!['p!c!}'p#R#S'p#T#o'p~(ZO#Z~~(`Od~~(eOh~~(jO#X~~(oO#[~~(tOY~Z(yQ#]Q!Q![)P!`!a)lX)WQoXfX!O!P)^!Q![)PX)aP!Q![)dX)iPoX!Q![)dP)qO!jP~)tP!O!P)w~)|Og~~*RO#Y~Z*YRoXfX!O!P)^!Q![)P#l#m*cQ*fR!Q![*o!c!i*o#T#Z*oQ*tR#{Q!Q![*o!c!i*o#T#Z*o~+SO!f~~+XO$Z~~+^P#u~!_!`+a~+fO#v~]+kP!}T!_!`+nW+sO#qW~+xP#s~!_!`+{~,QO#t~~,VO!|~~,YX#T#U,u#V#W-v#W#X/P#]#^0Q#_#`1e#f#g1v#g#h3P#i#j3t#j#k4i~,xP#U#V,{~-OP#g#h-R~-UP#h#i-X~-[P#f#g-_~-bP#T#U-e~-hP#V#W-k~-nP#h#i-q~-vO]~~-yP#T#U-|~.PQ#f#g.V#g#h.b~.YP#W#X.]~.bOc~~.eP#V#W.h~.kP#T#U.n~.qP#W#X.t~.wP#X#Y.z~/PO^~~/SP#]#^/V~/YP#g#h/]~/`P#h#i/c~/fP#]#^/i~/lP#b#c/o~/rP#V#W/u~/xP#h#i/{~0QO_~~0TP#b#c0W~0ZP#W#X0^~0aP#X#Y0d~0gP#d#e0j~0mP#X#Y0p~0sP#b#c0v~0yP#W#X0|~1PP#X#Y1S~1VP#b#c1Y~1]P#h#i1`~1eO`~~1hP#X#Y1k~1nP#m#n1q~1vOa~~1yQ#T#U2P#X#Y2h~2SP#b#c2V~2YP#Z#[2]~2`P#X#Y2c~2hOi~~2kP#Z#[2n~2qP#X#Y2t~2wP#l#m2z~3POp~~3SP#i#j3V~3YP#U#V3]~3`P#_#`3c~3fP#X#Y3i~3lP#m#n3o~3tOq~~3wP#b#c3z~3}P#]#^4Q~4TP#e#f4W~4ZP#i#j4^~4aP#X#Y4d~4iOb~~4lP#T#U4o~4rP#`#a4u~4xP#i#j4{~5OP#X#Y5R~5UP#g#h5X~5^Os~Y5aT}!O5p!Q![5p!c!}5p#R#S5p#T#o5pY5uUXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pY6[T}!O6k!Q![6k!c!}6k#R#S6k#T#o6kY6pTXY}!O6k!Q![6k!c!}6k#R#S6k#T#o6k~7UO!X~~7ZO!Y~~7`O#W~~7cW}!O5p!Q![5p!c!}5p#R#S5p#T#g5p#g#h7{#h#i9V#i#o5p_8SW!_PXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W8l#W#o5p^8sU$mSXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~9[WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i9t#i#o5p~9yWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g:c#g#o5p~:hWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^;Q#^#o5p~;VWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V;o#V#o5p~;tWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j<^#j#o5p~WV}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#d>m#d#o5p~>rWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d?[#d#o5p~?aWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a?y#a#o5p~@OWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y@h#Y#o5p~@mVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#UAS#U#o5p~AXWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cAq#c#o5p~AxU{~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~B_V}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#dBt#d#o5p~ByYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cCi#c#i5p#i#jGW#j#o5p~CnWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iDW#i#o5p~D]VXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#UDr#U#o5p~DwWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^Ea#^#o5p~EfWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cFO#c#o5p~FTWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#hFm#h#o5p~FtU#x~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZG]WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cGu#c#o5pZGzWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iHd#i#o5pZHkU$zPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~IQ^}!O5p!Q![5p!c!}5p#R#S5p#T#UI|#U#X5p#X#Y!!R#Y#]5p#]#^!+k#^#c5p#c#d!/z#d#i5p#i#j!2}#j#o5p~JRWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iJk#i#o5p~JpWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#YKY#Y#o5p~KaW!R~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iKy#i#o5p~LOWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^Lh#^#o5p~LmWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#bMV#b#o5p~M[WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#YMt#Y#o5p~M{U!Q~XY}!ON_!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~NdWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iN|#i#o5p~! RVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#n5p#n#o! h~! oU!P~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!!W^XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W!#S#W#Y5p#Y#Z!&S#Z#`5p#`#a!(h#a#g5p#g#h!*|#h#o5p~!#XWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!#q#^#o5p~!#vWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#b!$`#b#o5p~!$eVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U!$z#U#o5p~!%PWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!%i#a#o5p~!%pU!O~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!&XWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!&q#^#o5pZ!&vWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!'`#c#o5pZ!'eWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!'}#Y#o5pZ!(UUSPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!(mWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!)V#Y#o5p~!)[WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!)t#i#o5p~!)yWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!*c#Y#o5p~!*jU$c~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p^!+RWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W8l#W#o5p~!+pWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!,Y#h#o5p~!,_WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!,w#i#o5p~!,|WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!-f#^#o5p~!-kWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!.T#c#o5p~!.YWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W!.r#W#o5p~!.wWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!/a#i#o5p~!/hU$o~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!0PWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!0i#j#o5p~!0nWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V!1W#V#o5p~!1]WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!1u#a#o5p~!1zWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!2d#Y#o5p~!2kU}~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!3SWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!3l#g#o5p~!3qVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U!4W#U#o5p~!4]WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!4u#i#o5p~!4zWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!5d#^#o5p~!5iWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!6R#d#o5p~!6WWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!6p#c#o5p~!6wU!S~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!7^V}!O5p!Q![5p!c!}5p#R#S5p#T#b5p#b#c!7s#c#o5p~!7xYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X!8h#X#h5p#h#i!9R#i#o5p~!8oU%v~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!9WWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!9p#^#o5p~!9uWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!:_#i#o5p~!:dWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n=j#n#o5p~!;P[}!O5p!Q![5p!c!}5p#R#S5p#T#U!;u#U#]5p#]#^!>Z#^#f5p#f#g!@o#g#i5p#i#j!Bf#j#o5p~!;zWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!`WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!>x#g#o5pZ!>}WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!?g#h#o5pZ!?lWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!@U#i#o5pZ!@]U%YPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!@tWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!A^#d#o5pZ!AcWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#b!A{#b#o5pZ!BSU%jPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!BkWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!CT#c#o5p~!C[U!a~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!CqV}!O5p!Q![5p!c!}5p#R#S5p#T#f5p#f#g!DW#g#o5pZ!D]WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!Du#d#o5pZ!DzWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!Ed#j#o5pZ!EiWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#d5p#d#e!FR#e#o5pZ!FWWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V!Fp#V#o5pZ!FuWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n!G_#n#o5pZ!GfU%SPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!G{U}!O5p!Q![5p!c!}5p#R#S5p#T#U!H_#U#o5pZ!HdWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!H|#h#o5pZ!ITU#}PXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_!IjZ}!O5p!Q![5p!c!}5p#R#S5p#T#]5p#]#^!J]#^#b5p#b#c!Ke#c#g5p#g#h##p#h#o5pZ!JbWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X!Jz#X#o5pZ!KRU#zPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_!KlX#OSXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!LX#h#i!Nm#i#o5pZ!L^WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!Lv#Y#o5pZ!L{WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!Me#g#o5pZ!MjWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!NS#i#o5pZ!NZU$]RXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!NrWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y# [#Y#o5pZ# aWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Z5p#Z#[# y#[#o5pZ#!OWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#!h#Y#o5pZ#!mWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g##V#g#o5pZ##^U|RXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ##uVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#$[#U#o5pZ#$cU#nPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#$xY}!O5p!Q![5p!c!}5p#R#S5p#T#U#%h#U#X5p#X#Y#)[#Y#]5p#]#^#*d#^#o5pZ#%mYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V#&]#V#g5p#g#h#(S#h#o5pZ#&bWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#&z#Y#o5pZ#'PWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a#'i#a#o5pZ#'pU#ePXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#(XWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#(q#i#o5pZ#(xU%ZPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#)aWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#)y#i#o5p~#*QU!x~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#*i]XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#_5p#_#`#+b#`#a5p#a#b#,j#b#c#.a#c#g5p#g#h#0W#h#o5p~#+gWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#,P#Y#o5p~#,WU#w~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#,oWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#-X#^#o5p~#-^WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#-v#i#o5p~#-}U$s~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#.fWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#_5p#_#`#/O#`#o5pZ#/TWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#/m#h#o5pZ#/tU$PPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#0]WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#0u#i#o5pZ#0|U%RPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#1cY}!O5p!Q![5p!c!}5p#R#S5p#T#U#2R#U#X5p#X#Y#5W#Y#]5p#]#^#9c#^#o5p~#2WYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#2v#i#l5p#l#m#4m#m#o5p~#2{WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W#3e#W#o5p~#3jWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#[5p#[#]#4S#]#o5p~#4ZU!s~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#4tU${PXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#5]XXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#5x#U#W5p#W#X#7Q#X#o5pZ#5}WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#6g#c#o5pZ#6nU$}PXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#7VWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#7o#^#o5pZ#7tVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#8Z#U#o5pZ#8`WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#8x#c#o5pZ#9PU%OPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#9hWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#:Q#c#o5pZ#:XU$|PXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#:nV}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#d#;T#d#o5p~#;YWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#;r#i#o5p~#;yU$W~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_#<`Z}!O5p!Q![5p!c!}5p#R#S5p#T#Y5p#Y#Z#=R#Z#f5p#f#g#@W#g#k5p#k#l#@q#l#o5p_#=YW$eSXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z#=r#Z#o5pZ#=wWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#>a#h#o5pZ#>fWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#?O#Y#o5pZ#?TWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#?m#i#o5pZ#?tU$qRXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#@_U$TPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#@vWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#A`#c#o5pZ#AeWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#A}#h#o5pZ#BUU!VPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#BkX}!O5p!Q![5p!c!}5p#R#S5p#T#`5p#`#a#CW#a#i5p#i#j#Ei#j#o5pZ#C]VXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#Cr#U#o5pZ#CwWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n#Da#n#o5pZ#DfWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#EO#h#o5pZ#EVU![PXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#EnWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#FW#i#o5p~#F_U$_~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#FtV}!O5p!Q![5p!c!}5p#R#S5p#T#X5p#X#Y#GZ#Y#o5p~#G`^XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X#H[#X#`5p#`#a#My#a#e5p#e#f$$]#f#h5p#h#i$'`#i#o5p~#HaYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#IP#Y#i5p#i#j#LS#j#o5pZ#IUWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z#In#Z#o5pZ#IsWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#J]#^#o5pZ#JbWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#Jz#c#o5pZ#KPWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#Ki#Y#o5pZ#KpU%pPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#LXWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W#Lq#W#o5p~#LvWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#M`#Y#o5p~#MgU$w~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#NOVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#Ne#U#o5p~#NjWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$ S#i#o5p~$ XYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$ w#Y#]5p#]#^$#P#^#o5pZ$ |WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h$!f#h#o5pZ$!mU!^PXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$#UWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d$#n#d#o5p~$#sWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c=j#c#o5p~$$bWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$$z#j#o5p~$%PWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$%i#^#o5p~$%nWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$&W#g#o5p~$&]WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$&u#Y#o5p~$&|U$u~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$'eWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$'}#j#o5p~$(SWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$(l#g#o5p~$(qWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c$)Z#c#o5p~$)bU%U~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$)w[}!O5p!Q![5p!c!}5p#R#S5p#T#X5p#X#Y$*m#Y#c5p#c#d$-p#d#h5p#h#i$/g#i#j$5W#j#o5p~$*rWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a$+[#a#o5p~$+aWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$+y#Y#o5p~$,OWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W$,h#W#o5p~$,mWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$-V#i#o5p~$-^U$h~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$-uWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$._#g#o5p~$.dWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$.|#i#o5p~$/TU$k~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$/lYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X$0[#X#f5p#f#g$0u#g#o5pZ$0cU%PPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$0zYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$1j#^#i5p#i#j$3a#j#o5p~$1oWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c$2X#c#o5p~$2^WXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Z5p#Z#[$2v#[#o5p~$2}U!T~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$3fWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W$4O#W#o5p~$4TWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$4m#i#o5p~$4tU%^~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$5]YXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V$5{#V#a5p#a#b$6f#b#o5pZ$6SUwPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$6mU%QPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$7SV}!O5p!Q![5p!c!}5p#R#S5p#T#f5p#f#g$7i#g#o5p~$7nYXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!=R#j#m5p#m#n$8^#n#o5p~$8eU$Y~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$8zX}!O5p!Q![5p!c!}5p#R#S5p#T#b5p#b#c$9g#c#d5p#d#e$=v#e#o5pZ$9lWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X$:U#X#o5pZ$:ZWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$:s#Y#o5pZ$:xWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z$;b#Z#o5pZ$;gWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$e#X#o5p~$>jVXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U$?P#U#o5p~$?UWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$?n#i#o5p~$?sWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$@]#Y#o5p~$@dU$a~XY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$@yU}!O5p!Q![5p!c!}5p#R#S5p#T#U$A]#U#o5pZ$AbWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a$Az#a#o5pZ$BPWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$Bi#j#o5pZ$BnWXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$CW#Y#o5pZ$C_UyPXY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$CvO!m~~$C{O!n~", + tokenizers: [0, 1, 2, 3], + topRules: {"Query":[0,1]}, + tokenPrec: 2459 +}) diff --git a/src/framework/codemirror-lang-typeql/index.cjs b/src/framework/codemirror-lang-typeql/index.cjs deleted file mode 100644 index 42e809e4..00000000 --- a/src/framework/codemirror-lang-typeql/index.cjs +++ /dev/null @@ -1,198 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -var lr = require('@lezer/lr'); -var language = require('@codemirror/language'); -var highlight = require('@lezer/highlight'); -var lint = require('@codemirror/lint'); - -// This file was generated by lezer-generator. You probably shouldn't edit it. -const parser = lr.LRParser.deserialize({ - version: 14, - states: "!+xOYQPOOO!ZQQO'#EPO!rQPO'#FiO!rQPO'#FkO!rQPO'#FmO!wQPO'#FoO#VQPO'#FtO#[QPO'#FwO#aQPO'#F{O#fQPO'#F}O#kQPO'#GPO#VQPO'#GRO#pQPO'#GTOOQO'#Fs'#FsOOQO'#EO'#EOOOQO'#H['#H[O$mQPO'#HSO$wQQO'#C`O%VQPO'#GpO&dQQO'#G|OOQO'#C_'#C_OOQO'#HR'#HROOQO'#C^'#C^Q&oQPOOO&tQPO'#EUOOQO'#El'#ElO'[QPO'#ElO(ZQPO'#EkO(bQQO'#EkOOQO'#ET'#ETO!ZQQO'#FcO(jQPO'#FdO(oQPO'#FfOOQO'#ES'#ESO(tQPO'#H]O(yQQO'#EROOQO,5:k,5:kO&yQPO'#EwO*PQPO'#HeO*UQPO,5P,5>POOQO-E;c-E;cO8_QPO'#ElO8mQPO'#EtO8pQPO'#F`O8uQPO,5;yO8}QPO,5<]O9SQSO,5<]OOQO,5>Q,5>QOOQO-E;d-E;dO9XQPO,5fQPO,5:UO>pQPO,5:UO>wQQO,5=dO>|QQO,5=eO?RQPO,5=fOOQO,5=g,5=gOOQO,5=_,5=_OOQO,5>W,5>WOOQO-E;j-E;jO?WQPO,5=lO?`QPO,5=lOOQO,5>X,5>XOOQO-E;k-E;kQYQPO,5>YOOQO-E;l-E;lOOQO,5:t,5:tO?gQSO,5:sO?rQSO,5:sO?}QWO1G0[O@fQWO,5;fOOQO,5;r,5;rOAYQPO,5;uOAqQPO,5;uOOQO,5;w,5;wOBSQPO,5;dOBeQPO,5;dOOQO1G0}1G0}OOQO,5;Z,5;ZOOQO,5;[,5;[OOQO,5;],5;]OBmQPO,5;_OOQO,5;_,5;_ODfQPO,5;aODpQPO,5;aOOQO,5;b,5;bOOQO,5;X,5;XODwQPO1G0qOEOQPO1G0qOEOQPO1G0qOEWQPO1G1iOE`QPO1G1jOEeQPO1G1lOEjQPO,5;`OEoQPO,5;zOEtQQO1G1eOFPQPO1G1eOOQO1G1e1G1eOOQO1G1w1G1wOFXQPO1G1wOOQO,5>R,5>ROF^QPO1G1|OOQO-E;e-E;eOOQO,5>S,5>SOOQO-E;f-E;fOOQO7+'i7+'iOFiQPO'#GWOFwQPO'#GWOOQO1G2]1G2]OOQO,5>T,5>TOOQO-E;g-E;gOOQO7+'u7+'uO#VQPO7+'uOF|QPO7+'uOGRQPO,59TOGWQPO,59TOGiQPO,59TO2^QQO,59TOGnQPO,59TOOQO-E;T-E;TOOQO,59m,59mOG|QPO1G.jOHbQPO1G.jOHbQPO1G.jOHjQPO1G.jOHqQPO,5:ZO#uQPO1G/sO2^QQO1G2pOHyQPO,5:POIOQQO1G/pOITQQO1G/pOIYQPO1G3OOOQO1G3P1G3POIaQQO1G3QOIfQPO1G3WOImQPO1G3WOImQPO1G3WOOQO1G3t1G3tOOQO,5=x,5=xOIuQSO1G0_OOQO-E;[-E;[OOQO'#Cy'#CyO?}QWO'#E`OOQO'#Eb'#EbOJQQPO'#EaOOQO'#E_'#E_OJkQQO'#E^OOQO7+%v7+%vOOQO'#E}'#E}O?}QWO'#E|OOQO1G1Q1G1QOOQO1G1a1G1aOOQO,5=},5=}OJxQPO1G1OOOQO-E;a-E;aO(bQQO1G0{O(bQQO1G0{OOQO,5={,5={OKZQPO7+&]OOQO-E;_-E;_OKbQPO7+&]OKjQPO'#HdOKoQPO7+'ROOQO7+'U7+'UOOQO7+'W7+'WOOQO1G0z1G0zOOQO1G1f1G1fOOQO,5=|,5=|OOQO7+'P7+'POKwQQO7+'POOQO-E;`-E;`OOQO7+'c7+'cPLSQPO'#HgOLXQPO,5OOOQO-E;b-E;bOOQO<V,5>VO!&nQQO1G2rOOQO-E;i-E;iO!&vQWO,5:}O!&}QPO,5:}OOQO7+&R7+&ROOQO7+)U7+)UOOQOAN=aAN=aPGnQPO'#HWO!'VQPO,5:aPHtQPO'#HYO!'[QPO,5:eO!'dQQO,5:hO!'nQPO,5:hOOQOAN>gAN>gO!'vQPO,5=OOOQO,5=P,5=PO!'{QPO,5=TO!(SQPO,5=TOOQO,5=Z,5=ZP2^QQO'#HkOOQO,5=y,5=yO!([QWO1G0iOOQO-E;]-E;]OOQO1G/{1G/{O!(cQQO1G0PO!(mQPO1G0POOQO1G0P1G0POOQO,5=u,5=uO!(uQQO1G0SOOQO-E;X-E;XOOQO1G2j1G2jOOQO,5>U,5>UO!)PQPO1G2oOOQO-E;h-E;hP?}QWO'#H_OOQO7+%k7+%kO!)WQQO7+%kP!#OQQO'#HZP9wQPO'#HjOOQO<T#V#WB[#W#XH}#X#Y!7Z#Y#Z!:|#Z#[!Cn#[#]!Gx#]#^!Ig#^#`5^#`#a#$u#a#b#1`#b#c#:k#c#d#<]#d#e#Bh#e#f5^#f#g#Fq#g#h$)t#h#i$7P#i#j$8w#j#k$@v#k#o5^#o#p$Cq#q#r$Cv~$pS&e~XY$kYZ$k]^$kpq$k~%PP!_!`%S~%XO#s~~%[VOr%Xrs%qs#O%X#O#P%v#P;'S%X;'S;=`&o<%lO%X~%vOo~~%yRO;'S%X;'S;=`&S;=`O%X~&VWOr%Xrs%qs#O%X#O#P%v#P;'S%X;'S;=`&o;=`<%l%X<%lO%X~&rP;=`<%l%X~&zS&f~OY&uZ;'S&u;'S;=`'W<%lO&u~'ZP;=`<%l&u~'aT}!O'p!Q!['p!c!}'p#R#S'p#T#o'p~'uT!f~}!O'p!Q!['p!c!}'p#R#S'p#T#o'p~(ZO#[~~(`Oe~~(eOi~~(jO#Y~~(oO#]~~(tOZ~Z(yQ#^Q!Q![)P!`!a)lX)WQpXgX!O!P)^!Q![)PX)aP!Q![)dX)iPpX!Q![)dP)qO!kP~)tP!O!P)w~)|Oh~~*RO#Z~Z*YRpXgX!O!P)^!Q![)P#l#m*cQ*fR!Q![*o!c!i*o#T#Z*oQ*tR#|Q!Q![*o!c!i*o#T#Z*o~+SO!g~~+XO$[~~+^P#v~!_!`+a~+fO#w~]+kP#OT!_!`+nW+sO#rW~+xP#t~!_!`+{~,QO#u~~,VO!}~~,YX#T#U,u#V#W-v#W#X/P#]#^0Q#_#`1e#f#g1v#g#h3P#i#j3t#j#k4i~,xP#U#V,{~-OP#g#h-R~-UP#h#i-X~-[P#f#g-_~-bP#T#U-e~-hP#V#W-k~-nP#h#i-q~-vO^~~-yP#T#U-|~.PQ#f#g.V#g#h.b~.YP#W#X.]~.bOd~~.eP#V#W.h~.kP#T#U.n~.qP#W#X.t~.wP#X#Y.z~/PO_~~/SP#]#^/V~/YP#g#h/]~/`P#h#i/c~/fP#]#^/i~/lP#b#c/o~/rP#V#W/u~/xP#h#i/{~0QO`~~0TP#b#c0W~0ZP#W#X0^~0aP#X#Y0d~0gP#d#e0j~0mP#X#Y0p~0sP#b#c0v~0yP#W#X0|~1PP#X#Y1S~1VP#b#c1Y~1]P#h#i1`~1eOa~~1hP#X#Y1k~1nP#m#n1q~1vOb~~1yQ#T#U2P#X#Y2h~2SP#b#c2V~2YP#Z#[2]~2`P#X#Y2c~2hOj~~2kP#Z#[2n~2qP#X#Y2t~2wP#l#m2z~3POq~~3SP#i#j3V~3YP#U#V3]~3`P#_#`3c~3fP#X#Y3i~3lP#m#n3o~3tOr~~3wP#b#c3z~3}P#]#^4Q~4TP#e#f4W~4ZP#i#j4^~4aP#X#Y4d~4iOc~~4lP#T#U4o~4rP#`#a4u~4xP#i#j4{~5OP#X#Y5R~5UP#g#h5X~5^Ot~Y5aT}!O5p!Q![5p!c!}5p#R#S5p#T#o5pY5uUYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pY6[T}!O6k!Q![6k!c!}6k#R#S6k#T#o6kY6pTYY}!O6k!Q![6k!c!}6k#R#S6k#T#o6k~7UO!Y~~7ZO!Z~~7`O#X~~7cW}!O5p!Q![5p!c!}5p#R#S5p#T#g5p#g#h7{#h#i9V#i#o5p_8SW!`PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W8l#W#o5p^8sU$nSYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~9[WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i9t#i#o5p~9yWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g:c#g#o5p~:hWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^;Q#^#o5p~;VWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V;o#V#o5p~;tWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j<^#j#o5p~WV}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#d>m#d#o5p~>rWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d?[#d#o5p~?aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a?y#a#o5p~@OWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y@h#Y#o5p~@mVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#UAS#U#o5p~AXWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cAq#c#o5p~AxU|~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~B_V}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#dBt#d#o5p~ByYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cCi#c#i5p#i#jGW#j#o5p~CnWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iDW#i#o5p~D]VYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#UDr#U#o5p~DwWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^Ea#^#o5p~EfWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cFO#c#o5p~FTWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#hFm#h#o5p~FtU#y~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZG]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cGu#c#o5pZGzWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iHd#i#o5pZHkU${PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~IQ^}!O5p!Q![5p!c!}5p#R#S5p#T#UI|#U#X5p#X#Y!!R#Y#]5p#]#^!+k#^#c5p#c#d!/z#d#i5p#i#j!2}#j#o5p~JRWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iJk#i#o5p~JpWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#YKY#Y#o5p~KaW!S~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iKy#i#o5p~LOWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^Lh#^#o5p~LmWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#bMV#b#o5p~M[WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#YMt#Y#o5p~M{U!R~YY}!ON_!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~NdWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iN|#i#o5p~! RVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#n5p#n#o! h~! oU!Q~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!!W^YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W!#S#W#Y5p#Y#Z!&S#Z#`5p#`#a!(h#a#g5p#g#h!*|#h#o5p~!#XWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!#q#^#o5p~!#vWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#b!$`#b#o5p~!$eVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U!$z#U#o5p~!%PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!%i#a#o5p~!%pU!P~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!&XWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!&q#^#o5pZ!&vWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!'`#c#o5pZ!'eWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!'}#Y#o5pZ!(UUTPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!(mWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!)V#Y#o5p~!)[WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!)t#i#o5p~!)yWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!*c#Y#o5p~!*jU$d~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p^!+RWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W8l#W#o5p~!+pWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!,Y#h#o5p~!,_WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!,w#i#o5p~!,|WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!-f#^#o5p~!-kWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!.T#c#o5p~!.YWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W!.r#W#o5p~!.wWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!/a#i#o5p~!/hU$p~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!0PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!0i#j#o5p~!0nWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V!1W#V#o5p~!1]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!1u#a#o5p~!1zWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!2d#Y#o5p~!2kU!O~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!3SWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!3l#g#o5p~!3qVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U!4W#U#o5p~!4]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!4u#i#o5p~!4zWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!5d#^#o5p~!5iWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!6R#d#o5p~!6WWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!6p#c#o5p~!6wU!T~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!7^V}!O5p!Q![5p!c!}5p#R#S5p#T#b5p#b#c!7s#c#o5p~!7xYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X!8h#X#h5p#h#i!9R#i#o5p~!8oU%w~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!9WWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!9p#^#o5p~!9uWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!:_#i#o5p~!:dWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n=j#n#o5p~!;P[}!O5p!Q![5p!c!}5p#R#S5p#T#U!;u#U#]5p#]#^!>Z#^#f5p#f#g!@o#g#i5p#i#j!Bf#j#o5p~!;zWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!`WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!>x#g#o5pZ!>}WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!?g#h#o5pZ!?lWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!@U#i#o5pZ!@]U%ZPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!@tWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!A^#d#o5pZ!AcWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#b!A{#b#o5pZ!BSU%kPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!BkWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!CT#c#o5p~!C[U!b~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!CqV}!O5p!Q![5p!c!}5p#R#S5p#T#f5p#f#g!DW#g#o5pZ!D]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!Du#d#o5pZ!DzWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!Ed#j#o5pZ!EiWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#d5p#d#e!FR#e#o5pZ!FWWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V!Fp#V#o5pZ!FuWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n!G_#n#o5pZ!GfU%TPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!G{U}!O5p!Q![5p!c!}5p#R#S5p#T#U!H_#U#o5pZ!HdWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!H|#h#o5pZ!ITU$OPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_!IjZ}!O5p!Q![5p!c!}5p#R#S5p#T#]5p#]#^!J]#^#b5p#b#c!Ke#c#g5p#g#h##p#h#o5pZ!JbWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X!Jz#X#o5pZ!KRU#{PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_!KlX#PSYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!LX#h#i!Nm#i#o5pZ!L^WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!Lv#Y#o5pZ!L{WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!Me#g#o5pZ!MjWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!NS#i#o5pZ!NZU$^RYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!NrWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y# [#Y#o5pZ# aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Z5p#Z#[# y#[#o5pZ#!OWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#!h#Y#o5pZ#!mWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g##V#g#o5pZ##^U}RYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ##uVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#$[#U#o5pZ#$cU#oPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#$xY}!O5p!Q![5p!c!}5p#R#S5p#T#U#%h#U#X5p#X#Y#)[#Y#]5p#]#^#*d#^#o5pZ#%mYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V#&]#V#g5p#g#h#(S#h#o5pZ#&bWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#&z#Y#o5pZ#'PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a#'i#a#o5pZ#'pU#fPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#(XWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#(q#i#o5pZ#(xU%[PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#)aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#)y#i#o5p~#*QU!y~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#*i]YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#_5p#_#`#+b#`#a5p#a#b#,j#b#c#.a#c#g5p#g#h#0W#h#o5p~#+gWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#,P#Y#o5p~#,WU#x~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#,oWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#-X#^#o5p~#-^WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#-v#i#o5p~#-}U$t~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#.fWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#_5p#_#`#/O#`#o5pZ#/TWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#/m#h#o5pZ#/tU$QPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#0]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#0u#i#o5pZ#0|U%SPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#1cY}!O5p!Q![5p!c!}5p#R#S5p#T#U#2R#U#X5p#X#Y#5W#Y#]5p#]#^#9c#^#o5p~#2WYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#2v#i#l5p#l#m#4m#m#o5p~#2{WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W#3e#W#o5p~#3jWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#[5p#[#]#4S#]#o5p~#4ZU!t~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#4tU$|PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#5]XYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#5x#U#W5p#W#X#7Q#X#o5pZ#5}WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#6g#c#o5pZ#6nU%OPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#7VWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#7o#^#o5pZ#7tVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#8Z#U#o5pZ#8`WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#8x#c#o5pZ#9PU%PPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#9hWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#:Q#c#o5pZ#:XU$}PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#:nV}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#d#;T#d#o5p~#;YWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#;r#i#o5p~#;yU$X~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_#<`Z}!O5p!Q![5p!c!}5p#R#S5p#T#Y5p#Y#Z#=R#Z#f5p#f#g#@W#g#k5p#k#l#@q#l#o5p_#=YW$fSYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z#=r#Z#o5pZ#=wWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#>a#h#o5pZ#>fWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#?O#Y#o5pZ#?TWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#?m#i#o5pZ#?tU$rRYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#@_U$UPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#@vWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#A`#c#o5pZ#AeWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#A}#h#o5pZ#BUU!WPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#BkX}!O5p!Q![5p!c!}5p#R#S5p#T#`5p#`#a#CW#a#i5p#i#j#Ei#j#o5pZ#C]VYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#Cr#U#o5pZ#CwWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n#Da#n#o5pZ#DfWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#EO#h#o5pZ#EVU!]PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#EnWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#FW#i#o5p~#F_U$`~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#FtV}!O5p!Q![5p!c!}5p#R#S5p#T#X5p#X#Y#GZ#Y#o5p~#G`^YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X#H[#X#`5p#`#a#My#a#e5p#e#f$$]#f#h5p#h#i$'`#i#o5p~#HaYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#IP#Y#i5p#i#j#LS#j#o5pZ#IUWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z#In#Z#o5pZ#IsWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#J]#^#o5pZ#JbWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#Jz#c#o5pZ#KPWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#Ki#Y#o5pZ#KpU%qPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#LXWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W#Lq#W#o5p~#LvWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#M`#Y#o5p~#MgU$x~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#NOVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#Ne#U#o5p~#NjWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$ S#i#o5p~$ XYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$ w#Y#]5p#]#^$#P#^#o5pZ$ |WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h$!f#h#o5pZ$!mU!_PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$#UWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d$#n#d#o5p~$#sWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c=j#c#o5p~$$bWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$$z#j#o5p~$%PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$%i#^#o5p~$%nWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$&W#g#o5p~$&]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$&u#Y#o5p~$&|U$v~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$'eWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$'}#j#o5p~$(SWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$(l#g#o5p~$(qWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c$)Z#c#o5p~$)bU%V~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$)w[}!O5p!Q![5p!c!}5p#R#S5p#T#X5p#X#Y$*m#Y#c5p#c#d$-p#d#h5p#h#i$/g#i#j$5W#j#o5p~$*rWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a$+[#a#o5p~$+aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$+y#Y#o5p~$,OWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W$,h#W#o5p~$,mWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$-V#i#o5p~$-^U$i~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$-uWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$._#g#o5p~$.dWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$.|#i#o5p~$/TU$l~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$/lYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X$0[#X#f5p#f#g$0u#g#o5pZ$0cU%QPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$0zYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$1j#^#i5p#i#j$3a#j#o5p~$1oWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c$2X#c#o5p~$2^WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Z5p#Z#[$2v#[#o5p~$2}U!U~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$3fWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W$4O#W#o5p~$4TWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$4m#i#o5p~$4tU%_~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$5]YYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V$5{#V#a5p#a#b$6f#b#o5pZ$6SUxPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$6mU%RPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$7SV}!O5p!Q![5p!c!}5p#R#S5p#T#f5p#f#g$7i#g#o5p~$7nYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!=R#j#m5p#m#n$8^#n#o5p~$8eU$Z~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$8zX}!O5p!Q![5p!c!}5p#R#S5p#T#b5p#b#c$9g#c#d5p#d#e$=v#e#o5pZ$9lWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X$:U#X#o5pZ$:ZWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$:s#Y#o5pZ$:xWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z$;b#Z#o5pZ$;gWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$e#X#o5p~$>jVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U$?P#U#o5p~$?UWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$?n#i#o5p~$?sWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$@]#Y#o5p~$@dU$b~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$@yU}!O5p!Q![5p!c!}5p#R#S5p#T#U$A]#U#o5pZ$AbWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a$Az#a#o5pZ$BPWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$Bi#j#o5pZ$BnWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$CW#Y#o5pZ$C_UzPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$CvO!n~~$C{O!o~", - tokenizers: [0, 1, 2, 3], - topRules: {"Query":[0,1]}, - tokenPrec: 2502 -}); - -// TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. -// We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. -function suggestLabel(label) { - return { - label: label, - type: "label", - apply: label, - info: "label suggestion", - }; -} -function suggestVariable(varName) { - return { - label: varName, - type: "variable", - apply: varName, - info: "variable suggestion", - }; -} -function collectLabelSuggestions(context, tree, atNode) { - // This is a placeholder function. You will need to implement the logic to collect label suggestions. - // For now, we return an empty array. - var options = []; - tree.iterate({ - enter: (other) => { - if (other.name == "LABEL") { - let content = context.state.sliceDoc(other.from, other.to); - options.push(suggestLabel(content)); - } - } - }); - return options; -} -function collectVariableSuggestions(context, tree, atNode) { - var options = []; - tree.iterate({ - enter: (other) => { - if (other.name == "VAR") { - let content = context.state.sliceDoc(other.from, other.to); - options.push(suggestVariable(content)); - } - } - }); - return options; -} -// See: https://codemirror.net/examples/autocompletion/ and maybe the SQL / HTML Example there. -function autocompleteTypeQL(context) { - let tree = language.syntaxTree(context.state); - let currentNode = tree.resolveInner(context.pos, -1); // https://lezer.codemirror.net/docs/ref/#common.SyntaxNode - // We may have to walk the tree to find the most appropriate node to suggest things based on. - let options = null; - if (currentNode.name == "LABEL") { - options = collectLabelSuggestions(context, tree); - } - else if (currentNode.name == "VAR") { - options = collectVariableSuggestions(context, tree); - } - else ; - if (options != null) { - // And once we figure out, we have to create a list of completion objects - // It may be worth changing the grammar to be able to do this more easily, rather than replicate the original TypeQL grammar. - // https://codemirror.net/docs/ref/#autocomplete.Completion - return { - from: currentNode.from, - options: options, - // Docs: "regular expression that tells the extension that, as long as the updated input (the range between the result's from property and the completion point) matches that value, it can continue to use the list of completions." - validFor: /^(\w+)?$/ - }; - } - else { - return null; - } -} - -const TypeQLLanguage = language.LRLanguage.define({ - parser: parser.configure({ - props: [ - language.indentNodeProp.add({}), - language.foldNodeProp.add({ - QueryStage: language.foldInside - }), - highlight.styleTags({ - // See: https://lezer.codemirror.net/docs/ref/#highlight.tags - VAR: highlight.tags.variableName, - // Literals - STRINGLITERAL: highlight.tags.string, - INTEGERLITERAL: highlight.tags.number, - DOUBLELITERAL: highlight.tags.number, - BOOLEANLITERAL: highlight.tags.bool, - // Types - LABEL: highlight.tags.typeName, - BOOLEAN: highlight.tags.typeName, - INTEGER: highlight.tags.typeName, - DOUBLE: highlight.tags.typeName, - DECIMAL: highlight.tags.typeName, - DATETIMETZ: highlight.tags.typeName, - DATETIME: highlight.tags.typeName, - DATE: highlight.tags.typeName, - DURATION: highlight.tags.typeName, - STRING: highlight.tags.typeName, - // Keywords - ISA: highlight.tags.keyword, - HAS: highlight.tags.keyword, - LINKS: highlight.tags.keyword, - OWNS: highlight.tags.keyword, - RELATES: highlight.tags.keyword, - PLAYS: highlight.tags.keyword, - FUN: highlight.tags.keyword, - LET: highlight.tags.keyword, - FIRST: highlight.tags.keyword, - LAST: highlight.tags.keyword, - // Value type names? - // Stages - DEFINE: highlight.tags.heading1, - UNDEFINE: highlight.tags.heading1, - REDEFINE: highlight.tags.heading1, - MATCH: highlight.tags.heading1, - INSERT: highlight.tags.heading1, - DELETE: highlight.tags.heading1, - UPDATE: highlight.tags.heading1, - PUT: highlight.tags.heading1, - END: highlight.tags.heading1, - SELECT: highlight.tags.heading1, - REDUCE: highlight.tags.heading1, - SORT: highlight.tags.heading1, - OFFSET: highlight.tags.heading1, - LIMIT: highlight.tags.heading1, - REQUIRE: highlight.tags.heading1, - DISTINCT: highlight.tags.heading1, - GROUPBY: highlight.tags.heading1, - // SubPattern - OR: highlight.tags.controlOperator, - NOT: highlight.tags.controlOperator, - TRY: highlight.tags.controlOperator, - // Misc - Annotation: highlight.tags.meta, - LINECOMMENT: highlight.tags.lineComment, - }) - ] - }), - languageData: { - commentTokens: { line: "#" } - } -}); -function TypeQL() { - return new language.LanguageSupport(TypeQLLanguage, [ - TypeQLLanguage.data.of({ - autocomplete: autocompleteTypeQL - }), - ]); -} -// A Linter which flags syntax errors from: https://discuss.codemirror.net/t/showing-syntax-errors/3111/6 -function otherExampleLinter() { - return lint.linter((view) => { - const diagnostics = []; - language.syntaxTree(view.state).iterate({ - enter: n => { - if (n.type.isError) { - diagnostics.push({ - from: n.from, - to: n.to, - severity: "error", - message: "Syntax error.", - }); - } - }, - }); - return diagnostics; - }); -} - -exports.TypeQL = TypeQL; -exports.TypeQLLanguage = TypeQLLanguage; -exports.otherExampleLinter = otherExampleLinter; diff --git a/src/framework/codemirror-lang-typeql/index.d.cts b/src/framework/codemirror-lang-typeql/index.d.cts deleted file mode 100644 index 2bf6cd43..00000000 --- a/src/framework/codemirror-lang-typeql/index.d.cts +++ /dev/null @@ -1,5 +0,0 @@ -import { LRLanguage, LanguageSupport } from "@codemirror/language"; -declare const TypeQLLanguage: LRLanguage; -declare function TypeQL(): LanguageSupport; -declare function otherExampleLinter(): import("@codemirror/state").Extension; -export { TypeQLLanguage, TypeQL, otherExampleLinter }; diff --git a/src/framework/codemirror-lang-typeql/index.d.ts b/src/framework/codemirror-lang-typeql/index.d.ts deleted file mode 100644 index 2bf6cd43..00000000 --- a/src/framework/codemirror-lang-typeql/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { LRLanguage, LanguageSupport } from "@codemirror/language"; -declare const TypeQLLanguage: LRLanguage; -declare function TypeQL(): LanguageSupport; -declare function otherExampleLinter(): import("@codemirror/state").Extension; -export { TypeQLLanguage, TypeQL, otherExampleLinter }; diff --git a/src/framework/codemirror-lang-typeql/index.js b/src/framework/codemirror-lang-typeql/index.js deleted file mode 100644 index 54739ba3..00000000 --- a/src/framework/codemirror-lang-typeql/index.js +++ /dev/null @@ -1,192 +0,0 @@ -import { LRParser } from '@lezer/lr'; -import { syntaxTree, LRLanguage, indentNodeProp, foldNodeProp, foldInside, LanguageSupport } from '@codemirror/language'; -import { styleTags, tags } from '@lezer/highlight'; -import { linter } from '@codemirror/lint'; - -// This file was generated by lezer-generator. You probably shouldn't edit it. -const parser = LRParser.deserialize({ - version: 14, - states: "!+xOYQPOOO!ZQQO'#EPO!rQPO'#FiO!rQPO'#FkO!rQPO'#FmO!wQPO'#FoO#VQPO'#FtO#[QPO'#FwO#aQPO'#F{O#fQPO'#F}O#kQPO'#GPO#VQPO'#GRO#pQPO'#GTOOQO'#Fs'#FsOOQO'#EO'#EOOOQO'#H['#H[O$mQPO'#HSO$wQQO'#C`O%VQPO'#GpO&dQQO'#G|OOQO'#C_'#C_OOQO'#HR'#HROOQO'#C^'#C^Q&oQPOOO&tQPO'#EUOOQO'#El'#ElO'[QPO'#ElO(ZQPO'#EkO(bQQO'#EkOOQO'#ET'#ETO!ZQQO'#FcO(jQPO'#FdO(oQPO'#FfOOQO'#ES'#ESO(tQPO'#H]O(yQQO'#EROOQO,5:k,5:kO&yQPO'#EwO*PQPO'#HeO*UQPO,5P,5>POOQO-E;c-E;cO8_QPO'#ElO8mQPO'#EtO8pQPO'#F`O8uQPO,5;yO8}QPO,5<]O9SQSO,5<]OOQO,5>Q,5>QOOQO-E;d-E;dO9XQPO,5fQPO,5:UO>pQPO,5:UO>wQQO,5=dO>|QQO,5=eO?RQPO,5=fOOQO,5=g,5=gOOQO,5=_,5=_OOQO,5>W,5>WOOQO-E;j-E;jO?WQPO,5=lO?`QPO,5=lOOQO,5>X,5>XOOQO-E;k-E;kQYQPO,5>YOOQO-E;l-E;lOOQO,5:t,5:tO?gQSO,5:sO?rQSO,5:sO?}QWO1G0[O@fQWO,5;fOOQO,5;r,5;rOAYQPO,5;uOAqQPO,5;uOOQO,5;w,5;wOBSQPO,5;dOBeQPO,5;dOOQO1G0}1G0}OOQO,5;Z,5;ZOOQO,5;[,5;[OOQO,5;],5;]OBmQPO,5;_OOQO,5;_,5;_ODfQPO,5;aODpQPO,5;aOOQO,5;b,5;bOOQO,5;X,5;XODwQPO1G0qOEOQPO1G0qOEOQPO1G0qOEWQPO1G1iOE`QPO1G1jOEeQPO1G1lOEjQPO,5;`OEoQPO,5;zOEtQQO1G1eOFPQPO1G1eOOQO1G1e1G1eOOQO1G1w1G1wOFXQPO1G1wOOQO,5>R,5>ROF^QPO1G1|OOQO-E;e-E;eOOQO,5>S,5>SOOQO-E;f-E;fOOQO7+'i7+'iOFiQPO'#GWOFwQPO'#GWOOQO1G2]1G2]OOQO,5>T,5>TOOQO-E;g-E;gOOQO7+'u7+'uO#VQPO7+'uOF|QPO7+'uOGRQPO,59TOGWQPO,59TOGiQPO,59TO2^QQO,59TOGnQPO,59TOOQO-E;T-E;TOOQO,59m,59mOG|QPO1G.jOHbQPO1G.jOHbQPO1G.jOHjQPO1G.jOHqQPO,5:ZO#uQPO1G/sO2^QQO1G2pOHyQPO,5:POIOQQO1G/pOITQQO1G/pOIYQPO1G3OOOQO1G3P1G3POIaQQO1G3QOIfQPO1G3WOImQPO1G3WOImQPO1G3WOOQO1G3t1G3tOOQO,5=x,5=xOIuQSO1G0_OOQO-E;[-E;[OOQO'#Cy'#CyO?}QWO'#E`OOQO'#Eb'#EbOJQQPO'#EaOOQO'#E_'#E_OJkQQO'#E^OOQO7+%v7+%vOOQO'#E}'#E}O?}QWO'#E|OOQO1G1Q1G1QOOQO1G1a1G1aOOQO,5=},5=}OJxQPO1G1OOOQO-E;a-E;aO(bQQO1G0{O(bQQO1G0{OOQO,5={,5={OKZQPO7+&]OOQO-E;_-E;_OKbQPO7+&]OKjQPO'#HdOKoQPO7+'ROOQO7+'U7+'UOOQO7+'W7+'WOOQO1G0z1G0zOOQO1G1f1G1fOOQO,5=|,5=|OOQO7+'P7+'POKwQQO7+'POOQO-E;`-E;`OOQO7+'c7+'cPLSQPO'#HgOLXQPO,5OOOQO-E;b-E;bOOQO<V,5>VO!&nQQO1G2rOOQO-E;i-E;iO!&vQWO,5:}O!&}QPO,5:}OOQO7+&R7+&ROOQO7+)U7+)UOOQOAN=aAN=aPGnQPO'#HWO!'VQPO,5:aPHtQPO'#HYO!'[QPO,5:eO!'dQQO,5:hO!'nQPO,5:hOOQOAN>gAN>gO!'vQPO,5=OOOQO,5=P,5=PO!'{QPO,5=TO!(SQPO,5=TOOQO,5=Z,5=ZP2^QQO'#HkOOQO,5=y,5=yO!([QWO1G0iOOQO-E;]-E;]OOQO1G/{1G/{O!(cQQO1G0PO!(mQPO1G0POOQO1G0P1G0POOQO,5=u,5=uO!(uQQO1G0SOOQO-E;X-E;XOOQO1G2j1G2jOOQO,5>U,5>UO!)PQPO1G2oOOQO-E;h-E;hP?}QWO'#H_OOQO7+%k7+%kO!)WQQO7+%kP!#OQQO'#HZP9wQPO'#HjOOQO<T#V#WB[#W#XH}#X#Y!7Z#Y#Z!:|#Z#[!Cn#[#]!Gx#]#^!Ig#^#`5^#`#a#$u#a#b#1`#b#c#:k#c#d#<]#d#e#Bh#e#f5^#f#g#Fq#g#h$)t#h#i$7P#i#j$8w#j#k$@v#k#o5^#o#p$Cq#q#r$Cv~$pS&e~XY$kYZ$k]^$kpq$k~%PP!_!`%S~%XO#s~~%[VOr%Xrs%qs#O%X#O#P%v#P;'S%X;'S;=`&o<%lO%X~%vOo~~%yRO;'S%X;'S;=`&S;=`O%X~&VWOr%Xrs%qs#O%X#O#P%v#P;'S%X;'S;=`&o;=`<%l%X<%lO%X~&rP;=`<%l%X~&zS&f~OY&uZ;'S&u;'S;=`'W<%lO&u~'ZP;=`<%l&u~'aT}!O'p!Q!['p!c!}'p#R#S'p#T#o'p~'uT!f~}!O'p!Q!['p!c!}'p#R#S'p#T#o'p~(ZO#[~~(`Oe~~(eOi~~(jO#Y~~(oO#]~~(tOZ~Z(yQ#^Q!Q![)P!`!a)lX)WQpXgX!O!P)^!Q![)PX)aP!Q![)dX)iPpX!Q![)dP)qO!kP~)tP!O!P)w~)|Oh~~*RO#Z~Z*YRpXgX!O!P)^!Q![)P#l#m*cQ*fR!Q![*o!c!i*o#T#Z*oQ*tR#|Q!Q![*o!c!i*o#T#Z*o~+SO!g~~+XO$[~~+^P#v~!_!`+a~+fO#w~]+kP#OT!_!`+nW+sO#rW~+xP#t~!_!`+{~,QO#u~~,VO!}~~,YX#T#U,u#V#W-v#W#X/P#]#^0Q#_#`1e#f#g1v#g#h3P#i#j3t#j#k4i~,xP#U#V,{~-OP#g#h-R~-UP#h#i-X~-[P#f#g-_~-bP#T#U-e~-hP#V#W-k~-nP#h#i-q~-vO^~~-yP#T#U-|~.PQ#f#g.V#g#h.b~.YP#W#X.]~.bOd~~.eP#V#W.h~.kP#T#U.n~.qP#W#X.t~.wP#X#Y.z~/PO_~~/SP#]#^/V~/YP#g#h/]~/`P#h#i/c~/fP#]#^/i~/lP#b#c/o~/rP#V#W/u~/xP#h#i/{~0QO`~~0TP#b#c0W~0ZP#W#X0^~0aP#X#Y0d~0gP#d#e0j~0mP#X#Y0p~0sP#b#c0v~0yP#W#X0|~1PP#X#Y1S~1VP#b#c1Y~1]P#h#i1`~1eOa~~1hP#X#Y1k~1nP#m#n1q~1vOb~~1yQ#T#U2P#X#Y2h~2SP#b#c2V~2YP#Z#[2]~2`P#X#Y2c~2hOj~~2kP#Z#[2n~2qP#X#Y2t~2wP#l#m2z~3POq~~3SP#i#j3V~3YP#U#V3]~3`P#_#`3c~3fP#X#Y3i~3lP#m#n3o~3tOr~~3wP#b#c3z~3}P#]#^4Q~4TP#e#f4W~4ZP#i#j4^~4aP#X#Y4d~4iOc~~4lP#T#U4o~4rP#`#a4u~4xP#i#j4{~5OP#X#Y5R~5UP#g#h5X~5^Ot~Y5aT}!O5p!Q![5p!c!}5p#R#S5p#T#o5pY5uUYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pY6[T}!O6k!Q![6k!c!}6k#R#S6k#T#o6kY6pTYY}!O6k!Q![6k!c!}6k#R#S6k#T#o6k~7UO!Y~~7ZO!Z~~7`O#X~~7cW}!O5p!Q![5p!c!}5p#R#S5p#T#g5p#g#h7{#h#i9V#i#o5p_8SW!`PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W8l#W#o5p^8sU$nSYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~9[WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i9t#i#o5p~9yWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g:c#g#o5p~:hWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^;Q#^#o5p~;VWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V;o#V#o5p~;tWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j<^#j#o5p~WV}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#d>m#d#o5p~>rWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d?[#d#o5p~?aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a?y#a#o5p~@OWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y@h#Y#o5p~@mVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#UAS#U#o5p~AXWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cAq#c#o5p~AxU|~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~B_V}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#dBt#d#o5p~ByYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cCi#c#i5p#i#jGW#j#o5p~CnWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iDW#i#o5p~D]VYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#UDr#U#o5p~DwWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^Ea#^#o5p~EfWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cFO#c#o5p~FTWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#hFm#h#o5p~FtU#y~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZG]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#cGu#c#o5pZGzWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iHd#i#o5pZHkU${PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~IQ^}!O5p!Q![5p!c!}5p#R#S5p#T#UI|#U#X5p#X#Y!!R#Y#]5p#]#^!+k#^#c5p#c#d!/z#d#i5p#i#j!2}#j#o5p~JRWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iJk#i#o5p~JpWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#YKY#Y#o5p~KaW!S~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iKy#i#o5p~LOWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^Lh#^#o5p~LmWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#bMV#b#o5p~M[WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#YMt#Y#o5p~M{U!R~YY}!ON_!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~NdWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#iN|#i#o5p~! RVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#n5p#n#o! h~! oU!Q~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!!W^YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W!#S#W#Y5p#Y#Z!&S#Z#`5p#`#a!(h#a#g5p#g#h!*|#h#o5p~!#XWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!#q#^#o5p~!#vWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#b!$`#b#o5p~!$eVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U!$z#U#o5p~!%PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!%i#a#o5p~!%pU!P~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!&XWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!&q#^#o5pZ!&vWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!'`#c#o5pZ!'eWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!'}#Y#o5pZ!(UUTPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!(mWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!)V#Y#o5p~!)[WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!)t#i#o5p~!)yWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!*c#Y#o5p~!*jU$d~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p^!+RWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W8l#W#o5p~!+pWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!,Y#h#o5p~!,_WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!,w#i#o5p~!,|WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!-f#^#o5p~!-kWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!.T#c#o5p~!.YWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W!.r#W#o5p~!.wWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!/a#i#o5p~!/hU$p~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!0PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!0i#j#o5p~!0nWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V!1W#V#o5p~!1]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!1u#a#o5p~!1zWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!2d#Y#o5p~!2kU!O~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!3SWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!3l#g#o5p~!3qVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U!4W#U#o5p~!4]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!4u#i#o5p~!4zWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!5d#^#o5p~!5iWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!6R#d#o5p~!6WWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!6p#c#o5p~!6wU!T~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!7^V}!O5p!Q![5p!c!}5p#R#S5p#T#b5p#b#c!7s#c#o5p~!7xYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X!8h#X#h5p#h#i!9R#i#o5p~!8oU%w~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!9WWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^!9p#^#o5p~!9uWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!:_#i#o5p~!:dWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n=j#n#o5p~!;P[}!O5p!Q![5p!c!}5p#R#S5p#T#U!;u#U#]5p#]#^!>Z#^#f5p#f#g!@o#g#i5p#i#j!Bf#j#o5p~!;zWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a!`WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!>x#g#o5pZ!>}WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!?g#h#o5pZ!?lWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!@U#i#o5pZ!@]U%ZPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!@tWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!A^#d#o5pZ!AcWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#a5p#a#b!A{#b#o5pZ!BSU%kPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~!BkWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c!CT#c#o5p~!C[U!b~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!CqV}!O5p!Q![5p!c!}5p#R#S5p#T#f5p#f#g!DW#g#o5pZ!D]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d!Du#d#o5pZ!DzWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!Ed#j#o5pZ!EiWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#d5p#d#e!FR#e#o5pZ!FWWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V!Fp#V#o5pZ!FuWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n!G_#n#o5pZ!GfU%TPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!G{U}!O5p!Q![5p!c!}5p#R#S5p#T#U!H_#U#o5pZ!HdWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!H|#h#o5pZ!ITU$OPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_!IjZ}!O5p!Q![5p!c!}5p#R#S5p#T#]5p#]#^!J]#^#b5p#b#c!Ke#c#g5p#g#h##p#h#o5pZ!JbWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X!Jz#X#o5pZ!KRU#{PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_!KlX#PSYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h!LX#h#i!Nm#i#o5pZ!L^WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y!Lv#Y#o5pZ!L{WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g!Me#g#o5pZ!MjWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i!NS#i#o5pZ!NZU$^RYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ!NrWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y# [#Y#o5pZ# aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Z5p#Z#[# y#[#o5pZ#!OWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#!h#Y#o5pZ#!mWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g##V#g#o5pZ##^U}RYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ##uVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#$[#U#o5pZ#$cU#oPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#$xY}!O5p!Q![5p!c!}5p#R#S5p#T#U#%h#U#X5p#X#Y#)[#Y#]5p#]#^#*d#^#o5pZ#%mYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V#&]#V#g5p#g#h#(S#h#o5pZ#&bWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#&z#Y#o5pZ#'PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a#'i#a#o5pZ#'pU#fPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#(XWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#(q#i#o5pZ#(xU%[PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#)aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#)y#i#o5p~#*QU!y~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#*i]YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#_5p#_#`#+b#`#a5p#a#b#,j#b#c#.a#c#g5p#g#h#0W#h#o5p~#+gWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#,P#Y#o5p~#,WU#x~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#,oWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#-X#^#o5p~#-^WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#-v#i#o5p~#-}U$t~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#.fWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#_5p#_#`#/O#`#o5pZ#/TWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#/m#h#o5pZ#/tU$QPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#0]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#0u#i#o5pZ#0|U%SPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#1cY}!O5p!Q![5p!c!}5p#R#S5p#T#U#2R#U#X5p#X#Y#5W#Y#]5p#]#^#9c#^#o5p~#2WYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#2v#i#l5p#l#m#4m#m#o5p~#2{WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W#3e#W#o5p~#3jWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#[5p#[#]#4S#]#o5p~#4ZU!t~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#4tU$|PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#5]XYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#5x#U#W5p#W#X#7Q#X#o5pZ#5}WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#6g#c#o5pZ#6nU%OPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#7VWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#7o#^#o5pZ#7tVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#8Z#U#o5pZ#8`WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#8x#c#o5pZ#9PU%PPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#9hWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#:Q#c#o5pZ#:XU$}PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#:nV}!O5p!Q![5p!c!}5p#R#S5p#T#c5p#c#d#;T#d#o5p~#;YWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#;r#i#o5p~#;yU$X~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p_#<`Z}!O5p!Q![5p!c!}5p#R#S5p#T#Y5p#Y#Z#=R#Z#f5p#f#g#@W#g#k5p#k#l#@q#l#o5p_#=YW$fSYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z#=r#Z#o5pZ#=wWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#>a#h#o5pZ#>fWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#?O#Y#o5pZ#?TWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#?m#i#o5pZ#?tU$rRYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#@_U$UPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ#@vWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#A`#c#o5pZ#AeWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#A}#h#o5pZ#BUU!WPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#BkX}!O5p!Q![5p!c!}5p#R#S5p#T#`5p#`#a#CW#a#i5p#i#j#Ei#j#o5pZ#C]VYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#Cr#U#o5pZ#CwWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#m5p#m#n#Da#n#o5pZ#DfWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h#EO#h#o5pZ#EVU!]PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#EnWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i#FW#i#o5p~#F_U$`~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#FtV}!O5p!Q![5p!c!}5p#R#S5p#T#X5p#X#Y#GZ#Y#o5p~#G`^YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X#H[#X#`5p#`#a#My#a#e5p#e#f$$]#f#h5p#h#i$'`#i#o5p~#HaYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#IP#Y#i5p#i#j#LS#j#o5pZ#IUWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z#In#Z#o5pZ#IsWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^#J]#^#o5pZ#JbWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c#Jz#c#o5pZ#KPWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#Ki#Y#o5pZ#KpU%qPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#LXWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W#Lq#W#o5p~#LvWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y#M`#Y#o5p~#MgU$x~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~#NOVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U#Ne#U#o5p~#NjWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$ S#i#o5p~$ XYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$ w#Y#]5p#]#^$#P#^#o5pZ$ |WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#g5p#g#h$!f#h#o5pZ$!mU!_PYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$#UWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#c5p#c#d$#n#d#o5p~$#sWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c=j#c#o5p~$$bWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$$z#j#o5p~$%PWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$%i#^#o5p~$%nWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$&W#g#o5p~$&]WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$&u#Y#o5p~$&|U$v~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$'eWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$'}#j#o5p~$(SWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$(l#g#o5p~$(qWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c$)Z#c#o5p~$)bU%V~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$)w[}!O5p!Q![5p!c!}5p#R#S5p#T#X5p#X#Y$*m#Y#c5p#c#d$-p#d#h5p#h#i$/g#i#j$5W#j#o5p~$*rWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a$+[#a#o5p~$+aWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$+y#Y#o5p~$,OWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W$,h#W#o5p~$,mWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$-V#i#o5p~$-^U$i~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$-uWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#f5p#f#g$._#g#o5p~$.dWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$.|#i#o5p~$/TU$l~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$/lYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X$0[#X#f5p#f#g$0u#g#o5pZ$0cU%QPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$0zYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$1j#^#i5p#i#j$3a#j#o5p~$1oWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#b5p#b#c$2X#c#o5p~$2^WYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Z5p#Z#[$2v#[#o5p~$2}U!U~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$3fWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#V5p#V#W$4O#W#o5p~$4TWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$4m#i#o5p~$4tU%_~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$5]YYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U5p#U#V$5{#V#a5p#a#b$6f#b#o5pZ$6SUxPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$6mU%RPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$7SV}!O5p!Q![5p!c!}5p#R#S5p#T#f5p#f#g$7i#g#o5p~$7nYYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j!=R#j#m5p#m#n$8^#n#o5p~$8eU$Z~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$8zX}!O5p!Q![5p!c!}5p#R#S5p#T#b5p#b#c$9g#c#d5p#d#e$=v#e#o5pZ$9lWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#W5p#W#X$:U#X#o5pZ$:ZWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$:s#Y#o5pZ$:xWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#Y5p#Y#Z$;b#Z#o5pZ$;gWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#]5p#]#^$e#X#o5p~$>jVYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#U$?P#U#o5p~$?UWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#h5p#h#i$?n#i#o5p~$?sWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$@]#Y#o5p~$@dU$b~YY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5pZ$@yU}!O5p!Q![5p!c!}5p#R#S5p#T#U$A]#U#o5pZ$AbWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#`5p#`#a$Az#a#o5pZ$BPWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#i5p#i#j$Bi#j#o5pZ$BnWYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#X5p#X#Y$CW#Y#o5pZ$C_UzPYY}!O5p!Q![5p![!]6X!c!}5p#R#S5p#T#o5p~$CvO!n~~$C{O!o~", - tokenizers: [0, 1, 2, 3], - topRules: {"Query":[0,1]}, - tokenPrec: 2502 -}); - -// TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. -// We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. -function suggestLabel(label) { - return { - label: label, - type: "label", - apply: label, - info: "label suggestion", - }; -} -function suggestVariable(varName) { - return { - label: varName, - type: "variable", - apply: varName, - info: "variable suggestion", - }; -} -function collectLabelSuggestions(context, tree, atNode) { - // This is a placeholder function. You will need to implement the logic to collect label suggestions. - // For now, we return an empty array. - var options = []; - tree.iterate({ - enter: (other) => { - if (other.name == "LABEL") { - let content = context.state.sliceDoc(other.from, other.to); - options.push(suggestLabel(content)); - } - } - }); - return options; -} -function collectVariableSuggestions(context, tree, atNode) { - var options = []; - tree.iterate({ - enter: (other) => { - if (other.name == "VAR") { - let content = context.state.sliceDoc(other.from, other.to); - options.push(suggestVariable(content)); - } - } - }); - return options; -} -// See: https://codemirror.net/examples/autocompletion/ and maybe the SQL / HTML Example there. -function autocompleteTypeQL(context) { - let tree = syntaxTree(context.state); - let currentNode = tree.resolveInner(context.pos, -1); // https://lezer.codemirror.net/docs/ref/#common.SyntaxNode - // We may have to walk the tree to find the most appropriate node to suggest things based on. - let options = null; - if (currentNode.name == "LABEL") { - options = collectLabelSuggestions(context, tree); - } - else if (currentNode.name == "VAR") { - options = collectVariableSuggestions(context, tree); - } - else ; - if (options != null) { - // And once we figure out, we have to create a list of completion objects - // It may be worth changing the grammar to be able to do this more easily, rather than replicate the original TypeQL grammar. - // https://codemirror.net/docs/ref/#autocomplete.Completion - return { - from: currentNode.from, - options: options, - // Docs: "regular expression that tells the extension that, as long as the updated input (the range between the result's from property and the completion point) matches that value, it can continue to use the list of completions." - validFor: /^(\w+)?$/ - }; - } - else { - return null; - } -} - -const TypeQLLanguage = LRLanguage.define({ - parser: parser.configure({ - props: [ - indentNodeProp.add({}), - foldNodeProp.add({ - QueryStage: foldInside - }), - styleTags({ - // See: https://lezer.codemirror.net/docs/ref/#highlight.tags - VAR: tags.variableName, - // Literals - STRINGLITERAL: tags.string, - INTEGERLITERAL: tags.number, - DOUBLELITERAL: tags.number, - BOOLEANLITERAL: tags.bool, - // Types - LABEL: tags.typeName, - BOOLEAN: tags.typeName, - INTEGER: tags.typeName, - DOUBLE: tags.typeName, - DECIMAL: tags.typeName, - DATETIMETZ: tags.typeName, - DATETIME: tags.typeName, - DATE: tags.typeName, - DURATION: tags.typeName, - STRING: tags.typeName, - // Keywords - ISA: tags.keyword, - HAS: tags.keyword, - LINKS: tags.keyword, - OWNS: tags.keyword, - RELATES: tags.keyword, - PLAYS: tags.keyword, - FUN: tags.keyword, - LET: tags.keyword, - FIRST: tags.keyword, - LAST: tags.keyword, - // Value type names? - // Stages - DEFINE: tags.heading1, - UNDEFINE: tags.heading1, - REDEFINE: tags.heading1, - MATCH: tags.heading1, - INSERT: tags.heading1, - DELETE: tags.heading1, - UPDATE: tags.heading1, - PUT: tags.heading1, - END: tags.heading1, - SELECT: tags.heading1, - REDUCE: tags.heading1, - SORT: tags.heading1, - OFFSET: tags.heading1, - LIMIT: tags.heading1, - REQUIRE: tags.heading1, - DISTINCT: tags.heading1, - GROUPBY: tags.heading1, - // SubPattern - OR: tags.controlOperator, - NOT: tags.controlOperator, - TRY: tags.controlOperator, - // Misc - Annotation: tags.meta, - LINECOMMENT: tags.lineComment, - }) - ] - }), - languageData: { - commentTokens: { line: "#" } - } -}); -function TypeQL() { - return new LanguageSupport(TypeQLLanguage, [ - TypeQLLanguage.data.of({ - autocomplete: autocompleteTypeQL - }), - ]); -} -// A Linter which flags syntax errors from: https://discuss.codemirror.net/t/showing-syntax-errors/3111/6 -function otherExampleLinter() { - return linter((view) => { - const diagnostics = []; - syntaxTree(view.state).iterate({ - enter: n => { - if (n.type.isError) { - diagnostics.push({ - from: n.from, - to: n.to, - severity: "error", - message: "Syntax error.", - }); - } - }, - }); - return diagnostics; - }); -} - -export { TypeQL, TypeQLLanguage, otherExampleLinter }; diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts new file mode 100644 index 00000000..df48d486 --- /dev/null +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -0,0 +1,133 @@ + +import { parser } from "./generated/typeql.grammar.generated" +import { LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, delimitedIndent, syntaxTree } from "@codemirror/language" +import { styleTags, tags as t } from "@lezer/highlight" +import { Diagnostic } from "@codemirror/lint"; +import { EditorView } from "@codemirror/view"; +import { linter } from '@codemirror/lint' +import { autocompletion, CompletionContext } from "@codemirror/autocomplete"; +import { NodePrefixAutoComplete } from "./complete" +import { Schema, SchemaImpl } from "./schema"; +import { SUGGESTION_MAP } from "./typeql_suggestions"; + +export const TypeQLLanguage = LRLanguage.define({ + parser: parser.configure({ + props: [ + indentNodeProp.add({ + + }), + foldNodeProp.add({ + QueryStage: foldInside + }), + styleTags({ + // See: https://lezer.codemirror.net/docs/ref/#highlight.tags + + VAR: t.variableName, + + // Literals + STRINGLITERAL: t.string, + INTEGERLITERAL: t.number, + DOUBLELITERAL: t.number, + BOOLEANLITERAL: t.bool, + + // Types + LABEL: t.typeName, + + BOOLEAN: t.typeName, + INTEGER: t.typeName, + DOUBLE: t.typeName, + DECIMAL: t.typeName, + DATETIMETZ: t.typeName, + DATETIME: t.typeName, + DATE: t.typeName, + DURATION: t.typeName, + STRING: t.typeName, + + + // Keywords + ISA: t.keyword, + HAS: t.keyword, + LINKS: t.keyword, + OWNS: t.keyword, + RELATES: t.keyword, + PLAYS: t.keyword, + + FUN: t.keyword, + LET: t.keyword, + FIRST: t.keyword, + LAST: t.keyword, + + // Value type names? + + // Stages + DEFINE: t.heading1, + UNDEFINE: t.heading1, + REDEFINE: t.heading1, + + MATCH: t.heading1, + INSERT: t.heading1, + DELETE: t.heading1, + UPDATE: t.heading1, + PUT: t.heading1, + END: t.heading1, + + SELECT: t.heading1, + REDUCE: t.heading1, + SORT: t.heading1, + OFFSET: t.heading1, + LIMIT: t.heading1, + REQUIRE: t.heading1, + DISTINCT: t.heading1, + GROUPBY: t.heading1, + + // SubPattern + OR: t.controlOperator, + NOT: t.controlOperator, + TRY: t.controlOperator, + + // Misc + Annotation: t.meta, + LINECOMMENT: t.lineComment, + }) + ] + }), + languageData: { + commentTokens: { line: "#" } + } +}) + + +export function TypeQL() { + return new LanguageSupport(TypeQLLanguage, []) +} + + +export function typeqlAutocompleteExtension() { + let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new Schema()); + let autocomplete_fn = (context: CompletionContext) => typeqlAutocomplete.autocomplete(context); + return autocompletion({ activateOnTypingDelay: 100, override: [autocomplete_fn] }); +} + +// A Linter which flags syntax errors from: https://discuss.codemirror.net/t/showing-syntax-errors/3111/6 +export function otherExampleLinter() { + return linter((view: EditorView) => { + const diagnostics: Diagnostic[] = []; + syntaxTree(view.state).iterate({ + enter: n => { + if (n.type.isError) { + diagnostics.push({ + from: n.from, + to: n.to, + severity: "error", + message: "Syntax error.", + }); + } + }, + }); + return diagnostics; + }); +} + +export function typeqlSchemaFromText(text: string): SchemaImpl { + return SchemaImpl.fromTypeQL(text, parser.parse(text)); +} diff --git a/src/framework/codemirror-lang-typeql/schema.ts b/src/framework/codemirror-lang-typeql/schema.ts new file mode 100644 index 00000000..3d3fb544 --- /dev/null +++ b/src/framework/codemirror-lang-typeql/schema.ts @@ -0,0 +1,191 @@ +import { SyntaxNode, Tree, TreeCursor } from "@lezer/common"; +import { CompletionContext } from "@codemirror/autocomplete"; +import * as tokens from "./generated/typeql.grammar.generated.terms"; + +type TypeLabel = string; +type AttributeType = {}; + +interface ObjectType { + owns: TypeLabel[]; + plays: TypeLabel[]; + relates: TypeLabel[]; +}; + +function extractText(text: string, from: number, to: number): string { + return text.slice(from, to); +} + +export class Schema { + fromDB: SchemaImpl; + fromEditor: SchemaImpl; + + constructor() { + this.fromDB = new SchemaImpl({}, {}); + this.fromEditor = new SchemaImpl({}, {}); + } + + updateFromDB(schema: SchemaImpl): void { + this.fromDB = schema; + } + + mayUpdateFromEditorState(context: CompletionContext, tree: Tree): void { + this.fromEditor = SchemaImpl.fromTypeQL(context.state.sliceDoc(), tree); + } + + attributeTypes(): TypeLabel[] { + return Object.keys(this.fromDB.attributes).concat(Object.keys(this.fromEditor.attributes)); + } + + objectTypes(): TypeLabel[] { + return Object.keys(this.fromDB.objectTypes).concat(Object.keys(this.fromEditor.objectTypes)); + } + + attributeType(type: TypeLabel): AttributeType { + if (this.fromDB.attributes[type]) { + return this.fromDB.attributes[type]; + } else { + return this.fromEditor.attributes[type]; + } + } + + objectType(type: TypeLabel): ObjectType { + if (this.fromDB.objectTypes[type]) { + return this.fromDB.objectTypes[type]; + } else { + return this.fromEditor.objectTypes[type]; + } + } + + getOwns(label: TypeLabel): TypeLabel[] { + const objectType = this.objectType(label); + return objectType ? objectType.owns : []; + } + + getPlays(label: TypeLabel): TypeLabel[] { + const objectType = this.objectType(label); + return objectType ? objectType.plays : []; + } + getRelates(label: TypeLabel): TypeLabel[] { + const objectType = this.objectType(label); + return objectType ? objectType.relates : []; + } +} + +export class SchemaImpl { + objectTypes: Record; + attributes: Record; + constructor( + objectTypes: Record, + attributes: Record, + ) { + this.attributes = attributes; + this.objectTypes = objectTypes; + } + + static fromTypeQL(text: string, tree: Tree) : SchemaImpl { + let builder = new SchemaBuilder(); + // TODO: Replace iterate with a more targetted traversal that considers only define queries. + // Extract all type declarations from the tree + tree.iterate({ + enter: (cursor: TreeCursor) => { + let node = cursor.node; + if (node.type?.id === tokens.DefinitionType) { + if (node.firstChild?.type?.id === tokens.KIND) { + let labelNode = node.firstChild!.nextSibling!; + let label = extractText(text, labelNode.from, labelNode.to); + let kind = extractText(text, node.firstChild!.from, node.firstChild!.to); + switch (kind) { + case "entity": { + builder.objectType(label); + break; + } + case "relation": { + builder.objectType(label); + break; + } + case "attribute": { + builder.attributeType(label); + break; + } + } + } + } + } + }); + + // Extract owns/relates/plays. Idk what to do with sub or annotations. + tree.iterate({ + enter: (cursor: TreeCursor) => { + let node = cursor.node; + if (node.type.id === tokens.DefinitionType) { + let labelNode = (node.firstChild?.type?.id === tokens.KIND) ? node.firstChild!.nextSibling! : node.firstChild!; + let label = extractText(text, labelNode.from, labelNode.to); + node.getChildren(tokens.TypeCapability).forEach((typeCapabilityBaseNode: SyntaxNode) => { + let actualCapabilityNode = typeCapabilityBaseNode.firstChild!.firstChild!; + switch (actualCapabilityNode.type.id) { + // We actually only want type-declarations for now. + case tokens.RelatesDeclaration: { + let roleTypeNode = actualCapabilityNode.firstChild!.nextSibling!; + let roleType = extractText(text, roleTypeNode.from, roleTypeNode.to); + builder.recordRelates(label, `${label}:${roleType}`); + break; + } + default: { + // Ignore other capabilities for now + break; + } + } + }); + } + } + }); + return builder.build(); + } +} + +class SchemaBuilder { + objectTypes: Record; + attributes: Record; + + constructor() { + this.objectTypes = {}; + this.attributes = {}; + } + + attributeType(type: TypeLabel): AttributeType { + if (!this.attributes[type]) { + this.attributes[type] = { owners: [] }; + } + return this.attributes[type]; + } + + objectType(type: TypeLabel): ObjectType { + if (!this.objectTypes[type]) { + this.objectTypes[type] = { owns: [], plays: [], relates: [] }; + } + return this.objectTypes[type]; + } + + recordOwns(type: TypeLabel, ownedType: TypeLabel): void { + const objectType = this.objectType(type); + if (!objectType.owns.includes(ownedType)) { + objectType.owns.push(ownedType); + } + } + recordPlays(type: TypeLabel, playedType: TypeLabel): void { + const objectType = this.objectType(type); + if (!objectType.plays.includes(playedType)) { + objectType.plays.push(playedType); + } + } + recordRelates(type: TypeLabel, relatedType: TypeLabel): void { + const objectType = this.objectType(type); + if (!objectType.relates.includes(relatedType)) { + objectType.relates.push(relatedType); + } + } + + build(): SchemaImpl { + return new SchemaImpl(this.objectTypes, this.attributes); + } +} diff --git a/src/framework/codemirror-lang-typeql/typeql.grammar b/src/framework/codemirror-lang-typeql/typeql.grammar new file mode 100644 index 00000000..4bc07fa5 --- /dev/null +++ b/src/framework/codemirror-lang-typeql/typeql.grammar @@ -0,0 +1,776 @@ +// TODO: This is a direct translation of the original pest grammar (which is PEG iirc) +// It may be better to rewrite as much as possible so that it's better suited to the LR parser. +@top Query { QuerySchema | QueryPipelinePreambled (END SEMICOLON)? } +@skip { whitespace | linecomment } + +// Schema +QuerySchema { + QueryDefine | + QueryUndefine | + QueryRedefine +} + +QueryDefine { + DEFINE Definables +} + +QueryUndefine { + UNDEFINE Undefinables +} + +QueryRedefine { + REDEFINE Redefinables +} + +Definables { + (Definable SEMICOLON)+ +} + +Definable { + DefinitionType | + DefinitionFunction | + DefinitionStruct +} + +Undefinables { + (Undefinable SEMICOLON)+ +} + +Undefinable { + UndefineFrom | + // UndefineFunction | + UndefineStruct | + KIND LABEL +} + +Redefinables { + (Redefinable SEMICOLON)+ +} + +Redefinable { + RedefinableType | DefinitionFunction +} + +RedefinableType { + KIND? LABEL ((Annotations | TypeCapability) (COMMA TypeCapability)* COMMA?)? +} + +DefinitionType { + KIND? LABEL COMMA? ((Annotations | TypeCapability) (COMMA TypeCapability)* COMMA?)? +} + +TypeCapability { + TypeCapabilityBase Annotations? +} + +TypeCapabilityBase { + SubDeclaration | + ValueTypeDeclaration | + // AliasDeclaration | + OwnsDeclaration | + PlaysDeclaration | + RelatesDeclaration +} + +SubDeclaration { + SUB LABEL +} + +ValueTypeDeclaration { + VALUEKEYWORD ValueType +} + +// AliasDeclaration { +// ALIAS LABEL +// } + +OwnsDeclaration { + OWNS LABEL | + OWNS LabelList +} + +PlaysDeclaration { + PLAYS LABEL // | PLAYS LabelScoped +} + +RelatesDeclaration { + RELATES LABEL (AS LABEL)? | + RELATES LabelList (AS LabelList)? +} + + +DefinitionStruct { + STRUCT IDENTIFIER COLON DefinitionStructFields +} + +DefinitionStructFields { + DefinitionStructField (COMMA DefinitionStructField)* COMMA? +} + +DefinitionStructField { + IDENTIFIER VALUEKEYWORD StructFieldValueType +} + +StructFieldValueType { + ValueTypeOptional | ValueType +} + +ValueTypeOptional { + ValueType QUESTIONMARK +} + + +UndefineFrom { + UndefineAnnotationFromCapability | + UndefineAnnotationFromType | + UndefineCapability | + UndefineSpecialise +} + +UndefineAnnotationFromCapability { + AnnotationCategory FROM LABEL TypeCapabilityBase +} + +UndefineAnnotationFromType { + AnnotationCategory FROM LABEL +} + +UndefineCapability { + TypeCapabilityBase FROM LABEL +} + +UndefineSpecialise { + AS LABEL FROM LABEL RelatesDeclaration +} + +// UndefineFunction { +// FUN IDENTIFIER +// } + +UndefineStruct { + STRUCT IDENTIFIER +} + +LabelList { LABEL SQBRACKETOPEN SQBRACKETCLOSE } +NamedTypeAny { NamedTypeList | NamedType } +NamedTypeList { NamedType SQBRACKETOPEN SQBRACKETCLOSE } +NamedType { LABEL | ValueType } + +// Annotations +AnnotationCategory { + ANNOTATION_ABSTRACT + | ANNOTATION_CARD + | ANNOTATION_CASCADE + | ANNOTATION_DISTINCT + | ANNOTATION_INDEPENDENT + | ANNOTATION_KEY + | ANNOTATION_RANGE + | ANNOTATION_REGEX + | ANNOTATION_SUBKEY + | ANNOTATION_UNIQUE + | ANNOTATION_VALUES +} + + +Annotations { + Annotation+ +} + +Annotation { + ANNOTATION_ABSTRACT | + ANNOTATION_CASCADE | + ANNOTATION_DISTINCT | + ANNOTATION_INDEPENDENT | + ANNOTATION_KEY | + ANNOTATION_UNIQUE | + ANNOTATION_CARD PARENOPEN Cardinality PARENCLOSE | + ANNOTATION_RANGE PARENOPEN Range PARENCLOSE | + ANNOTATION_REGEX PARENOPEN STRINGLITERAL PARENCLOSE | + ANNOTATION_SUBKEY PARENOPEN IDENTIFIER PARENCLOSE | + ANNOTATION_VALUES PARENOPEN ValueLiteral (COMMA ValueLiteral)* COMMA? PARENCLOSE +} + +Cardinality { + INTEGERLITERAL DOUBLE_DOT INTEGERLITERAL? | + INTEGERLITERAL +} + +Range { + RangeBound DOUBLE_DOT RangeBound | + RangeBound DOUBLE_DOT | + DOUBLE_DOT RangeBound +} + +RangeBound { + ValueLiteral +} + + +// Pipelines +QueryPipelinePreambled { + Pipeline +// Preamble* Pipeline +} + +//Pipeline { +// QueryStage+ QueryStageTerminal? +//} + + +//Preamble { +// WITH DefinitionFunction +//} + +Pipeline { + QueryStage+ +} +QueryStage { + ClauseMatch | + ClauseInsert | + ClausePut | + ClauseUpdate | + ClauseDelete | + OperatorStream +} + +//QueryStageTerminal { +// ClauseFetch SEMICOLON +//} + + +// +// Stages +ClauseMatch { + MATCH Patterns +} + + +ClauseInsert { + INSERT (StatementThing SEMICOLON)+ +// INSERT (StatementThing SEMICOLON | StatementAssignment SEMICOLON)+ +} + +ClausePut { + PUT (StatementThing SEMICOLON)+ +} + +ClauseUpdate { + UPDATE (StatementThing SEMICOLON)+ +} + +ClauseDelete { + DELETE (StatementDeletable SEMICOLON)+ +} + +OperatorStream { + OperatorSelect | + OperatorSort | + OperatorDistinct | + OperatorOffset | + OperatorLimit | + OperatorRequire | + OperatorReduce +} + +OperatorSelect { + SELECT Vars SEMICOLON +} + +OperatorSort { + SORT VAROrder (COMMA VAROrder)* SEMICOLON +} + +OperatorOffset { + OFFSET INTEGERLITERAL SEMICOLON +} + +OperatorLimit { + LIMIT INTEGERLITERAL SEMICOLON +} + +OperatorRequire { + REQUIRE Vars SEMICOLON +} + +OperatorDistinct { + DISTINCT SEMICOLON +} + +OperatorReduce { + REDUCE ReduceAssign (COMMA ReduceAssign)* (GROUPBY Vars)? SEMICOLON +} + +// Stage arguments +Vars { + VAR (COMMA VAR)* COMMA? +} + +VAROrder { + VAR ORDER? +} + +ReduceAssign { + (VAR ASSIGN Reducer) +} + +Reducer { + COUNT (PARENOPEN VAR PARENCLOSE)? | + MAX PARENOPEN VAR PARENCLOSE | + MIN PARENOPEN VAR PARENCLOSE | + MEAN PARENOPEN VAR PARENCLOSE | + MEDIAN PARENOPEN VAR PARENCLOSE | + STD PARENOPEN VAR PARENCLOSE | + SUM PARENOPEN VAR PARENCLOSE | + LIST PARENOPEN VAR PARENCLOSE +} + +// Patterns +Patterns { + (Pattern SEMICOLON)+ +} + +Pattern { + Statement | + PatternDisjunction | + PatternConjunction | + PatternNegation | + PatternTry +} + +PatternConjunction { + CURLYOPEN Patterns CURLYCLOSE +} + +PatternDisjunction { + CURLYOPEN Patterns CURLYCLOSE (OR CURLYOPEN Patterns CURLYCLOSE)+ +} + +PatternNegation { + NOT CURLYOPEN Patterns CURLYCLOSE +} + +PatternTry { + TRY CURLYOPEN Patterns CURLYCLOSE +} + +StatementDeletable { + HAS? VAR OF VAR | + LINKS? Relation OF VAR | + VAR +} + +StatementType { + KIND? TypeRef (TypeConstraint (COMMA TypeConstraint)* COMMA?)? +} + +TypeConstraint { + TypeConstraintBase Annotations? +} + +TypeConstraintBase { + SubConstraint | + ValueTypeConstraint | + LabelConstraint | + OwnsConstraint | + RelatesConstraint | + PlaysConstraint +} + +SubConstraint { + SUB TypeRef +} + +ValueTypeConstraint { + VALUEKEYWORD ValueType +} + +LabelConstraint { + LABELKEYWORD LABEL // (LabelScoped | LABEL) +} + + +OwnsConstraint { + OWNS TypeRef | + OWNS TypeRefList +} + +RelatesConstraint { + RELATES TypeRef (AS TypeRef)? | + RELATES TypeRefList (AS TypeRefList)? +} + +PlaysConstraint { + PLAYS TypeRef +} + +Statement { + StatementAssignment | + // StatementSingle | + StatementType | + StatementThing +} + + +StatementThing { + VAR COMMA? ThingConstraintList + // ThingRelationAnonymous (COMMA? ThingConstraintList)? +} + +// ThingRelationAnonymous { +// TypeRef? Relation +// } + +ThingConstraintList { + ThingConstraint (COMMA ThingConstraint)* COMMA? +} + +ThingConstraint { + IsaConstraint | + IidConstraint | + HasConstraint | + LinksConstraint +} + +IsaConstraint { + ISA TypeRef (Expression | Comparison)? // | ExpressionStruct // | ValueLiteral is subsumed by Expression // | Relation is ambiguous with paranthesis expressions +} + +IidConstraint { + IID IID_VALUE +} + +HasConstraint { + HAS TypeRef VAR | + HAS TypeRef ValueLiteral | + HAS VAR +} + +LinksConstraint { + LINKS Relation +} + +Relation { + PARENOPEN RolePlayer (COMMA RolePlayer)* COMMA? PARENCLOSE +} + +RolePlayer { + TypeRefList COLON VAR | + TypeRef COLON VAR | + VAR +} + +TypeRefList { + TypeRef SQBRACKETOPEN SQBRACKETCLOSE +} + +TypeRef { + // LabelScoped | + LABEL | + VAR +} + +// LabelScoped { +// LABEL COLON LABEL +// } + + +ValueLiteral { + BOOLEANLITERAL | + STRINGLITERAL | + INTEGERLITERAL | + DOUBLELITERAL + // TODO +} + +ValueType { + BOOLEAN | + INTEGER | + DOUBLE | + DECIMAL | + DATETIMETZ | + DATETIME | + DATE | + DURATION | + STRING +} + +Comparison { + ComparisonOperator Expression +} + +ComparisonOperator { + EQUAL | NOT_EQUAL | GREATER | GREATER_EQUAL | LESS | LESS_EQUAL | LIKE | CONTAINS +} + +// Function +IDENTIFIER { LABEL } // hack + +DefinitionFunction { + FUN FunctionSignature COLON FunctionBlock +} + +FunctionSignature { + IDENTIFIER PARENOPEN FunctionArguments? PARENCLOSE ARROW FunctionOutput +} + + +FunctionArguments { + FunctionArgument (COMMA FunctionArgument)* COMMA? +} + +FunctionArgument { + VAR COLON NamedTypeAny +} + +FunctionOutput { + FunctionOutputStream | + FunctionOutputSingle +} + +FunctionOutputStream { + CURLYOPEN NamedTypeAny (COMMA NamedTypeAny)* COMMA? CURLYCLOSE +} + +FunctionOutputSingle { + NamedTypeAny (COMMA NamedTypeAny)* COMMA? +} + +FunctionBlock { + QueryStage+ ReturnStatement +} + +ReturnStatement { + RETURN (ReturnStream | ReturnSingle | ReturnReduce) +} + +ReturnStream { + CURLYOPEN Vars CURLYCLOSE +} + +ReturnSingle { + ReturnSingleSelector Vars +} + +ReturnSingleSelector { + FIRST | LAST +} + +ReturnReduce { + Reducer (COMMA Reducer)* COMMA? +} + +// Assignment +StatementAssignment { + LET AssignmentLeft (ASSIGN | IN) Expression +} + +AssignmentLeft { + VarsAssignment // | StructDestructor +} + +VarsAssignment { + VarAssignment (COMMA VarAssignment)* COMMA? +} + +VarAssignment { + VAR QUESTIONMARK? +} + +// StructDestructor { +// CURLYOPEN StructKey COLON StructDestructorValue CURLYCLOSE +// } + +// // StructDestructorValue { +// // VAR | StructDestructor +// // } + +// Expression +Expression { + ExpressionValue (ExpressionOperator ExpressionValue)* +} + +ExpressionValue { + // ExpressionListIndex | + ExpressionParenthesis | + FunctionCall | + ValueLiteral | + VAR +} + +FunctionCall { + FunctionName PARENOPEN FunctionCallArguments? PARENCLOSE +} + +FunctionName { + IDENTIFIER +} + +FunctionCallArguments { + Expression (COMMA Expression)* COMMA? +} +ExpressionOperator { + POWER | TIMES | DIVIDE | MODULO | PLUS | MINUS +} + +ExpressionParenthesis { + PARENOPEN Expression PARENCLOSE +} + +// ExpressionListIndex { +// VAR SQBRACKETOPEN Expression SQBRACKETCLOSE +// } + + +@tokens { + VAR { DOLLAR $[a-zA-Z_\-0-9]+ } + // IDENTIFIER { $[a-zA-Z_\-0-9]+ } // Consider splitting this into Label and LabelScoped + LABEL { $[a-zA-Z] $[a-zA-Z_\-0-9]+ (':' $[a-zA-Z_\-0-9]+)? } // Consider splitting this into Label and LabelScoped + DOLLAR { "$" } + + DISTINCT { "distinct" } + // + DEFINE { "define" } + UNDEFINE { "undefine" } + REDEFINE { "redefine" } + + ANNOTATION_ABSTRACT { "@abstract" } + ANNOTATION_CASCADE { "@cascade" } + ANNOTATION_DISTINCT { "@distinct" } + ANNOTATION_INDEPENDENT { "@independent" } + ANNOTATION_KEY { "@key" } + ANNOTATION_UNIQUE { "@unique" } + ANNOTATION_CARD { "@card" } + ANNOTATION_RANGE { "@range" } + ANNOTATION_REGEX { "@regex" } + ANNOTATION_SUBKEY { "@subkey" } + ANNOTATION_VALUES { "@values" } + + + // WITH { "with" } + MATCH { "match" } + INSERT { "insert" } + PUT { "put" } + UPDATE { "update" } + DELETE { "delete" } + SELECT { "select" } + SORT { "sort" } + OFFSET { "offset" } + LIMIT { "limit" } + REQUIRE { "require" } + // DISTINCT is already covered + REDUCE { "reduce" } + GROUPBY { "groupby" } + + END { "end" } + + COUNT { "count" } + MAX { "max" } + MIN { "min" } + MEAN { "mean" } + MEDIAN { "median" } + STD { "std" } + SUM { "sum" } + LIST { "list" } + ORDER { ASCENDINGORDER | DESCENDINGORDER } + ASCENDINGORDER { "asc" } + DESCENDINGORDER { "desc" } + + + // Patterns + OR { "or" } + NOT { "not" } + TRY { "try" } + + // Statements + HAS { "has" } + ISA { "isa" } + LINKS { "links" } + IID { "iid" } + + SUB { "sub" } + VALUEKEYWORD { "value" } + LABELKEYWORD { "label" } + OWNS { "owns" } + RELATES { "relates" } + PLAYS { "plays" } + + KIND { "entity" | "relation" | "attribute" } + + OF { "of" } + AS { "as" } + FROM { "from" } + + // ValueTypes + BOOLEAN { "boolean" } + INTEGER { "integer" } + DOUBLE { "double" } + DECIMAL { "decimal" } + DATETIMETZ { "datetime-tz" } + DATETIME { "datetime" } + DATE { "date" } + DURATION { "duration" } + STRING { "string" } + + // Functions & structs + FUN { "fun" } + ARROW { "->" } + STRUCT { "struct" } + RETURN { "return" } + + FIRST { "first" } + LAST { "last" } + + // Punctuation + SEMICOLON { ";" } + COMMA { "," } + COLON { ":" } + PARENOPEN { "(" } + PARENCLOSE { ")" } + CURLYOPEN { "{" } + CURLYCLOSE { "}" } + SQBRACKETOPEN { "[" } + SQBRACKETCLOSE { "]" } + DOUBLE_DOT { ".." } + QUESTIONMARK { "?" } + + // Expression operators + LET { "let" } + ASSIGN { "=" } + IN { "in" } + POWER { "^" } + TIMES { "*" } + DIVIDE { "/" } + MODULO { "%" } + PLUS { "+" } + MINUS { "-" } + + // Comparison operators + EQUAL { "==" } + NOT_EQUAL { "!=" } + GREATER { ">" } + GREATER_EQUAL { ">=" } + LESS { "<" } + LESS_EQUAL { "<=" } + LIKE { "like" } + CONTAINS { "contains" } + + //@precedence { ASCENDINGORDER, DESCENDINGORDER } + @precedence { + END, KIND, STRUCT, LET, FUN, RETURN, + BOOLEAN, INTEGER, STRING, DURATION, DOUBLE, DECIMAL, DATETIME, DATETIMETZ, DATE, + BOOLEANLITERAL, LIKE, CONTAINS, + NOT, TRY, DELETE, DISTINCT, MATCH, INSERT, PUT, UPDATE, SELECT, SORT, OFFSET, LIMIT, REQUIRE, REDUCE, + LABEL } // This causes some weird parses sometimes. + + // Literals + IID_VALUE { "0x" $[0-9a-fA-F]+ } + + BOOLEANLITERAL { "false" | "true" } + STRINGLITERAL { '"' (!["\\] | "\\" _)* '"' } + INTEGERLITERAL { "-"? $[0-9]+ } + DOUBLELITERAL { "-"? $[0-9]+('.' $[0-9]+)? } + @precedence { DOUBLELITERAL, INTEGERLITERAL } + + linecomment { "#" ![\n]* } + whitespace { $[ \t\n\r]+ } +} diff --git a/src/framework/codemirror-lang-typeql/typeql.grammar.d.ts b/src/framework/codemirror-lang-typeql/typeql.grammar.d.ts new file mode 100644 index 00000000..38f49b47 --- /dev/null +++ b/src/framework/codemirror-lang-typeql/typeql.grammar.d.ts @@ -0,0 +1,3 @@ +import {LRParser} from "@lezer/lr" + +export declare const parser: LRParser diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts new file mode 100644 index 00000000..2ecb09d7 --- /dev/null +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -0,0 +1,178 @@ +import * as tokens from "./generated/typeql.grammar.generated.terms"; +import { CompletionContext, Completion } from "@codemirror/autocomplete"; +import { SyntaxNode, NodeType, Tree } from "@lezer/common" +import { SuggestionMap, SuffixOfPrefixSuggestion, suggest } from "./complete"; +import { Schema } from "./schema"; + +// The actual suggestions +// TODO: See if we can make this declarative based on token sequences expected as prefixes of a given node. +function suggestAttributeTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + // TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. + // We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. + var options: Completion[] = []; + schema.attributeTypes().forEach((label) => {options.push(suggest("AttributeType", label));}) + return options; +} + +function suggestObjectTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + // TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. + // We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. + var options: Completion[] = []; + schema.objectTypes().forEach((label) => {options.push(suggest("ObjectType", label));}) + return options; +} + +function suggestRoleTypeLabelsScoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + var options: Completion[] = []; + schema.objectTypes() + .flatMap((label) => schema.objectType(label).relates) + .forEach((label) => { options.push(suggest("RoleType", label));}); + return options; +} + +function suggestRoleTypeLabelsUnscoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + var options: Completion[] = []; + schema.objectTypes() + .flatMap((label) => schema.objectType(label).relates) + .forEach((label) => { options.push(suggest("RoleType", label.split(":")[1]));}); return options; +} + + +// The actual suggestions +// TODO: See if we can make this declarative based on token sequences expected as prefixes of a given node. +function suggestThingTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + return suggestAttributeTypeLabels(context, tree, parseAt, climbedTo, prefix, schema).concat( + suggestObjectTypeLabels(context, tree, parseAt, climbedTo, prefix, schema) + ); +} + + +// // TODO: See if we can make this declarative based on token sequences expected as prefixes of a given node. +// function suggestLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +// // TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. +// // We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. +// var options: Completion[] = []; +// tree.iterate({ +// enter: (other: SyntaxNode) => { +// if (other.type.id == tokens.LABEL) { +// let label = context.state.sliceDoc(other.from, other.to); +// options.push(suggest("type", label)); +// } +// } +// }); +// return options; + +// } + +function suggestVariables(context: CompletionContext, tree: Tree, boost=0): Completion[] { + var options: Completion[] = []; + tree.iterate({ + enter: (other: SyntaxNode) => { + if (other.type.id == tokens.VAR) { + let varName = context.state.sliceDoc(other.from, other.to); + options.push(suggest("variable", varName, 0)); + } + } + }); + return options; +} + +function suggestVariablesAt10(context: CompletionContext, tree: Tree): Completion[] { + return suggestVariables(context, tree, 10); +} + +function suggestVariablesAtMinus10(context: CompletionContext, tree: Tree): Completion[] { + return suggestVariables(context, tree, -10); +} + + +function suggestThingConstraintKeywords(): Completion[] { + return ["isa", "has", "links"].map((constraintName) => { + return { + label: constraintName, + type: "thingConstraint", + apply: constraintName, + info: "Thing constraint keyword", + }; + }); +} +function suggestTypeConstraintKeywords(): Completion[] { + return ["sub", "owns", "relates", "plays"].map((constraintName) => { + return { + label: constraintName, + type: "typeConstraint", + apply: constraintName, + info: "Type constraint keyword", + }; + }); +} + +function suggestDefinedKeywords(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + return ["define", "redefine", "undefine"].map((keyword) => suggest("keyword", keyword, 1)); +} + +function suggestPipelineStages(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + return ["match", "insert", "delete", "update", "put", "select", "reduce", "sort", "limit", "offset", "end"].map((keyword) => suggest("keyword", keyword, 1)) +} + +function suggestKinds(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + return ["entity", "attribute", "relation"].map((keyword) => suggest("kind", keyword, 2)); +} + +function suggestNestedPatterns(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { + return ["not {};", "{} or {};", "try {};"].map((keyword) => suggest("method", keyword, 2)); +} + +// Hopefully you only have to touch this. + +const SUFFIX_VAR_OR_COMMA = [[tokens.COMMA], [tokens.VAR]]; + + +// Will pick the first matching suffix. If you want to handle things manually, use an empty suffix, duh. + +const SUGGESTION_GROUP_FOR_THING_STATEMENTS: SuffixOfPrefixSuggestion[] = [ + { suffixes: SUFFIX_VAR_OR_COMMA, suggestions: [suggestThingConstraintKeywords] }, + { suffixes: [[tokens.HAS]], suggestions: [suggestAttributeTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.ISA]], suggestions: [suggestThingTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.HAS, tokens.TypeRef], [tokens.ISA, tokens.TypeRef]], suggestions: [suggestVariablesAtMinus10] }, +]; + +export const SUGGESTION_MAP: SuggestionMap = { + [tokens.LABEL]: [{ suffixes: [[]], suggestions: [suggestThingTypeLabels] }], + [tokens.VAR]: [{ suffixes: [[]], suggestions: [suggestVariablesAt10] }], + + [tokens.Statement]: [ + { suffixes: SUFFIX_VAR_OR_COMMA, suggestions: [suggestThingConstraintKeywords, suggestTypeConstraintKeywords] }, + { suffixes: [[tokens.HAS]], suggestions: [suggestAttributeTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.ISA]], suggestions: [suggestThingTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.HAS, tokens.TypeRef], [tokens.ISA, tokens.TypeRef]], suggestions: [suggestVariablesAtMinus10] }, + { suffixes: [[tokens.SEMICOLON, tokens.TypeRef]], suggestions: [suggestTypeConstraintKeywords] }, + { suffixes: [[tokens.OWNS]], suggestions: [suggestAttributeTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.SUB]], suggestions: [suggestThingTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.RELATES]], suggestions: [suggestRoleTypeLabelsUnscoped] }, + { suffixes: [[tokens.PLAYS]], suggestions: [suggestRoleTypeLabelsScoped] }, + ], + [tokens.ClauseMatch]: [ + { suffixes: [[tokens.MATCH, tokens.TypeRef]], suggestions: [suggestTypeConstraintKeywords] }, + { suffixes: [[tokens.MATCH]], suggestions: [suggestVariablesAt10, suggestThingTypeLabels] }, + ], + [tokens.ClauseInsert]: SUGGESTION_GROUP_FOR_THING_STATEMENTS, + [tokens.Query]: [ + { suffixes: [[tokens.QuerySchema]], suggestions: [suggestThingTypeLabels, suggestKinds] }, + { suffixes: [[tokens.QueryPipelinePreambled]], suggestions: [suggestPipelineStages, suggestNestedPatterns, suggestVariablesAt10] }, + ], + + // Now some for define statements + [tokens.QuerySchema]: [ + { suffixes: [[tokens.DEFINE]], suggestions: [ suggestThingTypeLabels, suggestKinds] }, + { suffixes: [[tokens.DEFINE, tokens.LABEL]], suggestions: [ suggestTypeConstraintKeywords] } + ], + [tokens.Definable]: [ + { suffixes: [[tokens.COMMA], [tokens.KIND, tokens.LABEL]], suggestions: [ suggestTypeConstraintKeywords ] }, + { suffixes: [[tokens.OWNS]], suggestions: [suggestAttributeTypeLabels] }, + { suffixes: [[tokens.SUB]], suggestions: [suggestThingTypeLabels] }, + { suffixes: [ [tokens.PLAYS] ], suggestions: [ suggestRoleTypeLabelsScoped ] }, + { suffixes: [ [tokens.RELATES] ], suggestions: [ suggestRoleTypeLabelsUnscoped ] }, + ], + // TODO: ... +}; diff --git a/src/module/query/query-tool.component.html b/src/module/query/query-tool.component.html index 81a487d1..ba76b88b 100644 --- a/src/module/query/query-tool.component.html +++ b/src/module/query/query-tool.component.html @@ -42,7 +42,7 @@

Query

@if (!codeEditorHidden) { - + }
diff --git a/src/module/query/query-tool.component.ts b/src/module/query/query-tool.component.ts index 307f8eae..cbb00fbd 100644 --- a/src/module/query/query-tool.component.ts +++ b/src/module/query/query-tool.component.ts @@ -21,7 +21,7 @@ import { MatTooltipModule } from "@angular/material/tooltip"; import { RouterLink } from "@angular/router"; import { ResizableDirective } from "@hhangular/resizable"; import { distinctUntilChanged, filter, first, map, startWith } from "rxjs"; -import { otherExampleLinter, TypeQL } from "../../framework/codemirror-lang-typeql"; +import { otherExampleLinter, TypeQL, typeqlAutocompleteExtension } from "../../framework/codemirror-lang-typeql"; import { DriverAction, TransactionOperationAction, isQueryRun, isTransactionOperation } from "../../concept/action"; import { basicDark } from "../../framework/code-editor/theme"; import { DetectScrollDirective } from "../../framework/scroll-container/detect-scroll.directive"; @@ -134,4 +134,5 @@ export class QueryToolComponent implements OnInit, AfterViewInit, OnDestroy { readonly JSON = JSON; readonly TypeQL = TypeQL; readonly linter = otherExampleLinter; + readonly typeqlAutocompleteExtension = typeqlAutocompleteExtension; } From 288a0e8ec7329089df35a2ca864a90caa327184a Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 12 Jun 2025 18:33:20 +0530 Subject: [PATCH 02/19] Cleanup some comments --- src/framework/codemirror-lang-typeql/index.ts | 8 +-- ...{schema.ts => typeQLAutocompleteSchema.ts} | 22 +++---- .../codemirror-lang-typeql/typeql.grammar | 2 + .../typeql_suggestions.ts | 57 +++++-------------- 4 files changed, 28 insertions(+), 61 deletions(-) rename src/framework/codemirror-lang-typeql/{schema.ts => typeQLAutocompleteSchema.ts} (90%) diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index df48d486..a6852038 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -7,7 +7,7 @@ import { EditorView } from "@codemirror/view"; import { linter } from '@codemirror/lint' import { autocompletion, CompletionContext } from "@codemirror/autocomplete"; import { NodePrefixAutoComplete } from "./complete" -import { Schema, SchemaImpl } from "./schema"; +import { TypeQLAutocompleteSchema, TypeQLAutocompleteSchemaImpl } from "./typeQLAutocompleteSchema"; import { SUGGESTION_MAP } from "./typeql_suggestions"; export const TypeQLLanguage = LRLanguage.define({ @@ -103,7 +103,7 @@ export function TypeQL() { export function typeqlAutocompleteExtension() { - let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new Schema()); + let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new TypeQLAutocompleteSchema()); let autocomplete_fn = (context: CompletionContext) => typeqlAutocomplete.autocomplete(context); return autocompletion({ activateOnTypingDelay: 100, override: [autocomplete_fn] }); } @@ -127,7 +127,3 @@ export function otherExampleLinter() { return diagnostics; }); } - -export function typeqlSchemaFromText(text: string): SchemaImpl { - return SchemaImpl.fromTypeQL(text, parser.parse(text)); -} diff --git a/src/framework/codemirror-lang-typeql/schema.ts b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts similarity index 90% rename from src/framework/codemirror-lang-typeql/schema.ts rename to src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts index 3d3fb544..3487cb37 100644 --- a/src/framework/codemirror-lang-typeql/schema.ts +++ b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts @@ -15,21 +15,21 @@ function extractText(text: string, from: number, to: number): string { return text.slice(from, to); } -export class Schema { - fromDB: SchemaImpl; - fromEditor: SchemaImpl; +export class TypeQLAutocompleteSchema { + fromDB: TypeQLAutocompleteSchemaImpl; + fromEditor: TypeQLAutocompleteSchemaImpl; constructor() { - this.fromDB = new SchemaImpl({}, {}); - this.fromEditor = new SchemaImpl({}, {}); + this.fromDB = new TypeQLAutocompleteSchemaImpl({}, {}); + this.fromEditor = new TypeQLAutocompleteSchemaImpl({}, {}); } - updateFromDB(schema: SchemaImpl): void { + updateFromDB(schema: TypeQLAutocompleteSchemaImpl): void { this.fromDB = schema; } mayUpdateFromEditorState(context: CompletionContext, tree: Tree): void { - this.fromEditor = SchemaImpl.fromTypeQL(context.state.sliceDoc(), tree); + this.fromEditor = TypeQLAutocompleteSchemaImpl.fromTypeQL(context.state.sliceDoc(), tree); } attributeTypes(): TypeLabel[] { @@ -71,7 +71,7 @@ export class Schema { } } -export class SchemaImpl { +export class TypeQLAutocompleteSchemaImpl { objectTypes: Record; attributes: Record; constructor( @@ -82,7 +82,7 @@ export class SchemaImpl { this.objectTypes = objectTypes; } - static fromTypeQL(text: string, tree: Tree) : SchemaImpl { + static fromTypeQL(text: string, tree: Tree) : TypeQLAutocompleteSchemaImpl { let builder = new SchemaBuilder(); // TODO: Replace iterate with a more targetted traversal that considers only define queries. // Extract all type declarations from the tree @@ -185,7 +185,7 @@ class SchemaBuilder { } } - build(): SchemaImpl { - return new SchemaImpl(this.objectTypes, this.attributes); + build(): TypeQLAutocompleteSchemaImpl { + return new TypeQLAutocompleteSchemaImpl(this.objectTypes, this.attributes); } } diff --git a/src/framework/codemirror-lang-typeql/typeql.grammar b/src/framework/codemirror-lang-typeql/typeql.grammar index 4bc07fa5..3dc8c9a8 100644 --- a/src/framework/codemirror-lang-typeql/typeql.grammar +++ b/src/framework/codemirror-lang-typeql/typeql.grammar @@ -1,3 +1,5 @@ +// TypeQL grammar for lezer. run `pnpm run generate-grammar` to generate the parser and propagate these changes to typescript. + // TODO: This is a direct translation of the original pest grammar (which is PEG iirc) // It may be better to rewrite as much as possible so that it's better suited to the LR parser. @top Query { QuerySchema | QueryPipelinePreambled (END SEMICOLON)? } diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts index 2ecb09d7..1e42abb5 100644 --- a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -2,27 +2,21 @@ import * as tokens from "./generated/typeql.grammar.generated.terms"; import { CompletionContext, Completion } from "@codemirror/autocomplete"; import { SyntaxNode, NodeType, Tree } from "@lezer/common" import { SuggestionMap, SuffixOfPrefixSuggestion, suggest } from "./complete"; -import { Schema } from "./schema"; +import { TypeQLAutocompleteSchema } from "./typeQLAutocompleteSchema"; -// The actual suggestions -// TODO: See if we can make this declarative based on token sequences expected as prefixes of a given node. -function suggestAttributeTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { - // TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. - // We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. +function suggestAttributeTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { var options: Completion[] = []; schema.attributeTypes().forEach((label) => {options.push(suggest("AttributeType", label));}) return options; } -function suggestObjectTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { - // TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. - // We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. +function suggestObjectTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { var options: Completion[] = []; schema.objectTypes().forEach((label) => {options.push(suggest("ObjectType", label));}) return options; } -function suggestRoleTypeLabelsScoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +function suggestRoleTypeLabelsScoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { var options: Completion[] = []; schema.objectTypes() .flatMap((label) => schema.objectType(label).relates) @@ -30,40 +24,19 @@ function suggestRoleTypeLabelsScoped(context: CompletionContext, tree: Tree, par return options; } -function suggestRoleTypeLabelsUnscoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +function suggestRoleTypeLabelsUnscoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { var options: Completion[] = []; schema.objectTypes() .flatMap((label) => schema.objectType(label).relates) .forEach((label) => { options.push(suggest("RoleType", label.split(":")[1]));}); return options; } - -// The actual suggestions -// TODO: See if we can make this declarative based on token sequences expected as prefixes of a given node. -function suggestThingTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +function suggestThingTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return suggestAttributeTypeLabels(context, tree, parseAt, climbedTo, prefix, schema).concat( suggestObjectTypeLabels(context, tree, parseAt, climbedTo, prefix, schema) ); } - -// // TODO: See if we can make this declarative based on token sequences expected as prefixes of a given node. -// function suggestLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { -// // TODO: We could do better by climbing up the tree using `atNode.parentNode` to predict based on position as well. -// // We could also refine the suggestions by creating datastructures based on the declarations in the schema, rather than blindly suggesting every label. -// var options: Completion[] = []; -// tree.iterate({ -// enter: (other: SyntaxNode) => { -// if (other.type.id == tokens.LABEL) { -// let label = context.state.sliceDoc(other.from, other.to); -// options.push(suggest("type", label)); -// } -// } -// }); -// return options; - -// } - function suggestVariables(context: CompletionContext, tree: Tree, boost=0): Completion[] { var options: Completion[] = []; tree.iterate({ @@ -85,7 +58,6 @@ function suggestVariablesAtMinus10(context: CompletionContext, tree: Tree): Comp return suggestVariables(context, tree, -10); } - function suggestThingConstraintKeywords(): Completion[] { return ["isa", "has", "links"].map((constraintName) => { return { @@ -107,37 +79,34 @@ function suggestTypeConstraintKeywords(): Completion[] { }); } -function suggestDefinedKeywords(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +function suggestDefinedKeywords(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return ["define", "redefine", "undefine"].map((keyword) => suggest("keyword", keyword, 1)); } -function suggestPipelineStages(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +function suggestPipelineStages(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return ["match", "insert", "delete", "update", "put", "select", "reduce", "sort", "limit", "offset", "end"].map((keyword) => suggest("keyword", keyword, 1)) } -function suggestKinds(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +function suggestKinds(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return ["entity", "attribute", "relation"].map((keyword) => suggest("kind", keyword, 2)); } -function suggestNestedPatterns(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: Schema): Completion[] { +function suggestNestedPatterns(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return ["not {};", "{} or {};", "try {};"].map((keyword) => suggest("method", keyword, 2)); } -// Hopefully you only have to touch this. - const SUFFIX_VAR_OR_COMMA = [[tokens.COMMA], [tokens.VAR]]; -// Will pick the first matching suffix. If you want to handle things manually, use an empty suffix, duh. - -const SUGGESTION_GROUP_FOR_THING_STATEMENTS: SuffixOfPrefixSuggestion[] = [ +// Will pick the first matching suffix. If you want to handle things manually, use an empty suffix. +const SUGGESTION_GROUP_FOR_THING_STATEMENTS: SuffixOfPrefixSuggestion[] = [ { suffixes: SUFFIX_VAR_OR_COMMA, suggestions: [suggestThingConstraintKeywords] }, { suffixes: [[tokens.HAS]], suggestions: [suggestAttributeTypeLabels, suggestVariablesAtMinus10] }, { suffixes: [[tokens.ISA]], suggestions: [suggestThingTypeLabels, suggestVariablesAtMinus10] }, { suffixes: [[tokens.HAS, tokens.TypeRef], [tokens.ISA, tokens.TypeRef]], suggestions: [suggestVariablesAtMinus10] }, ]; -export const SUGGESTION_MAP: SuggestionMap = { +export const SUGGESTION_MAP: SuggestionMap = { [tokens.LABEL]: [{ suffixes: [[]], suggestions: [suggestThingTypeLabels] }], [tokens.VAR]: [{ suffixes: [[]], suggestions: [suggestVariablesAt10] }], From 8c34fdab716d3eb4f04fa718f43bc589e522d2f3 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 12 Jun 2025 18:49:13 +0530 Subject: [PATCH 03/19] Eeps. Why can't i select autocomplete stuff --- package.json | 3 ++- src/module/query/query-tool.component.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 85b45e9e..31ff0728 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@codemirror/lint": "6.8.5", "@codemirror/autocomplete": "6.18.6", "@customerio/cdp-analytics-browser": "0.2.0", + "@codemirror/commands": "6.8.1", "@hhangular/resizable": "1.18.1", "@intercom/messenger-js-sdk": "0.0.14", "@lezer/common": "^1.0.0", @@ -72,7 +73,7 @@ "typescript": "5.8.3", "unplugin-lezer": "1.0.1", "@tauri-apps/cli": "^2", - "@lezer/generator": "^1.7.3" + "@lezer/generator": "1.7.3" }, "browserslist": [ "defaults and fully supports es6-module" diff --git a/src/module/query/query-tool.component.ts b/src/module/query/query-tool.component.ts index cbb00fbd..42f447f7 100644 --- a/src/module/query/query-tool.component.ts +++ b/src/module/query/query-tool.component.ts @@ -33,6 +33,9 @@ import { QueryToolState } from "../../service/query-tool-state.service"; import { SnackbarService } from "../../service/snackbar.service"; import { PageScaffoldComponent } from "../scaffold/page/page-scaffold.component"; import { SchemaTreeNodeComponent } from "./schema-tree-node/schema-tree-node.component"; +import {keymap} from "@codemirror/view"; +import {defaultKeymap} from "@codemirror/commands"; +import {startCompletion} from "@codemirror/autocomplete"; @Component({ selector: "ts-query-tool", @@ -135,4 +138,5 @@ export class QueryToolComponent implements OnInit, AfterViewInit, OnDestroy { readonly TypeQL = TypeQL; readonly linter = otherExampleLinter; readonly typeqlAutocompleteExtension = typeqlAutocompleteExtension; + // readonly codeEditorKeymap = keymap.of([...defaultKeymap,{key: "Alt-Space", run: startCompletion, preventDefault: true}]); } From 1227834a574ba77b80c73fae3a4c20f8c2f26a1a Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 12 Jun 2025 20:02:00 +0530 Subject: [PATCH 04/19] OOf, works somewhat ok --- src/framework/codemirror-lang-typeql/index.ts | 9 +- .../typeQLAutocompleteSchema.ts | 85 +++++++++++++++---- src/module/query/query-tool.component.html | 2 +- src/module/query/query-tool.component.ts | 4 +- 4 files changed, 78 insertions(+), 22 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index a6852038..992515ed 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -101,11 +101,12 @@ export function TypeQL() { return new LanguageSupport(TypeQLLanguage, []) } - +let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new TypeQLAutocompleteSchema()); +function wrappedSuggest(context: CompletionContext) { + return typeqlAutocomplete.autocomplete(context); +} export function typeqlAutocompleteExtension() { - let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new TypeQLAutocompleteSchema()); - let autocomplete_fn = (context: CompletionContext) => typeqlAutocomplete.autocomplete(context); - return autocompletion({ activateOnTypingDelay: 100, override: [autocomplete_fn] }); + return autocompletion({ override: [wrappedSuggest] }); } // A Linter which flags syntax errors from: https://discuss.codemirror.net/t/showing-syntax-errors/3111/6 diff --git a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts index 3487cb37..e279741d 100644 --- a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts +++ b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts @@ -1,11 +1,15 @@ import { SyntaxNode, Tree, TreeCursor } from "@lezer/common"; import { CompletionContext } from "@codemirror/autocomplete"; import * as tokens from "./generated/typeql.grammar.generated.terms"; - +import {TypeDBHttpDriver} from "../typedb-driver"; +import {finalize} from "rxjs"; +import {DriverState} from "../../service/driver-state.service"; +import {ConceptRow, ConceptRowsQueryResponse} from "../typedb-driver/response"; +import {Concept, EntityType, RelationType, Type} from "../typedb-driver/concept"; +type ConceptRowAnswer = { involvedBlocks: number[]; data: ConceptRow }; type TypeLabel = string; -type AttributeType = {}; - -interface ObjectType { +type AttributeTypeEntry = {}; +interface ObjectTypeEntry { owns: TypeLabel[]; plays: TypeLabel[]; relates: TypeLabel[]; @@ -40,7 +44,7 @@ export class TypeQLAutocompleteSchema { return Object.keys(this.fromDB.objectTypes).concat(Object.keys(this.fromEditor.objectTypes)); } - attributeType(type: TypeLabel): AttributeType { + attributeType(type: TypeLabel): AttributeTypeEntry { if (this.fromDB.attributes[type]) { return this.fromDB.attributes[type]; } else { @@ -48,7 +52,7 @@ export class TypeQLAutocompleteSchema { } } - objectType(type: TypeLabel): ObjectType { + objectType(type: TypeLabel): ObjectTypeEntry { if (this.fromDB.objectTypes[type]) { return this.fromDB.objectTypes[type]; } else { @@ -72,16 +76,16 @@ export class TypeQLAutocompleteSchema { } export class TypeQLAutocompleteSchemaImpl { - objectTypes: Record; - attributes: Record; + objectTypes: Record; + attributes: Record; constructor( - objectTypes: Record, - attributes: Record, + objectTypes: Record, + attributes: Record, ) { this.attributes = attributes; this.objectTypes = objectTypes; } - + static fromTypeQL(text: string, tree: Tree) : TypeQLAutocompleteSchemaImpl { let builder = new SchemaBuilder(); // TODO: Replace iterate with a more targetted traversal that considers only define queries. @@ -141,25 +145,76 @@ export class TypeQLAutocompleteSchemaImpl { }); return builder.build(); } + + static fromDriver(driver: DriverState, database: string): TypeQLAutocompleteSchemaImpl | null { + function runQuery(driver: DriverState, database: string, query: string): ConceptRowAnswer[] { + // todo: Help please alex + return [ + { + involvedBlocks: [0], + data: { // Unioning them all makes it pass + ["owner"]: {kind: "entityType", label: "person"}, + ["owned"]: {kind: "attributeType", label: "name", valueType: "string"}, + ["relation"]: {kind: "relationType", label: "friendship"}, + ["related"]: {kind: "roleType", label: "friend"}, + ["player"]: {kind: "entityType", label: "person"}, + ["played"]: {kind: "roleType", label: "friend"}, + } + } + ]; + } + let ownsQuery = "match $owner owns $owned;" + let relatesQuery = "match $relation relates $related;"; + let playsQuery = "match $player plays $played"; + + let ownsAnswers = runQuery(driver, database, ownsQuery); // TODO: + let relatesAnswers = runQuery(driver, database, relatesQuery); + let playsAnswers = runQuery(driver, database, playsQuery); + + let builder = new SchemaBuilder(); + ownsAnswers.forEach((answer) => { + let data: ConceptRow = answer.data; + let owner = (data["owner"] as Type).label; + let owned = (data["owned"] as Type).label; + builder.objectType(owner); + builder.attributeType(owned); + builder.recordOwns(owner, owned); + }); + relatesAnswers.forEach((answer) => { + let data: ConceptRow = answer.data; + let relation = (data["relation"] as Type).label; + let related = (data["related"] as Type).label; + builder.objectType(relation); + builder.recordRelates(relation, related) + }); + playsAnswers.forEach((answer) => { + let data: ConceptRow = answer.data; + let player = (data["player"] as Type).label; + let played = (data["played"] as Type).label; + builder.objectType(player); + builder.recordRelates(player, played) + }) + return builder.build(); + } } class SchemaBuilder { - objectTypes: Record; - attributes: Record; + objectTypes: Record; + attributes: Record; constructor() { this.objectTypes = {}; this.attributes = {}; } - attributeType(type: TypeLabel): AttributeType { + attributeType(type: TypeLabel): AttributeTypeEntry { if (!this.attributes[type]) { this.attributes[type] = { owners: [] }; } return this.attributes[type]; } - objectType(type: TypeLabel): ObjectType { + objectType(type: TypeLabel): ObjectTypeEntry { if (!this.objectTypes[type]) { this.objectTypes[type] = { owns: [], plays: [], relates: [] }; } diff --git a/src/module/query/query-tool.component.html b/src/module/query/query-tool.component.html index ba76b88b..feb2bcd8 100644 --- a/src/module/query/query-tool.component.html +++ b/src/module/query/query-tool.component.html @@ -42,7 +42,7 @@

Query

@if (!codeEditorHidden) { - + }
diff --git a/src/module/query/query-tool.component.ts b/src/module/query/query-tool.component.ts index 42f447f7..02884cba 100644 --- a/src/module/query/query-tool.component.ts +++ b/src/module/query/query-tool.component.ts @@ -35,7 +35,7 @@ import { PageScaffoldComponent } from "../scaffold/page/page-scaffold.component" import { SchemaTreeNodeComponent } from "./schema-tree-node/schema-tree-node.component"; import {keymap} from "@codemirror/view"; import {defaultKeymap} from "@codemirror/commands"; -import {startCompletion} from "@codemirror/autocomplete"; +import {startCompletion, completionKeymap} from "@codemirror/autocomplete"; @Component({ selector: "ts-query-tool", @@ -138,5 +138,5 @@ export class QueryToolComponent implements OnInit, AfterViewInit, OnDestroy { readonly TypeQL = TypeQL; readonly linter = otherExampleLinter; readonly typeqlAutocompleteExtension = typeqlAutocompleteExtension; - // readonly codeEditorKeymap = keymap.of([...defaultKeymap,{key: "Alt-Space", run: startCompletion, preventDefault: true}]); + readonly codeEditorKeymap = keymap.of([...defaultKeymap, {key: "Alt-Space", run: startCompletion, preventDefault: true}]); } From 1f4abe95a3ccda039aac3bd4416e0235b07a0774 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 12 Jun 2025 20:19:27 +0530 Subject: [PATCH 05/19] Rearrange --- .../codemirror-lang-typeql/complete.ts | 4 +++ src/framework/codemirror-lang-typeql/index.ts | 25 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/complete.ts b/src/framework/codemirror-lang-typeql/complete.ts index d4889a52..d4aa2530 100644 --- a/src/framework/codemirror-lang-typeql/complete.ts +++ b/src/framework/codemirror-lang-typeql/complete.ts @@ -44,6 +44,10 @@ export class NodePrefixAutoComplete { this.suggestorState = suggestorState; } + getState(): STATE { + return this.suggestorState; + } + autocomplete(context: CompletionContext): CompletionResult | null { let tree: Tree = syntaxTree(context.state); this.suggestorState.mayUpdateFromEditorState(context, tree); diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index 992515ed..a80f9865 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -9,6 +9,7 @@ import { autocompletion, CompletionContext } from "@codemirror/autocomplete"; import { NodePrefixAutoComplete } from "./complete" import { TypeQLAutocompleteSchema, TypeQLAutocompleteSchemaImpl } from "./typeQLAutocompleteSchema"; import { SUGGESTION_MAP } from "./typeql_suggestions"; +import {DriverState} from "../../service/driver-state.service"; export const TypeQLLanguage = LRLanguage.define({ parser: parser.configure({ @@ -101,14 +102,6 @@ export function TypeQL() { return new LanguageSupport(TypeQLLanguage, []) } -let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new TypeQLAutocompleteSchema()); -function wrappedSuggest(context: CompletionContext) { - return typeqlAutocomplete.autocomplete(context); -} -export function typeqlAutocompleteExtension() { - return autocompletion({ override: [wrappedSuggest] }); -} - // A Linter which flags syntax errors from: https://discuss.codemirror.net/t/showing-syntax-errors/3111/6 export function otherExampleLinter() { return linter((view: EditorView) => { @@ -128,3 +121,19 @@ export function otherExampleLinter() { return diagnostics; }); } + +// Autocomplete +let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new TypeQLAutocompleteSchema()); +function wrappedAutocomplete(context: CompletionContext) { + return typeqlAutocomplete.autocomplete(context); +} +export function typeqlAutocompleteExtension() { + return autocompletion({ override: [wrappedAutocomplete] }); +} + +function updateSchemaFromDB(driver: DriverState, database: string) { + let updatedSchema = TypeQLAutocompleteSchemaImpl.fromDriver(driver, database); + if (updatedSchema) { + typeqlAutocomplete.getState().updateFromDB(updatedSchema); + } +} From 52acba16c2ecc0054023684009e1e11775545899 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 12 Jun 2025 22:52:08 +0530 Subject: [PATCH 06/19] More clenaups, esp with navigation helpers --- .../codemirror-lang-typeql/complete.ts | 16 +- src/framework/codemirror-lang-typeql/index.ts | 47 ++++-- .../codemirror-lang-typeql/navigation.ts | 24 +++ .../typeQLAutocompleteSchema.ts | 151 ++++++------------ src/framework/graph-visualiser/index.ts | 2 +- src/framework/typedb-driver/response.ts | 6 +- 6 files changed, 119 insertions(+), 127 deletions(-) create mode 100644 src/framework/codemirror-lang-typeql/navigation.ts diff --git a/src/framework/codemirror-lang-typeql/complete.ts b/src/framework/codemirror-lang-typeql/complete.ts index d4aa2530..ab78be39 100644 --- a/src/framework/codemirror-lang-typeql/complete.ts +++ b/src/framework/codemirror-lang-typeql/complete.ts @@ -2,18 +2,18 @@ import { CompletionContext, Completion, CompletionResult } from "@codemirror/autocomplete"; import { syntaxTree } from "@codemirror/language" import { SyntaxNode, NodeType, Tree } from "@lezer/common" -import { SUGGESTION_MAP } from "./typeql_suggestions"; +type TokenID = number; export interface SuggestionMap { - [key: number]: SuffixOfPrefixSuggestion[] -}; + [key: TokenID]: SuffixOfPrefixSuggestion[] +} -export type SuffixCandidate = number[]; // A SuffixCandidate 's' "matches" a prefix if prefix[-s.length:] == s +export type SuffixCandidate = TokenID[]; // A SuffixCandidate 's' "matches" a prefix if prefix[-s.length:] == s export interface SuffixOfPrefixSuggestion { suffixes: SuffixCandidate[], // If any of the suffix candidates match, the suggestions will be used. suggestions: SuggestionFunction[] -}; +} export type SuggestionFunction = (context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], state: STATE) => Completion[] | null; @@ -81,7 +81,7 @@ export class NodePrefixAutoComplete { } let suggestionEither = this.suggestionMap[climbedTo.type.id]; if (suggestionEither != null) { - for (var sops of (suggestionEither as SuffixOfPrefixSuggestion[])) { + for (let sops of (suggestionEither as SuffixOfPrefixSuggestion[])) { if (prefixHasAnyOfSuffixes(prefix, sops.suffixes)) { return this.combineSuggestions(context, tree, parseAt, climbedTo, prefix, sops.suggestions); } @@ -139,7 +139,7 @@ function collectSiblingsOf(node: SyntaxNode): NodeType[] { let prev: SyntaxNode | null = node; while (null != (prev = prev.prevSibling)) { siblings.push(prev.type); - }; + } return siblings.reverse(); } @@ -162,7 +162,7 @@ function prefixHasAnyOfSuffixes(prefix: NodeType[], suffixes: SuffixCandidate[]) return false; } -function prefixHasSuffix(prefix: NodeType[], suffix: number[]): boolean { +function prefixHasSuffix(prefix: NodeType[], suffix: TokenID[]): boolean { if (prefix.length < suffix.length) { return false; } diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index a80f9865..606721c3 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -1,15 +1,16 @@ import { parser } from "./generated/typeql.grammar.generated" -import { LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, delimitedIndent, syntaxTree } from "@codemirror/language" +import { LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, syntaxTree } from "@codemirror/language" import { styleTags, tags as t } from "@lezer/highlight" import { Diagnostic } from "@codemirror/lint"; import { EditorView } from "@codemirror/view"; import { linter } from '@codemirror/lint' import { autocompletion, CompletionContext } from "@codemirror/autocomplete"; import { NodePrefixAutoComplete } from "./complete" -import { TypeQLAutocompleteSchema, TypeQLAutocompleteSchemaImpl } from "./typeQLAutocompleteSchema"; +import {SchemaBuilder, TypeQLAutocompleteSchema} from "./typeQLAutocompleteSchema"; import { SUGGESTION_MAP } from "./typeql_suggestions"; -import {DriverState} from "../../service/driver-state.service"; +import {ConceptRow, ConceptRowAnswer} from "../typedb-driver/response"; +import {Type} from "../typedb-driver/concept"; export const TypeQLLanguage = LRLanguage.define({ parser: parser.configure({ @@ -130,10 +131,38 @@ function wrappedAutocomplete(context: CompletionContext) { export function typeqlAutocompleteExtension() { return autocompletion({ override: [wrappedAutocomplete] }); } - -function updateSchemaFromDB(driver: DriverState, database: string) { - let updatedSchema = TypeQLAutocompleteSchemaImpl.fromDriver(driver, database); - if (updatedSchema) { - typeqlAutocomplete.getState().updateFromDB(updatedSchema); - } +// Manually run (in a single query is fine) and collect the following results using _lastQueryAnswers +// match $owner owns $owned; +// match $relation relates $related; +// match $player plays $played; +// and then call _updateSchemaFromDB(_lastQueryAnswers, _lastQueryAnswers, _lastQueryAnswers); + +function updateSchemaFromDB(ownsAnswers: ConceptRowAnswer[], relatesAnswers: ConceptRowAnswer[], playsAnswers: ConceptRowAnswer[]) { + let builder = new SchemaBuilder(); + ownsAnswers.forEach((answer) => { + let data: ConceptRow = answer.data; + let owner = (data["owner"] as Type).label; + let owned = (data["owned"] as Type).label; + builder.objectType(owner); + builder.attributeType(owned); + builder.recordOwns(owner, owned); + }); + relatesAnswers.forEach((answer) => { + let data: ConceptRow = answer.data; + let relation = (data["relation"] as Type).label; + let related = (data["related"] as Type).label; + builder.objectType(relation); + builder.recordRelates(relation, related) + }); + playsAnswers.forEach((answer) => { + let data: ConceptRow = answer.data; + let player = (data["player"] as Type).label; + let played = (data["played"] as Type).label; + builder.objectType(player); + builder.recordRelates(player, played) + }) + typeqlAutocomplete.getState().updateFromDB(builder.build()); } + +(window as any)._updateSchemaFromDB = updateSchemaFromDB; +(window as any)._typeqlAutoComplete = typeqlAutocomplete; \ No newline at end of file diff --git a/src/framework/codemirror-lang-typeql/navigation.ts b/src/framework/codemirror-lang-typeql/navigation.ts new file mode 100644 index 00000000..f723fd6d --- /dev/null +++ b/src/framework/codemirror-lang-typeql/navigation.ts @@ -0,0 +1,24 @@ +import {SyntaxNode} from "@lezer/common"; + +// path should be token ids +type TokenID = number; +export function nodesWithPath(root: SyntaxNode, path: TokenID[]) : SyntaxNode[] { + return nodesWithPathImpl([root], path, 0); +} + +function nodesWithPathImpl(from: SyntaxNode[], path: TokenID[], index: number) : SyntaxNode[] { + if (index >= path.length) { + return from; + } else { + let next = from.flatMap((node) => node.getChildren(path[index])); + return nodesWithPathImpl(next, path, index + 1) + } +} + +export function climbTill(from: SyntaxNode, till: TokenID): SyntaxNode | null { + let at: SyntaxNode | null = from; + while (at != null && at.type.id != till) { + at = at.parent; + } + return at; +} diff --git a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts index e279741d..f241bb56 100644 --- a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts +++ b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts @@ -1,19 +1,15 @@ -import { SyntaxNode, Tree, TreeCursor } from "@lezer/common"; +import { Tree } from "@lezer/common"; import { CompletionContext } from "@codemirror/autocomplete"; import * as tokens from "./generated/typeql.grammar.generated.terms"; -import {TypeDBHttpDriver} from "../typedb-driver"; -import {finalize} from "rxjs"; -import {DriverState} from "../../service/driver-state.service"; -import {ConceptRow, ConceptRowsQueryResponse} from "../typedb-driver/response"; -import {Concept, EntityType, RelationType, Type} from "../typedb-driver/concept"; -type ConceptRowAnswer = { involvedBlocks: number[]; data: ConceptRow }; +import {nodesWithPath} from "./navigation"; + type TypeLabel = string; type AttributeTypeEntry = {}; interface ObjectTypeEntry { owns: TypeLabel[]; plays: TypeLabel[]; relates: TypeLabel[]; -}; +} function extractText(text: string, from: number, to: number): string { return text.slice(from, to); @@ -90,115 +86,60 @@ export class TypeQLAutocompleteSchemaImpl { let builder = new SchemaBuilder(); // TODO: Replace iterate with a more targetted traversal that considers only define queries. // Extract all type declarations from the tree - tree.iterate({ - enter: (cursor: TreeCursor) => { - let node = cursor.node; - if (node.type?.id === tokens.DefinitionType) { - if (node.firstChild?.type?.id === tokens.KIND) { - let labelNode = node.firstChild!.nextSibling!; - let label = extractText(text, labelNode.from, labelNode.to); - let kind = extractText(text, node.firstChild!.from, node.firstChild!.to); - switch (kind) { - case "entity": { - builder.objectType(label); - break; - } - case "relation": { - builder.objectType(label); - break; - } - case "attribute": { - builder.attributeType(label); - break; - } - } + let root = tree.topNode; + let definitionTypes = nodesWithPath(root, [tokens.QuerySchema, tokens.QueryDefine, tokens.Definables, tokens.Definable, tokens.DefinitionType]) + definitionTypes.forEach(node => { + let kindNode = node.getChild(tokens.KIND); + let labelNode = node.getChild(tokens.LABEL); + if (kindNode != null && labelNode != null) { + let kind = extractText(text, kindNode.from, kindNode.to); + let label = extractText(text, labelNode.from, labelNode.to); + switch (kind) { + case "entity": { + builder.objectType(label); + break; + } + case "relation": { + builder.objectType(label); + break; + } + case "attribute": { + builder.attributeType(label); + break; } } } - }); + }) + // Extract owns/relates/plays. Idk what to do with sub or annotations. - tree.iterate({ - enter: (cursor: TreeCursor) => { - let node = cursor.node; - if (node.type.id === tokens.DefinitionType) { - let labelNode = (node.firstChild?.type?.id === tokens.KIND) ? node.firstChild!.nextSibling! : node.firstChild!; - let label = extractText(text, labelNode.from, labelNode.to); - node.getChildren(tokens.TypeCapability).forEach((typeCapabilityBaseNode: SyntaxNode) => { - let actualCapabilityNode = typeCapabilityBaseNode.firstChild!.firstChild!; - switch (actualCapabilityNode.type.id) { - // We actually only want type-declarations for now. - case tokens.RelatesDeclaration: { - let roleTypeNode = actualCapabilityNode.firstChild!.nextSibling!; - let roleType = extractText(text, roleTypeNode.from, roleTypeNode.to); - builder.recordRelates(label, `${label}:${roleType}`); - break; - } - default: { - // Ignore other capabilities for now - break; - } + definitionTypes.forEach(node => { + let labelNode = node.getChild(tokens.LABEL); + if (labelNode == null) return; + let label = extractText(text, labelNode.from, labelNode.to); + nodesWithPath(node, [tokens.TypeCapability, tokens.TypeCapabilityBase]) + .map(typeCapabilityBaseNode => typeCapabilityBaseNode.firstChild) + .forEach(actualCapabilityNode => { + switch (actualCapabilityNode?.type.id) { + // We actually only want type-declarations for now. + case tokens.RelatesDeclaration: { + let roleTypeNode = actualCapabilityNode.firstChild!.nextSibling!; + let roleType = extractText(text, roleTypeNode.from, roleTypeNode.to); + builder.recordRelates(label, `${label}:${roleType}`); + break; + } + default: { + // Ignore other capabilities for now + break; } - }); - } - } - }); - return builder.build(); - } - - static fromDriver(driver: DriverState, database: string): TypeQLAutocompleteSchemaImpl | null { - function runQuery(driver: DriverState, database: string, query: string): ConceptRowAnswer[] { - // todo: Help please alex - return [ - { - involvedBlocks: [0], - data: { // Unioning them all makes it pass - ["owner"]: {kind: "entityType", label: "person"}, - ["owned"]: {kind: "attributeType", label: "name", valueType: "string"}, - ["relation"]: {kind: "relationType", label: "friendship"}, - ["related"]: {kind: "roleType", label: "friend"}, - ["player"]: {kind: "entityType", label: "person"}, - ["played"]: {kind: "roleType", label: "friend"}, } - } - ]; - } - let ownsQuery = "match $owner owns $owned;" - let relatesQuery = "match $relation relates $related;"; - let playsQuery = "match $player plays $played"; - - let ownsAnswers = runQuery(driver, database, ownsQuery); // TODO: - let relatesAnswers = runQuery(driver, database, relatesQuery); - let playsAnswers = runQuery(driver, database, playsQuery); - - let builder = new SchemaBuilder(); - ownsAnswers.forEach((answer) => { - let data: ConceptRow = answer.data; - let owner = (data["owner"] as Type).label; - let owned = (data["owned"] as Type).label; - builder.objectType(owner); - builder.attributeType(owned); - builder.recordOwns(owner, owned); + }); }); - relatesAnswers.forEach((answer) => { - let data: ConceptRow = answer.data; - let relation = (data["relation"] as Type).label; - let related = (data["related"] as Type).label; - builder.objectType(relation); - builder.recordRelates(relation, related) - }); - playsAnswers.forEach((answer) => { - let data: ConceptRow = answer.data; - let player = (data["player"] as Type).label; - let played = (data["played"] as Type).label; - builder.objectType(player); - builder.recordRelates(player, played) - }) return builder.build(); } } -class SchemaBuilder { +export class SchemaBuilder { objectTypes: Record; attributes: Record; diff --git a/src/framework/graph-visualiser/index.ts b/src/framework/graph-visualiser/index.ts index bcd6dcfc..35dcf210 100644 --- a/src/framework/graph-visualiser/index.ts +++ b/src/framework/graph-visualiser/index.ts @@ -47,8 +47,8 @@ export class GraphVisualiser { handleQueryResult(res: ApiResponse) { if (isApiErrorResponse(res)) return; - if (res.ok.answerType == "conceptRows" && res.ok.query != null) { + (window as any)._lastQueryAnswers = res.ok.answers; let converter = new StudioConverter(this.graph, res.ok.query, false, this.structureParameters, this.styleParameters); let logicalGraph = constructGraphFromRowsResult(res.ok); // In memory, not visualised convertLogicalGraphWith(logicalGraph, converter); diff --git a/src/framework/typedb-driver/response.ts b/src/framework/typedb-driver/response.ts index a287e78f..725b4706 100644 --- a/src/framework/typedb-driver/response.ts +++ b/src/framework/typedb-driver/response.ts @@ -36,6 +36,7 @@ export interface ConceptRow { } export interface ConceptRowAnswer { + involvedBlocks: number[]; data: ConceptRow; } @@ -56,10 +57,7 @@ export interface OkQueryResponse extends QueryResponseBase { export interface ConceptRowsQueryResponse extends QueryResponseBase { answerType: "conceptRows"; - answers: { - involvedBlocks: number[]; - data: ConceptRow - }[]; + answers: ConceptRowAnswer[]; } export interface ConceptDocumentsQueryResponse extends QueryResponseBase { From cdbbc8a9ce1e4dd46701ccf1e94fb11866ed74db Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Fri, 13 Jun 2025 00:27:05 +0530 Subject: [PATCH 07/19] Suggestions for has and links constraints are nicely scoped --- .../codemirror-lang-typeql/complete.ts | 1 + src/framework/codemirror-lang-typeql/index.ts | 18 ++++-- .../typeql_suggestions.ts | 62 ++++++++++++++++--- 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/complete.ts b/src/framework/codemirror-lang-typeql/complete.ts index ab78be39..1606e375 100644 --- a/src/framework/codemirror-lang-typeql/complete.ts +++ b/src/framework/codemirror-lang-typeql/complete.ts @@ -76,6 +76,7 @@ export class NodePrefixAutoComplete { climbTillWeRecogniseSomething(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode | null, prefix: NodeType[]): Completion[] | null { if (climbedTo == null) { + // Uncomment this if you don't see suggestions // this.logInterestingStuff(context, tree, parseAt, climbedTo, prefix); return null; } diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index 606721c3..7c03c507 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -131,12 +131,22 @@ function wrappedAutocomplete(context: CompletionContext) { export function typeqlAutocompleteExtension() { return autocompletion({ override: [wrappedAutocomplete] }); } -// Manually run (in a single query is fine) and collect the following results using _lastQueryAnswers -// match $owner owns $owned; -// match $relation relates $related; -// match $player plays $played; +// Manually run and collect the following results using _lastQueryAnswers +// match $default-owner owns $default-owned; limit 1; +// match $default-relation relates $default-related; limit 1; +// match $default-player plays $default-played; limit 1; +// match +// { $owner owns $owned; $relation is $default-relation; $related is $default-related; $player is $default-player; $played is $default-played; } or +// { $owner is $default-owner; $owned is $default-owned; $relation relates $related; $player is $default-player; $played is $default-played; } or +// { $owner is $default-owner; $owned is $default-owned; $relation is $default-relation; $related is $default-related; $player plays $played; }; // and then call _updateSchemaFromDB(_lastQueryAnswers, _lastQueryAnswers, _lastQueryAnswers); +// Or finer queries: +// # match $owner owns $owned; +// # match $relation relates $related; +// # match $player plays $played; +// And you have to set each argument separately + function updateSchemaFromDB(ownsAnswers: ConceptRowAnswer[], relatesAnswers: ConceptRowAnswer[], playsAnswers: ConceptRowAnswer[]) { let builder = new SchemaBuilder(); ownsAnswers.forEach((answer) => { diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts index 1e42abb5..c247ba21 100644 --- a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -1,13 +1,54 @@ import * as tokens from "./generated/typeql.grammar.generated.terms"; import { CompletionContext, Completion } from "@codemirror/autocomplete"; -import { SyntaxNode, NodeType, Tree } from "@lezer/common" +import {SyntaxNode, NodeType, Tree, SyntaxNodeRef} from "@lezer/common" import { SuggestionMap, SuffixOfPrefixSuggestion, suggest } from "./complete"; import { TypeQLAutocompleteSchema } from "./typeQLAutocompleteSchema"; +import {climbTill, nodesWithPath} from "./navigation"; +import {ThingConstraint} from "./generated/typeql.grammar.generated.terms"; + +type TokenID = number; + +function findIsaConstraintLabelsForVar(context: CompletionContext, parseAt: SyntaxNode): string[] { + let parentStatementThing = climbTill(parseAt, tokens.StatementThing)!; + let varNode = parentStatementThing.getChild(tokens.VAR)!; + let varName = context.state.sliceDoc(varNode.from, varNode.to); + let pipelineNode = climbTill(parentStatementThing, tokens.Pipeline)!; + // TODO: Maybe consider disjunctions? + let statements = nodesWithPath(pipelineNode, [tokens.QueryStage, tokens.ClauseMatch, tokens.Patterns, tokens.Pattern, tokens.Statement, tokens.StatementThing]); + let relevantStatementThings = statements + .filter(n => { + let otherVarNode = n.getChild(tokens.VAR)!; + return context.state.sliceDoc(otherVarNode.from, otherVarNode.to) == varName; + }); + let possibleLabels = relevantStatementThings + .flatMap(n => nodesWithPath(n, [tokens.ThingConstraintList, tokens.ThingConstraint, tokens.IsaConstraint, tokens.TypeRef])) + .map(n => context.state.sliceDoc(n.from, n.to)); + // console.log(parentStatementThing, varNode, varName, pipelineNode, statements, relevantStatementThings, possibleLabels); + return possibleLabels; +} + +function suggestAttributeTypeForHas(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { + let possibleLabels = findIsaConstraintLabelsForVar(context, parseAt); + if (possibleLabels.length > 0) { + return possibleLabels.flatMap(owner => schema.getOwns(owner)) + .map((label) => suggest("AttributeType", label)) + } else { + return suggestAttributeTypeLabels(context, tree, parseAt, climbedTo, prefix, schema); + } +} + +function suggestRoleTypeForLinks(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { + let possibleLabels = findIsaConstraintLabelsForVar(context, parseAt); + if (possibleLabels.length > 0) { + return possibleLabels.flatMap(owner => schema.getRelates(owner)) + .map((label) => suggest("RoleType", label.split(":")[1])) + } else { + return suggestRoleTypeLabelsUnscoped(context, tree, parseAt, climbedTo, prefix, schema); + } +} function suggestAttributeTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { - var options: Completion[] = []; - schema.attributeTypes().forEach((label) => {options.push(suggest("AttributeType", label));}) - return options; + return schema.attributeTypes().map((label) => { return suggest("AttributeType", label); }); } function suggestObjectTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { @@ -101,7 +142,7 @@ const SUFFIX_VAR_OR_COMMA = [[tokens.COMMA], [tokens.VAR]]; // Will pick the first matching suffix. If you want to handle things manually, use an empty suffix. const SUGGESTION_GROUP_FOR_THING_STATEMENTS: SuffixOfPrefixSuggestion[] = [ { suffixes: SUFFIX_VAR_OR_COMMA, suggestions: [suggestThingConstraintKeywords] }, - { suffixes: [[tokens.HAS]], suggestions: [suggestAttributeTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.HAS]], suggestions: [suggestAttributeTypeForHas, suggestVariablesAtMinus10] }, { suffixes: [[tokens.ISA]], suggestions: [suggestThingTypeLabels, suggestVariablesAtMinus10] }, { suffixes: [[tokens.HAS, tokens.TypeRef], [tokens.ISA, tokens.TypeRef]], suggestions: [suggestVariablesAtMinus10] }, ]; @@ -109,10 +150,13 @@ const SUGGESTION_GROUP_FOR_THING_STATEMENTS: SuffixOfPrefixSuggestion = { [tokens.LABEL]: [{ suffixes: [[]], suggestions: [suggestThingTypeLabels] }], [tokens.VAR]: [{ suffixes: [[]], suggestions: [suggestVariablesAt10] }], - + [tokens.Relation]: [ + {suffixes: [[tokens.PARENOPEN], [tokens.COMMA]], suggestions: [suggestRoleTypeForLinks, suggestVariablesAtMinus10]}, + {suffixes: [[tokens.COLON]], suggestions: [suggestVariablesAt10]}, + ], [tokens.Statement]: [ { suffixes: SUFFIX_VAR_OR_COMMA, suggestions: [suggestThingConstraintKeywords, suggestTypeConstraintKeywords] }, - { suffixes: [[tokens.HAS]], suggestions: [suggestAttributeTypeLabels, suggestVariablesAtMinus10] }, + { suffixes: [[tokens.HAS]], suggestions: [suggestAttributeTypeForHas, suggestVariablesAtMinus10] }, { suffixes: [[tokens.ISA]], suggestions: [suggestThingTypeLabels, suggestVariablesAtMinus10] }, { suffixes: [[tokens.HAS, tokens.TypeRef], [tokens.ISA, tokens.TypeRef]], suggestions: [suggestVariablesAtMinus10] }, { suffixes: [[tokens.SEMICOLON, tokens.TypeRef]], suggestions: [suggestTypeConstraintKeywords] }, @@ -123,12 +167,12 @@ export const SUGGESTION_MAP: SuggestionMap = { ], [tokens.ClauseMatch]: [ { suffixes: [[tokens.MATCH, tokens.TypeRef]], suggestions: [suggestTypeConstraintKeywords] }, - { suffixes: [[tokens.MATCH]], suggestions: [suggestVariablesAt10, suggestThingTypeLabels] }, + { suffixes: [[tokens.MATCH]], suggestions: [suggestNestedPatterns, suggestVariablesAt10, suggestThingTypeLabels ] }, ], [tokens.ClauseInsert]: SUGGESTION_GROUP_FOR_THING_STATEMENTS, [tokens.Query]: [ { suffixes: [[tokens.QuerySchema]], suggestions: [suggestThingTypeLabels, suggestKinds] }, - { suffixes: [[tokens.QueryPipelinePreambled]], suggestions: [suggestPipelineStages, suggestNestedPatterns, suggestVariablesAt10] }, + { suffixes: [[tokens.QueryPipelinePreambled]], suggestions: [suggestNestedPatterns, suggestVariablesAt10, suggestPipelineStages, ] }, ], // Now some for define statements From 80c010109d517b7470eb5187b0e50701b24c0cf1 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Fri, 13 Jun 2025 00:27:59 +0530 Subject: [PATCH 08/19] Fix boost in variables not being used --- .../codemirror-lang-typeql/typeql_suggestions.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts index c247ba21..0a7e4235 100644 --- a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -1,12 +1,9 @@ import * as tokens from "./generated/typeql.grammar.generated.terms"; import { CompletionContext, Completion } from "@codemirror/autocomplete"; -import {SyntaxNode, NodeType, Tree, SyntaxNodeRef} from "@lezer/common" +import {SyntaxNode, NodeType, Tree} from "@lezer/common" import { SuggestionMap, SuffixOfPrefixSuggestion, suggest } from "./complete"; import { TypeQLAutocompleteSchema } from "./typeQLAutocompleteSchema"; import {climbTill, nodesWithPath} from "./navigation"; -import {ThingConstraint} from "./generated/typeql.grammar.generated.terms"; - -type TokenID = number; function findIsaConstraintLabelsForVar(context: CompletionContext, parseAt: SyntaxNode): string[] { let parentStatementThing = climbTill(parseAt, tokens.StatementThing)!; @@ -78,13 +75,13 @@ function suggestThingTypeLabels(context: CompletionContext, tree: Tree, parseAt: ); } -function suggestVariables(context: CompletionContext, tree: Tree, boost=0): Completion[] { +function suggestVariables(context: CompletionContext, tree: Tree, boost= 0): Completion[] { var options: Completion[] = []; tree.iterate({ enter: (other: SyntaxNode) => { if (other.type.id == tokens.VAR) { let varName = context.state.sliceDoc(other.from, other.to); - options.push(suggest("variable", varName, 0)); + options.push(suggest("variable", varName, boost)); } } }); From 5fde472b84d486a8f25e8471a445e5c81ebed30c Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Fri, 13 Jun 2025 00:34:57 +0530 Subject: [PATCH 09/19] Minor refactors --- .../typeql_suggestions.ts | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts index 0a7e4235..db0e192d 100644 --- a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -44,29 +44,24 @@ function suggestRoleTypeForLinks(context: CompletionContext, tree: Tree, parseAt } } -function suggestAttributeTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { +function suggestAttributeTypeLabels(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return schema.attributeTypes().map((label) => { return suggest("AttributeType", label); }); } -function suggestObjectTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { - var options: Completion[] = []; - schema.objectTypes().forEach((label) => {options.push(suggest("ObjectType", label));}) - return options; +function suggestObjectTypeLabels(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { + return schema.objectTypes().map((label) => suggest("ObjectType", label)); } -function suggestRoleTypeLabelsScoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { - var options: Completion[] = []; - schema.objectTypes() +function suggestRoleTypeLabelsScoped(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { + return schema.objectTypes() .flatMap((label) => schema.objectType(label).relates) - .forEach((label) => { options.push(suggest("RoleType", label));}); - return options; + .map((label) => suggest("RoleType", label)); } -function suggestRoleTypeLabelsUnscoped(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { - var options: Completion[] = []; - schema.objectTypes() +function suggestRoleTypeLabelsUnscoped(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { + return schema.objectTypes() .flatMap((label) => schema.objectType(label).relates) - .forEach((label) => { options.push(suggest("RoleType", label.split(":")[1]));}); return options; + .map((label) => suggest("RoleType", label.split(":")[1])); } function suggestThingTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { @@ -117,19 +112,19 @@ function suggestTypeConstraintKeywords(): Completion[] { }); } -function suggestDefinedKeywords(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { +function suggestDefinedKeywords(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], _schema: TypeQLAutocompleteSchema): Completion[] { return ["define", "redefine", "undefine"].map((keyword) => suggest("keyword", keyword, 1)); } -function suggestPipelineStages(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { +function suggestPipelineStages(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], _schema: TypeQLAutocompleteSchema): Completion[] { return ["match", "insert", "delete", "update", "put", "select", "reduce", "sort", "limit", "offset", "end"].map((keyword) => suggest("keyword", keyword, 1)) } -function suggestKinds(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { +function suggestKinds(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], _schema: TypeQLAutocompleteSchema): Completion[] { return ["entity", "attribute", "relation"].map((keyword) => suggest("kind", keyword, 2)); } -function suggestNestedPatterns(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { +function suggestNestedPatterns(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], _schema: TypeQLAutocompleteSchema): Completion[] { return ["not {};", "{} or {};", "try {};"].map((keyword) => suggest("method", keyword, 2)); } @@ -169,13 +164,14 @@ export const SUGGESTION_MAP: SuggestionMap = { [tokens.ClauseInsert]: SUGGESTION_GROUP_FOR_THING_STATEMENTS, [tokens.Query]: [ { suffixes: [[tokens.QuerySchema]], suggestions: [suggestThingTypeLabels, suggestKinds] }, - { suffixes: [[tokens.QueryPipelinePreambled]], suggestions: [suggestNestedPatterns, suggestVariablesAt10, suggestPipelineStages, ] }, + { suffixes: [[tokens.QueryPipelinePreambled]], suggestions: [suggestNestedPatterns, suggestVariablesAt10, suggestPipelineStages ] }, ], // Now some for define statements [tokens.QuerySchema]: [ { suffixes: [[tokens.DEFINE]], suggestions: [ suggestThingTypeLabels, suggestKinds] }, - { suffixes: [[tokens.DEFINE, tokens.LABEL]], suggestions: [ suggestTypeConstraintKeywords] } + { suffixes: [[tokens.DEFINE, tokens.LABEL]], suggestions: [ suggestTypeConstraintKeywords] }, + { suffixes: [[tokens.SEMICOLON, tokens.LABEL]], suggestions: [ suggestTypeConstraintKeywords] }, ], [tokens.Definable]: [ { suffixes: [[tokens.COMMA], [tokens.KIND, tokens.LABEL]], suggestions: [ suggestTypeConstraintKeywords ] }, From 150f61394fb504ee57dab5c6c6929ff09a12d859 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Fri, 13 Jun 2025 00:49:32 +0530 Subject: [PATCH 10/19] Add TODO to remove window.OC_lastQueryAnswers --- src/framework/graph-visualiser/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/graph-visualiser/index.ts b/src/framework/graph-visualiser/index.ts index 35dcf210..041a5292 100644 --- a/src/framework/graph-visualiser/index.ts +++ b/src/framework/graph-visualiser/index.ts @@ -48,7 +48,7 @@ export class GraphVisualiser { handleQueryResult(res: ApiResponse) { if (isApiErrorResponse(res)) return; if (res.ok.answerType == "conceptRows" && res.ok.query != null) { - (window as any)._lastQueryAnswers = res.ok.answers; + (window as any)._lastQueryAnswers = res.ok.answers; // TODO: Remove once schema based autocomplete is stable. let converter = new StudioConverter(this.graph, res.ok.query, false, this.structureParameters, this.styleParameters); let logicalGraph = constructGraphFromRowsResult(res.ok); // In memory, not visualised convertLogicalGraphWith(logicalGraph, converter); From 8d0bc18606d6e810a6758cb2157390232d4ba769 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Fri, 13 Jun 2025 21:26:21 +0530 Subject: [PATCH 11/19] Retry CI From 7297973116bf65be47da0077f99aac75dc4e71e9 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 18 Jun 2025 18:35:35 +0530 Subject: [PATCH 12/19] Post rebase working in --- src/framework/codemirror-lang-typeql/index.ts | 84 ++--- .../typeQLAutocompleteSchema.ts | 307 +++++++++--------- .../typeql_suggestions.ts | 34 +- src/service/schema-state.service.ts | 4 +- 4 files changed, 226 insertions(+), 203 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index 7c03c507..baa2cdee 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -7,10 +7,11 @@ import { EditorView } from "@codemirror/view"; import { linter } from '@codemirror/lint' import { autocompletion, CompletionContext } from "@codemirror/autocomplete"; import { NodePrefixAutoComplete } from "./complete" -import {SchemaBuilder, TypeQLAutocompleteSchema} from "./typeQLAutocompleteSchema"; +import {TypeQLAutocompleteSchema} from "./typeQLAutocompleteSchema"; import { SUGGESTION_MAP } from "./typeql_suggestions"; import {ConceptRow, ConceptRowAnswer} from "../typedb-driver/response"; import {Type} from "../typedb-driver/concept"; +import {Schema} from "../../service/schema-state.service"; export const TypeQLLanguage = LRLanguage.define({ parser: parser.configure({ @@ -124,13 +125,22 @@ export function otherExampleLinter() { } // Autocomplete -let typeqlAutocomplete = new NodePrefixAutoComplete(SUGGESTION_MAP, new TypeQLAutocompleteSchema()); +let typeqlAutocomplete = new NodePrefixAutoComplete( + SUGGESTION_MAP, + new TypeQLAutocompleteSchema({entities: {}, relations: {}, attributes: {}}, {entities: {}, relations: {}, attributes: {}}) +); + function wrappedAutocomplete(context: CompletionContext) { return typeqlAutocomplete.autocomplete(context); } export function typeqlAutocompleteExtension() { return autocompletion({ override: [wrappedAutocomplete] }); } + +function updateSchemaFromDB(schema: Schema) { + typeqlAutocomplete.getState().updateFromDB(schema); +} + // Manually run and collect the following results using _lastQueryAnswers // match $default-owner owns $default-owned; limit 1; // match $default-relation relates $default-related; limit 1; @@ -140,39 +150,39 @@ export function typeqlAutocompleteExtension() { // { $owner is $default-owner; $owned is $default-owned; $relation relates $related; $player is $default-player; $played is $default-played; } or // { $owner is $default-owner; $owned is $default-owned; $relation is $default-relation; $related is $default-related; $player plays $played; }; // and then call _updateSchemaFromDB(_lastQueryAnswers, _lastQueryAnswers, _lastQueryAnswers); - -// Or finer queries: -// # match $owner owns $owned; -// # match $relation relates $related; -// # match $player plays $played; -// And you have to set each argument separately - -function updateSchemaFromDB(ownsAnswers: ConceptRowAnswer[], relatesAnswers: ConceptRowAnswer[], playsAnswers: ConceptRowAnswer[]) { - let builder = new SchemaBuilder(); - ownsAnswers.forEach((answer) => { - let data: ConceptRow = answer.data; - let owner = (data["owner"] as Type).label; - let owned = (data["owned"] as Type).label; - builder.objectType(owner); - builder.attributeType(owned); - builder.recordOwns(owner, owned); - }); - relatesAnswers.forEach((answer) => { - let data: ConceptRow = answer.data; - let relation = (data["relation"] as Type).label; - let related = (data["related"] as Type).label; - builder.objectType(relation); - builder.recordRelates(relation, related) - }); - playsAnswers.forEach((answer) => { - let data: ConceptRow = answer.data; - let player = (data["player"] as Type).label; - let played = (data["played"] as Type).label; - builder.objectType(player); - builder.recordRelates(player, played) - }) - typeqlAutocomplete.getState().updateFromDB(builder.build()); -} - -(window as any)._updateSchemaFromDB = updateSchemaFromDB; +// +// // Or finer queries: +// // # match $owner owns $owned; +// // # match $relation relates $related; +// // # match $player plays $played; +// // And you have to set each argument separately +// +// function updateSchemaFromDB(ownsAnswers: ConceptRowAnswer[], relatesAnswers: ConceptRowAnswer[], playsAnswers: ConceptRowAnswer[]) { +// let builder = new SchemaBuilder(); +// ownsAnswers.forEach((answer) => { +// let data: ConceptRow = answer.data; +// let owner = (data["owner"] as Type).label; +// let owned = (data["owned"] as Type).label; +// builder.objectType(owner); +// builder.attributeType(owned); +// builder.recordOwns(owner, owned); +// }); +// relatesAnswers.forEach((answer) => { +// let data: ConceptRow = answer.data; +// let relation = (data["relation"] as Type).label; +// let related = (data["related"] as Type).label; +// builder.objectType(relation); +// builder.recordRelates(relation, related) +// }); +// playsAnswers.forEach((answer) => { +// let data: ConceptRow = answer.data; +// let player = (data["player"] as Type).label; +// let played = (data["played"] as Type).label; +// builder.objectType(player); +// builder.recordRelates(player, played) +// }) +// typeqlAutocomplete.getState().updateFromDB(builder.build()); +// } + +// (window as any)._updateSchemaFromDB = updateSchemaFromDB; (window as any)._typeqlAutoComplete = typeqlAutocomplete; \ No newline at end of file diff --git a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts index f241bb56..edb1d156 100644 --- a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts +++ b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts @@ -2,186 +2,199 @@ import { Tree } from "@lezer/common"; import { CompletionContext } from "@codemirror/autocomplete"; import * as tokens from "./generated/typeql.grammar.generated.terms"; import {nodesWithPath} from "./navigation"; +import { + Schema, + SchemaAttribute, + SchemaRole, + SchemaEntity, + SchemaRelation, + SchemaConcept +} from "../../service/schema-state.service"; +type SchemaObject = SchemaEntity | SchemaRelation; type TypeLabel = string; -type AttributeTypeEntry = {}; -interface ObjectTypeEntry { - owns: TypeLabel[]; - plays: TypeLabel[]; - relates: TypeLabel[]; -} function extractText(text: string, from: number, to: number): string { return text.slice(from, to); } +function labels(types: SchemaConcept[]): string[] { + return types.map(type => type.label); +} + export class TypeQLAutocompleteSchema { - fromDB: TypeQLAutocompleteSchemaImpl; - fromEditor: TypeQLAutocompleteSchemaImpl; + fromDB: Schema; + fromEditor: Schema; - constructor() { - this.fromDB = new TypeQLAutocompleteSchemaImpl({}, {}); - this.fromEditor = new TypeQLAutocompleteSchemaImpl({}, {}); + constructor(fromDB: Schema, fromEditor: Schema) { + this.fromDB = fromDB; + this.fromEditor = fromEditor; } - updateFromDB(schema: TypeQLAutocompleteSchemaImpl): void { - this.fromDB = schema; - } - - mayUpdateFromEditorState(context: CompletionContext, tree: Tree): void { - this.fromEditor = TypeQLAutocompleteSchemaImpl.fromTypeQL(context.state.sliceDoc(), tree); + updateFromDB(fromDB: Schema): void { + this.fromDB = fromDB; } - attributeTypes(): TypeLabel[] { - return Object.keys(this.fromDB.attributes).concat(Object.keys(this.fromEditor.attributes)); + mayUpdateFromEditorState(context: CompletionContext, tree: Tree): void { + this.fromEditor = buildSchemafromTypeQL(context.state.sliceDoc(), tree); } - objectTypes(): TypeLabel[] { - return Object.keys(this.fromDB.objectTypes).concat(Object.keys(this.fromEditor.objectTypes)); + attributeTypes(): SchemaAttribute[] { + return Object.values(this.fromDB.attributes).concat(Object.values(this.fromEditor.attributes)); } - attributeType(type: TypeLabel): AttributeTypeEntry { - if (this.fromDB.attributes[type]) { - return this.fromDB.attributes[type]; - } else { - return this.fromEditor.attributes[type]; - } + objectTypes(): SchemaObject[] { + return (this.entityTypes() as SchemaObject[]).concat(this.relationTypes() as SchemaObject[]); } - objectType(type: TypeLabel): ObjectTypeEntry { - if (this.fromDB.objectTypes[type]) { - return this.fromDB.objectTypes[type]; - } else { - return this.fromEditor.objectTypes[type]; - } + entityTypes(): SchemaEntity[] { + return Object.values(this.fromDB.entities) + .concat(Object.values(this.fromEditor.entities)); } - getOwns(label: TypeLabel): TypeLabel[] { - const objectType = this.objectType(label); - return objectType ? objectType.owns : []; + relationTypes(): SchemaRelation[] { + return Object.values(this.fromDB.relations).concat(Object.values(this.fromEditor.relations)); } - getPlays(label: TypeLabel): TypeLabel[] { - const objectType = this.objectType(label); - return objectType ? objectType.plays : []; - } - getRelates(label: TypeLabel): TypeLabel[] { - const objectType = this.objectType(label); - return objectType ? objectType.relates : []; + attributeType(type: TypeLabel): SchemaAttribute { + return this.fromDB.attributes[type] ?? + this.fromEditor.attributes[type]; } -} -export class TypeQLAutocompleteSchemaImpl { - objectTypes: Record; - attributes: Record; - constructor( - objectTypes: Record, - attributes: Record, - ) { - this.attributes = attributes; - this.objectTypes = objectTypes; + objectType(type: TypeLabel): SchemaObject | null { + return this.entityType(type) ?? this.relationType(type); } - static fromTypeQL(text: string, tree: Tree) : TypeQLAutocompleteSchemaImpl { - let builder = new SchemaBuilder(); - // TODO: Replace iterate with a more targetted traversal that considers only define queries. - // Extract all type declarations from the tree - let root = tree.topNode; - let definitionTypes = nodesWithPath(root, [tokens.QuerySchema, tokens.QueryDefine, tokens.Definables, tokens.Definable, tokens.DefinitionType]) - definitionTypes.forEach(node => { - let kindNode = node.getChild(tokens.KIND); - let labelNode = node.getChild(tokens.LABEL); - if (kindNode != null && labelNode != null) { - let kind = extractText(text, kindNode.from, kindNode.to); - let label = extractText(text, labelNode.from, labelNode.to); - switch (kind) { - case "entity": { - builder.objectType(label); - break; - } - case "relation": { - builder.objectType(label); - break; - } - case "attribute": { - builder.attributeType(label); - break; - } - } - } - }) - - - // Extract owns/relates/plays. Idk what to do with sub or annotations. - definitionTypes.forEach(node => { - let labelNode = node.getChild(tokens.LABEL); - if (labelNode == null) return; - let label = extractText(text, labelNode.from, labelNode.to); - nodesWithPath(node, [tokens.TypeCapability, tokens.TypeCapabilityBase]) - .map(typeCapabilityBaseNode => typeCapabilityBaseNode.firstChild) - .forEach(actualCapabilityNode => { - switch (actualCapabilityNode?.type.id) { - // We actually only want type-declarations for now. - case tokens.RelatesDeclaration: { - let roleTypeNode = actualCapabilityNode.firstChild!.nextSibling!; - let roleType = extractText(text, roleTypeNode.from, roleTypeNode.to); - builder.recordRelates(label, `${label}:${roleType}`); - break; - } - default: { - // Ignore other capabilities for now - break; - } - } - }); - }); - return builder.build(); + entityType(type: TypeLabel): SchemaEntity | null { + return this.fromDB.entities[type] ?? this.fromEditor.entities[type]; } -} -export class SchemaBuilder { - objectTypes: Record; - attributes: Record; - - constructor() { - this.objectTypes = {}; - this.attributes = {}; + relationType(type: TypeLabel): SchemaRelation { + return this.fromDB.relations[type] ?? this.fromEditor.relations[type]; } - attributeType(type: TypeLabel): AttributeTypeEntry { - if (!this.attributes[type]) { - this.attributes[type] = { owners: [] }; - } - return this.attributes[type]; - } - - objectType(type: TypeLabel): ObjectTypeEntry { - if (!this.objectTypes[type]) { - this.objectTypes[type] = { owns: [], plays: [], relates: [] }; - } - return this.objectTypes[type]; + getOwns(label: TypeLabel): SchemaAttribute[] { + const objectType = this.objectType(label); + return objectType ? objectType.ownedAttributes : []; } - recordOwns(type: TypeLabel, ownedType: TypeLabel): void { - const objectType = this.objectType(type); - if (!objectType.owns.includes(ownedType)) { - objectType.owns.push(ownedType); - } - } - recordPlays(type: TypeLabel, playedType: TypeLabel): void { - const objectType = this.objectType(type); - if (!objectType.plays.includes(playedType)) { - objectType.plays.push(playedType); - } - } - recordRelates(type: TypeLabel, relatedType: TypeLabel): void { - const objectType = this.objectType(type); - if (!objectType.relates.includes(relatedType)) { - objectType.relates.push(relatedType); - } + getPlays(label: TypeLabel): SchemaRole[] { + const objectType = this.objectType(label); + return objectType ? objectType.playableRoles : []; } + getRelates(label: TypeLabel): SchemaRole[] { + const objectType = this.objectType(label); + return objectType ? objectType.playableRoles : []; + } + + // static fromTypeQL(text: string, tree: Tree) : TypeQLAutocompleteSchemaImpl { + // let builder = new SchemaBuilder(); + // // TODO: Replace iterate with a more targetted traversal that considers only define queries. + // // Extract all type declarations from the tree + // let root = tree.topNode; + // let definitionTypes = nodesWithPath(root, [tokens.QuerySchema, tokens.QueryDefine, tokens.Definables, tokens.Definable, tokens.DefinitionType]) + // definitionTypes.forEach(node => { + // let kindNode = node.getChild(tokens.KIND); + // let labelNode = node.getChild(tokens.LABEL); + // if (kindNode != null && labelNode != null) { + // let kind = extractText(text, kindNode.from, kindNode.to); + // let label = extractText(text, labelNode.from, labelNode.to); + // switch (kind) { + // case "entity": { + // builder.objectType(label); + // break; + // } + // case "relation": { + // builder.objectType(label); + // break; + // } + // case "attribute": { + // builder.attributeType(label); + // break; + // } + // } + // } + // }) + // + // + // // Extract owns/relates/plays. Idk what to do with sub or annotations. + // definitionTypes.forEach(node => { + // let labelNode = node.getChild(tokens.LABEL); + // if (labelNode == null) return; + // let label = extractText(text, labelNode.from, labelNode.to); + // nodesWithPath(node, [tokens.TypeCapability, tokens.TypeCapabilityBase]) + // .map(typeCapabilityBaseNode => typeCapabilityBaseNode.firstChild) + // .forEach(actualCapabilityNode => { + // switch (actualCapabilityNode?.type.id) { + // // We actually only want type-declarations for now. + // case tokens.RelatesDeclaration: { + // let roleTypeNode = actualCapabilityNode.firstChild!.nextSibling!; + // let roleType = extractText(text, roleTypeNode.from, roleTypeNode.to); + // builder.recordRelates(label, `${label}:${roleType}`); + // break; + // } + // default: { + // // Ignore other capabilities for now + // break; + // } + // } + // }); + // }); + // return builder.build(); + // } +} - build(): TypeQLAutocompleteSchemaImpl { - return new TypeQLAutocompleteSchemaImpl(this.objectTypes, this.attributes); - } +// +// export class SchemaBuilder { +// objectTypes: Record; +// attributes: Record; +// +// constructor() { +// this.objectTypes = {}; +// this.attributes = {}; +// } +// +// attributeType(type: TypeLabel): SchemaAttribute { +// if (!this.attributes[type]) { +// this.attributes[type] = { owners: [] }; +// } +// return this.attributes[type]; +// } +// +// objectType(type: TypeLabel): SchemaObject { +// if (!this.objectTypes[type]) { +// this.objectTypes[type] = { owns: [], plays: [], relates: [] }; +// } +// return this.objectTypes[type]; +// } +// +// recordOwns(type: TypeLabel, ownedType: TypeLabel): void { +// const objectType = this.objectType(type); +// if (!objectType.owns.includes(ownedType)) { +// objectType.owns.push(ownedType); +// } +// } +// recordPlays(type: TypeLabel, playedType: TypeLabel): void { +// const objectType = this.objectType(type); +// if (!objectType.plays.includes(playedType)) { +// objectType.plays.push(playedType); +// } +// } +// +// recordRelates(type: TypeLabel, relatedType: TypeLabel): void { +// const objectType = this.relationType(type); +// if (!objectType.roleplayers.includes(relatedType)) { +// objectType.roleplayers.push(relatedType); +// } +// } +// +// build(): TypeQLAutocompleteSchemaImpl { +// return new TypeQLAutocompleteSchemaImpl(this.objectTypes, this.attributes); +// } +// } + + +function buildSchemafromTypeQL(text: string, tree: Tree) : Schema { + // TODO; + return {entities: {}, relations: {}, attributes: {}}; } diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts index db0e192d..86f18a0f 100644 --- a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -28,7 +28,7 @@ function suggestAttributeTypeForHas(context: CompletionContext, tree: Tree, pars let possibleLabels = findIsaConstraintLabelsForVar(context, parseAt); if (possibleLabels.length > 0) { return possibleLabels.flatMap(owner => schema.getOwns(owner)) - .map((label) => suggest("AttributeType", label)) + .map((attributeType) => suggest("AttributeType", attributeType.label)) } else { return suggestAttributeTypeLabels(context, tree, parseAt, climbedTo, prefix, schema); } @@ -37,31 +37,31 @@ function suggestAttributeTypeForHas(context: CompletionContext, tree: Tree, pars function suggestRoleTypeForLinks(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { let possibleLabels = findIsaConstraintLabelsForVar(context, parseAt); if (possibleLabels.length > 0) { - return possibleLabels.flatMap(owner => schema.getRelates(owner)) - .map((label) => suggest("RoleType", label.split(":")[1])) + return possibleLabels.flatMap(relationType => schema.getRelates(relationType)) + .map((roleType) => suggest("RoleType", roleType.label.split(":")[1])) } else { - return suggestRoleTypeLabelsUnscoped(context, tree, parseAt, climbedTo, prefix, schema); + return suggestRelatedRoleTypeLabelsUnscoped(context, tree, parseAt, climbedTo, prefix, schema); } } function suggestAttributeTypeLabels(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { - return schema.attributeTypes().map((label) => { return suggest("AttributeType", label); }); + return schema.attributeTypes().map((attributeType) => { return suggest("AttributeType", attributeType.label); }); } function suggestObjectTypeLabels(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { - return schema.objectTypes().map((label) => suggest("ObjectType", label)); + return schema.objectTypes().map((objectType) => suggest("ObjectType", objectType.label)); } -function suggestRoleTypeLabelsScoped(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { +function suggestRoleTypesUnscopedForPlaysDeclaration(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return schema.objectTypes() - .flatMap((label) => schema.objectType(label).relates) - .map((label) => suggest("RoleType", label)); + .flatMap((objectType) => objectType.playableRoles) + .map((role) => suggest("RoleType", role.label)); } -function suggestRoleTypeLabelsUnscoped(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { - return schema.objectTypes() - .flatMap((label) => schema.objectType(label).relates) - .map((label) => suggest("RoleType", label.split(":")[1])); +function suggestRelatedRoleTypeLabelsUnscoped(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { + return schema.relationTypes() + .flatMap((relation) => relation.roleplayers) + .map((role) => suggest("RoleType", role.label.split(":")[1])); } function suggestThingTypeLabels(context: CompletionContext, tree: Tree, parseAt: SyntaxNode, climbedTo: SyntaxNode, prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { @@ -154,8 +154,8 @@ export const SUGGESTION_MAP: SuggestionMap = { { suffixes: [[tokens.SEMICOLON, tokens.TypeRef]], suggestions: [suggestTypeConstraintKeywords] }, { suffixes: [[tokens.OWNS]], suggestions: [suggestAttributeTypeLabels, suggestVariablesAtMinus10] }, { suffixes: [[tokens.SUB]], suggestions: [suggestThingTypeLabels, suggestVariablesAtMinus10] }, - { suffixes: [[tokens.RELATES]], suggestions: [suggestRoleTypeLabelsUnscoped] }, - { suffixes: [[tokens.PLAYS]], suggestions: [suggestRoleTypeLabelsScoped] }, + { suffixes: [[tokens.RELATES]], suggestions: [suggestRelatedRoleTypeLabelsUnscoped] }, + { suffixes: [[tokens.PLAYS]], suggestions: [suggestRoleTypesUnscopedForPlaysDeclaration] }, ], [tokens.ClauseMatch]: [ { suffixes: [[tokens.MATCH, tokens.TypeRef]], suggestions: [suggestTypeConstraintKeywords] }, @@ -177,8 +177,8 @@ export const SUGGESTION_MAP: SuggestionMap = { { suffixes: [[tokens.COMMA], [tokens.KIND, tokens.LABEL]], suggestions: [ suggestTypeConstraintKeywords ] }, { suffixes: [[tokens.OWNS]], suggestions: [suggestAttributeTypeLabels] }, { suffixes: [[tokens.SUB]], suggestions: [suggestThingTypeLabels] }, - { suffixes: [ [tokens.PLAYS] ], suggestions: [ suggestRoleTypeLabelsScoped ] }, - { suffixes: [ [tokens.RELATES] ], suggestions: [ suggestRoleTypeLabelsUnscoped ] }, + { suffixes: [ [tokens.PLAYS] ], suggestions: [ suggestRoleTypesUnscopedForPlaysDeclaration ] }, + { suffixes: [ [tokens.RELATES] ], suggestions: [ suggestRelatedRoleTypeLabelsUnscoped ] }, ], // TODO: ... }; diff --git a/src/service/schema-state.service.ts b/src/service/schema-state.service.ts index ad17e5b4..d99c8596 100644 --- a/src/service/schema-state.service.ts +++ b/src/service/schema-state.service.ts @@ -30,14 +30,14 @@ const schemaQueriesList = Object.values(schemaQueries); type VisualiserStatus = "ok" | "running" | "noAnswers" | "error"; -interface SchemaEntity extends EntityType { +export interface SchemaEntity extends EntityType { supertype?: SchemaEntity; subtypes: SchemaEntity[]; ownedAttributes: SchemaAttribute[]; playedRoles: SchemaRole[]; } -interface SchemaRelation extends RelationType { +export interface SchemaRelation extends RelationType { supertype?: SchemaRelation; subtypes: SchemaRelation[]; ownedAttributes: SchemaAttribute[]; From 79d49e5a24f38d742e7c7cbe8ab71030d2017342 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 18 Jun 2025 19:35:14 +0530 Subject: [PATCH 13/19] Update deriving partial schema from text editor state --- .../typeQLAutocompleteSchema.ts | 246 +++++++++--------- 1 file changed, 130 insertions(+), 116 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts index edb1d156..44d7f204 100644 --- a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts +++ b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts @@ -7,8 +7,7 @@ import { SchemaAttribute, SchemaRole, SchemaEntity, - SchemaRelation, - SchemaConcept + SchemaRelation } from "../../service/schema-state.service"; type SchemaObject = SchemaEntity | SchemaRelation; @@ -18,13 +17,9 @@ function extractText(text: string, from: number, to: number): string { return text.slice(from, to); } -function labels(types: SchemaConcept[]): string[] { - return types.map(type => type.label); -} - export class TypeQLAutocompleteSchema { fromDB: Schema; - fromEditor: Schema; + fromEditor: Schema; // Partial because we don't care about subtypes, supertype or valueType constructor(fromDB: Schema, fromEditor: Schema) { this.fromDB = fromDB; @@ -40,7 +35,7 @@ export class TypeQLAutocompleteSchema { } attributeTypes(): SchemaAttribute[] { - return Object.values(this.fromDB.attributes).concat(Object.values(this.fromEditor.attributes)); + return record_values(this.fromDB.attributes).concat(record_values(this.fromEditor.attributes)); } objectTypes(): SchemaObject[] { @@ -48,12 +43,12 @@ export class TypeQLAutocompleteSchema { } entityTypes(): SchemaEntity[] { - return Object.values(this.fromDB.entities) - .concat(Object.values(this.fromEditor.entities)); + return record_values(this.fromDB.entities) + .concat(record_values(this.fromEditor.entities)); } relationTypes(): SchemaRelation[] { - return Object.values(this.fromDB.relations).concat(Object.values(this.fromEditor.relations)); + return record_values(this.fromDB.relations).concat(record_values(this.fromEditor.relations)); } attributeType(type: TypeLabel): SchemaAttribute { @@ -86,115 +81,134 @@ export class TypeQLAutocompleteSchema { const objectType = this.objectType(label); return objectType ? objectType.playableRoles : []; } +} + +export class SchemaBuilder { + schema: Schema; + constructor() { + this.schema = { entities: {}, relations:{}, attributes: {} }; + } + + entityType(label: TypeLabel): SchemaEntity { + if (!this.schema.entities[label]) { + this.schema.entities[label] = { + kind: "entityType", label, ownedAttributes: [], playableRoles: [], + subtypes: [], + }; + } + return this.schema.entities[label]; + } + + relationType(label: TypeLabel): SchemaRelation { + if (!this.schema.relations[label]) { + this.schema.relations[label] = { + kind: "relationType", label, ownedAttributes: [], playableRoles: [], roleplayers: [], + subtypes: [], + }; + } + return this.schema.relations[label]; + } + + attributeType(label: TypeLabel): SchemaAttribute { + if (!this.schema.attributes[label]) { + this.schema.attributes[label] = { + kind: "attributeType", label, valueType: "string", + subtypes: [], + }; + } + return this.schema.attributes[label]; + } - // static fromTypeQL(text: string, tree: Tree) : TypeQLAutocompleteSchemaImpl { - // let builder = new SchemaBuilder(); - // // TODO: Replace iterate with a more targetted traversal that considers only define queries. - // // Extract all type declarations from the tree - // let root = tree.topNode; - // let definitionTypes = nodesWithPath(root, [tokens.QuerySchema, tokens.QueryDefine, tokens.Definables, tokens.Definable, tokens.DefinitionType]) - // definitionTypes.forEach(node => { - // let kindNode = node.getChild(tokens.KIND); - // let labelNode = node.getChild(tokens.LABEL); - // if (kindNode != null && labelNode != null) { - // let kind = extractText(text, kindNode.from, kindNode.to); - // let label = extractText(text, labelNode.from, labelNode.to); - // switch (kind) { - // case "entity": { - // builder.objectType(label); - // break; - // } - // case "relation": { - // builder.objectType(label); - // break; - // } - // case "attribute": { - // builder.attributeType(label); - // break; - // } - // } - // } - // }) - // - // - // // Extract owns/relates/plays. Idk what to do with sub or annotations. - // definitionTypes.forEach(node => { - // let labelNode = node.getChild(tokens.LABEL); - // if (labelNode == null) return; - // let label = extractText(text, labelNode.from, labelNode.to); - // nodesWithPath(node, [tokens.TypeCapability, tokens.TypeCapabilityBase]) - // .map(typeCapabilityBaseNode => typeCapabilityBaseNode.firstChild) - // .forEach(actualCapabilityNode => { - // switch (actualCapabilityNode?.type.id) { - // // We actually only want type-declarations for now. - // case tokens.RelatesDeclaration: { - // let roleTypeNode = actualCapabilityNode.firstChild!.nextSibling!; - // let roleType = extractText(text, roleTypeNode.from, roleTypeNode.to); - // builder.recordRelates(label, `${label}:${roleType}`); - // break; - // } - // default: { - // // Ignore other capabilities for now - // break; - // } - // } - // }); - // }); - // return builder.build(); - // } + getObjectType(label: TypeLabel): SchemaObject | null{ + return this.schema.entities[label] ?? this.schema.relations[label]; + } + + recordOwns(type: TypeLabel, ownedType: TypeLabel): void { + const objectType = this.getObjectType(type)!; + const attributeType = this.attributeType(ownedType); + if (!objectType.ownedAttributes.includes(attributeType)) { + objectType.ownedAttributes.push(attributeType); + } + } + recordPlays(type: TypeLabel, playedType: TypeLabel): void { + const objectType = this.getObjectType(type)!; + let roleType: SchemaRole = { kind: "roleType", label: playedType }; + if (!objectType.playableRoles.includes(roleType)) { + objectType.playableRoles.push(roleType); + } + } + + recordRelates(type: TypeLabel, relatedType: TypeLabel): void { + const objectType = this.relationType(type); + let roleType: SchemaRole = { kind: "roleType", label: relatedType }; + if (!objectType.roleplayers.includes(roleType)) { + objectType.roleplayers.push(roleType); + } + } + + build(): Schema { + return this.schema; + } } -// -// export class SchemaBuilder { -// objectTypes: Record; -// attributes: Record; -// -// constructor() { -// this.objectTypes = {}; -// this.attributes = {}; -// } -// -// attributeType(type: TypeLabel): SchemaAttribute { -// if (!this.attributes[type]) { -// this.attributes[type] = { owners: [] }; -// } -// return this.attributes[type]; -// } -// -// objectType(type: TypeLabel): SchemaObject { -// if (!this.objectTypes[type]) { -// this.objectTypes[type] = { owns: [], plays: [], relates: [] }; -// } -// return this.objectTypes[type]; -// } -// -// recordOwns(type: TypeLabel, ownedType: TypeLabel): void { -// const objectType = this.objectType(type); -// if (!objectType.owns.includes(ownedType)) { -// objectType.owns.push(ownedType); -// } -// } -// recordPlays(type: TypeLabel, playedType: TypeLabel): void { -// const objectType = this.objectType(type); -// if (!objectType.plays.includes(playedType)) { -// objectType.plays.push(playedType); -// } -// } -// -// recordRelates(type: TypeLabel, relatedType: TypeLabel): void { -// const objectType = this.relationType(type); -// if (!objectType.roleplayers.includes(relatedType)) { -// objectType.roleplayers.push(relatedType); -// } -// } -// -// build(): TypeQLAutocompleteSchemaImpl { -// return new TypeQLAutocompleteSchemaImpl(this.objectTypes, this.attributes); -// } -// } function buildSchemafromTypeQL(text: string, tree: Tree) : Schema { - // TODO; - return {entities: {}, relations: {}, attributes: {}}; + let builder = new SchemaBuilder(); + // TODO: Replace iterate with a more targetted traversal that considers only define queries. + // Extract all type declarations from the tree + let root = tree.topNode; + let definitionTypes = nodesWithPath(root, [tokens.QuerySchema, tokens.QueryDefine, tokens.Definables, tokens.Definable, tokens.DefinitionType]) + definitionTypes.forEach(node => { + let kindNode = node.getChild(tokens.KIND); + let labelNode = node.getChild(tokens.LABEL); + if (kindNode != null && labelNode != null) { + let kind = extractText(text, kindNode.from, kindNode.to); + let label = extractText(text, labelNode.from, labelNode.to); + switch (kind) { + case "entity": { + builder.entityType(label); + break; + } + case "relation": { + builder.relationType(label); + break; + } + case "attribute": { + builder.attributeType(label); + break; + } + } + } + }) + + + // Extract owns/relates/plays. Idk what to do with sub or annotations. + definitionTypes.forEach(node => { + let labelNode = node.getChild(tokens.LABEL); + if (labelNode == null) return; + let label = extractText(text, labelNode.from, labelNode.to); + nodesWithPath(node, [tokens.TypeCapability, tokens.TypeCapabilityBase]) + .map(typeCapabilityBaseNode => typeCapabilityBaseNode.firstChild) + .forEach(actualCapabilityNode => { + switch (actualCapabilityNode?.type.id) { + // We actually only want type-declarations for now. + case tokens.RelatesDeclaration: { + let roleTypeNode = actualCapabilityNode.firstChild!.nextSibling!; + let roleType = extractText(text, roleTypeNode.from, roleTypeNode.to); + builder.recordRelates(label, `${label}:${roleType}`); + break; + } + default: { + // Ignore other capabilities for now + break; + } + } + }); + }); + return builder.build(); +} + +function record_values(records: Record): Y[] { + return Object.entries(records).map(([_, value]) => value); } From 0c7cd2065cbcc42fbb7e8fcdd9e32e2f5ce20b07 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 18 Jun 2025 19:39:09 +0530 Subject: [PATCH 14/19] Plug in schema from schema state to autocomplete --- src/framework/codemirror-lang-typeql/index.ts | 2 +- src/service/schema-state.service.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index baa2cdee..75a67019 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -137,7 +137,7 @@ export function typeqlAutocompleteExtension() { return autocompletion({ override: [wrappedAutocomplete] }); } -function updateSchemaFromDB(schema: Schema) { +export function updateAutocomleteSchemaFromDB(schema: Schema) { typeqlAutocomplete.getState().updateFromDB(schema); } diff --git a/src/service/schema-state.service.ts b/src/service/schema-state.service.ts index d99c8596..4e84499f 100644 --- a/src/service/schema-state.service.ts +++ b/src/service/schema-state.service.ts @@ -16,6 +16,7 @@ import { AttributeType, EntityType, RelationType, RoleType, Type } from "../fram import { ApiOkResponse, ApiResponse, ConceptRowsQueryResponse, isApiErrorResponse, QueryResponse } from "../framework/typedb-driver/response"; import { DriverState } from "./driver-state.service"; import { SnackbarService } from "./snackbar.service"; +import {updateAutocomleteSchemaFromDB} from "../framework/codemirror-lang-typeql"; const NO_SERVER_CONNECTED = `No server connected`; const NO_DATABASE_SELECTED = `No database selected`; @@ -86,6 +87,11 @@ export class SchemaState { this.queryResponses$.subscribe(data => { this.push(data); }); + this.value$.subscribe(schema => { + if (schema != null) { + updateAutocomleteSchemaFromDB(schema) + } + }) } refresh() { From edca4e11e8d878904ec7ebe3110dfb729ded8952 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 18 Jun 2025 19:42:45 +0530 Subject: [PATCH 15/19] Small cleanup of todos and unused imports --- src/framework/codemirror-lang-typeql/index.ts | 2 -- .../codemirror-lang-typeql/typeQLAutocompleteSchema.ts | 1 - src/framework/codemirror-lang-typeql/typeql_suggestions.ts | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index 75a67019..3a99878f 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -9,8 +9,6 @@ import { autocompletion, CompletionContext } from "@codemirror/autocomplete"; import { NodePrefixAutoComplete } from "./complete" import {TypeQLAutocompleteSchema} from "./typeQLAutocompleteSchema"; import { SUGGESTION_MAP } from "./typeql_suggestions"; -import {ConceptRow, ConceptRowAnswer} from "../typedb-driver/response"; -import {Type} from "../typedb-driver/concept"; import {Schema} from "../../service/schema-state.service"; export const TypeQLLanguage = LRLanguage.define({ diff --git a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts index 44d7f204..dcdba578 100644 --- a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts +++ b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts @@ -155,7 +155,6 @@ export class SchemaBuilder { function buildSchemafromTypeQL(text: string, tree: Tree) : Schema { let builder = new SchemaBuilder(); - // TODO: Replace iterate with a more targetted traversal that considers only define queries. // Extract all type declarations from the tree let root = tree.topNode; let definitionTypes = nodesWithPath(root, [tokens.QuerySchema, tokens.QueryDefine, tokens.Definables, tokens.Definable, tokens.DefinitionType]) diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts index 86f18a0f..ac0d2b36 100644 --- a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -71,7 +71,7 @@ function suggestThingTypeLabels(context: CompletionContext, tree: Tree, parseAt: } function suggestVariables(context: CompletionContext, tree: Tree, boost= 0): Completion[] { - var options: Completion[] = []; + let options: Completion[] = []; tree.iterate({ enter: (other: SyntaxNode) => { if (other.type.id == tokens.VAR) { @@ -180,5 +180,5 @@ export const SUGGESTION_MAP: SuggestionMap = { { suffixes: [ [tokens.PLAYS] ], suggestions: [ suggestRoleTypesUnscopedForPlaysDeclaration ] }, { suffixes: [ [tokens.RELATES] ], suggestions: [ suggestRelatedRoleTypeLabelsUnscoped ] }, ], - // TODO: ... + // TODO: Add any cases krishnan didn't think of. }; From a7ce29cbaead137ad0141943fa221e47c7f86039 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 18 Jun 2025 19:45:42 +0530 Subject: [PATCH 16/19] Move lezer to the appropriate line in devDependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 31ff0728..f630a655 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@codemirror/language": "6.11.0", "@codemirror/state": "6.5.2", "@codemirror/view": "6.36.5", + "@lezer/generator": "1.7.3", "@lezer/highlight": "1.2.1", "@sanity/asset-utils": "1.3.0", "@sanity/icons": "3.4.0", @@ -72,8 +73,7 @@ "netlify-plugin-discord": "0.0.2", "typescript": "5.8.3", "unplugin-lezer": "1.0.1", - "@tauri-apps/cli": "^2", - "@lezer/generator": "1.7.3" + "@tauri-apps/cli": "^2" }, "browserslist": [ "defaults and fully supports es6-module" From 0f6b70863a79346d165a4cac875e71ab8d78316f Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 18 Jun 2025 23:51:18 +0530 Subject: [PATCH 17/19] Update names for rebase --- .../typeQLAutocompleteSchema.ts | 16 ++++++++-------- .../codemirror-lang-typeql/typeql_suggestions.ts | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts index dcdba578..0db4eba8 100644 --- a/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts +++ b/src/framework/codemirror-lang-typeql/typeQLAutocompleteSchema.ts @@ -75,11 +75,11 @@ export class TypeQLAutocompleteSchema { getPlays(label: TypeLabel): SchemaRole[] { const objectType = this.objectType(label); - return objectType ? objectType.playableRoles : []; + return objectType ? objectType.playedRoles : []; } getRelates(label: TypeLabel): SchemaRole[] { const objectType = this.objectType(label); - return objectType ? objectType.playableRoles : []; + return objectType ? objectType.playedRoles : []; } } @@ -92,7 +92,7 @@ export class SchemaBuilder { entityType(label: TypeLabel): SchemaEntity { if (!this.schema.entities[label]) { this.schema.entities[label] = { - kind: "entityType", label, ownedAttributes: [], playableRoles: [], + kind: "entityType", label, ownedAttributes: [], playedRoles: [], subtypes: [], }; } @@ -102,7 +102,7 @@ export class SchemaBuilder { relationType(label: TypeLabel): SchemaRelation { if (!this.schema.relations[label]) { this.schema.relations[label] = { - kind: "relationType", label, ownedAttributes: [], playableRoles: [], roleplayers: [], + kind: "relationType", label, ownedAttributes: [], playedRoles: [], relatedRoles: [], subtypes: [], }; } @@ -133,16 +133,16 @@ export class SchemaBuilder { recordPlays(type: TypeLabel, playedType: TypeLabel): void { const objectType = this.getObjectType(type)!; let roleType: SchemaRole = { kind: "roleType", label: playedType }; - if (!objectType.playableRoles.includes(roleType)) { - objectType.playableRoles.push(roleType); + if (!objectType.playedRoles.includes(roleType)) { + objectType.playedRoles.push(roleType); } } recordRelates(type: TypeLabel, relatedType: TypeLabel): void { const objectType = this.relationType(type); let roleType: SchemaRole = { kind: "roleType", label: relatedType }; - if (!objectType.roleplayers.includes(roleType)) { - objectType.roleplayers.push(roleType); + if (!objectType.relatedRoles.includes(roleType)) { + objectType.relatedRoles.push(roleType); } } diff --git a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts index ac0d2b36..021d4ffd 100644 --- a/src/framework/codemirror-lang-typeql/typeql_suggestions.ts +++ b/src/framework/codemirror-lang-typeql/typeql_suggestions.ts @@ -54,13 +54,13 @@ function suggestObjectTypeLabels(_context: CompletionContext, _tree: Tree, _pars function suggestRoleTypesUnscopedForPlaysDeclaration(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return schema.objectTypes() - .flatMap((objectType) => objectType.playableRoles) + .flatMap((objectType) => objectType.playedRoles) .map((role) => suggest("RoleType", role.label)); } function suggestRelatedRoleTypeLabelsUnscoped(_context: CompletionContext, _tree: Tree, _parseAt: SyntaxNode, _climbedTo: SyntaxNode, _prefix: NodeType[], schema: TypeQLAutocompleteSchema): Completion[] { return schema.relationTypes() - .flatMap((relation) => relation.roleplayers) + .flatMap((relation) => relation.relatedRoles) .map((role) => suggest("RoleType", role.label.split(":")[1])); } From 6b76ebb1e41388cbb007128e84edee0bb6e74b40 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Mon, 23 Jun 2025 16:22:55 +0530 Subject: [PATCH 18/19] run pnpm install --- pnpm-lock.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63ac5472..550b73c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,12 @@ importers: '@angular/router': specifier: 20.0.3 version: 20.0.3(@angular/common@20.0.3(@angular/core@20.0.3(@angular/compiler@20.0.3)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.0.3(@angular/compiler@20.0.3)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.0.3(@angular/animations@20.0.3(@angular/common@20.0.3(@angular/core@20.0.3(@angular/compiler@20.0.3)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.0.3(@angular/compiler@20.0.3)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.0.3(@angular/core@20.0.3(@angular/compiler@20.0.3)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.0.3(@angular/compiler@20.0.3)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@codemirror/autocomplete': + specifier: 6.18.6 + version: 6.18.6 + '@codemirror/commands': + specifier: 6.8.1 + version: 6.8.1 '@codemirror/lint': specifier: 6.8.5 version: 6.8.5 @@ -56,6 +62,9 @@ importers: '@intercom/messenger-js-sdk': specifier: 0.0.14 version: 0.0.14 + '@lezer/common': + specifier: ^1.0.0 + version: 1.2.3 '@lezer/lr': specifier: 1.4.2 version: 1.4.2 @@ -129,6 +138,9 @@ importers: '@codemirror/view': specifier: 6.36.5 version: 6.36.5 + '@lezer/generator': + specifier: 1.7.3 + version: 1.7.3 '@lezer/highlight': specifier: 1.2.1 version: 1.2.1 From d9c63889e7b7092df4c95dd7ecbc69bc3f6a5264 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Mon, 23 Jun 2025 18:01:33 +0530 Subject: [PATCH 19/19] Cleanup comment from index.ts --- src/framework/codemirror-lang-typeql/index.ts | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/src/framework/codemirror-lang-typeql/index.ts b/src/framework/codemirror-lang-typeql/index.ts index 3a99878f..bf8d31da 100644 --- a/src/framework/codemirror-lang-typeql/index.ts +++ b/src/framework/codemirror-lang-typeql/index.ts @@ -139,48 +139,5 @@ export function updateAutocomleteSchemaFromDB(schema: Schema) { typeqlAutocomplete.getState().updateFromDB(schema); } -// Manually run and collect the following results using _lastQueryAnswers -// match $default-owner owns $default-owned; limit 1; -// match $default-relation relates $default-related; limit 1; -// match $default-player plays $default-played; limit 1; -// match -// { $owner owns $owned; $relation is $default-relation; $related is $default-related; $player is $default-player; $played is $default-played; } or -// { $owner is $default-owner; $owned is $default-owned; $relation relates $related; $player is $default-player; $played is $default-played; } or -// { $owner is $default-owner; $owned is $default-owned; $relation is $default-relation; $related is $default-related; $player plays $played; }; -// and then call _updateSchemaFromDB(_lastQueryAnswers, _lastQueryAnswers, _lastQueryAnswers); -// -// // Or finer queries: -// // # match $owner owns $owned; -// // # match $relation relates $related; -// // # match $player plays $played; -// // And you have to set each argument separately -// -// function updateSchemaFromDB(ownsAnswers: ConceptRowAnswer[], relatesAnswers: ConceptRowAnswer[], playsAnswers: ConceptRowAnswer[]) { -// let builder = new SchemaBuilder(); -// ownsAnswers.forEach((answer) => { -// let data: ConceptRow = answer.data; -// let owner = (data["owner"] as Type).label; -// let owned = (data["owned"] as Type).label; -// builder.objectType(owner); -// builder.attributeType(owned); -// builder.recordOwns(owner, owned); -// }); -// relatesAnswers.forEach((answer) => { -// let data: ConceptRow = answer.data; -// let relation = (data["relation"] as Type).label; -// let related = (data["related"] as Type).label; -// builder.objectType(relation); -// builder.recordRelates(relation, related) -// }); -// playsAnswers.forEach((answer) => { -// let data: ConceptRow = answer.data; -// let player = (data["player"] as Type).label; -// let played = (data["played"] as Type).label; -// builder.objectType(player); -// builder.recordRelates(player, played) -// }) -// typeqlAutocomplete.getState().updateFromDB(builder.build()); -// } - // (window as any)._updateSchemaFromDB = updateSchemaFromDB; -(window as any)._typeqlAutoComplete = typeqlAutocomplete; \ No newline at end of file +(window as any)._typeqlAutoComplete = typeqlAutocomplete;