@cratis/screenplay-language 4.55.1 → 4.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnalysisCommand.d.ts +16 -0
- package/dist/AnalysisCommand.d.ts.map +1 -0
- package/dist/AnalysisDiagnostic.d.ts +8 -0
- package/dist/AnalysisDiagnostic.d.ts.map +1 -0
- package/dist/AnalysisLocation.d.ts +6 -0
- package/dist/AnalysisLocation.d.ts.map +1 -0
- package/dist/AnalysisSpecification.d.ts +16 -0
- package/dist/AnalysisSpecification.d.ts.map +1 -0
- package/dist/AnalysisType.d.ts +6 -0
- package/dist/AnalysisType.d.ts.map +1 -0
- package/dist/AuthoringDocument.d.ts +6 -0
- package/dist/AuthoringDocument.d.ts.map +1 -0
- package/dist/CommandResponseSymbol.d.ts +13 -0
- package/dist/CommandResponseSymbol.d.ts.map +1 -0
- package/dist/ResponseAnalysis.d.ts +9 -0
- package/dist/ResponseAnalysis.d.ts.map +1 -0
- package/dist/ResponseFieldSymbol.d.ts +10 -0
- package/dist/ResponseFieldSymbol.d.ts.map +1 -0
- package/dist/ResponseSourceSymbol.d.ts +6 -0
- package/dist/ResponseSourceSymbol.d.ts.map +1 -0
- package/dist/bundles/chunk-TWF2T5S4.js +13 -0
- package/dist/bundles/chunk-TWF2T5S4.js.map +7 -0
- package/dist/bundles/index.js +18 -1362
- package/dist/bundles/index.js.map +4 -4
- package/dist/bundles/sub-languages/capture/index.js +35 -672
- package/dist/bundles/sub-languages/capture/index.js.map +1 -1
- package/dist/bundles/sub-languages/projection/index.js +53 -1625
- package/dist/bundles/sub-languages/projection/index.js.map +1 -1
- package/dist/completion-items.d.ts.map +1 -1
- package/dist/completion-planner.d.ts.map +1 -1
- package/dist/completions.d.ts +2 -0
- package/dist/completions.d.ts.map +1 -1
- package/dist/diagnostic-codes.d.ts +11 -0
- package/dist/diagnostic-codes.d.ts.map +1 -1
- package/dist/hover-content.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/response-analysis.d.ts +15 -0
- package/dist/response-analysis.d.ts.map +1 -0
- package/dist/response-completions.d.ts +4 -0
- package/dist/response-completions.d.ts.map +1 -0
- package/dist/response-tokens.d.ts +8 -0
- package/dist/response-tokens.d.ts.map +1 -0
- package/dist/symbols.d.ts +11 -0
- package/dist/symbols.d.ts.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/validation.d.ts +3 -0
- package/dist/validation.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/bundles/chunk-LXM5L4YK.js +0 -4893
- package/dist/bundles/chunk-LXM5L4YK.js.map +0 -7
|
@@ -1,1647 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
eventContextMemberAt,
|
|
5
|
-
eventContextMembers,
|
|
6
|
-
eventContextMembersAfter,
|
|
7
|
-
fenceMap,
|
|
8
|
-
validateEventContextPaths
|
|
9
|
-
} from "../../chunk-LXM5L4YK.js";
|
|
1
|
+
import{describeEventContext,describeEventContextMember,eventContextMemberAt,eventContextMembers,eventContextMembersAfter,fenceMap,validateEventContextPaths}from"../../chunk-TWF2T5S4.js";var languageId="pdl",KEYWORDS=["projection","sequence","all","every","on","from","key","parent","join","automap","no","children","nested","identified","by","add","subtract","remove","via","with","default","increment","decrement","count","set","unset","clear","events","id","exclude","variant","enters"],BUILTINS=["$eventSourceId","$causedBy","$eventContext"],OPERATORS=["=","+=","-=","=>","."],configuration={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"`",close:"`"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"`",close:"`"}],indentationRules:{increaseIndentPattern:/^.*(:|\bon\b|\ball\b|\bevery\b|\bchildren\b|\bnested\b|\bparent\b|\bvariant\b)\s*$/,decreaseIndentPattern:/^.*\}.*$/},folding:{offSide:!0,markers:{start:/^\s*(projection|all|every|on|children|nested|parent|variant)/,end:/^\s*$/}},wordPattern:/@?[a-zA-Z_$][\w$-]*/},monarchLanguage={defaultToken:"",tokenPostfix:".pdl",keywords:KEYWORDS,builtins:BUILTINS,operators:OPERATORS,symbols:/[=><!~?:&|+\-*/^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[{include:"@whitespace"},[/`/,{token:"string.template",next:"@templateString"}],[/@[a-zA-Z_$][\w$]*/,"identifier.escape"],[/[A-Z][a-zA-Z_$][\w$]*/,{cases:{"@keywords":"invalid","@default":"identifier"}}],[/[a-z_$][\w$]*/,{cases:{"@keywords":"keyword","@builtins":"variable.predefined","@default":"identifier"}}],[/[{}()[\]]/,"@brackets"],[/=>/,"operator.arrow"],[/@symbols/,{cases:{"@operators":"operator","@default":""}}],[/\d*\.\d+([eE][-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F]+/,"number.hex"],[/\d+/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,{token:"string.quote",next:"@string"}]],whitespace:[[/[ \t\r\n]+/,""],[/#.*$/,"comment"]],templateString:[[/\$\{/,{token:"delimiter.bracket",next:"@templateExpression"}],[/[^\\`$]+/,"string.template"],[/@escapes/,"string.template.escape"],[/\\./,"string.template.escape.invalid"],[/`/,{token:"string.template",next:"@pop"}]],templateExpression:[[/\}/,{token:"delimiter.bracket",next:"@pop"}],{include:"@root"}],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,{token:"string.quote",next:"@pop"}]]}};var EVENT_CONTEXT_PATH=/\$eventContext\.((?:\w+\.)*)(\w*)$/,CompletionProvider=class{readModels=[];eventSchemas={};eventSequences=[];setReadModels(readModels){this.readModels=readModels||[]}setEventSequences(sequences){this.eventSequences=sequences||[]}setReadModelSchemas(schemas){this.readModels=schemas.map(schema=>({identifier:this.getSchemaName(schema)||"",displayName:this.getSchemaName(schema)||"",schema}))}setEventSchemas(schemas){if(!schemas){this.eventSchemas={};return}if(Array.isArray(schemas)){let map={};schemas.forEach((s,i)=>{if(!s)return;let name=this.getSchemaName(s)||`Event${i+1}`;map[name]=s}),this.eventSchemas=map;return}this.eventSchemas=schemas}provideCompletionItems(model,position){let context=this.buildContext(model,position),suggestions=[],activeSchema=this.getActiveReadModelSchema(model);return context.lineNumber===1?this.addProjectionLineCompletions(suggestions,context):this.isAfterKeyword(context,"sequence")?this.addEventSequenceCompletions(suggestions,context):this.isAfterKeyword(context,"from")||this.isAfterKeyword(context,"every")?this.addEventTypeCompletions(suggestions,context):/\benters\s+on\s+\w*$/.test(context.textBeforeCursor)?this.addEventTypeCompletions(suggestions,context):/\benters\s+\w*$/.test(context.textBeforeCursor)?suggestions.push({label:"on",kind:14,insertText:"on ",documentation:"Declares the event that activates this variant.",detail:"enters on <EventType>",range:this.getRangeForWord(context)}):this.isAfterKeyword(context,"variant")?this.addVariantNameCompletions(suggestions,context):this.isAfterKeyword(context,"key")?this.addKeyCompletions(suggestions,context,activeSchema):this.isAfterKeyword(context,"parent")?this.addParentCompletions(suggestions,context):this.isAfterKeyword(context,"children")?this.addChildrenCompletions(suggestions,context,activeSchema):this.isAfterKeyword(context,"join")?this.addJoinCompletions(suggestions,context,activeSchema):this.isAfterKeyword(context,"with")?this.addEventTypeCompletions(suggestions,context):this.isAfterNumericOperationKeyword(context)?this.addNumericPropertyCompletions(suggestions,context,activeSchema):this.isAfterAddOrSubtractBy(context)?this.addAssignmentValueCompletions(suggestions,context,model,position):this.isInAssignment(context)?this.addAssignmentValueCompletions(suggestions,context,model,position):this.isPropertyDotAccess(context)?this.addPropertyMemberCompletions(suggestions,context):(this.isStartOfLine(context)||this.isAfterIndent(context))&&this.addStatementCompletions(suggestions,context,activeSchema),{suggestions}}buildContext(model,position){let lineContent=model.getLineContent(position.lineNumber),textBeforeCursor=lineContent.substring(0,position.column-1),textAfterCursor=lineContent.substring(position.column-1),currentWord=model.getWordAtPosition(position)?.word||"",indentLevel=this.getIndentLevel(textBeforeCursor);return{lineContent,textBeforeCursor,textAfterCursor,currentWord,indentLevel,lineNumber:position.lineNumber}}getIndentLevel(text){let match=text.match(/^(\s*)/);return match?Math.floor(match[1].length/2):0}isStartOfLine(context){return context.textBeforeCursor.trim()===""}isAfterIndent(context){return/^\s+$/.test(context.textBeforeCursor)}isAfterKeyword(context,keyword){return new RegExp(`\\b${keyword}\\s+\\w*$`).test(context.textBeforeCursor)}isAfterNumericOperationKeyword(context){let countIncrDecrPattern=/\b(count|increment|decrement)\s+\w*$/,addSubtractPattern=/\b(add|subtract)\s+\w*$/;return countIncrDecrPattern.test(context.textBeforeCursor)?!0:addSubtractPattern.test(context.textBeforeCursor)?!this.isAfterAddOrSubtractBy(context):!1}isAfterAddOrSubtractBy(context){return/\b(add|subtract)\s+\w+\s+by\s+\w*$/.test(context.textBeforeCursor)}isInAssignment(context){return/\w+\s*=\s*\w*$/.test(context.textBeforeCursor)}isPropertyDotAccess(context){return/(\w+|\$\w+)\.(\w*)$/.test(context.textBeforeCursor)||EVENT_CONTEXT_PATH.test(context.textBeforeCursor)}addProjectionLineCompletions(suggestions,context){let trimmed=context.textBeforeCursor.trim();(trimmed===""||"projection".startsWith(trimmed))&&suggestions.push({label:"projection",kind:14,insertText:"projection ",documentation:"Define a projection that transforms events into a read model",detail:"projection <ProjectionName>",range:this.getRangeForWord(context)});let arrowMatch=trimmed.match(/^projection\s+[\w.]+\s*=>\s*([\w.]*)$/);if(arrowMatch){let partialReadModel=arrowMatch[1]||"";this.readModels.forEach(readModel=>{if(!partialReadModel||readModel.displayName.toLowerCase().startsWith(partialReadModel.toLowerCase())){let label=`${readModel.displayName} (${readModel.identifier})`;suggestions.push({label,kind:6,insertText:readModel.displayName,filterText:`${readModel.displayName} ${readModel.identifier}`,documentation:`Read model: ${readModel.displayName} (${readModel.identifier})`,detail:readModel.identifier,range:this.getRangeForWord(context)})}});return}if(trimmed.startsWith("projection ")&&!trimmed.includes("=>")){let afterProjection=trimmed.substring(11);this.readModels.forEach(readModel=>{if(!afterProjection||readModel.displayName.toLowerCase().startsWith(afterProjection.toLowerCase())){let label=`${readModel.displayName} (${readModel.identifier})`;suggestions.push({label,kind:6,insertText:`${readModel.displayName} => ${readModel.displayName}`,filterText:`${readModel.displayName} ${readModel.identifier}`,documentation:`Read model: ${readModel.displayName} (${readModel.identifier})`,detail:readModel.identifier,range:this.getRangeForWord(context)})}})}}addEventTypeCompletions(suggestions,context){Object.keys(this.eventSchemas).forEach(eventName=>{if(!context.currentWord||eventName.startsWith(context.currentWord)){let schema=this.eventSchemas[eventName];suggestions.push({label:eventName,kind:6,insertText:eventName,documentation:this.getSchemaDescription(schema)||`Event type: ${eventName}`,detail:"Event Type",range:this.getRangeForWord(context)})}})}addEventSequenceCompletions(suggestions,context){this.eventSequences.forEach(sequenceId=>{(!context.currentWord||sequenceId.startsWith(context.currentWord))&&suggestions.push({label:sequenceId,kind:13,insertText:sequenceId,documentation:`Event sequence: ${sequenceId}`,detail:"Event Sequence",range:this.getRangeForWord(context)})})}addKeyCompletions(suggestions,context,activeSchema){activeSchema?.definitions&&Object.keys(activeSchema.definitions).forEach(typeName=>{activeSchema.definitions[typeName].type==="object"&&suggestions.push({label:typeName,kind:6,insertText:typeName,documentation:`Composite key type: ${typeName}`,detail:"Composite Key Type",range:this.getRangeForWord(context)})}),this.addExpressionCompletions(suggestions,context)}addParentCompletions(suggestions,context){this.addExpressionCompletions(suggestions,context)}addNumericPropertyCompletions(suggestions,context,activeSchema){activeSchema?.properties&&Object.entries(activeSchema.properties).forEach(([propName,propSchema])=>{(propSchema.type==="integer"||propSchema.type==="number")&&(!context.currentWord||propName.startsWith(context.currentWord))&&suggestions.push({label:propName,kind:9,insertText:propName,documentation:`Numeric property: ${propName}`,detail:this.getPropertyTypeDescription(propSchema),range:this.getRangeForWord(context)})});let addSubtractMatch=context.textBeforeCursor.match(/\b(add|subtract)\s+(\w*)$/);if(addSubtractMatch){let partialProp=addSubtractMatch[2]||"";activeSchema?.properties&&Object.entries(activeSchema.properties).forEach(([propName,propSchema])=>{(propSchema.type==="integer"||propSchema.type==="number")&&(!partialProp||propName.startsWith(partialProp))&&suggestions.push({label:propName,kind:9,insertText:`${propName} by `,documentation:`Numeric property: ${propName}`,detail:this.getPropertyTypeDescription(propSchema),range:this.getRangeForWord(context)})})}}addChildrenCompletions(suggestions,context,activeSchema){activeSchema?.properties&&Object.entries(activeSchema.properties).forEach(([propName,propSchema])=>{(propSchema.type==="array"||propSchema.items)&&suggestions.push({label:propName,kind:9,insertText:`${propName} id `,documentation:`Collection property: ${propName}`,detail:this.getPropertyTypeDescription(propSchema),range:this.getRangeForWord(context)})})}addJoinCompletions(suggestions,context,activeSchema){let trimmed=context.textBeforeCursor.trim();trimmed==="join"||!trimmed.includes(" ")?activeSchema?.properties&&Object.entries(activeSchema.properties).forEach(([propName,propSchema])=>{(propSchema.type==="array"||propSchema.items)&&suggestions.push({label:propName,kind:9,insertText:`${propName} on `,documentation:`Join on collection: ${propName}`,range:this.getRangeForWord(context)})}):trimmed.match(/join\s+\w+\s+on\s+/)&&activeSchema?.properties&&Object.keys(activeSchema.properties).forEach(propName=>{suggestions.push({label:propName,kind:9,insertText:propName,documentation:`Property: ${propName}`,range:this.getRangeForWord(context)})})}addVariantNameCompletions(suggestions,context){this.readModels.forEach(readModel=>{(!context.currentWord||readModel.displayName.toLowerCase().startsWith(context.currentWord.toLowerCase()))&&suggestions.push({label:readModel.displayName,kind:6,insertText:readModel.displayName,documentation:`Read model: ${readModel.displayName} (${readModel.identifier})`,detail:readModel.identifier,range:this.getRangeForWord(context)})})}addStatementCompletions(suggestions,context,activeSchema){[{label:"sequence",insertText:"sequence ",documentation:"Specify which event sequence to use for this projection",detail:"sequence <event-sequence-id>"},{label:"variant",insertText:"variant ",documentation:"Declare one of several mutually exclusive named read models sharing this projection's identity",detail:"variant <Name>"},{label:"enters on",insertText:"enters on ",documentation:"Declare the event that activates a variant",detail:"enters on <EventType>"},{label:"from",insertText:"from ",documentation:"Handle specific event types",detail:"from <EventType>"},{label:"all",insertText:"all",documentation:"Subscribe to all event types in the system \u2014 mappings run for every event system-wide, regardless of explicit from blocks",detail:"all"},{label:"every",insertText:"every",documentation:"Handle all events for automatic mapping (only events explicitly subscribed to via from blocks)",detail:"every"},{label:"key",insertText:"key ",documentation:"Define the projection key",detail:"key <expression>"},{label:"parent",insertText:"parent ",documentation:"Define parent relationship",detail:"parent <expression>"},{label:"join",insertText:"join ",documentation:"Join with child collection",detail:"join <collection> on <property>"},{label:"children",insertText:"children ",documentation:"Define child collection operations",detail:"children <collection> identified by <expression>"},{label:"remove with",insertText:"remove with ",documentation:"Remove items based on event",detail:"remove with <EventType>"},{label:"remove via",insertText:"remove via join on ",documentation:"Remove items via join",detail:"remove via join on <EventType>"},{label:"no automap",insertText:"no automap",documentation:"Disable automatic property mapping",detail:"no automap"},{label:"clear",insertText:"clear ",documentation:"Remove the value a property holds",detail:"clear <property>"},{label:"increment",insertText:"increment ",documentation:"Increment a numeric property",detail:"increment <property>"},{label:"decrement",insertText:"decrement ",documentation:"Decrement a numeric property",detail:"decrement <property>"},{label:"count",insertText:"count ",documentation:"Count occurrences",detail:"count <property>"},{label:"add",insertText:"add ",documentation:"Add value to property",detail:"add <property> by <expression>"},{label:"subtract",insertText:"subtract ",documentation:"Subtract value from property",detail:"subtract <property> by <expression>"},{label:"exclude children",insertText:"exclude children",documentation:"Exclude child collections from automap",detail:"exclude children"}].forEach(kw=>{(!context.currentWord||kw.label.startsWith(context.currentWord))&&suggestions.push({label:kw.label,kind:14,insertText:kw.insertText,documentation:kw.documentation,detail:kw.detail,range:this.getRangeForWord(context)})}),activeSchema?.properties&&Object.entries(activeSchema.properties).forEach(([propName,propSchema])=>{(!context.currentWord||propName.startsWith(context.currentWord))&&suggestions.push({label:propName,kind:9,insertText:`${propName} = `,documentation:`Assign value to property: ${propName}`,detail:this.getPropertyTypeDescription(propSchema),range:this.getRangeForWord(context)})})}addAssignmentValueCompletions(suggestions,context,model,position){let eventType=this.getCurrentEventType(model,position.lineNumber);if(eventType&&this.eventSchemas[eventType]){let eventSchema=this.eventSchemas[eventType];eventSchema.properties&&Object.entries(eventSchema.properties).forEach(([propName,propSchema])=>{suggestions.push({label:propName,kind:9,insertText:propName,documentation:`Event property: ${propName}`,detail:this.getPropertyTypeDescription(propSchema),range:this.getRangeForWord(context)})})}this.addExpressionCompletions(suggestions,context)}addExpressionCompletions(suggestions,context){[{label:"$eventSourceId",insertText:"$eventSourceId",documentation:"The identifier of the event source (aggregate/entity)",detail:"Event Source Expression"},{label:"$causedBy",insertText:"$causedBy",documentation:"The identity that caused the event. Chronicle cannot yet evaluate this short form (Cratis/Chronicle#4119) - prefer $eventContext.causedBy.<property>.",detail:"Identity Expression"},{label:"$eventContext",insertText:"$eventContext.",documentation:`Metadata about the event: ${eventContextMembers.map(member=>member.name).join(", ")}`,detail:"Event Context Expression"}].forEach(expr=>{(!context.currentWord||expr.label.startsWith(context.currentWord)||expr.label.startsWith("$"+context.currentWord))&&suggestions.push({label:expr.label,kind:14,insertText:expr.insertText,documentation:expr.documentation,detail:expr.detail,range:this.getRangeForWord(context)})})}addPropertyMemberCompletions(suggestions,context){let eventContextMatch=context.textBeforeCursor.match(EVENT_CONTEXT_PATH);if(eventContextMatch){let segments=eventContextMatch[1].split(".").filter(segment=>segment.length>0);this.addEventContextMemberCompletions(suggestions,context,segments,eventContextMatch[2]);return}let match=context.textBeforeCursor.match(/(\w+|\$\w+)\.(\w*)$/);if(!match)return;let[,objectName,partialProp]=match,causedByMatch=context.textBeforeCursor.match(/\$causedBy\.((?:\w+\.)*)(\w*)$/);if(causedByMatch){let segments=causedByMatch[1].split(".").filter(segment=>segment.length>0);this.addEventContextMemberCompletions(suggestions,context,["causedBy",...segments],causedByMatch[2]);return}let eventSchema=this.eventSchemas[objectName];eventSchema?.properties&&Object.entries(eventSchema.properties).forEach(([propName,propSchema])=>{(!partialProp||propName.startsWith(partialProp))&&suggestions.push({label:propName,kind:9,insertText:propName,documentation:`Event property: ${propName}`,detail:this.getPropertyTypeDescription(propSchema),range:this.getRangeForWord(context)})})}addEventContextMemberCompletions(suggestions,context,segments,partial){(eventContextMembersAfter(segments)??[]).filter(member=>!partial||member.name.startsWith(partial)).forEach(member=>{suggestions.push({label:member.name,kind:member.kind==="function"?1:9,insertText:member.name,documentation:member.description,detail:member.type,range:this.getRangeForWord(context)})})}getCurrentEventType(model,lineNumber){for(let i=lineNumber;i>=1;i--){let line=model.getLineContent(i).trim(),fromMatch=line.match(/^from\s+([\w-]+)/);if(fromMatch)return fromMatch[1];if(line.match(/^every$/))return null}return null}getActiveReadModelSchema(model){let match=model.getLineContent(1).trim().match(/^projection\s+[\w.]+\s*=>\s*([\w.]+)/);if(!match)return;let readModelName=match[1];return this.resolveReadModelInfo(readModelName)?.schema}resolveReadModelInfo(readModelToken){return this.readModels.find(rm=>rm.identifier===readModelToken||rm.displayName===readModelToken)||null}getSchemaName(schema){if(schema.name)return schema.name;if(schema.title)return schema.title;if(typeof schema.$id=="string"){let parts=schema.$id.split("/");return parts[parts.length-1]||schema.$id}}getSchemaDescription(schema){return schema.description||void 0}getPropertyTypeDescription(propSchema){return propSchema.type==="array"&&propSchema.items?`${propSchema.items.type||propSchema.items.$id?.split("/").pop()||"unknown"}[]`:propSchema.type||propSchema.$ref?.split("/").pop()||"unknown"}getRangeForWord(context){let startColumn=context.textBeforeCursor.length-context.currentWord.length+1,endColumn=startColumn+context.currentWord.length;return{startLineNumber:context.lineNumber,startColumn:Math.max(1,startColumn),endLineNumber:context.lineNumber,endColumn:Math.max(1,endColumn)}}};var Validator=class{readModels=[];eventSchemas={};draftReadModel=null;setReadModels(readModels){this.readModels=readModels||[]}setDraftReadModel(draft){this.draftReadModel=draft}setReadModelSchemas(schemas){this.readModels=schemas.map(schema=>({identifier:this.getSchemaName(schema)||"",displayName:this.getSchemaName(schema)||"",schema}))}setEventSchemas(schemas){if(!schemas){this.eventSchemas={};return}if(Array.isArray(schemas)){let map={};schemas.forEach((s,i)=>{if(!s)return;let name=this.getSchemaName(s)||`Event${i+1}`;map[name]=s}),this.eventSchemas=map;return}this.eventSchemas=schemas}validate(model){let markers=[],content=model.getValue();if(!content.trim())return markers;let lines=content.replace(/\r\n/g,`
|
|
2
|
+
`).replace(/\r/g,`
|
|
3
|
+
`).split(`
|
|
4
|
+
`),firstNonEmptyLineIndex=0,firstLine="";for(let i=0;i<lines.length;i++){let trimmed=lines[i].trim();if(trimmed&&!trimmed.startsWith("#")){firstNonEmptyLineIndex=i,firstLine=trimmed;break}}if(!firstLine)return markers;let projectionWithReadModelMatch=firstLine.match(/^projection\s+([\w.]+)\s*=>\s*([\w.]+)\s*$/),projectionAdHocMatch=!projectionWithReadModelMatch&&firstLine.match(/^projection\s+([\w.]+)\s*$/);if(!projectionWithReadModelMatch&&!projectionAdHocMatch){let lineNumber=firstNonEmptyLineIndex+1;return markers.push(this.createError(lineNumber,1,firstLine.length+1,'Projection definition must start with "projection <ProjectionName>" or "projection <ProjectionName> => <ReadModelIdentifier>"')),markers}let activeSchema,readModelName=null;if(projectionWithReadModelMatch){readModelName=projectionWithReadModelMatch[2];let readModelExists=this.resolveReadModelIdentifier(readModelName)!==null,isDraftReadModel=this.isDraftReadModel(readModelName),readModelStartColumn=this.getReadModelStartColumn(firstLine,readModelName);if(!readModelExists&&!isDraftReadModel){let lineNumber=firstNonEmptyLineIndex+1,col=readModelStartColumn;markers.push(this.createError(lineNumber,col,col+readModelName.length,`Read model '${readModelName}' not found`))}else if(isDraftReadModel){let lineNumber=firstNonEmptyLineIndex+1,col=readModelStartColumn;markers.push(this.createInfo(lineNumber,col,col+readModelName.length,`Read model '${readModelName}' is a draft (not yet saved)`))}let isDraft=this.isDraftReadModel(readModelName),activeReadModel=isDraft?null:this.resolveReadModelInfo(readModelName);activeSchema=isDraft?this.draftReadModel.schema:activeReadModel?.schema}let contextStack=[activeSchema],indentStack=[0],lastIndent=0,eventContextStack=[new Map];for(let i=1;i<lines.length;i++){let lineNumber=i+1,line=lines[i],trimmed=line.trim();if(!trimmed||trimmed.startsWith("#"))continue;let currentIndent=line.search(/\S/);for(;currentIndent<lastIndent&&indentStack.length>1;){let poppedIndent=indentStack.pop();poppedIndent!==void 0&¤tIndent<poppedIndent&&(contextStack.pop(),eventContextStack.pop())}lastIndent=currentIndent;let currentEventContext=eventContextStack[eventContextStack.length-1],fromMatch=trimmed.match(/^from\s+([\w-]+)/);if(fromMatch){let eventType=fromMatch[1],col=line.indexOf(eventType)+1;Object.keys(this.eventSchemas).length>0&&!this.eventSchemas[eventType]&&markers.push(this.createError(lineNumber,col,col+eventType.length,`Event type '${eventType}' not found`)),currentEventContext.has(eventType)?markers.push(this.createError(lineNumber,col,col+eventType.length,`Duplicate event type '${eventType}' - event types can only be used once at each level`)):currentEventContext.set(eventType,{lineNumber,col})}let variantMatch=trimmed.match(/^variant\s+([\w.]+)\s*$/);if(variantMatch){let[,variantName]=variantMatch,col=line.indexOf(variantName)+1,isDraft=this.isDraftReadModel(variantName);!(this.resolveReadModelIdentifier(variantName)!==null)&&!isDraft?markers.push(this.createError(lineNumber,col,col+variantName.length,`Read model '${variantName}' not found`)):isDraft&&markers.push(this.createInfo(lineNumber,col,col+variantName.length,`Read model '${variantName}' is a draft (not yet saved)`));let variantSchema=isDraft?this.draftReadModel.schema:this.resolveReadModelInfo(variantName)?.schema;contextStack.push(variantSchema),indentStack.push(currentIndent),eventContextStack.push(new Map)}let entersOnMatch=trimmed.match(/^enters\s+on\s+([\w-]+)/);if(entersOnMatch){let eventType=entersOnMatch[1],col=line.indexOf(eventType)+1;Object.keys(this.eventSchemas).length>0&&!this.eventSchemas[eventType]&&markers.push(this.createError(lineNumber,col,col+eventType.length,`Event type '${eventType}' not found`)),currentEventContext.has(eventType)?markers.push(this.createError(lineNumber,col,col+eventType.length,`Duplicate event type '${eventType}' - event types can only be used once at each level`)):currentEventContext.set(eventType,{lineNumber,col})}let eventsMatch=trimmed.match(/^events\s+([\w-]+)/);if(eventsMatch){let eventType=eventsMatch[1],col=line.indexOf(eventType)+1;Object.keys(this.eventSchemas).length>0&&!this.eventSchemas[eventType]&&markers.push(this.createError(lineNumber,col,col+eventType.length,`Event type '${eventType}' not found`)),currentEventContext.has(eventType)?markers.push(this.createError(lineNumber,col,col+eventType.length,`Duplicate event type '${eventType}' - event types can only be used once at each level`)):currentEventContext.set(eventType,{lineNumber,col})}let removeWithMatch=trimmed.match(/^remove\s+(?:with|via\s+join\s+on)\s+([\w-]+)/);if(removeWithMatch){let eventType=removeWithMatch[1],col=line.indexOf(eventType)+1;Object.keys(this.eventSchemas).length>0&&!this.eventSchemas[eventType]&&markers.push(this.createError(lineNumber,col,col+eventType.length,`Event type '${eventType}' not found`)),currentEventContext.has(eventType)?markers.push(this.createError(lineNumber,col,col+eventType.length,`Duplicate event type '${eventType}' - event types can only be used once at each level`)):currentEventContext.set(eventType,{lineNumber,col})}let assignmentMatch=trimmed.match(/^(\w+)\s*=\s*(.+)$/);if(assignmentMatch){let currentSchema=contextStack[contextStack.length-1];if(currentSchema?.properties){let[,propertyName]=assignmentMatch;if(!currentSchema.properties[propertyName]){let col=line.indexOf(propertyName)+1,schemaName=this.getSchemaName(currentSchema)||"current schema";markers.push(this.createWarning(lineNumber,col,col+propertyName.length,`Property '${propertyName}' not found in ${schemaName}`))}}}let numericOpMatch=trimmed.match(/^(?:increment|decrement|count)\s+(\w+)$/);if(numericOpMatch){let currentSchema=contextStack[contextStack.length-1];if(currentSchema?.properties){let[,propertyName]=numericOpMatch;if(!currentSchema.properties[propertyName]){let col=line.indexOf(propertyName)+1,schemaName=this.getSchemaName(currentSchema)||"current schema";markers.push(this.createWarning(lineNumber,col,col+propertyName.length,`Property '${propertyName}' not found in ${schemaName}`))}}}let addSubMatch=trimmed.match(/^(?:add|subtract)\s+(\w+)\s+by\s+/);if(addSubMatch){let currentSchema=contextStack[contextStack.length-1];if(currentSchema?.properties){let[,propertyName]=addSubMatch;if(!currentSchema.properties[propertyName]){let col=line.indexOf(propertyName)+1,schemaName=this.getSchemaName(currentSchema)||"current schema";markers.push(this.createWarning(lineNumber,col,col+propertyName.length,`Property '${propertyName}' not found in ${schemaName}`))}}}let childrenMatch=trimmed.match(/^children\s+(\w+)\s+identified\s+by\s+/);if(childrenMatch){let currentSchema=contextStack[contextStack.length-1];if(currentSchema?.properties){let[,propertyName]=childrenMatch,prop=currentSchema.properties[propertyName];if(prop)if(prop.type!=="array"&&!prop.items){let col=line.indexOf(propertyName)+1;markers.push(this.createWarning(lineNumber,col,col+propertyName.length,`Property '${propertyName}' is not a collection (array) type`))}else{let childSchema=prop.items;childSchema&&(contextStack.push(childSchema),indentStack.push(currentIndent),eventContextStack.push(new Map))}else{let col=line.indexOf(propertyName)+1,schemaName=this.getSchemaName(currentSchema)||"current schema";markers.push(this.createWarning(lineNumber,col,col+propertyName.length,`Collection property '${propertyName}' not found in ${schemaName}`))}}}let joinMatch=trimmed.match(/^join\s+(\w+)\s+on\s+(\w+)/);if(joinMatch&&activeSchema?.properties){let[,collectionName,joinProperty]=joinMatch,collectionProp=activeSchema.properties[collectionName];if(collectionProp){if(collectionProp.type!=="array"&&!collectionProp.items){let col=line.indexOf(collectionName)+1;markers.push(this.createWarning(lineNumber,col,col+collectionName.length,`Property '${collectionName}' is not a collection (array) type`))}}else{let col=line.indexOf(collectionName)+1;markers.push(this.createWarning(lineNumber,col,col+collectionName.length,`Collection property '${collectionName}' not found in read model '${readModelName}'`))}if(!activeSchema.properties[joinProperty]){let col=line.indexOf(joinProperty,line.indexOf("on"))+1;markers.push(this.createWarning(lineNumber,col,col+joinProperty.length,`Property '${joinProperty}' not found in read model '${readModelName}'`))}eventContextStack.push(new Map),indentStack.push(currentIndent)}let keyMatch=trimmed.match(/^key\s+(\w+)\s*$/);if(keyMatch&&activeSchema){let[,keyName]=keyMatch,isInDefinitions=activeSchema.definitions&&activeSchema.definitions[keyName],isProperty=activeSchema.properties&&activeSchema.properties[keyName];if(!isInDefinitions&&!isProperty&&!this.isBuiltInExpression(keyName)){let col=line.indexOf(keyName)+1;markers.push(this.createWarning(lineNumber,col,col+keyName.length,`Composite key type '${keyName}' not found in read model schema`))}}}for(let issue of validateEventContextPaths(lines,fenceMap(lines)))markers.push({severity:issue.severity==="error"?8:4,startLineNumber:issue.line+1,endLineNumber:issue.line+1,startColumn:issue.startColumn,endColumn:issue.endColumn,message:issue.message,code:issue.code});return markers}resolveReadModelInfo(readModelToken){return this.readModels.find(rm=>rm.identifier===readModelToken||rm.displayName===readModelToken)||null}getReadModelStartColumn(firstLine,readModelName){let prefixMatch=firstLine.match(/^\s*projection\s+[\w.]+\s*=>\s*/);if(!prefixMatch){let fallbackIndex=firstLine.lastIndexOf(readModelName);return(fallbackIndex>=0?fallbackIndex:0)+1}return prefixMatch[0].length+1}resolveReadModelIdentifier(readModelToken){return this.isDraftReadModel(readModelToken)?this.draftReadModel.identifier:this.resolveReadModelInfo(readModelToken)?.identifier??null}isDraftReadModel(readModelToken){return!!this.draftReadModel&&(this.draftReadModel.identifier===readModelToken||this.draftReadModel.displayName===readModelToken)}isBuiltInExpression(text){return text.startsWith("$")||["true","false","null"].includes(text)}createError(line,startCol,endCol,message){return{severity:8,startLineNumber:line,startColumn:startCol,endLineNumber:line,endColumn:endCol,message}}createWarning(line,startCol,endCol,message){return{severity:4,startLineNumber:line,startColumn:startCol,endLineNumber:line,endColumn:endCol,message}}createInfo(line,startCol,endCol,message){return{severity:2,startLineNumber:line,startColumn:startCol,endLineNumber:line,endColumn:endCol,message}}getSchemaName(schema){if(schema.name)return schema.name;if(schema.title)return schema.title;if(typeof schema.$id=="string"){let parts=schema.$id.split("/");return parts[parts.length-1]||schema.$id}}};var HoverProvider=class{readModels=[];eventSchemas={};draftReadModel=null;setReadModels(readModels){this.readModels=readModels||[]}setReadModelSchemas(schemas){this.readModels=schemas.map(schema=>({identifier:this.getSchemaName(schema)||"",displayName:this.getSchemaName(schema)||"",schema}))}setEventSchemas(schemas){if(!schemas){this.eventSchemas={};return}if(Array.isArray(schemas)){let map={};schemas.forEach((s,i)=>{if(!s)return;let name=this.getSchemaName(s)||`Event${i+1}`;map[name]=s}),this.eventSchemas=map;return}this.eventSchemas=schemas}setDraftReadModel(draft){this.draftReadModel=draft}provideHover(model,position){let word=model.getWordAtPosition(position);if(!word)return null;let wordText=word.word,lineContent=model.getLineContent(position.lineNumber);if(this.getProjectionNameAtPosition(lineContent,position.column))return{contents:[{value:"The name of the projection"}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}};let eventContextInfo=this.getEventContextInfo(lineContent,word.startColumn);if(eventContextInfo)return{contents:[{value:eventContextInfo}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}};let keywordInfo=this.getKeywordInfo(wordText);if(keywordInfo)return{contents:[{value:keywordInfo}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}};let expressionInfo=this.getExpressionInfo(wordText);if(expressionInfo)return{contents:[{value:expressionInfo}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}};if(this.eventSchemas[wordText]){let schema=this.eventSchemas[wordText],description=this.getSchemaDescription(schema),properties=this.getSchemaProperties(schema),content=`**Event Type:** \`${wordText}\`
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
var languageId = "pdl";
|
|
13
|
-
var KEYWORDS = [
|
|
14
|
-
"projection",
|
|
15
|
-
"sequence",
|
|
16
|
-
"all",
|
|
17
|
-
"every",
|
|
18
|
-
"on",
|
|
19
|
-
"from",
|
|
20
|
-
"key",
|
|
21
|
-
"parent",
|
|
22
|
-
"join",
|
|
23
|
-
"automap",
|
|
24
|
-
"no",
|
|
25
|
-
"children",
|
|
26
|
-
"nested",
|
|
27
|
-
"identified",
|
|
28
|
-
"by",
|
|
29
|
-
"add",
|
|
30
|
-
"subtract",
|
|
31
|
-
"remove",
|
|
32
|
-
"via",
|
|
33
|
-
"with",
|
|
34
|
-
"default",
|
|
35
|
-
"increment",
|
|
36
|
-
"decrement",
|
|
37
|
-
"count",
|
|
38
|
-
"set",
|
|
39
|
-
"unset",
|
|
40
|
-
"clear",
|
|
41
|
-
"events",
|
|
42
|
-
"id",
|
|
43
|
-
"exclude",
|
|
44
|
-
"variant",
|
|
45
|
-
"enters"
|
|
46
|
-
];
|
|
47
|
-
var BUILTINS = [
|
|
48
|
-
"$eventSourceId",
|
|
49
|
-
"$causedBy",
|
|
50
|
-
"$eventContext"
|
|
51
|
-
];
|
|
52
|
-
var OPERATORS = ["=", "+=", "-=", "=>", "."];
|
|
53
|
-
var configuration = {
|
|
54
|
-
comments: {
|
|
55
|
-
lineComment: "#"
|
|
56
|
-
},
|
|
57
|
-
brackets: [
|
|
58
|
-
["{", "}"],
|
|
59
|
-
["[", "]"],
|
|
60
|
-
["(", ")"]
|
|
61
|
-
],
|
|
62
|
-
autoClosingPairs: [
|
|
63
|
-
{ open: "{", close: "}" },
|
|
64
|
-
{ open: "[", close: "]" },
|
|
65
|
-
{ open: "(", close: ")" },
|
|
66
|
-
{ open: '"', close: '"' },
|
|
67
|
-
{ open: "'", close: "'" },
|
|
68
|
-
{ open: "`", close: "`" }
|
|
69
|
-
],
|
|
70
|
-
surroundingPairs: [
|
|
71
|
-
{ open: "{", close: "}" },
|
|
72
|
-
{ open: "[", close: "]" },
|
|
73
|
-
{ open: "(", close: ")" },
|
|
74
|
-
{ open: '"', close: '"' },
|
|
75
|
-
{ open: "'", close: "'" },
|
|
76
|
-
{ open: "`", close: "`" }
|
|
77
|
-
],
|
|
78
|
-
indentationRules: {
|
|
79
|
-
increaseIndentPattern: /^.*(:|\bon\b|\ball\b|\bevery\b|\bchildren\b|\bnested\b|\bparent\b|\bvariant\b)\s*$/,
|
|
80
|
-
decreaseIndentPattern: /^.*\}.*$/
|
|
81
|
-
},
|
|
82
|
-
folding: {
|
|
83
|
-
offSide: true,
|
|
84
|
-
markers: {
|
|
85
|
-
start: /^\s*(projection|all|every|on|children|nested|parent|variant)/,
|
|
86
|
-
end: /^\s*$/
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
wordPattern: /@?[a-zA-Z_$][\w$-]*/
|
|
90
|
-
};
|
|
91
|
-
var monarchLanguage = {
|
|
92
|
-
defaultToken: "",
|
|
93
|
-
tokenPostfix: ".pdl",
|
|
94
|
-
keywords: KEYWORDS,
|
|
95
|
-
builtins: BUILTINS,
|
|
96
|
-
operators: OPERATORS,
|
|
97
|
-
// Symbols
|
|
98
|
-
symbols: /[=><!~?:&|+\-*/^%]+/,
|
|
99
|
-
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
|
|
100
|
-
tokenizer: {
|
|
101
|
-
root: [
|
|
102
|
-
// Whitespace
|
|
103
|
-
{ include: "@whitespace" },
|
|
104
|
-
// Template strings with ${...} interpolation
|
|
105
|
-
[/`/, { token: "string.template", next: "@templateString" }],
|
|
106
|
-
// Escaped identifiers (@ prefix allows using keywords as identifiers)
|
|
107
|
-
[/@[a-zA-Z_$][\w$]*/, "identifier.escape"],
|
|
108
|
-
// Check for uppercase keywords (error case - keywords must be lowercase)
|
|
109
|
-
[
|
|
110
|
-
/[A-Z][a-zA-Z_$][\w$]*/,
|
|
111
|
-
{
|
|
112
|
-
cases: {
|
|
113
|
-
"@keywords": "invalid",
|
|
114
|
-
"@default": "identifier"
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
],
|
|
118
|
-
// Identifiers and keywords (lowercase only for keywords)
|
|
119
|
-
[
|
|
120
|
-
/[a-z_$][\w$]*/,
|
|
121
|
-
{
|
|
122
|
-
cases: {
|
|
123
|
-
"@keywords": "keyword",
|
|
124
|
-
"@builtins": "variable.predefined",
|
|
125
|
-
"@default": "identifier"
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
],
|
|
129
|
-
// Delimiters and operators
|
|
130
|
-
[/[{}()[\]]/, "@brackets"],
|
|
131
|
-
[/=>/, "operator.arrow"],
|
|
132
|
-
[
|
|
133
|
-
/@symbols/,
|
|
134
|
-
{
|
|
135
|
-
cases: {
|
|
136
|
-
"@operators": "operator",
|
|
137
|
-
"@default": ""
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
],
|
|
141
|
-
// Numbers
|
|
142
|
-
[/\d*\.\d+([eE][-+]?\d+)?/, "number.float"],
|
|
143
|
-
[/0[xX][0-9a-fA-F]+/, "number.hex"],
|
|
144
|
-
[/\d+/, "number"],
|
|
145
|
-
// Delimiter: after number because of .\d floats
|
|
146
|
-
[/[;,.]/, "delimiter"],
|
|
147
|
-
// Strings
|
|
148
|
-
[/"([^"\\]|\\.)*$/, "string.invalid"],
|
|
149
|
-
[/"/, { token: "string.quote", next: "@string" }]
|
|
150
|
-
],
|
|
151
|
-
whitespace: [
|
|
152
|
-
[/[ \t\r\n]+/, ""],
|
|
153
|
-
[/#.*$/, "comment"]
|
|
154
|
-
],
|
|
155
|
-
// Template string state
|
|
156
|
-
templateString: [
|
|
157
|
-
[/\$\{/, { token: "delimiter.bracket", next: "@templateExpression" }],
|
|
158
|
-
[/[^\\`$]+/, "string.template"],
|
|
159
|
-
[/@escapes/, "string.template.escape"],
|
|
160
|
-
[/\\./, "string.template.escape.invalid"],
|
|
161
|
-
[/`/, { token: "string.template", next: "@pop" }]
|
|
162
|
-
],
|
|
163
|
-
// Template expression inside ${}
|
|
164
|
-
templateExpression: [
|
|
165
|
-
[/\}/, { token: "delimiter.bracket", next: "@pop" }],
|
|
166
|
-
{ include: "@root" }
|
|
167
|
-
],
|
|
168
|
-
// Regular string state
|
|
169
|
-
string: [
|
|
170
|
-
[/[^\\"]+/, "string"],
|
|
171
|
-
[/@escapes/, "string.escape"],
|
|
172
|
-
[/\\./, "string.escape.invalid"],
|
|
173
|
-
[/"/, { token: "string.quote", next: "@pop" }]
|
|
174
|
-
]
|
|
175
|
-
}
|
|
176
|
-
};
|
|
6
|
+
`;return description&&(content+=`${description}
|
|
177
7
|
|
|
178
|
-
|
|
179
|
-
var EVENT_CONTEXT_PATH = /\$eventContext\.((?:\w+\.)*)(\w*)$/;
|
|
180
|
-
var CompletionProvider = class {
|
|
181
|
-
readModels = [];
|
|
182
|
-
eventSchemas = {};
|
|
183
|
-
eventSequences = [];
|
|
184
|
-
setReadModels(readModels) {
|
|
185
|
-
this.readModels = readModels || [];
|
|
186
|
-
}
|
|
187
|
-
setEventSequences(sequences) {
|
|
188
|
-
this.eventSequences = sequences || [];
|
|
189
|
-
}
|
|
190
|
-
// Keep for backwards compatibility
|
|
191
|
-
setReadModelSchemas(schemas) {
|
|
192
|
-
this.readModels = schemas.map((schema) => ({
|
|
193
|
-
identifier: this.getSchemaName(schema) || "",
|
|
194
|
-
displayName: this.getSchemaName(schema) || "",
|
|
195
|
-
schema
|
|
196
|
-
}));
|
|
197
|
-
}
|
|
198
|
-
setEventSchemas(schemas) {
|
|
199
|
-
if (!schemas) {
|
|
200
|
-
this.eventSchemas = {};
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (Array.isArray(schemas)) {
|
|
204
|
-
const map = {};
|
|
205
|
-
schemas.forEach((s, i) => {
|
|
206
|
-
if (!s) return;
|
|
207
|
-
const name = this.getSchemaName(s) || `Event${i + 1}`;
|
|
208
|
-
map[name] = s;
|
|
209
|
-
});
|
|
210
|
-
this.eventSchemas = map;
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
this.eventSchemas = schemas;
|
|
214
|
-
}
|
|
215
|
-
provideCompletionItems(model, position) {
|
|
216
|
-
const context = this.buildContext(model, position);
|
|
217
|
-
const suggestions = [];
|
|
218
|
-
const activeSchema = this.getActiveReadModelSchema(model);
|
|
219
|
-
if (context.lineNumber === 1) {
|
|
220
|
-
this.addProjectionLineCompletions(suggestions, context);
|
|
221
|
-
} else if (this.isAfterKeyword(context, "sequence")) {
|
|
222
|
-
this.addEventSequenceCompletions(suggestions, context);
|
|
223
|
-
} else if (this.isAfterKeyword(context, "from") || this.isAfterKeyword(context, "every")) {
|
|
224
|
-
this.addEventTypeCompletions(suggestions, context);
|
|
225
|
-
} else if (/\benters\s+on\s+\w*$/.test(context.textBeforeCursor)) {
|
|
226
|
-
this.addEventTypeCompletions(suggestions, context);
|
|
227
|
-
} else if (/\benters\s+\w*$/.test(context.textBeforeCursor)) {
|
|
228
|
-
suggestions.push({
|
|
229
|
-
label: "on",
|
|
230
|
-
kind: 14,
|
|
231
|
-
// Keyword
|
|
232
|
-
insertText: "on ",
|
|
233
|
-
documentation: "Declares the event that activates this variant.",
|
|
234
|
-
detail: "enters on <EventType>",
|
|
235
|
-
range: this.getRangeForWord(context)
|
|
236
|
-
});
|
|
237
|
-
} else if (this.isAfterKeyword(context, "variant")) {
|
|
238
|
-
this.addVariantNameCompletions(suggestions, context);
|
|
239
|
-
} else if (this.isAfterKeyword(context, "key")) {
|
|
240
|
-
this.addKeyCompletions(suggestions, context, activeSchema);
|
|
241
|
-
} else if (this.isAfterKeyword(context, "parent")) {
|
|
242
|
-
this.addParentCompletions(suggestions, context);
|
|
243
|
-
} else if (this.isAfterKeyword(context, "children")) {
|
|
244
|
-
this.addChildrenCompletions(suggestions, context, activeSchema);
|
|
245
|
-
} else if (this.isAfterKeyword(context, "join")) {
|
|
246
|
-
this.addJoinCompletions(suggestions, context, activeSchema);
|
|
247
|
-
} else if (this.isAfterKeyword(context, "with")) {
|
|
248
|
-
this.addEventTypeCompletions(suggestions, context);
|
|
249
|
-
} else if (this.isAfterNumericOperationKeyword(context)) {
|
|
250
|
-
this.addNumericPropertyCompletions(suggestions, context, activeSchema);
|
|
251
|
-
} else if (this.isAfterAddOrSubtractBy(context)) {
|
|
252
|
-
this.addAssignmentValueCompletions(suggestions, context, model, position);
|
|
253
|
-
} else if (this.isInAssignment(context)) {
|
|
254
|
-
this.addAssignmentValueCompletions(suggestions, context, model, position);
|
|
255
|
-
} else if (this.isPropertyDotAccess(context)) {
|
|
256
|
-
this.addPropertyMemberCompletions(suggestions, context);
|
|
257
|
-
} else if (this.isStartOfLine(context) || this.isAfterIndent(context)) {
|
|
258
|
-
this.addStatementCompletions(suggestions, context, activeSchema);
|
|
259
|
-
}
|
|
260
|
-
return { suggestions };
|
|
261
|
-
}
|
|
262
|
-
buildContext(model, position) {
|
|
263
|
-
const lineContent = model.getLineContent(position.lineNumber);
|
|
264
|
-
const textBeforeCursor = lineContent.substring(0, position.column - 1);
|
|
265
|
-
const textAfterCursor = lineContent.substring(position.column - 1);
|
|
266
|
-
const word = model.getWordAtPosition(position);
|
|
267
|
-
const currentWord = word?.word || "";
|
|
268
|
-
const indentLevel = this.getIndentLevel(textBeforeCursor);
|
|
269
|
-
return {
|
|
270
|
-
lineContent,
|
|
271
|
-
textBeforeCursor,
|
|
272
|
-
textAfterCursor,
|
|
273
|
-
currentWord,
|
|
274
|
-
indentLevel,
|
|
275
|
-
lineNumber: position.lineNumber
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
getIndentLevel(text) {
|
|
279
|
-
const match = text.match(/^(\s*)/);
|
|
280
|
-
return match ? Math.floor(match[1].length / 2) : 0;
|
|
281
|
-
}
|
|
282
|
-
isStartOfLine(context) {
|
|
283
|
-
return context.textBeforeCursor.trim() === "";
|
|
284
|
-
}
|
|
285
|
-
isAfterIndent(context) {
|
|
286
|
-
return /^\s+$/.test(context.textBeforeCursor);
|
|
287
|
-
}
|
|
288
|
-
isAfterKeyword(context, keyword) {
|
|
289
|
-
const pattern = new RegExp(`\\b${keyword}\\s+\\w*$`);
|
|
290
|
-
return pattern.test(context.textBeforeCursor);
|
|
291
|
-
}
|
|
292
|
-
isAfterNumericOperationKeyword(context) {
|
|
293
|
-
const countIncrDecrPattern = /\b(count|increment|decrement)\s+\w*$/;
|
|
294
|
-
const addSubtractPattern = /\b(add|subtract)\s+\w*$/;
|
|
295
|
-
if (countIncrDecrPattern.test(context.textBeforeCursor)) {
|
|
296
|
-
return true;
|
|
297
|
-
}
|
|
298
|
-
if (addSubtractPattern.test(context.textBeforeCursor)) {
|
|
299
|
-
return !this.isAfterAddOrSubtractBy(context);
|
|
300
|
-
}
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
isAfterAddOrSubtractBy(context) {
|
|
304
|
-
const pattern = /\b(add|subtract)\s+\w+\s+by\s+\w*$/;
|
|
305
|
-
return pattern.test(context.textBeforeCursor);
|
|
306
|
-
}
|
|
307
|
-
isInAssignment(context) {
|
|
308
|
-
return /\w+\s*=\s*\w*$/.test(context.textBeforeCursor);
|
|
309
|
-
}
|
|
310
|
-
isPropertyDotAccess(context) {
|
|
311
|
-
return /(\w+|\$\w+)\.(\w*)$/.test(context.textBeforeCursor) || EVENT_CONTEXT_PATH.test(context.textBeforeCursor);
|
|
312
|
-
}
|
|
313
|
-
addProjectionLineCompletions(suggestions, context) {
|
|
314
|
-
const trimmed = context.textBeforeCursor.trim();
|
|
315
|
-
if (trimmed === "" || "projection".startsWith(trimmed)) {
|
|
316
|
-
suggestions.push({
|
|
317
|
-
label: "projection",
|
|
318
|
-
kind: 14,
|
|
319
|
-
// Keyword
|
|
320
|
-
insertText: "projection ",
|
|
321
|
-
documentation: "Define a projection that transforms events into a read model",
|
|
322
|
-
detail: "projection <ProjectionName>",
|
|
323
|
-
range: this.getRangeForWord(context)
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
const arrowMatch = trimmed.match(/^projection\s+[\w.]+\s*=>\s*([\w.]*)$/);
|
|
327
|
-
if (arrowMatch) {
|
|
328
|
-
const partialReadModel = arrowMatch[1] || "";
|
|
329
|
-
this.readModels.forEach((readModel) => {
|
|
330
|
-
if (!partialReadModel || readModel.displayName.toLowerCase().startsWith(partialReadModel.toLowerCase())) {
|
|
331
|
-
const label = `${readModel.displayName} (${readModel.identifier})`;
|
|
332
|
-
suggestions.push({
|
|
333
|
-
label,
|
|
334
|
-
kind: 6,
|
|
335
|
-
// Class
|
|
336
|
-
insertText: readModel.displayName,
|
|
337
|
-
filterText: `${readModel.displayName} ${readModel.identifier}`,
|
|
338
|
-
documentation: `Read model: ${readModel.displayName} (${readModel.identifier})`,
|
|
339
|
-
detail: readModel.identifier,
|
|
340
|
-
range: this.getRangeForWord(context)
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
if (trimmed.startsWith("projection ") && !trimmed.includes("=>")) {
|
|
347
|
-
const afterProjection = trimmed.substring("projection ".length);
|
|
348
|
-
this.readModels.forEach((readModel) => {
|
|
349
|
-
if (!afterProjection || readModel.displayName.toLowerCase().startsWith(afterProjection.toLowerCase())) {
|
|
350
|
-
const label = `${readModel.displayName} (${readModel.identifier})`;
|
|
351
|
-
suggestions.push({
|
|
352
|
-
label,
|
|
353
|
-
kind: 6,
|
|
354
|
-
// Class
|
|
355
|
-
insertText: `${readModel.displayName} => ${readModel.displayName}`,
|
|
356
|
-
filterText: `${readModel.displayName} ${readModel.identifier}`,
|
|
357
|
-
documentation: `Read model: ${readModel.displayName} (${readModel.identifier})`,
|
|
358
|
-
detail: readModel.identifier,
|
|
359
|
-
range: this.getRangeForWord(context)
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
addEventTypeCompletions(suggestions, context) {
|
|
366
|
-
Object.keys(this.eventSchemas).forEach((eventName) => {
|
|
367
|
-
if (!context.currentWord || eventName.startsWith(context.currentWord)) {
|
|
368
|
-
const schema = this.eventSchemas[eventName];
|
|
369
|
-
suggestions.push({
|
|
370
|
-
label: eventName,
|
|
371
|
-
kind: 6,
|
|
372
|
-
// Class
|
|
373
|
-
insertText: eventName,
|
|
374
|
-
documentation: this.getSchemaDescription(schema) || `Event type: ${eventName}`,
|
|
375
|
-
detail: "Event Type",
|
|
376
|
-
range: this.getRangeForWord(context)
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
addEventSequenceCompletions(suggestions, context) {
|
|
382
|
-
this.eventSequences.forEach((sequenceId) => {
|
|
383
|
-
if (!context.currentWord || sequenceId.startsWith(context.currentWord)) {
|
|
384
|
-
suggestions.push({
|
|
385
|
-
label: sequenceId,
|
|
386
|
-
kind: 13,
|
|
387
|
-
// Value
|
|
388
|
-
insertText: sequenceId,
|
|
389
|
-
documentation: `Event sequence: ${sequenceId}`,
|
|
390
|
-
detail: "Event Sequence",
|
|
391
|
-
range: this.getRangeForWord(context)
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
addKeyCompletions(suggestions, context, activeSchema) {
|
|
397
|
-
if (activeSchema?.definitions) {
|
|
398
|
-
Object.keys(activeSchema.definitions).forEach((typeName) => {
|
|
399
|
-
const typeDef = activeSchema.definitions[typeName];
|
|
400
|
-
if (typeDef.type === "object") {
|
|
401
|
-
suggestions.push({
|
|
402
|
-
label: typeName,
|
|
403
|
-
kind: 6,
|
|
404
|
-
// Class
|
|
405
|
-
insertText: typeName,
|
|
406
|
-
documentation: `Composite key type: ${typeName}`,
|
|
407
|
-
detail: "Composite Key Type",
|
|
408
|
-
range: this.getRangeForWord(context)
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
this.addExpressionCompletions(suggestions, context);
|
|
414
|
-
}
|
|
415
|
-
addParentCompletions(suggestions, context) {
|
|
416
|
-
this.addExpressionCompletions(suggestions, context);
|
|
417
|
-
}
|
|
418
|
-
addNumericPropertyCompletions(suggestions, context, activeSchema) {
|
|
419
|
-
if (activeSchema?.properties) {
|
|
420
|
-
Object.entries(activeSchema.properties).forEach(([propName, propSchema]) => {
|
|
421
|
-
const isNumeric = propSchema.type === "integer" || propSchema.type === "number";
|
|
422
|
-
if (isNumeric) {
|
|
423
|
-
if (!context.currentWord || propName.startsWith(context.currentWord)) {
|
|
424
|
-
suggestions.push({
|
|
425
|
-
label: propName,
|
|
426
|
-
kind: 9,
|
|
427
|
-
// Property
|
|
428
|
-
insertText: propName,
|
|
429
|
-
documentation: `Numeric property: ${propName}`,
|
|
430
|
-
detail: this.getPropertyTypeDescription(propSchema),
|
|
431
|
-
range: this.getRangeForWord(context)
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
const addSubtractMatch = context.textBeforeCursor.match(/\b(add|subtract)\s+(\w*)$/);
|
|
438
|
-
if (addSubtractMatch) {
|
|
439
|
-
const partialProp = addSubtractMatch[2] || "";
|
|
440
|
-
if (activeSchema?.properties) {
|
|
441
|
-
Object.entries(activeSchema.properties).forEach(([propName, propSchema]) => {
|
|
442
|
-
const isNumeric = propSchema.type === "integer" || propSchema.type === "number";
|
|
443
|
-
if (isNumeric && (!partialProp || propName.startsWith(partialProp))) {
|
|
444
|
-
suggestions.push({
|
|
445
|
-
label: propName,
|
|
446
|
-
kind: 9,
|
|
447
|
-
// Property
|
|
448
|
-
insertText: `${propName} by `,
|
|
449
|
-
documentation: `Numeric property: ${propName}`,
|
|
450
|
-
detail: this.getPropertyTypeDescription(propSchema),
|
|
451
|
-
range: this.getRangeForWord(context)
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
addChildrenCompletions(suggestions, context, activeSchema) {
|
|
459
|
-
if (activeSchema?.properties) {
|
|
460
|
-
Object.entries(activeSchema.properties).forEach(([propName, propSchema]) => {
|
|
461
|
-
if (propSchema.type === "array" || propSchema.items) {
|
|
462
|
-
suggestions.push({
|
|
463
|
-
label: propName,
|
|
464
|
-
kind: 9,
|
|
465
|
-
// Property
|
|
466
|
-
insertText: `${propName} id `,
|
|
467
|
-
documentation: `Collection property: ${propName}`,
|
|
468
|
-
detail: this.getPropertyTypeDescription(propSchema),
|
|
469
|
-
range: this.getRangeForWord(context)
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
addJoinCompletions(suggestions, context, activeSchema) {
|
|
476
|
-
const trimmed = context.textBeforeCursor.trim();
|
|
477
|
-
if (trimmed === "join" || !trimmed.includes(" ")) {
|
|
478
|
-
if (activeSchema?.properties) {
|
|
479
|
-
Object.entries(activeSchema.properties).forEach(([propName, propSchema]) => {
|
|
480
|
-
if (propSchema.type === "array" || propSchema.items) {
|
|
481
|
-
suggestions.push({
|
|
482
|
-
label: propName,
|
|
483
|
-
kind: 9,
|
|
484
|
-
insertText: `${propName} on `,
|
|
485
|
-
documentation: `Join on collection: ${propName}`,
|
|
486
|
-
range: this.getRangeForWord(context)
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
} else if (trimmed.match(/join\s+\w+\s+on\s+/)) {
|
|
492
|
-
if (activeSchema?.properties) {
|
|
493
|
-
Object.keys(activeSchema.properties).forEach((propName) => {
|
|
494
|
-
suggestions.push({
|
|
495
|
-
label: propName,
|
|
496
|
-
kind: 9,
|
|
497
|
-
insertText: propName,
|
|
498
|
-
documentation: `Property: ${propName}`,
|
|
499
|
-
range: this.getRangeForWord(context)
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
addVariantNameCompletions(suggestions, context) {
|
|
506
|
-
this.readModels.forEach((readModel) => {
|
|
507
|
-
if (!context.currentWord || readModel.displayName.toLowerCase().startsWith(context.currentWord.toLowerCase())) {
|
|
508
|
-
suggestions.push({
|
|
509
|
-
label: readModel.displayName,
|
|
510
|
-
kind: 6,
|
|
511
|
-
// Class
|
|
512
|
-
insertText: readModel.displayName,
|
|
513
|
-
documentation: `Read model: ${readModel.displayName} (${readModel.identifier})`,
|
|
514
|
-
detail: readModel.identifier,
|
|
515
|
-
range: this.getRangeForWord(context)
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
addStatementCompletions(suggestions, context, activeSchema) {
|
|
521
|
-
const keywords = [
|
|
522
|
-
{ label: "sequence", insertText: "sequence ", documentation: "Specify which event sequence to use for this projection", detail: "sequence <event-sequence-id>" },
|
|
523
|
-
{ label: "variant", insertText: "variant ", documentation: "Declare one of several mutually exclusive named read models sharing this projection's identity", detail: "variant <Name>" },
|
|
524
|
-
{ label: "enters on", insertText: "enters on ", documentation: "Declare the event that activates a variant", detail: "enters on <EventType>" },
|
|
525
|
-
{ label: "from", insertText: "from ", documentation: "Handle specific event types", detail: "from <EventType>" },
|
|
526
|
-
{ label: "all", insertText: "all", documentation: "Subscribe to all event types in the system \u2014 mappings run for every event system-wide, regardless of explicit from blocks", detail: "all" },
|
|
527
|
-
{ label: "every", insertText: "every", documentation: "Handle all events for automatic mapping (only events explicitly subscribed to via from blocks)", detail: "every" },
|
|
528
|
-
{ label: "key", insertText: "key ", documentation: "Define the projection key", detail: "key <expression>" },
|
|
529
|
-
{ label: "parent", insertText: "parent ", documentation: "Define parent relationship", detail: "parent <expression>" },
|
|
530
|
-
{ label: "join", insertText: "join ", documentation: "Join with child collection", detail: "join <collection> on <property>" },
|
|
531
|
-
{ label: "children", insertText: "children ", documentation: "Define child collection operations", detail: "children <collection> identified by <expression>" },
|
|
532
|
-
{ label: "remove with", insertText: "remove with ", documentation: "Remove items based on event", detail: "remove with <EventType>" },
|
|
533
|
-
{ label: "remove via", insertText: "remove via join on ", documentation: "Remove items via join", detail: "remove via join on <EventType>" },
|
|
534
|
-
{ label: "no automap", insertText: "no automap", documentation: "Disable automatic property mapping", detail: "no automap" },
|
|
535
|
-
{ label: "clear", insertText: "clear ", documentation: "Remove the value a property holds", detail: "clear <property>" },
|
|
536
|
-
{ label: "increment", insertText: "increment ", documentation: "Increment a numeric property", detail: "increment <property>" },
|
|
537
|
-
{ label: "decrement", insertText: "decrement ", documentation: "Decrement a numeric property", detail: "decrement <property>" },
|
|
538
|
-
{ label: "count", insertText: "count ", documentation: "Count occurrences", detail: "count <property>" },
|
|
539
|
-
{ label: "add", insertText: "add ", documentation: "Add value to property", detail: "add <property> by <expression>" },
|
|
540
|
-
{ label: "subtract", insertText: "subtract ", documentation: "Subtract value from property", detail: "subtract <property> by <expression>" },
|
|
541
|
-
{ label: "exclude children", insertText: "exclude children", documentation: "Exclude child collections from automap", detail: "exclude children" }
|
|
542
|
-
];
|
|
543
|
-
keywords.forEach((kw) => {
|
|
544
|
-
if (!context.currentWord || kw.label.startsWith(context.currentWord)) {
|
|
545
|
-
suggestions.push({
|
|
546
|
-
label: kw.label,
|
|
547
|
-
kind: 14,
|
|
548
|
-
// Keyword
|
|
549
|
-
insertText: kw.insertText,
|
|
550
|
-
documentation: kw.documentation,
|
|
551
|
-
detail: kw.detail,
|
|
552
|
-
range: this.getRangeForWord(context)
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
if (activeSchema?.properties) {
|
|
557
|
-
Object.entries(activeSchema.properties).forEach(([propName, propSchema]) => {
|
|
558
|
-
if (!context.currentWord || propName.startsWith(context.currentWord)) {
|
|
559
|
-
suggestions.push({
|
|
560
|
-
label: propName,
|
|
561
|
-
kind: 9,
|
|
562
|
-
// Property
|
|
563
|
-
insertText: `${propName} = `,
|
|
564
|
-
documentation: `Assign value to property: ${propName}`,
|
|
565
|
-
detail: this.getPropertyTypeDescription(propSchema),
|
|
566
|
-
range: this.getRangeForWord(context)
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
addAssignmentValueCompletions(suggestions, context, model, position) {
|
|
573
|
-
const eventType = this.getCurrentEventType(model, position.lineNumber);
|
|
574
|
-
if (eventType && this.eventSchemas[eventType]) {
|
|
575
|
-
const eventSchema = this.eventSchemas[eventType];
|
|
576
|
-
if (eventSchema.properties) {
|
|
577
|
-
Object.entries(eventSchema.properties).forEach(([propName, propSchema]) => {
|
|
578
|
-
suggestions.push({
|
|
579
|
-
label: propName,
|
|
580
|
-
kind: 9,
|
|
581
|
-
insertText: propName,
|
|
582
|
-
documentation: `Event property: ${propName}`,
|
|
583
|
-
detail: this.getPropertyTypeDescription(propSchema),
|
|
584
|
-
range: this.getRangeForWord(context)
|
|
585
|
-
});
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
this.addExpressionCompletions(suggestions, context);
|
|
590
|
-
}
|
|
591
|
-
addExpressionCompletions(suggestions, context) {
|
|
592
|
-
const expressions = [
|
|
593
|
-
{
|
|
594
|
-
label: "$eventSourceId",
|
|
595
|
-
insertText: "$eventSourceId",
|
|
596
|
-
documentation: "The identifier of the event source (aggregate/entity)",
|
|
597
|
-
detail: "Event Source Expression"
|
|
598
|
-
},
|
|
599
|
-
{
|
|
600
|
-
label: "$causedBy",
|
|
601
|
-
insertText: "$causedBy",
|
|
602
|
-
documentation: "The identity that caused the event. Chronicle cannot yet evaluate this short form (Cratis/Chronicle#4119) - prefer $eventContext.causedBy.<property>.",
|
|
603
|
-
detail: "Identity Expression"
|
|
604
|
-
},
|
|
605
|
-
{
|
|
606
|
-
label: "$eventContext",
|
|
607
|
-
insertText: "$eventContext.",
|
|
608
|
-
documentation: `Metadata about the event: ${eventContextMembers.map((member) => member.name).join(", ")}`,
|
|
609
|
-
detail: "Event Context Expression"
|
|
610
|
-
}
|
|
611
|
-
];
|
|
612
|
-
expressions.forEach((expr) => {
|
|
613
|
-
if (!context.currentWord || expr.label.startsWith(context.currentWord) || expr.label.startsWith("$" + context.currentWord)) {
|
|
614
|
-
suggestions.push({
|
|
615
|
-
label: expr.label,
|
|
616
|
-
kind: 14,
|
|
617
|
-
// Keyword/builtin
|
|
618
|
-
insertText: expr.insertText,
|
|
619
|
-
documentation: expr.documentation,
|
|
620
|
-
detail: expr.detail,
|
|
621
|
-
range: this.getRangeForWord(context)
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
addPropertyMemberCompletions(suggestions, context) {
|
|
627
|
-
const eventContextMatch = context.textBeforeCursor.match(EVENT_CONTEXT_PATH);
|
|
628
|
-
if (eventContextMatch) {
|
|
629
|
-
const segments = eventContextMatch[1].split(".").filter((segment) => segment.length > 0);
|
|
630
|
-
this.addEventContextMemberCompletions(suggestions, context, segments, eventContextMatch[2]);
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
const match = context.textBeforeCursor.match(/(\w+|\$\w+)\.(\w*)$/);
|
|
634
|
-
if (!match) return;
|
|
635
|
-
const [, objectName, partialProp] = match;
|
|
636
|
-
const causedByMatch = context.textBeforeCursor.match(/\$causedBy\.((?:\w+\.)*)(\w*)$/);
|
|
637
|
-
if (causedByMatch) {
|
|
638
|
-
const segments = causedByMatch[1].split(".").filter((segment) => segment.length > 0);
|
|
639
|
-
this.addEventContextMemberCompletions(suggestions, context, ["causedBy", ...segments], causedByMatch[2]);
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
const eventSchema = this.eventSchemas[objectName];
|
|
643
|
-
if (eventSchema?.properties) {
|
|
644
|
-
Object.entries(eventSchema.properties).forEach(([propName, propSchema]) => {
|
|
645
|
-
if (!partialProp || propName.startsWith(partialProp)) {
|
|
646
|
-
suggestions.push({
|
|
647
|
-
label: propName,
|
|
648
|
-
kind: 9,
|
|
649
|
-
insertText: propName,
|
|
650
|
-
documentation: `Event property: ${propName}`,
|
|
651
|
-
detail: this.getPropertyTypeDescription(propSchema),
|
|
652
|
-
range: this.getRangeForWord(context)
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
addEventContextMemberCompletions(suggestions, context, segments, partial) {
|
|
659
|
-
const members = eventContextMembersAfter(segments) ?? [];
|
|
660
|
-
members.filter((member) => !partial || member.name.startsWith(partial)).forEach((member) => {
|
|
661
|
-
suggestions.push({
|
|
662
|
-
label: member.name,
|
|
663
|
-
kind: member.kind === "function" ? 1 : 9,
|
|
664
|
-
insertText: member.name,
|
|
665
|
-
documentation: member.description,
|
|
666
|
-
detail: member.type,
|
|
667
|
-
range: this.getRangeForWord(context)
|
|
668
|
-
});
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
getCurrentEventType(model, lineNumber) {
|
|
672
|
-
for (let i = lineNumber; i >= 1; i--) {
|
|
673
|
-
const line = model.getLineContent(i).trim();
|
|
674
|
-
const fromMatch = line.match(/^from\s+([\w-]+)/);
|
|
675
|
-
if (fromMatch) {
|
|
676
|
-
return fromMatch[1];
|
|
677
|
-
}
|
|
678
|
-
const everyMatch = line.match(/^every$/);
|
|
679
|
-
if (everyMatch) {
|
|
680
|
-
return null;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
return null;
|
|
684
|
-
}
|
|
685
|
-
getActiveReadModelSchema(model) {
|
|
686
|
-
const firstLine = model.getLineContent(1).trim();
|
|
687
|
-
const match = firstLine.match(/^projection\s+[\w.]+\s*=>\s*([\w.]+)/);
|
|
688
|
-
if (!match) return void 0;
|
|
689
|
-
const readModelName = match[1];
|
|
690
|
-
const readModel = this.resolveReadModelInfo(readModelName);
|
|
691
|
-
return readModel?.schema;
|
|
692
|
-
}
|
|
693
|
-
resolveReadModelInfo(readModelToken) {
|
|
694
|
-
return this.readModels.find((rm) => rm.identifier === readModelToken || rm.displayName === readModelToken) || null;
|
|
695
|
-
}
|
|
696
|
-
getSchemaName(schema) {
|
|
697
|
-
if (schema.name) return schema.name;
|
|
698
|
-
if (schema.title) return schema.title;
|
|
699
|
-
if (typeof schema.$id === "string") {
|
|
700
|
-
const parts = schema.$id.split("/");
|
|
701
|
-
return parts[parts.length - 1] || schema.$id;
|
|
702
|
-
}
|
|
703
|
-
return void 0;
|
|
704
|
-
}
|
|
705
|
-
getSchemaDescription(schema) {
|
|
706
|
-
return schema.description || void 0;
|
|
707
|
-
}
|
|
708
|
-
getPropertyTypeDescription(propSchema) {
|
|
709
|
-
if (propSchema.type === "array" && propSchema.items) {
|
|
710
|
-
const itemType = propSchema.items.type || propSchema.items.$id?.split("/").pop() || "unknown";
|
|
711
|
-
return `${itemType}[]`;
|
|
712
|
-
}
|
|
713
|
-
return propSchema.type || propSchema.$ref?.split("/").pop() || "unknown";
|
|
714
|
-
}
|
|
715
|
-
getRangeForWord(context) {
|
|
716
|
-
const startColumn = context.textBeforeCursor.length - context.currentWord.length + 1;
|
|
717
|
-
const endColumn = startColumn + context.currentWord.length;
|
|
718
|
-
return {
|
|
719
|
-
startLineNumber: context.lineNumber,
|
|
720
|
-
startColumn: Math.max(1, startColumn),
|
|
721
|
-
endLineNumber: context.lineNumber,
|
|
722
|
-
endColumn: Math.max(1, endColumn)
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
};
|
|
8
|
+
`),properties.length>0&&(content+=`**Properties:**
|
|
726
9
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
readModels = [];
|
|
730
|
-
eventSchemas = {};
|
|
731
|
-
draftReadModel = null;
|
|
732
|
-
setReadModels(readModels) {
|
|
733
|
-
this.readModels = readModels || [];
|
|
734
|
-
}
|
|
735
|
-
setDraftReadModel(draft) {
|
|
736
|
-
this.draftReadModel = draft;
|
|
737
|
-
}
|
|
738
|
-
// Keep for backwards compatibility
|
|
739
|
-
setReadModelSchemas(schemas) {
|
|
740
|
-
this.readModels = schemas.map((schema) => ({
|
|
741
|
-
identifier: this.getSchemaName(schema) || "",
|
|
742
|
-
displayName: this.getSchemaName(schema) || "",
|
|
743
|
-
schema
|
|
744
|
-
}));
|
|
745
|
-
}
|
|
746
|
-
setEventSchemas(schemas) {
|
|
747
|
-
if (!schemas) {
|
|
748
|
-
this.eventSchemas = {};
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (Array.isArray(schemas)) {
|
|
752
|
-
const map = {};
|
|
753
|
-
schemas.forEach((s, i) => {
|
|
754
|
-
if (!s) return;
|
|
755
|
-
const name = this.getSchemaName(s) || `Event${i + 1}`;
|
|
756
|
-
map[name] = s;
|
|
757
|
-
});
|
|
758
|
-
this.eventSchemas = map;
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
this.eventSchemas = schemas;
|
|
762
|
-
}
|
|
763
|
-
validate(model) {
|
|
764
|
-
const markers = [];
|
|
765
|
-
const content = model.getValue();
|
|
766
|
-
if (!content.trim()) {
|
|
767
|
-
return markers;
|
|
768
|
-
}
|
|
769
|
-
const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
770
|
-
let firstNonEmptyLineIndex = 0;
|
|
771
|
-
let firstLine = "";
|
|
772
|
-
for (let i = 0; i < lines.length; i++) {
|
|
773
|
-
const trimmed = lines[i].trim();
|
|
774
|
-
if (trimmed && !trimmed.startsWith("#")) {
|
|
775
|
-
firstNonEmptyLineIndex = i;
|
|
776
|
-
firstLine = trimmed;
|
|
777
|
-
break;
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
if (!firstLine) {
|
|
781
|
-
return markers;
|
|
782
|
-
}
|
|
783
|
-
const projectionWithReadModelMatch = firstLine.match(/^projection\s+([\w.]+)\s*=>\s*([\w.]+)\s*$/);
|
|
784
|
-
const projectionAdHocMatch = !projectionWithReadModelMatch && firstLine.match(/^projection\s+([\w.]+)\s*$/);
|
|
785
|
-
if (!projectionWithReadModelMatch && !projectionAdHocMatch) {
|
|
786
|
-
const lineNumber = firstNonEmptyLineIndex + 1;
|
|
787
|
-
markers.push(this.createError(lineNumber, 1, firstLine.length + 1, 'Projection definition must start with "projection <ProjectionName>" or "projection <ProjectionName> => <ReadModelIdentifier>"'));
|
|
788
|
-
return markers;
|
|
789
|
-
}
|
|
790
|
-
let activeSchema;
|
|
791
|
-
let readModelName = null;
|
|
792
|
-
if (projectionWithReadModelMatch) {
|
|
793
|
-
readModelName = projectionWithReadModelMatch[2];
|
|
794
|
-
const readModelExists = this.resolveReadModelIdentifier(readModelName) !== null;
|
|
795
|
-
const isDraftReadModel = this.isDraftReadModel(readModelName);
|
|
796
|
-
const readModelStartColumn = this.getReadModelStartColumn(firstLine, readModelName);
|
|
797
|
-
if (!readModelExists && !isDraftReadModel) {
|
|
798
|
-
const lineNumber = firstNonEmptyLineIndex + 1;
|
|
799
|
-
const col = readModelStartColumn;
|
|
800
|
-
markers.push(this.createError(lineNumber, col, col + readModelName.length, `Read model '${readModelName}' not found`));
|
|
801
|
-
} else if (isDraftReadModel) {
|
|
802
|
-
const lineNumber = firstNonEmptyLineIndex + 1;
|
|
803
|
-
const col = readModelStartColumn;
|
|
804
|
-
markers.push(this.createInfo(lineNumber, col, col + readModelName.length, `Read model '${readModelName}' is a draft (not yet saved)`));
|
|
805
|
-
}
|
|
806
|
-
const isDraft = this.isDraftReadModel(readModelName);
|
|
807
|
-
const activeReadModel = isDraft ? null : this.resolveReadModelInfo(readModelName);
|
|
808
|
-
activeSchema = isDraft ? this.draftReadModel.schema : activeReadModel?.schema;
|
|
809
|
-
}
|
|
810
|
-
const contextStack = [activeSchema];
|
|
811
|
-
const indentStack = [0];
|
|
812
|
-
let lastIndent = 0;
|
|
813
|
-
const eventContextStack = [/* @__PURE__ */ new Map()];
|
|
814
|
-
for (let i = 1; i < lines.length; i++) {
|
|
815
|
-
const lineNumber = i + 1;
|
|
816
|
-
const line = lines[i];
|
|
817
|
-
const trimmed = line.trim();
|
|
818
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
819
|
-
const currentIndent = line.search(/\S/);
|
|
820
|
-
while (currentIndent < lastIndent && indentStack.length > 1) {
|
|
821
|
-
const poppedIndent = indentStack.pop();
|
|
822
|
-
if (poppedIndent !== void 0 && currentIndent < poppedIndent) {
|
|
823
|
-
contextStack.pop();
|
|
824
|
-
eventContextStack.pop();
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
lastIndent = currentIndent;
|
|
828
|
-
const currentEventContext = eventContextStack[eventContextStack.length - 1];
|
|
829
|
-
const fromMatch = trimmed.match(/^from\s+([\w-]+)/);
|
|
830
|
-
if (fromMatch) {
|
|
831
|
-
const eventType = fromMatch[1];
|
|
832
|
-
const col = line.indexOf(eventType) + 1;
|
|
833
|
-
if (Object.keys(this.eventSchemas).length > 0 && !this.eventSchemas[eventType]) {
|
|
834
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Event type '${eventType}' not found`));
|
|
835
|
-
}
|
|
836
|
-
if (currentEventContext.has(eventType)) {
|
|
837
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Duplicate event type '${eventType}' - event types can only be used once at each level`));
|
|
838
|
-
} else {
|
|
839
|
-
currentEventContext.set(eventType, { lineNumber, col });
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
const variantMatch = trimmed.match(/^variant\s+([\w.]+)\s*$/);
|
|
843
|
-
if (variantMatch) {
|
|
844
|
-
const [, variantName] = variantMatch;
|
|
845
|
-
const col = line.indexOf(variantName) + 1;
|
|
846
|
-
const isDraft = this.isDraftReadModel(variantName);
|
|
847
|
-
const readModelExists = this.resolveReadModelIdentifier(variantName) !== null;
|
|
848
|
-
if (!readModelExists && !isDraft) {
|
|
849
|
-
markers.push(this.createError(lineNumber, col, col + variantName.length, `Read model '${variantName}' not found`));
|
|
850
|
-
} else if (isDraft) {
|
|
851
|
-
markers.push(this.createInfo(lineNumber, col, col + variantName.length, `Read model '${variantName}' is a draft (not yet saved)`));
|
|
852
|
-
}
|
|
853
|
-
const variantSchema = isDraft ? this.draftReadModel.schema : this.resolveReadModelInfo(variantName)?.schema;
|
|
854
|
-
contextStack.push(variantSchema);
|
|
855
|
-
indentStack.push(currentIndent);
|
|
856
|
-
eventContextStack.push(/* @__PURE__ */ new Map());
|
|
857
|
-
}
|
|
858
|
-
const entersOnMatch = trimmed.match(/^enters\s+on\s+([\w-]+)/);
|
|
859
|
-
if (entersOnMatch) {
|
|
860
|
-
const eventType = entersOnMatch[1];
|
|
861
|
-
const col = line.indexOf(eventType) + 1;
|
|
862
|
-
if (Object.keys(this.eventSchemas).length > 0 && !this.eventSchemas[eventType]) {
|
|
863
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Event type '${eventType}' not found`));
|
|
864
|
-
}
|
|
865
|
-
if (currentEventContext.has(eventType)) {
|
|
866
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Duplicate event type '${eventType}' - event types can only be used once at each level`));
|
|
867
|
-
} else {
|
|
868
|
-
currentEventContext.set(eventType, { lineNumber, col });
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
const eventsMatch = trimmed.match(/^events\s+([\w-]+)/);
|
|
872
|
-
if (eventsMatch) {
|
|
873
|
-
const eventType = eventsMatch[1];
|
|
874
|
-
const col = line.indexOf(eventType) + 1;
|
|
875
|
-
if (Object.keys(this.eventSchemas).length > 0 && !this.eventSchemas[eventType]) {
|
|
876
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Event type '${eventType}' not found`));
|
|
877
|
-
}
|
|
878
|
-
if (currentEventContext.has(eventType)) {
|
|
879
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Duplicate event type '${eventType}' - event types can only be used once at each level`));
|
|
880
|
-
} else {
|
|
881
|
-
currentEventContext.set(eventType, { lineNumber, col });
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
const removeWithMatch = trimmed.match(/^remove\s+(?:with|via\s+join\s+on)\s+([\w-]+)/);
|
|
885
|
-
if (removeWithMatch) {
|
|
886
|
-
const eventType = removeWithMatch[1];
|
|
887
|
-
const col = line.indexOf(eventType) + 1;
|
|
888
|
-
if (Object.keys(this.eventSchemas).length > 0 && !this.eventSchemas[eventType]) {
|
|
889
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Event type '${eventType}' not found`));
|
|
890
|
-
}
|
|
891
|
-
if (currentEventContext.has(eventType)) {
|
|
892
|
-
markers.push(this.createError(lineNumber, col, col + eventType.length, `Duplicate event type '${eventType}' - event types can only be used once at each level`));
|
|
893
|
-
} else {
|
|
894
|
-
currentEventContext.set(eventType, { lineNumber, col });
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
const assignmentMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
898
|
-
if (assignmentMatch) {
|
|
899
|
-
const currentSchema = contextStack[contextStack.length - 1];
|
|
900
|
-
if (currentSchema?.properties) {
|
|
901
|
-
const [, propertyName] = assignmentMatch;
|
|
902
|
-
if (!currentSchema.properties[propertyName]) {
|
|
903
|
-
const col = line.indexOf(propertyName) + 1;
|
|
904
|
-
const schemaName = this.getSchemaName(currentSchema) || "current schema";
|
|
905
|
-
markers.push(this.createWarning(lineNumber, col, col + propertyName.length, `Property '${propertyName}' not found in ${schemaName}`));
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
const numericOpMatch = trimmed.match(/^(?:increment|decrement|count)\s+(\w+)$/);
|
|
910
|
-
if (numericOpMatch) {
|
|
911
|
-
const currentSchema = contextStack[contextStack.length - 1];
|
|
912
|
-
if (currentSchema?.properties) {
|
|
913
|
-
const [, propertyName] = numericOpMatch;
|
|
914
|
-
if (!currentSchema.properties[propertyName]) {
|
|
915
|
-
const col = line.indexOf(propertyName) + 1;
|
|
916
|
-
const schemaName = this.getSchemaName(currentSchema) || "current schema";
|
|
917
|
-
markers.push(this.createWarning(lineNumber, col, col + propertyName.length, `Property '${propertyName}' not found in ${schemaName}`));
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
const addSubMatch = trimmed.match(/^(?:add|subtract)\s+(\w+)\s+by\s+/);
|
|
922
|
-
if (addSubMatch) {
|
|
923
|
-
const currentSchema = contextStack[contextStack.length - 1];
|
|
924
|
-
if (currentSchema?.properties) {
|
|
925
|
-
const [, propertyName] = addSubMatch;
|
|
926
|
-
if (!currentSchema.properties[propertyName]) {
|
|
927
|
-
const col = line.indexOf(propertyName) + 1;
|
|
928
|
-
const schemaName = this.getSchemaName(currentSchema) || "current schema";
|
|
929
|
-
markers.push(this.createWarning(lineNumber, col, col + propertyName.length, `Property '${propertyName}' not found in ${schemaName}`));
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
const childrenMatch = trimmed.match(/^children\s+(\w+)\s+identified\s+by\s+/);
|
|
934
|
-
if (childrenMatch) {
|
|
935
|
-
const currentSchema = contextStack[contextStack.length - 1];
|
|
936
|
-
if (currentSchema?.properties) {
|
|
937
|
-
const [, propertyName] = childrenMatch;
|
|
938
|
-
const prop = currentSchema.properties[propertyName];
|
|
939
|
-
if (!prop) {
|
|
940
|
-
const col = line.indexOf(propertyName) + 1;
|
|
941
|
-
const schemaName = this.getSchemaName(currentSchema) || "current schema";
|
|
942
|
-
markers.push(this.createWarning(lineNumber, col, col + propertyName.length, `Collection property '${propertyName}' not found in ${schemaName}`));
|
|
943
|
-
} else if (prop.type !== "array" && !prop.items) {
|
|
944
|
-
const col = line.indexOf(propertyName) + 1;
|
|
945
|
-
markers.push(this.createWarning(lineNumber, col, col + propertyName.length, `Property '${propertyName}' is not a collection (array) type`));
|
|
946
|
-
} else {
|
|
947
|
-
const childSchema = prop.items;
|
|
948
|
-
if (childSchema) {
|
|
949
|
-
contextStack.push(childSchema);
|
|
950
|
-
indentStack.push(currentIndent);
|
|
951
|
-
eventContextStack.push(/* @__PURE__ */ new Map());
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
const joinMatch = trimmed.match(/^join\s+(\w+)\s+on\s+(\w+)/);
|
|
957
|
-
if (joinMatch && activeSchema?.properties) {
|
|
958
|
-
const [, collectionName, joinProperty] = joinMatch;
|
|
959
|
-
const collectionProp = activeSchema.properties[collectionName];
|
|
960
|
-
if (!collectionProp) {
|
|
961
|
-
const col = line.indexOf(collectionName) + 1;
|
|
962
|
-
markers.push(this.createWarning(lineNumber, col, col + collectionName.length, `Collection property '${collectionName}' not found in read model '${readModelName}'`));
|
|
963
|
-
} else if (collectionProp.type !== "array" && !collectionProp.items) {
|
|
964
|
-
const col = line.indexOf(collectionName) + 1;
|
|
965
|
-
markers.push(this.createWarning(lineNumber, col, col + collectionName.length, `Property '${collectionName}' is not a collection (array) type`));
|
|
966
|
-
}
|
|
967
|
-
if (!activeSchema.properties[joinProperty]) {
|
|
968
|
-
const col = line.indexOf(joinProperty, line.indexOf("on")) + 1;
|
|
969
|
-
markers.push(this.createWarning(lineNumber, col, col + joinProperty.length, `Property '${joinProperty}' not found in read model '${readModelName}'`));
|
|
970
|
-
}
|
|
971
|
-
eventContextStack.push(/* @__PURE__ */ new Map());
|
|
972
|
-
indentStack.push(currentIndent);
|
|
973
|
-
}
|
|
974
|
-
const keyMatch = trimmed.match(/^key\s+(\w+)\s*$/);
|
|
975
|
-
if (keyMatch && activeSchema) {
|
|
976
|
-
const [, keyName] = keyMatch;
|
|
977
|
-
const isInDefinitions = activeSchema.definitions && activeSchema.definitions[keyName];
|
|
978
|
-
const isProperty = activeSchema.properties && activeSchema.properties[keyName];
|
|
979
|
-
if (!isInDefinitions && !isProperty && !this.isBuiltInExpression(keyName)) {
|
|
980
|
-
const col = line.indexOf(keyName) + 1;
|
|
981
|
-
markers.push(this.createWarning(lineNumber, col, col + keyName.length, `Composite key type '${keyName}' not found in read model schema`));
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
for (const issue of validateEventContextPaths(lines, fenceMap(lines))) {
|
|
986
|
-
markers.push({
|
|
987
|
-
severity: issue.severity === "error" ? 8 : 4,
|
|
988
|
-
startLineNumber: issue.line + 1,
|
|
989
|
-
endLineNumber: issue.line + 1,
|
|
990
|
-
startColumn: issue.startColumn,
|
|
991
|
-
endColumn: issue.endColumn,
|
|
992
|
-
message: issue.message,
|
|
993
|
-
code: issue.code
|
|
994
|
-
});
|
|
995
|
-
}
|
|
996
|
-
return markers;
|
|
997
|
-
}
|
|
998
|
-
resolveReadModelInfo(readModelToken) {
|
|
999
|
-
return this.readModels.find((rm) => rm.identifier === readModelToken || rm.displayName === readModelToken) || null;
|
|
1000
|
-
}
|
|
1001
|
-
getReadModelStartColumn(firstLine, readModelName) {
|
|
1002
|
-
const prefixMatch = firstLine.match(/^\s*projection\s+[\w.]+\s*=>\s*/);
|
|
1003
|
-
if (!prefixMatch) {
|
|
1004
|
-
const fallbackIndex = firstLine.lastIndexOf(readModelName);
|
|
1005
|
-
return (fallbackIndex >= 0 ? fallbackIndex : 0) + 1;
|
|
1006
|
-
}
|
|
1007
|
-
return prefixMatch[0].length + 1;
|
|
1008
|
-
}
|
|
1009
|
-
resolveReadModelIdentifier(readModelToken) {
|
|
1010
|
-
if (this.isDraftReadModel(readModelToken)) {
|
|
1011
|
-
return this.draftReadModel.identifier;
|
|
1012
|
-
}
|
|
1013
|
-
const readModel = this.resolveReadModelInfo(readModelToken);
|
|
1014
|
-
return readModel?.identifier ?? null;
|
|
1015
|
-
}
|
|
1016
|
-
isDraftReadModel(readModelToken) {
|
|
1017
|
-
return !!this.draftReadModel && (this.draftReadModel.identifier === readModelToken || this.draftReadModel.displayName === readModelToken);
|
|
1018
|
-
}
|
|
1019
|
-
isBuiltInExpression(text) {
|
|
1020
|
-
return text.startsWith("$") || ["true", "false", "null"].includes(text);
|
|
1021
|
-
}
|
|
1022
|
-
createError(line, startCol, endCol, message) {
|
|
1023
|
-
return {
|
|
1024
|
-
severity: 8,
|
|
1025
|
-
// Error
|
|
1026
|
-
startLineNumber: line,
|
|
1027
|
-
startColumn: startCol,
|
|
1028
|
-
endLineNumber: line,
|
|
1029
|
-
endColumn: endCol,
|
|
1030
|
-
message
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
createWarning(line, startCol, endCol, message) {
|
|
1034
|
-
return {
|
|
1035
|
-
severity: 4,
|
|
1036
|
-
// Warning
|
|
1037
|
-
startLineNumber: line,
|
|
1038
|
-
startColumn: startCol,
|
|
1039
|
-
endLineNumber: line,
|
|
1040
|
-
endColumn: endCol,
|
|
1041
|
-
message
|
|
1042
|
-
};
|
|
1043
|
-
}
|
|
1044
|
-
createInfo(line, startCol, endCol, message) {
|
|
1045
|
-
return {
|
|
1046
|
-
severity: 2,
|
|
1047
|
-
// Info (hint) - shows as blue/green squiggly
|
|
1048
|
-
startLineNumber: line,
|
|
1049
|
-
startColumn: startCol,
|
|
1050
|
-
endLineNumber: line,
|
|
1051
|
-
endColumn: endCol,
|
|
1052
|
-
message
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
getSchemaName(schema) {
|
|
1056
|
-
if (schema.name) return schema.name;
|
|
1057
|
-
if (schema.title) return schema.title;
|
|
1058
|
-
if (typeof schema.$id === "string") {
|
|
1059
|
-
const parts = schema.$id.split("/");
|
|
1060
|
-
return parts[parts.length - 1] || schema.$id;
|
|
1061
|
-
}
|
|
1062
|
-
return void 0;
|
|
1063
|
-
}
|
|
1064
|
-
};
|
|
10
|
+
${properties.map(p=>`- \`${p.name}\`: ${p.type}`).join(`
|
|
11
|
+
`)}`),{contents:[{value:content}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}}}if(this.isDraftReadModel(wordText)){let description=this.getSchemaDescription(this.draftReadModel.schema),properties=this.getSchemaProperties(this.draftReadModel.schema),identifier=this.draftReadModel.identifier,content=`**Read Model (Draft):** \`${this.draftReadModel.displayName}\`
|
|
1065
12
|
|
|
1066
|
-
|
|
1067
|
-
var HoverProvider = class {
|
|
1068
|
-
readModels = [];
|
|
1069
|
-
eventSchemas = {};
|
|
1070
|
-
draftReadModel = null;
|
|
1071
|
-
setReadModels(readModels) {
|
|
1072
|
-
this.readModels = readModels || [];
|
|
1073
|
-
}
|
|
1074
|
-
// Keep for backwards compatibility
|
|
1075
|
-
setReadModelSchemas(schemas) {
|
|
1076
|
-
this.readModels = schemas.map((schema) => ({
|
|
1077
|
-
identifier: this.getSchemaName(schema) || "",
|
|
1078
|
-
displayName: this.getSchemaName(schema) || "",
|
|
1079
|
-
schema
|
|
1080
|
-
}));
|
|
1081
|
-
}
|
|
1082
|
-
setEventSchemas(schemas) {
|
|
1083
|
-
if (!schemas) {
|
|
1084
|
-
this.eventSchemas = {};
|
|
1085
|
-
return;
|
|
1086
|
-
}
|
|
1087
|
-
if (Array.isArray(schemas)) {
|
|
1088
|
-
const map = {};
|
|
1089
|
-
schemas.forEach((s, i) => {
|
|
1090
|
-
if (!s) return;
|
|
1091
|
-
const name = this.getSchemaName(s) || `Event${i + 1}`;
|
|
1092
|
-
map[name] = s;
|
|
1093
|
-
});
|
|
1094
|
-
this.eventSchemas = map;
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
this.eventSchemas = schemas;
|
|
1098
|
-
}
|
|
1099
|
-
setDraftReadModel(draft) {
|
|
1100
|
-
this.draftReadModel = draft;
|
|
1101
|
-
}
|
|
1102
|
-
provideHover(model, position) {
|
|
1103
|
-
const word = model.getWordAtPosition(position);
|
|
1104
|
-
if (!word) return null;
|
|
1105
|
-
const wordText = word.word;
|
|
1106
|
-
const lineContent = model.getLineContent(position.lineNumber);
|
|
1107
|
-
const projectionNameMatch = this.getProjectionNameAtPosition(lineContent, position.column);
|
|
1108
|
-
if (projectionNameMatch) {
|
|
1109
|
-
return {
|
|
1110
|
-
contents: [{ value: "The name of the projection" }],
|
|
1111
|
-
range: {
|
|
1112
|
-
startLineNumber: position.lineNumber,
|
|
1113
|
-
startColumn: word.startColumn,
|
|
1114
|
-
endLineNumber: position.lineNumber,
|
|
1115
|
-
endColumn: word.endColumn
|
|
1116
|
-
}
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
const eventContextInfo = this.getEventContextInfo(lineContent, word.startColumn);
|
|
1120
|
-
if (eventContextInfo) {
|
|
1121
|
-
return {
|
|
1122
|
-
contents: [{ value: eventContextInfo }],
|
|
1123
|
-
range: {
|
|
1124
|
-
startLineNumber: position.lineNumber,
|
|
1125
|
-
startColumn: word.startColumn,
|
|
1126
|
-
endLineNumber: position.lineNumber,
|
|
1127
|
-
endColumn: word.endColumn
|
|
1128
|
-
}
|
|
1129
|
-
};
|
|
1130
|
-
}
|
|
1131
|
-
const keywordInfo = this.getKeywordInfo(wordText);
|
|
1132
|
-
if (keywordInfo) {
|
|
1133
|
-
return {
|
|
1134
|
-
contents: [{ value: keywordInfo }],
|
|
1135
|
-
range: {
|
|
1136
|
-
startLineNumber: position.lineNumber,
|
|
1137
|
-
startColumn: word.startColumn,
|
|
1138
|
-
endLineNumber: position.lineNumber,
|
|
1139
|
-
endColumn: word.endColumn
|
|
1140
|
-
}
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
const expressionInfo = this.getExpressionInfo(wordText);
|
|
1144
|
-
if (expressionInfo) {
|
|
1145
|
-
return {
|
|
1146
|
-
contents: [{ value: expressionInfo }],
|
|
1147
|
-
range: {
|
|
1148
|
-
startLineNumber: position.lineNumber,
|
|
1149
|
-
startColumn: word.startColumn,
|
|
1150
|
-
endLineNumber: position.lineNumber,
|
|
1151
|
-
endColumn: word.endColumn
|
|
1152
|
-
}
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
if (this.eventSchemas[wordText]) {
|
|
1156
|
-
const schema = this.eventSchemas[wordText];
|
|
1157
|
-
const description = this.getSchemaDescription(schema);
|
|
1158
|
-
const properties = this.getSchemaProperties(schema);
|
|
1159
|
-
let content = `**Event Type:** \`${wordText}\`
|
|
13
|
+
**Identifier:** \`${identifier}\`
|
|
1160
14
|
|
|
1161
|
-
`;
|
|
1162
|
-
if (description) {
|
|
1163
|
-
content += `${description}
|
|
15
|
+
`;return description&&(content+=`${description}
|
|
1164
16
|
|
|
1165
|
-
|
|
1166
|
-
}
|
|
1167
|
-
if (properties.length > 0) {
|
|
1168
|
-
content += `**Properties:**
|
|
17
|
+
`),properties.length>0&&(content+=`**Properties:**
|
|
1169
18
|
|
|
1170
|
-
${properties.map(
|
|
1171
|
-
|
|
1172
|
-
return {
|
|
1173
|
-
contents: [{ value: content }],
|
|
1174
|
-
range: {
|
|
1175
|
-
startLineNumber: position.lineNumber,
|
|
1176
|
-
startColumn: word.startColumn,
|
|
1177
|
-
endLineNumber: position.lineNumber,
|
|
1178
|
-
endColumn: word.endColumn
|
|
1179
|
-
}
|
|
1180
|
-
};
|
|
1181
|
-
}
|
|
1182
|
-
if (this.isDraftReadModel(wordText)) {
|
|
1183
|
-
const description = this.getSchemaDescription(this.draftReadModel.schema);
|
|
1184
|
-
const properties = this.getSchemaProperties(this.draftReadModel.schema);
|
|
1185
|
-
const identifier = this.draftReadModel.identifier;
|
|
1186
|
-
const displayName = this.draftReadModel.displayName;
|
|
1187
|
-
let content = `**Read Model (Draft):** \`${displayName}\`
|
|
19
|
+
${properties.map(p=>`- \`${p.name}\`: ${p.type}`).join(`
|
|
20
|
+
`)}`),{contents:[{value:content}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}}}let readModel=this.resolveReadModelInfo(wordText);if(readModel){let description=this.getSchemaDescription(readModel.schema),properties=this.getSchemaProperties(readModel.schema),content=`**Read Model:** \`${readModel.displayName}\`
|
|
1188
21
|
|
|
1189
|
-
**Identifier:** \`${identifier}\`
|
|
22
|
+
**Identifier:** \`${readModel.identifier}\`
|
|
1190
23
|
|
|
1191
|
-
`;
|
|
1192
|
-
if (description) {
|
|
1193
|
-
content += `${description}
|
|
24
|
+
`;return description&&(content+=`${description}
|
|
1194
25
|
|
|
1195
|
-
|
|
1196
|
-
}
|
|
1197
|
-
if (properties.length > 0) {
|
|
1198
|
-
content += `**Properties:**
|
|
26
|
+
`),properties.length>0&&(content+=`**Properties:**
|
|
1199
27
|
|
|
1200
|
-
${properties.map(
|
|
1201
|
-
|
|
1202
|
-
return {
|
|
1203
|
-
contents: [{ value: content }],
|
|
1204
|
-
range: {
|
|
1205
|
-
startLineNumber: position.lineNumber,
|
|
1206
|
-
startColumn: word.startColumn,
|
|
1207
|
-
endLineNumber: position.lineNumber,
|
|
1208
|
-
endColumn: word.endColumn
|
|
1209
|
-
}
|
|
1210
|
-
};
|
|
1211
|
-
}
|
|
1212
|
-
const readModel = this.resolveReadModelInfo(wordText);
|
|
1213
|
-
if (readModel) {
|
|
1214
|
-
const description = this.getSchemaDescription(readModel.schema);
|
|
1215
|
-
const properties = this.getSchemaProperties(readModel.schema);
|
|
1216
|
-
let content = `**Read Model:** \`${readModel.displayName}\`
|
|
28
|
+
${properties.map(p=>`- \`${p.name}\`: ${p.type}`).join(`
|
|
29
|
+
`)}`),{contents:[{value:content}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}}}let propertyInfo=this.getPropertyInfo(model,position,wordText);return propertyInfo?{contents:[{value:propertyInfo}],range:{startLineNumber:position.lineNumber,startColumn:word.startColumn,endLineNumber:position.lineNumber,endColumn:word.endColumn}}:null}getKeywordInfo(word){return{projection:"**projection** - Defines a projection that transforms events into a read model",from:`**from** *<EventType>*
|
|
1217
30
|
|
|
1218
|
-
**
|
|
31
|
+
Handles specific event types and maps their properties to the read model.`,all:"**all**\n\nSubscribes to **all event types in the system**, regardless of explicit `from` blocks. Mappings inside `all` run for every event that arrives system-wide.",every:"**every**\n\nHandles all events the projection **explicitly subscribes to** through `from` blocks, with automatic property mapping.",key:`**key** *<expression>*
|
|
32
|
+
|
|
33
|
+
Defines the projection key or composite key type.`,parent:`**parent** *<expression>*
|
|
34
|
+
|
|
35
|
+
Defines a parent relationship for hierarchical projections.`,join:`**join** *<collection>* **on** *<property>*
|
|
36
|
+
|
|
37
|
+
Joins with a child collection based on a property.`,children:`**children** *<collection>* **id** *<expression>*
|
|
38
|
+
|
|
39
|
+
Defines operations on child collections.`,remove:`**remove with** *<EventType>* or **remove via join on** *<EventType>*
|
|
40
|
+
|
|
41
|
+
Removes items from the read model based on events.`,clear:`**clear** *<property>*
|
|
42
|
+
|
|
43
|
+
Removes the value a property holds. **clear with** *<EventType>* nulls a whole nested object instead.`,automap:`**automap** or **no automap**
|
|
44
|
+
|
|
45
|
+
Controls automatic property mapping from events to the read model.`,increment:`**increment** *<property>*
|
|
46
|
+
|
|
47
|
+
Increments a numeric property by 1.`,decrement:`**decrement** *<property>*
|
|
48
|
+
|
|
49
|
+
Decrements a numeric property by 1.`,count:`**count** *<property>*
|
|
50
|
+
|
|
51
|
+
Counts occurrences and stores in a property.`,add:`**add** *<property>* **by** *<expression>*
|
|
52
|
+
|
|
53
|
+
Adds a value to a property.`,subtract:`**subtract** *<property>* **by** *<expression>*
|
|
54
|
+
|
|
55
|
+
Subtracts a value from a property.`,exclude:`**exclude children**
|
|
1219
56
|
|
|
1220
|
-
|
|
1221
|
-
if (description) {
|
|
1222
|
-
content += `${description}
|
|
57
|
+
Excludes child collections from automatic mapping.`,events:`**events** *<EventType>*, ...
|
|
1223
58
|
|
|
1224
|
-
|
|
1225
|
-
}
|
|
1226
|
-
if (properties.length > 0) {
|
|
1227
|
-
content += `**Properties:**
|
|
59
|
+
Specifies which event types to include in a join.`,via:"Used with **remove via join on** to remove items via a join relationship.",with:"Used with **remove with** to specify which event type triggers removal.",on:"Used with **join** to specify the join property.",by:"Used with **add** or **subtract** to specify the value.",id:"Used with **children** to specify the child identifier expression.",no:"Used with **no automap** to disable automatic property mapping.",variant:`**variant** *<Name>*
|
|
1228
60
|
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
return {
|
|
1232
|
-
contents: [{ value: content }],
|
|
1233
|
-
range: {
|
|
1234
|
-
startLineNumber: position.lineNumber,
|
|
1235
|
-
startColumn: word.startColumn,
|
|
1236
|
-
endLineNumber: position.lineNumber,
|
|
1237
|
-
endColumn: word.endColumn
|
|
1238
|
-
}
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
const propertyInfo = this.getPropertyInfo(model, position, wordText);
|
|
1242
|
-
if (propertyInfo) {
|
|
1243
|
-
return {
|
|
1244
|
-
contents: [{ value: propertyInfo }],
|
|
1245
|
-
range: {
|
|
1246
|
-
startLineNumber: position.lineNumber,
|
|
1247
|
-
startColumn: word.startColumn,
|
|
1248
|
-
endLineNumber: position.lineNumber,
|
|
1249
|
-
endColumn: word.endColumn
|
|
1250
|
-
}
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
return null;
|
|
1254
|
-
}
|
|
1255
|
-
getKeywordInfo(word) {
|
|
1256
|
-
const keywords = {
|
|
1257
|
-
"projection": "**projection** - Defines a projection that transforms events into a read model",
|
|
1258
|
-
"from": "**from** *<EventType>*\n\nHandles specific event types and maps their properties to the read model.",
|
|
1259
|
-
"all": "**all**\n\nSubscribes to **all event types in the system**, regardless of explicit `from` blocks. Mappings inside `all` run for every event that arrives system-wide.",
|
|
1260
|
-
"every": "**every**\n\nHandles all events the projection **explicitly subscribes to** through `from` blocks, with automatic property mapping.",
|
|
1261
|
-
"key": "**key** *<expression>*\n\nDefines the projection key or composite key type.",
|
|
1262
|
-
"parent": "**parent** *<expression>*\n\nDefines a parent relationship for hierarchical projections.",
|
|
1263
|
-
"join": "**join** *<collection>* **on** *<property>*\n\nJoins with a child collection based on a property.",
|
|
1264
|
-
"children": "**children** *<collection>* **id** *<expression>*\n\nDefines operations on child collections.",
|
|
1265
|
-
"remove": "**remove with** *<EventType>* or **remove via join on** *<EventType>*\n\nRemoves items from the read model based on events.",
|
|
1266
|
-
"clear": "**clear** *<property>*\n\nRemoves the value a property holds. **clear with** *<EventType>* nulls a whole nested object instead.",
|
|
1267
|
-
"automap": "**automap** or **no automap**\n\nControls automatic property mapping from events to the read model.",
|
|
1268
|
-
"increment": "**increment** *<property>*\n\nIncrements a numeric property by 1.",
|
|
1269
|
-
"decrement": "**decrement** *<property>*\n\nDecrements a numeric property by 1.",
|
|
1270
|
-
"count": "**count** *<property>*\n\nCounts occurrences and stores in a property.",
|
|
1271
|
-
"add": "**add** *<property>* **by** *<expression>*\n\nAdds a value to a property.",
|
|
1272
|
-
"subtract": "**subtract** *<property>* **by** *<expression>*\n\nSubtracts a value from a property.",
|
|
1273
|
-
"exclude": "**exclude children**\n\nExcludes child collections from automatic mapping.",
|
|
1274
|
-
"events": "**events** *<EventType>*, ...\n\nSpecifies which event types to include in a join.",
|
|
1275
|
-
"via": "Used with **remove via join on** to remove items via a join relationship.",
|
|
1276
|
-
"with": "Used with **remove with** to specify which event type triggers removal.",
|
|
1277
|
-
"on": "Used with **join** to specify the join property.",
|
|
1278
|
-
"by": "Used with **add** or **subtract** to specify the value.",
|
|
1279
|
-
"id": "Used with **children** to specify the child identifier expression.",
|
|
1280
|
-
"no": "Used with **no automap** to disable automatic property mapping.",
|
|
1281
|
-
"variant": "**variant** *<Name>*\n\nDeclares one of several mutually exclusive named read models sharing the projection's identity. Entering one variant removes the entity from every other variant of the same projection.",
|
|
1282
|
-
"enters": "**enters on** *<EventType>*\n\nDeclares the event that activates this variant - the only event allowed to create it. Every other event the variant subscribes to is update-only."
|
|
1283
|
-
};
|
|
1284
|
-
return keywords[word] || null;
|
|
1285
|
-
}
|
|
1286
|
-
getExpressionInfo(word) {
|
|
1287
|
-
const expressions = {
|
|
1288
|
-
"$eventSourceId": "**$eventSourceId**\n\nThe identifier of the event source (aggregate/entity) that generated the event.",
|
|
1289
|
-
"$causedBy": "**$causedBy**\n\nThe identity that caused the event - `subject`, `name`, `userName` or `onBehalfOf`, itself an identity.\n\nChronicle cannot yet evaluate this short form (Cratis/Chronicle#4119); prefer **$eventContext.causedBy**.*property*.",
|
|
1290
|
-
"$eventContext": describeEventContext()
|
|
1291
|
-
};
|
|
1292
|
-
return expressions[word] || null;
|
|
1293
|
-
}
|
|
1294
|
-
// Describes the $eventContext path segment starting at the given column, if the word there is one.
|
|
1295
|
-
getEventContextInfo(lineContent, startColumn) {
|
|
1296
|
-
for (const match of lineContent.matchAll(/\$eventContext((?:\.\w+(?:\(\))?)*)/g)) {
|
|
1297
|
-
let column = (match.index ?? 0) + 1;
|
|
1298
|
-
if (startColumn === column) return describeEventContext();
|
|
1299
|
-
column += "$eventContext".length;
|
|
1300
|
-
const segments = match[1].split(".").slice(1);
|
|
1301
|
-
for (let index = 0; index < segments.length; index++) {
|
|
1302
|
-
column += 1;
|
|
1303
|
-
if (startColumn === column) {
|
|
1304
|
-
const path = segments.slice(0, index + 1);
|
|
1305
|
-
const member = eventContextMemberAt(path);
|
|
1306
|
-
return member ? describeEventContextMember(path.join("."), member) : null;
|
|
1307
|
-
}
|
|
1308
|
-
column += segments[index].length;
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
return null;
|
|
1312
|
-
}
|
|
1313
|
-
getPropertyInfo(model, position, propertyName) {
|
|
1314
|
-
const firstLine = model.getLineContent(1).trim();
|
|
1315
|
-
const match = firstLine.match(/^projection\s+[\w.]+\s*=>\s*([\w.]+)/);
|
|
1316
|
-
if (!match) return null;
|
|
1317
|
-
const readModelName = match[1];
|
|
1318
|
-
const draftMatch = this.isDraftReadModel(readModelName) ? this.draftReadModel : null;
|
|
1319
|
-
const resolvedReadModel = draftMatch ? null : this.resolveReadModelInfo(readModelName);
|
|
1320
|
-
const activeSchema = draftMatch?.schema || resolvedReadModel?.schema;
|
|
1321
|
-
if (activeSchema?.properties && activeSchema.properties[propertyName]) {
|
|
1322
|
-
const prop = activeSchema.properties[propertyName];
|
|
1323
|
-
const type = this.getPropertyTypeDescription(prop);
|
|
1324
|
-
const description = prop.description;
|
|
1325
|
-
let content = `**Property:** \`${propertyName}\`
|
|
61
|
+
Declares one of several mutually exclusive named read models sharing the projection's identity. Entering one variant removes the entity from every other variant of the same projection.`,enters:`**enters on** *<EventType>*
|
|
1326
62
|
|
|
1327
|
-
|
|
1328
|
-
if (description) {
|
|
1329
|
-
content += `
|
|
63
|
+
Declares the event that activates this variant - the only event allowed to create it. Every other event the variant subscribes to is update-only.`}[word]||null}getExpressionInfo(word){return{$eventSourceId:`**$eventSourceId**
|
|
1330
64
|
|
|
1331
|
-
|
|
1332
|
-
}
|
|
1333
|
-
return content;
|
|
1334
|
-
}
|
|
1335
|
-
const eventType = this.getCurrentEventType(model, position.lineNumber);
|
|
1336
|
-
if (eventType && this.eventSchemas[eventType]) {
|
|
1337
|
-
const eventSchema = this.eventSchemas[eventType];
|
|
1338
|
-
if (eventSchema.properties && eventSchema.properties[propertyName]) {
|
|
1339
|
-
const prop = eventSchema.properties[propertyName];
|
|
1340
|
-
const type = this.getPropertyTypeDescription(prop);
|
|
1341
|
-
const description = prop.description;
|
|
1342
|
-
let content = `**Event Property:** \`${propertyName}\`
|
|
65
|
+
The identifier of the event source (aggregate/entity) that generated the event.`,$causedBy:"**$causedBy**\n\nThe identity that caused the event - `subject`, `name`, `userName` or `onBehalfOf`, itself an identity.\n\nChronicle cannot yet evaluate this short form (Cratis/Chronicle#4119); prefer **$eventContext.causedBy**.*property*.",$eventContext:describeEventContext()}[word]||null}getEventContextInfo(lineContent,startColumn){for(let match of lineContent.matchAll(/\$eventContext((?:\.\w+(?:\(\))?)*)/g)){let column=(match.index??0)+1;if(startColumn===column)return describeEventContext();column+=13;let segments=match[1].split(".").slice(1);for(let index=0;index<segments.length;index++){if(column+=1,startColumn===column){let path=segments.slice(0,index+1),member=eventContextMemberAt(path);return member?describeEventContextMember(path.join("."),member):null}column+=segments[index].length}}return null}getPropertyInfo(model,position,propertyName){let match=model.getLineContent(1).trim().match(/^projection\s+[\w.]+\s*=>\s*([\w.]+)/);if(!match)return null;let readModelName=match[1],draftMatch=this.isDraftReadModel(readModelName)?this.draftReadModel:null,resolvedReadModel=draftMatch?null:this.resolveReadModelInfo(readModelName),activeSchema=draftMatch?.schema||resolvedReadModel?.schema;if(activeSchema?.properties&&activeSchema.properties[propertyName]){let prop=activeSchema.properties[propertyName],type=this.getPropertyTypeDescription(prop),description=prop.description,content=`**Property:** \`${propertyName}\`
|
|
1343
66
|
|
|
1344
|
-
**Type:** \`${type}\``;
|
|
1345
|
-
if (description) {
|
|
1346
|
-
content += `
|
|
67
|
+
**Type:** \`${type}\``;return description&&(content+=`
|
|
1347
68
|
|
|
1348
|
-
${description}
|
|
1349
|
-
}
|
|
1350
|
-
return content;
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
return null;
|
|
1354
|
-
}
|
|
1355
|
-
resolveReadModelInfo(readModelToken) {
|
|
1356
|
-
return this.readModels.find((rm) => rm.identifier === readModelToken || rm.displayName === readModelToken) || null;
|
|
1357
|
-
}
|
|
1358
|
-
isDraftReadModel(readModelToken) {
|
|
1359
|
-
return !!this.draftReadModel && (this.draftReadModel.identifier === readModelToken || this.draftReadModel.displayName === readModelToken);
|
|
1360
|
-
}
|
|
1361
|
-
getCurrentEventType(model, lineNumber) {
|
|
1362
|
-
for (let i = lineNumber; i >= 1; i--) {
|
|
1363
|
-
const line = model.getLineContent(i).trim();
|
|
1364
|
-
const fromMatch = line.match(/^from\s+([\w-]+)/);
|
|
1365
|
-
if (fromMatch) {
|
|
1366
|
-
return fromMatch[1];
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
return null;
|
|
1370
|
-
}
|
|
1371
|
-
getProjectionNameAtPosition(lineContent, column) {
|
|
1372
|
-
const projectionMatch = lineContent.match(/^\s*projection\s+(\S+)\s*=>/);
|
|
1373
|
-
if (!projectionMatch) return false;
|
|
1374
|
-
const projectionKeywordEnd = lineContent.indexOf("projection") + "projection".length;
|
|
1375
|
-
const arrowStart = lineContent.indexOf("=>");
|
|
1376
|
-
return column > projectionKeywordEnd && column <= arrowStart;
|
|
1377
|
-
}
|
|
1378
|
-
getSchemaName(schema) {
|
|
1379
|
-
if (schema.name) return schema.name;
|
|
1380
|
-
if (schema.title) return schema.title;
|
|
1381
|
-
if (typeof schema.$id === "string") {
|
|
1382
|
-
const parts = schema.$id.split("/");
|
|
1383
|
-
return parts[parts.length - 1] || schema.$id;
|
|
1384
|
-
}
|
|
1385
|
-
return void 0;
|
|
1386
|
-
}
|
|
1387
|
-
getSchemaDescription(schema) {
|
|
1388
|
-
return schema.description || void 0;
|
|
1389
|
-
}
|
|
1390
|
-
getSchemaProperties(schema) {
|
|
1391
|
-
if (!schema.properties) return [];
|
|
1392
|
-
return Object.entries(schema.properties).map(([name, prop]) => ({
|
|
1393
|
-
name,
|
|
1394
|
-
type: this.getPropertyTypeDescription(prop)
|
|
1395
|
-
}));
|
|
1396
|
-
}
|
|
1397
|
-
getPropertyTypeDescription(propSchema) {
|
|
1398
|
-
if (propSchema.type === "array" && propSchema.items) {
|
|
1399
|
-
const itemType = propSchema.items.type || propSchema.items.$ref?.split("/").pop() || "unknown";
|
|
1400
|
-
return `${itemType}[]`;
|
|
1401
|
-
}
|
|
1402
|
-
return propSchema.type || propSchema.$ref?.split("/").pop() || "unknown";
|
|
1403
|
-
}
|
|
1404
|
-
};
|
|
69
|
+
${description}`),content}let eventType=this.getCurrentEventType(model,position.lineNumber);if(eventType&&this.eventSchemas[eventType]){let eventSchema=this.eventSchemas[eventType];if(eventSchema.properties&&eventSchema.properties[propertyName]){let prop=eventSchema.properties[propertyName],type=this.getPropertyTypeDescription(prop),description=prop.description,content=`**Event Property:** \`${propertyName}\`
|
|
1405
70
|
|
|
1406
|
-
|
|
1407
|
-
var CodeActionProvider = class {
|
|
1408
|
-
readModels = [];
|
|
1409
|
-
draftReadModel = null;
|
|
1410
|
-
onCreateReadModel;
|
|
1411
|
-
onEditReadModel;
|
|
1412
|
-
setReadModels(readModels) {
|
|
1413
|
-
this.readModels = readModels;
|
|
1414
|
-
}
|
|
1415
|
-
setDraftReadModel(draft) {
|
|
1416
|
-
this.draftReadModel = draft;
|
|
1417
|
-
}
|
|
1418
|
-
// Keep for backwards compatibility
|
|
1419
|
-
setReadModelSchemas(schemas) {
|
|
1420
|
-
this.readModels = schemas.map((schema) => ({
|
|
1421
|
-
identifier: schema.title || schema.name || "",
|
|
1422
|
-
displayName: schema.title || schema.name || "",
|
|
1423
|
-
schema
|
|
1424
|
-
}));
|
|
1425
|
-
}
|
|
1426
|
-
setCreateReadModelCallback(callback) {
|
|
1427
|
-
this.onCreateReadModel = callback;
|
|
1428
|
-
}
|
|
1429
|
-
setEditReadModelCallback(callback) {
|
|
1430
|
-
this.onEditReadModel = callback;
|
|
1431
|
-
}
|
|
1432
|
-
invokeCreateReadModel(readModelName) {
|
|
1433
|
-
this.onCreateReadModel?.(readModelName);
|
|
1434
|
-
}
|
|
1435
|
-
invokeEditReadModel(readModelName, currentSchema) {
|
|
1436
|
-
this.onEditReadModel?.(readModelName, currentSchema);
|
|
1437
|
-
}
|
|
1438
|
-
provideCodeActions(_model, _range, context) {
|
|
1439
|
-
const actions = [];
|
|
1440
|
-
const notFoundDiagnostics = context.markers.filter(
|
|
1441
|
-
(marker) => marker.message.includes("Read model") && marker.message.includes("not found")
|
|
1442
|
-
);
|
|
1443
|
-
const draftDiagnostics = context.markers.filter(
|
|
1444
|
-
(marker) => marker.message.includes("Read model") && marker.message.includes("is a draft")
|
|
1445
|
-
);
|
|
1446
|
-
for (const diagnostic of notFoundDiagnostics) {
|
|
1447
|
-
const match = diagnostic.message.match(/Read model ['"]([\w.]+)['"]/);
|
|
1448
|
-
if (match && match[1]) {
|
|
1449
|
-
const readModelName = match[1];
|
|
1450
|
-
const existsAsPersisted = this.readModels.some(
|
|
1451
|
-
(rm) => rm.identifier === readModelName || rm.displayName === readModelName
|
|
1452
|
-
);
|
|
1453
|
-
if (!existsAsPersisted && this.onCreateReadModel) {
|
|
1454
|
-
actions.push({
|
|
1455
|
-
title: `Create read model '${readModelName}'`,
|
|
1456
|
-
diagnostics: [diagnostic],
|
|
1457
|
-
kind: "quickfix",
|
|
1458
|
-
edit: void 0,
|
|
1459
|
-
isPreferred: true,
|
|
1460
|
-
command: {
|
|
1461
|
-
id: "pdl.createReadModel",
|
|
1462
|
-
title: `Create ${readModelName}`,
|
|
1463
|
-
arguments: [readModelName]
|
|
1464
|
-
}
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
for (const diagnostic of draftDiagnostics) {
|
|
1470
|
-
const match = diagnostic.message.match(/Read model ['"]([\w.]+)['"]/);
|
|
1471
|
-
if (match && match[1]) {
|
|
1472
|
-
const readModelName = match[1];
|
|
1473
|
-
if (this.draftReadModel && this.isDraftReadModel(readModelName) && this.onEditReadModel) {
|
|
1474
|
-
actions.push({
|
|
1475
|
-
title: `Edit read model '${readModelName}'`,
|
|
1476
|
-
diagnostics: [diagnostic],
|
|
1477
|
-
kind: "quickfix",
|
|
1478
|
-
edit: void 0,
|
|
1479
|
-
isPreferred: true,
|
|
1480
|
-
command: {
|
|
1481
|
-
id: "pdl.editReadModel",
|
|
1482
|
-
title: `Edit ${readModelName}`,
|
|
1483
|
-
arguments: [readModelName, this.draftReadModel.schema]
|
|
1484
|
-
}
|
|
1485
|
-
});
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
return {
|
|
1490
|
-
actions,
|
|
1491
|
-
dispose: () => {
|
|
1492
|
-
}
|
|
1493
|
-
};
|
|
1494
|
-
}
|
|
1495
|
-
isDraftReadModel(readModelToken) {
|
|
1496
|
-
return !!this.draftReadModel && (this.draftReadModel.identifier === readModelToken || this.draftReadModel.displayName === readModelToken);
|
|
1497
|
-
}
|
|
1498
|
-
};
|
|
71
|
+
**Type:** \`${type}\``;return description&&(content+=`
|
|
1499
72
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
var completionProvider;
|
|
1503
|
-
var hoverProvider;
|
|
1504
|
-
var codeActionProvider;
|
|
1505
|
-
var disposables = [];
|
|
1506
|
-
var monacoInstance = null;
|
|
1507
|
-
var isRegistered = false;
|
|
1508
|
-
var pendingCreateReadModelCallback = null;
|
|
1509
|
-
var pendingEditReadModelCallback = null;
|
|
1510
|
-
function registerProjectionLanguage(monaco) {
|
|
1511
|
-
if (!isRegistered) {
|
|
1512
|
-
isRegistered = true;
|
|
1513
|
-
monacoInstance = monaco;
|
|
1514
|
-
monaco.languages.register({ id: languageId });
|
|
1515
|
-
monaco.languages.setLanguageConfiguration(languageId, configuration);
|
|
1516
|
-
monaco.languages.setMonarchTokensProvider(languageId, monarchLanguage);
|
|
1517
|
-
validator = new Validator();
|
|
1518
|
-
completionProvider = new CompletionProvider();
|
|
1519
|
-
hoverProvider = new HoverProvider();
|
|
1520
|
-
codeActionProvider = new CodeActionProvider();
|
|
1521
|
-
if (pendingCreateReadModelCallback) {
|
|
1522
|
-
codeActionProvider.setCreateReadModelCallback(pendingCreateReadModelCallback);
|
|
1523
|
-
pendingCreateReadModelCallback = null;
|
|
1524
|
-
}
|
|
1525
|
-
if (pendingEditReadModelCallback) {
|
|
1526
|
-
codeActionProvider.setEditReadModelCallback(pendingEditReadModelCallback);
|
|
1527
|
-
pendingEditReadModelCallback = null;
|
|
1528
|
-
}
|
|
1529
|
-
disposables.push(monaco.languages.registerCompletionItemProvider(languageId, {
|
|
1530
|
-
provideCompletionItems: completionProvider.provideCompletionItems.bind(completionProvider),
|
|
1531
|
-
triggerCharacters: [".", " ", "=", "\n", "$"]
|
|
1532
|
-
}));
|
|
1533
|
-
disposables.push(monaco.languages.registerHoverProvider(languageId, {
|
|
1534
|
-
provideHover: hoverProvider.provideHover.bind(hoverProvider)
|
|
1535
|
-
}));
|
|
1536
|
-
disposables.push(monaco.languages.registerCodeActionProvider(languageId, {
|
|
1537
|
-
provideCodeActions: codeActionProvider.provideCodeActions.bind(codeActionProvider)
|
|
1538
|
-
}, {
|
|
1539
|
-
providedCodeActionKinds: ["quickfix"]
|
|
1540
|
-
}));
|
|
1541
|
-
disposables.push(monaco.editor.registerCommand("pdl.createReadModel", (_accessor, readModelName) => {
|
|
1542
|
-
codeActionProvider?.invokeCreateReadModel(readModelName);
|
|
1543
|
-
}));
|
|
1544
|
-
disposables.push(monaco.editor.registerCommand("pdl.editReadModel", (_accessor, readModelName, currentSchema) => {
|
|
1545
|
-
codeActionProvider?.invokeEditReadModel(readModelName, currentSchema);
|
|
1546
|
-
}));
|
|
1547
|
-
disposables.push(monaco.editor.onDidCreateModel((model) => {
|
|
1548
|
-
if (model.getLanguageId() === languageId) {
|
|
1549
|
-
validateModel(monaco, model);
|
|
1550
|
-
disposables.push(model.onDidChangeContent(() => validateModel(monaco, model)));
|
|
1551
|
-
}
|
|
1552
|
-
}));
|
|
1553
|
-
monaco.editor.getModels().forEach((model) => {
|
|
1554
|
-
if (model.getLanguageId() === languageId) {
|
|
1555
|
-
validateModel(monaco, model);
|
|
1556
|
-
}
|
|
1557
|
-
});
|
|
1558
|
-
}
|
|
1559
|
-
return { dispose };
|
|
1560
|
-
}
|
|
1561
|
-
function dispose() {
|
|
1562
|
-
disposables.forEach((disposable) => disposable.dispose());
|
|
1563
|
-
disposables = [];
|
|
1564
|
-
isRegistered = false;
|
|
1565
|
-
monacoInstance = null;
|
|
1566
|
-
}
|
|
1567
|
-
function setReadModelSchema(schema) {
|
|
1568
|
-
validator?.setReadModelSchemas([schema]);
|
|
1569
|
-
completionProvider?.setReadModelSchemas([schema]);
|
|
1570
|
-
}
|
|
1571
|
-
function setReadModelSchemas(readModels) {
|
|
1572
|
-
validator?.setReadModels(readModels);
|
|
1573
|
-
completionProvider?.setReadModels(readModels);
|
|
1574
|
-
hoverProvider?.setReadModels(readModels);
|
|
1575
|
-
codeActionProvider?.setReadModels(readModels);
|
|
1576
|
-
revalidateAllModels();
|
|
1577
|
-
}
|
|
1578
|
-
function setCreateReadModelCallback(callback) {
|
|
1579
|
-
if (codeActionProvider) {
|
|
1580
|
-
codeActionProvider.setCreateReadModelCallback(callback);
|
|
1581
|
-
} else {
|
|
1582
|
-
pendingCreateReadModelCallback = callback;
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
function setEditReadModelCallback(callback) {
|
|
1586
|
-
if (codeActionProvider) {
|
|
1587
|
-
codeActionProvider.setEditReadModelCallback(callback);
|
|
1588
|
-
} else {
|
|
1589
|
-
pendingEditReadModelCallback = callback;
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
function setDraftReadModel(draft) {
|
|
1593
|
-
codeActionProvider?.setDraftReadModel(draft);
|
|
1594
|
-
validator?.setDraftReadModel(draft);
|
|
1595
|
-
hoverProvider?.setDraftReadModel(draft);
|
|
1596
|
-
revalidateAllModels();
|
|
1597
|
-
}
|
|
1598
|
-
function setEventSchemas(eventSchemas) {
|
|
1599
|
-
const normalize = (input) => {
|
|
1600
|
-
if (!input) return {};
|
|
1601
|
-
if (Array.isArray(input)) {
|
|
1602
|
-
const out = {};
|
|
1603
|
-
input.forEach((s, i) => {
|
|
1604
|
-
if (!s) return;
|
|
1605
|
-
const name = s.title || s.name || (typeof s.$id === "string" ? s.$id.split("/").pop() : void 0) || `Event${i + 1}`;
|
|
1606
|
-
out[name] = s;
|
|
1607
|
-
});
|
|
1608
|
-
return out;
|
|
1609
|
-
}
|
|
1610
|
-
return input;
|
|
1611
|
-
};
|
|
1612
|
-
const normalized = normalize(eventSchemas);
|
|
1613
|
-
validator?.setEventSchemas(normalized);
|
|
1614
|
-
completionProvider?.setEventSchemas(normalized);
|
|
1615
|
-
hoverProvider?.setEventSchemas(normalized);
|
|
1616
|
-
revalidateAllModels();
|
|
1617
|
-
}
|
|
1618
|
-
function setEventSequences(sequences) {
|
|
1619
|
-
completionProvider?.setEventSequences(sequences);
|
|
1620
|
-
}
|
|
1621
|
-
function validateModel(monaco, model) {
|
|
1622
|
-
if (validator && !model.isDisposed()) {
|
|
1623
|
-
const markers = validator.validate(model);
|
|
1624
|
-
monaco.editor.setModelMarkers(model, "pdl-validator", markers);
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
function revalidateAllModels() {
|
|
1628
|
-
if (monacoInstance) {
|
|
1629
|
-
monacoInstance.editor.getModels().forEach((model) => {
|
|
1630
|
-
if (model.getLanguageId() === languageId) {
|
|
1631
|
-
validateModel(monacoInstance, model);
|
|
1632
|
-
}
|
|
1633
|
-
});
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
export {
|
|
1637
|
-
languageId,
|
|
1638
|
-
registerProjectionLanguage,
|
|
1639
|
-
setCreateReadModelCallback,
|
|
1640
|
-
setDraftReadModel,
|
|
1641
|
-
setEditReadModelCallback,
|
|
1642
|
-
setEventSchemas,
|
|
1643
|
-
setEventSequences,
|
|
1644
|
-
setReadModelSchema,
|
|
1645
|
-
setReadModelSchemas
|
|
1646
|
-
};
|
|
73
|
+
${description}`),content}}return null}resolveReadModelInfo(readModelToken){return this.readModels.find(rm=>rm.identifier===readModelToken||rm.displayName===readModelToken)||null}isDraftReadModel(readModelToken){return!!this.draftReadModel&&(this.draftReadModel.identifier===readModelToken||this.draftReadModel.displayName===readModelToken)}getCurrentEventType(model,lineNumber){for(let i=lineNumber;i>=1;i--){let fromMatch=model.getLineContent(i).trim().match(/^from\s+([\w-]+)/);if(fromMatch)return fromMatch[1]}return null}getProjectionNameAtPosition(lineContent,column){if(!lineContent.match(/^\s*projection\s+(\S+)\s*=>/))return!1;let projectionKeywordEnd=lineContent.indexOf("projection")+10,arrowStart=lineContent.indexOf("=>");return column>projectionKeywordEnd&&column<=arrowStart}getSchemaName(schema){if(schema.name)return schema.name;if(schema.title)return schema.title;if(typeof schema.$id=="string"){let parts=schema.$id.split("/");return parts[parts.length-1]||schema.$id}}getSchemaDescription(schema){return schema.description||void 0}getSchemaProperties(schema){return schema.properties?Object.entries(schema.properties).map(([name,prop])=>({name,type:this.getPropertyTypeDescription(prop)})):[]}getPropertyTypeDescription(propSchema){return propSchema.type==="array"&&propSchema.items?`${propSchema.items.type||propSchema.items.$ref?.split("/").pop()||"unknown"}[]`:propSchema.type||propSchema.$ref?.split("/").pop()||"unknown"}};var CodeActionProvider=class{readModels=[];draftReadModel=null;onCreateReadModel;onEditReadModel;setReadModels(readModels){this.readModels=readModels}setDraftReadModel(draft){this.draftReadModel=draft}setReadModelSchemas(schemas){this.readModels=schemas.map(schema=>({identifier:schema.title||schema.name||"",displayName:schema.title||schema.name||"",schema}))}setCreateReadModelCallback(callback){this.onCreateReadModel=callback}setEditReadModelCallback(callback){this.onEditReadModel=callback}invokeCreateReadModel(readModelName){this.onCreateReadModel?.(readModelName)}invokeEditReadModel(readModelName,currentSchema){this.onEditReadModel?.(readModelName,currentSchema)}provideCodeActions(_model,_range,context){let actions=[],notFoundDiagnostics=context.markers.filter(marker=>marker.message.includes("Read model")&&marker.message.includes("not found")),draftDiagnostics=context.markers.filter(marker=>marker.message.includes("Read model")&&marker.message.includes("is a draft"));for(let diagnostic of notFoundDiagnostics){let match=diagnostic.message.match(/Read model ['"]([\w.]+)['"]/);if(match&&match[1]){let readModelName=match[1];!this.readModels.some(rm=>rm.identifier===readModelName||rm.displayName===readModelName)&&this.onCreateReadModel&&actions.push({title:`Create read model '${readModelName}'`,diagnostics:[diagnostic],kind:"quickfix",edit:void 0,isPreferred:!0,command:{id:"pdl.createReadModel",title:`Create ${readModelName}`,arguments:[readModelName]}})}}for(let diagnostic of draftDiagnostics){let match=diagnostic.message.match(/Read model ['"]([\w.]+)['"]/);if(match&&match[1]){let readModelName=match[1];this.draftReadModel&&this.isDraftReadModel(readModelName)&&this.onEditReadModel&&actions.push({title:`Edit read model '${readModelName}'`,diagnostics:[diagnostic],kind:"quickfix",edit:void 0,isPreferred:!0,command:{id:"pdl.editReadModel",title:`Edit ${readModelName}`,arguments:[readModelName,this.draftReadModel.schema]}})}}return{actions,dispose:()=>{}}}isDraftReadModel(readModelToken){return!!this.draftReadModel&&(this.draftReadModel.identifier===readModelToken||this.draftReadModel.displayName===readModelToken)}};var validator,completionProvider,hoverProvider,codeActionProvider,disposables=[],monacoInstance=null,isRegistered=!1,pendingCreateReadModelCallback=null,pendingEditReadModelCallback=null;function registerProjectionLanguage(monaco){return isRegistered||(isRegistered=!0,monacoInstance=monaco,monaco.languages.register({id:"pdl"}),monaco.languages.setLanguageConfiguration("pdl",configuration),monaco.languages.setMonarchTokensProvider("pdl",monarchLanguage),validator=new Validator,completionProvider=new CompletionProvider,hoverProvider=new HoverProvider,codeActionProvider=new CodeActionProvider,pendingCreateReadModelCallback&&(codeActionProvider.setCreateReadModelCallback(pendingCreateReadModelCallback),pendingCreateReadModelCallback=null),pendingEditReadModelCallback&&(codeActionProvider.setEditReadModelCallback(pendingEditReadModelCallback),pendingEditReadModelCallback=null),disposables.push(monaco.languages.registerCompletionItemProvider("pdl",{provideCompletionItems:completionProvider.provideCompletionItems.bind(completionProvider),triggerCharacters:["."," ","=",`
|
|
74
|
+
`,"$"]})),disposables.push(monaco.languages.registerHoverProvider("pdl",{provideHover:hoverProvider.provideHover.bind(hoverProvider)})),disposables.push(monaco.languages.registerCodeActionProvider("pdl",{provideCodeActions:codeActionProvider.provideCodeActions.bind(codeActionProvider)},{providedCodeActionKinds:["quickfix"]})),disposables.push(monaco.editor.registerCommand("pdl.createReadModel",(_accessor,readModelName)=>{codeActionProvider?.invokeCreateReadModel(readModelName)})),disposables.push(monaco.editor.registerCommand("pdl.editReadModel",(_accessor,readModelName,currentSchema)=>{codeActionProvider?.invokeEditReadModel(readModelName,currentSchema)})),disposables.push(monaco.editor.onDidCreateModel(model=>{model.getLanguageId()==="pdl"&&(validateModel(monaco,model),disposables.push(model.onDidChangeContent(()=>validateModel(monaco,model))))})),monaco.editor.getModels().forEach(model=>{model.getLanguageId()==="pdl"&&validateModel(monaco,model)})),{dispose}}function dispose(){disposables.forEach(disposable=>disposable.dispose()),disposables=[],isRegistered=!1,monacoInstance=null}function setReadModelSchema(schema){validator?.setReadModelSchemas([schema]),completionProvider?.setReadModelSchemas([schema])}function setReadModelSchemas(readModels){validator?.setReadModels(readModels),completionProvider?.setReadModels(readModels),hoverProvider?.setReadModels(readModels),codeActionProvider?.setReadModels(readModels),revalidateAllModels()}function setCreateReadModelCallback(callback){codeActionProvider?codeActionProvider.setCreateReadModelCallback(callback):pendingCreateReadModelCallback=callback}function setEditReadModelCallback(callback){codeActionProvider?codeActionProvider.setEditReadModelCallback(callback):pendingEditReadModelCallback=callback}function setDraftReadModel(draft){codeActionProvider?.setDraftReadModel(draft),validator?.setDraftReadModel(draft),hoverProvider?.setDraftReadModel(draft),revalidateAllModels()}function setEventSchemas(eventSchemas){let normalized=(input=>{if(!input)return{};if(Array.isArray(input)){let out={};return input.forEach((s,i)=>{if(!s)return;let name=s.title||s.name||(typeof s.$id=="string"?s.$id.split("/").pop():void 0)||`Event${i+1}`;out[name]=s}),out}return input})(eventSchemas);validator?.setEventSchemas(normalized),completionProvider?.setEventSchemas(normalized),hoverProvider?.setEventSchemas(normalized),revalidateAllModels()}function setEventSequences(sequences){completionProvider?.setEventSequences(sequences)}function validateModel(monaco,model){if(validator&&!model.isDisposed()){let markers=validator.validate(model);monaco.editor.setModelMarkers(model,"pdl-validator",markers)}}function revalidateAllModels(){monacoInstance&&monacoInstance.editor.getModels().forEach(model=>{model.getLanguageId()==="pdl"&&validateModel(monacoInstance,model)})}export{languageId,registerProjectionLanguage,setCreateReadModelCallback,setDraftReadModel,setEditReadModelCallback,setEventSchemas,setEventSequences,setReadModelSchema,setReadModelSchemas};
|
|
1647
75
|
//# sourceMappingURL=index.js.map
|