sysml-v2-lsp 0.15.0 → 0.16.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/CHANGELOG.md +12 -0
- package/dist/server/mcpServer.js +2 -2
- package/dist/server/parseWorker.js +54 -1
- package/dist/server/server.js +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.16.0]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Complex SysML v2 example files: smart home, DFA coverage advanced, and multiplicity examples
|
|
8
|
+
- Warm-up text expanded with directional ports (`in`, `out`, `inout`), nested action definitions, and prefix metadata annotations (`#Safety`, `#Security`)
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Semantic validator: `require constraint` with documentation-only body no longer raises a false positive inside viewpoint definitions
|
|
13
|
+
- DFA snapshot regenerated with updated warm-up coverage
|
|
14
|
+
|
|
3
15
|
## [0.15.0]
|
|
4
16
|
|
|
5
17
|
### Added
|
package/dist/server/mcpServer.js
CHANGED
|
@@ -113,8 +113,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
113
113
|
`&&lineOffsets.push(j+1)}ranges.push({startLine:this.offsetToLine(lineOffsets,open),endLine:this.offsetToLine(lineOffsets,i-1)})}}return ranges}checkUnverifiedRequirements(allSymbols,indexes,workspaceTexts){let diagnostics=[],satisfiedNames,verifiedNames,satisfyBlockRanges;if(workspaceTexts){satisfiedNames=new Set,verifiedNames=new Set,satisfyBlockRanges=new Map;for(let text of workspaceTexts)this.extractSatisfyReferences(text,satisfiedNames),this.extractVerifyReferences(text,verifiedNames),satisfyBlockRanges.set("__static__",this.extractSatisfyBlockRanges(text))}else if(this.documentManager){let uris=this.documentManager.getUris(),versionKey=uris.map(u=>u+":"+this.documentManager.getVersion(u)).join("|"),satisfyCacheValid=this.satisfyCache&&this.satisfyCache.versionKey===versionKey,verifyCacheValid=this.verifyCache&&this.verifyCache.versionKey===versionKey;if(!satisfyCacheValid||!verifyCacheValid){let relNames=this.collectWorkspaceRequirementRelationshipNames();if(!satisfyCacheValid){satisfyBlockRanges=new Map;for(let uri of uris){let text=this.documentManager.getText(uri);text&&satisfyBlockRanges.set(uri,this.extractSatisfyBlockRanges(text))}this.satisfyCache={versionKey,satisfiedNames:relNames.satisfiedNames,satisfyBlockRanges}}verifyCacheValid||(this.verifyCache={versionKey,verifiedNames:relNames.verifiedNames})}satisfiedNames=this.satisfyCache.satisfiedNames,satisfyBlockRanges=this.satisfyCache.satisfyBlockRanges,verifiedNames=this.verifyCache.verifiedNames}else satisfiedNames=new Set,verifiedNames=new Set,satisfyBlockRanges=new Map;let requirementUsages=allSymbols.filter(s=>s.kind==="requirement");for(let req of requirementUsages){if(req.parentQualifiedName&&indexes.byQualifiedName.get(req.parentQualifiedName)?.kind==="requirement")continue;let uriRanges=satisfyBlockRanges.get(req.uri)??satisfyBlockRanges.get("__static__")??[],reqLine=req.selectionRange.start.line;if(uriRanges.some(r=>reqLine>=r.startLine&&reqLine<=r.endLine))continue;let isSatisfied=satisfiedNames.has(req.name)||satisfiedNames.has(req.qualifiedName);if(!(verifiedNames.has(req.name)||verifiedNames.has(req.qualifiedName))){let message=isSatisfied?`Requirement '${req.name}' is satisfied but has no verification case \u2014 consider adding: verify ${req.name} by <VerificationCase>;`:`Requirement '${req.name}' has no verification case \u2014 consider adding: verify ${req.name} by <VerificationCase>;`;diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range:req.selectionRange,message,source:"sysml",code:"unverified-requirement",data:{name:req.name,qualifiedName:req.qualifiedName}})}}return diagnostics}extractVerifyReferences(text,out){let stripped=stripComments(text),re=/\bverify\s+(?:requirement\s+)?([\w]+(?:::[\w]+)*)\s*(?:by\b|;|:|\{)/g,m2;for(;(m2=re.exec(stripped))!==null;){let ref=m2[1];out.add(ref);let lastSeg=ref.includes("::")?ref.split("::").pop():ref;out.add(lastSeg)}}offsetToLine(lineOffsets,offset){let lo=0,hi=lineOffsets.length-1;for(;lo<hi;){let mid=lo+hi+1>>>1;lineOffsets[mid]<=offset?lo=mid:hi=mid-1}return lo}checkViewpointSatisfaction(symbols,_indexes){let diagnostics=[],views=symbols.filter(s=>s.kind==="view"),_viewpointNames=new Set(symbols.filter(s=>s.kind==="viewpoint def"||s.kind==="viewpoint").map(s=>s.name));for(let view of views){let hasExpose=view.exposeTargets&&view.exposeTargets.length>0,hasFilter=view.viewFilters&&view.viewFilters.length>0;!hasExpose&&!hasFilter&&diagnostics.push({severity:import_node.DiagnosticSeverity.Information,range:view.selectionRange,message:`View '${view.name}' has no expose or filter directives \u2014 it will show all elements`,source:"sysml",code:"view-no-scope"})}return diagnostics}checkRedefinitionMultiplicity(symbolsInUri,indexes,text){let diagnostics=[],lines=text?.split(`
|
|
114
114
|
`);for(let s of symbolsInUri){if(!s.typeName||!s.multiplicityRange)continue;let c0=s.typeName.charCodeAt(0);if(!(c0>=97&&c0<=122))continue;if(lines){let lineIdx=s.selectionRange.start.line,line=lines[lineIdx]||"";if(!/:>>/.test(line)&&!/\bredefines\b/.test(line))continue}let base=(indexes.byName.get(s.typeName)??[]).find(c=>!!c.multiplicityRange);if(!base?.multiplicityRange)continue;let baseLower=base.multiplicityRange.lower,baseUpper=base.multiplicityRange.upper,curLower=s.multiplicityRange.lower,curUpper=s.multiplicityRange.upper,lowerOk=curLower>=baseLower,upperOk=baseUpper==="*"||curUpper!=="*"&&curUpper<=baseUpper;lowerOk&&upperOk||diagnostics.push({severity:import_node.DiagnosticSeverity.Error,range:s.selectionRange,message:`Redefinition multiplicity [${s.multiplicity??"?"}] is incompatible with base '${base.name}' multiplicity [${base.multiplicity??"?"}]`,source:"sysml",code:"invalid-redefinition-multiplicity"})}return diagnostics}checkPortCompatibility(text,uri,indexes){if(!text)return[];let diagnostics=[],lines=text.split(`
|
|
115
115
|
`),re=/\bconnect\s+([A-Za-z_][\w.]*)\s+to\s+([A-Za-z_][\w.]*)/g,m2;for(;(m2=re.exec(text))!==null;){let left=m2[1].split(".").pop(),right=m2[2].split(".").pop(),lSym=(indexes.portsByName.get(left)??[])[0],rSym=(indexes.portsByName.get(right)??[])[0];if(!lSym||!rSym)continue;let lType=lSym.typeNames[0]??lSym.typeName,rType=rSym.typeNames[0]??rSym.typeName;if(!lType||!rType||lType===rType||lType==="~"+rType||rType==="~"+lType||lType.startsWith("~")&&lType.slice(1)===rType||rType.startsWith("~")&&rType.slice(1)===lType)continue;let matchLine=text.substring(0,m2.index).split(`
|
|
116
|
-
`).length-1,insideInterfaceDef=!1;for(let i=matchLine;i>=0&&i>=matchLine-30;i--){let line=(lines[i]||"").trim();if(/\binterface\s+def\b/.test(line)){insideInterfaceDef=!0;break}if(/^\s*package\b/.test(line))break}if(insideInterfaceDef)continue;let precedingText=text.substring(Math.max(0,m2.index-300),m2.index);if(/\binterface\s+\w[\w']*(:\w[\w']*)?\s*$/m.test(precedingText))continue;let lDefSyms=indexes.byName.get(lType)??[],rDefSyms=indexes.byName.get(rType)??[],lDef=lDefSyms.find(s=>s.kind.includes("def")),rDef=rDefSyms.find(s=>s.kind.includes("def")),lChildren=lDef?indexes.byParent.get(lDef.qualifiedName)??[]:[],rChildren=rDef?indexes.byParent.get(rDef.qualifiedName)??[]:[];if(lChildren.length===0&&rChildren.length===0)continue;if(lDef&&rDef){let lHierarchy=this.resolveTypeHierarchy(lType,indexes),rHierarchy=this.resolveTypeHierarchy(rType,indexes);if(lHierarchy.has(rType)||rHierarchy.has(lType)||[...lHierarchy].some(t=>rHierarchy.has(t)))continue;let lChildNames=new Set(lChildren.map(c=>c.name));if(rChildren.some(c=>lChildNames.has(c.name)))continue}let range=this.indexToRange(text,m2.index,m2[0].length);diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range,message:`Port compatibility issue: '${left}' (${lType}) is connected to '${right}' (${rType})`,source:"sysml",code:"incompatible-port-types",data:{uri}})}return diagnostics}resolveTypeHierarchy(typeName,indexes){let hierarchy=new Set,queue=[typeName];for(;queue.length>0&&hierarchy.size<50;){let current=queue.shift();if(hierarchy.has(current))continue;hierarchy.add(current);let defs=indexes.byName.get(current)??[];for(let def of defs)if(def.kind.includes("def"))for(let parent of def.typeNames)hierarchy.has(parent)||queue.push(parent)}return hierarchy}checkConstraintBodyReferences(text,uri,symbolsInUri,indexes){if(!text)return[];let diagnostics=[],blocks=this.extractConstraintBlocks(text);for(let b of blocks){if(this.hasDocumentationOnlyConstraintBody(b.body)){let trimStart=b.body.search(/\S/),startInBody=trimStart>=0?trimStart:0,trimmedLen=Math.max(1,b.body.trim().length);diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range:this.indexToRange(text,b.bodyOffset+startInBody,trimmedLen),message:"Invalid constraint body: expected expression, found documentation text",source:"sysml",code:"invalid-constraint-body",data:{uri}});continue}let parent=this.findConstraintScopeSymbol(symbolsInUri,indexes,b.startLine);if(!parent)continue;let parentMembers=indexes.byParent.get(parent.qualifiedName)??[];if(parentMembers.length===0)continue;let ignoredRanges=this.getIgnoredBodyRanges(b.body),idRe=/\b([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*)\b/g,im;for(;(im=idRe.exec(b.body))!==null;){if(this.isIndexInRanges(im.index,ignoredRanges))continue;let expr=im[1];if(this.isConstraintKeyword(expr)||/^\d/.test(expr))continue;let path=expr.split(".");if(this.resolvePathFromParent(path,parentMembers,indexes))continue;if(path.length===1){let root=path[0];if(indexes.byName.has(root)||resolveLibraryType(root)!==void 0)continue}let absoluteStart=b.bodyOffset+im.index;diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range:this.indexToRange(text,absoluteStart,expr.length),message:`Unresolved constraint reference '${expr}' in scope '${parent.name}'`,source:"sysml",code:"unresolved-constraint-reference",data:{uri}})}}return diagnostics}extractConstraintBlocks(text){let out=[],re=/\b(
|
|
117
|
-
`).length-1;out.push({body,bodyOffset:bodyStart,startLine})}return out}resolvePathFromParent(path,parentMembers,indexes){let root=parentMembers.find(s=>s.name===path[0]);if(!root)return!1;if(path.length===1)return!0;let typeName=root.typeNames[0]??root.typeName;for(let i=1;i<path.length;i++){if(!typeName)return!1;let typeDef=(indexes.definitionsByName.get(typeName)??[])[0];if(!typeDef)return!1;let member=(indexes.byParent.get(typeDef.qualifiedName)??[]).find(s=>s.name===path[i]);if(!member)return!1;typeName=member.typeNames[0]??member.typeName}return!0}findContainingSymbolByLine(symbols,line){return symbols.filter(s=>s.range.start.line<=line&&s.range.end.line>=line).sort((a,b)=>{let ar=(a.range.end.line-a.range.start.line)*1e3+(a.range.end.character-a.range.start.character),br=(b.range.end.line-b.range.start.line)*1e3+(b.range.end.character-b.range.start.character);return ar-br})[0]}findConstraintScopeSymbol(symbolsInUri,indexes,line){let scope=this.findContainingSymbolByLine(symbolsInUri,line);for(;scope;){if((indexes.byParent.get(scope.qualifiedName)??[]).length>0||!scope.parentQualifiedName)return scope;scope=indexes.byQualifiedName.get(scope.parentQualifiedName)}}getOrBuildIndexes(allSymbols){if(this.indexCache&&this.indexCache.symbols===allSymbols)return this.indexCache.indexes;let indexes=this.buildSymbolIndexes(allSymbols);return this.indexCache={symbols:allSymbols,indexes},indexes}buildSymbolIndexes(allSymbols){let byName=new Map,byParent=new Map,byQualifiedName=new Map,definitionsByName=new Map,portsByName=new Map;for(let s of allSymbols){let nameList=byName.get(s.name)??[];if(nameList.push(s),byName.set(s.name,nameList),byQualifiedName.set(s.qualifiedName,s),s.parentQualifiedName){let children=byParent.get(s.parentQualifiedName)??[];children.push(s),byParent.set(s.parentQualifiedName,children)}if(isDefinition(s.kind)){let defs=definitionsByName.get(s.name)??[];defs.push(s),definitionsByName.set(s.name,defs)}if(s.kind==="port"||s.kind==="port def"){let ports=portsByName.get(s.name)??[];ports.push(s),portsByName.set(s.name,ports)}}return{byName,byParent,byQualifiedName,definitionsByName,portsByName}}isConstraintKeyword(value){return CONSTRAINT_KEYWORDS.has(value)}getIgnoredBodyRanges(body){let ranges=[],blockRe=/\/\*[\s\S]*?\*\//g,m2;for(;(m2=blockRe.exec(body))!==null;)ranges.push({start:m2.index,end:m2.index+m2[0].length});let lineRe=/\/\/[^\n\r]*/g;for(;(m2=lineRe.exec(body))!==null;)ranges.push({start:m2.index,end:m2.index+m2[0].length});return ranges}isIndexInRanges(index2,ranges){for(let r of ranges)if(index2>=r.start&&index2<r.end)return!0;return!1}hasDocumentationOnlyConstraintBody(body){let trimmed=body.trim();if(!trimmed)return!1;let withoutComments=trimmed.replace(/\/\*[\s\S]*?\*\//g," ").replace(/\/\/[^\n\r]*/g," ").trim();if(withoutComments==="doc"||withoutComments==="comment")return!0;let hasExprSignal=/[<>=!+\-*/%()[\].,:]|\b(and|or|not)\b/.test(withoutComments);return/^[A-Za-z_\s]+$/.test(withoutComments)&&!hasExprSignal}lineOffsetCache;indexToRange(text,start,length){let offsets;if(this.lineOffsetCache&&this.lineOffsetCache.text===text)offsets=this.lineOffsetCache.offsets;else{offsets=[0];for(let i=0;i<text.length;i++)text[i]===`
|
|
116
|
+
`).length-1,insideInterfaceDef=!1;for(let i=matchLine;i>=0&&i>=matchLine-30;i--){let line=(lines[i]||"").trim();if(/\binterface\s+def\b/.test(line)){insideInterfaceDef=!0;break}if(/^\s*package\b/.test(line))break}if(insideInterfaceDef)continue;let precedingText=text.substring(Math.max(0,m2.index-300),m2.index);if(/\binterface\s+\w[\w']*(:\w[\w']*)?\s*$/m.test(precedingText))continue;let lDefSyms=indexes.byName.get(lType)??[],rDefSyms=indexes.byName.get(rType)??[],lDef=lDefSyms.find(s=>s.kind.includes("def")),rDef=rDefSyms.find(s=>s.kind.includes("def")),lChildren=lDef?indexes.byParent.get(lDef.qualifiedName)??[]:[],rChildren=rDef?indexes.byParent.get(rDef.qualifiedName)??[]:[];if(lChildren.length===0&&rChildren.length===0)continue;if(lDef&&rDef){let lHierarchy=this.resolveTypeHierarchy(lType,indexes),rHierarchy=this.resolveTypeHierarchy(rType,indexes);if(lHierarchy.has(rType)||rHierarchy.has(lType)||[...lHierarchy].some(t=>rHierarchy.has(t)))continue;let lChildNames=new Set(lChildren.map(c=>c.name));if(rChildren.some(c=>lChildNames.has(c.name)))continue}let range=this.indexToRange(text,m2.index,m2[0].length);diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range,message:`Port compatibility issue: '${left}' (${lType}) is connected to '${right}' (${rType})`,source:"sysml",code:"incompatible-port-types",data:{uri}})}return diagnostics}resolveTypeHierarchy(typeName,indexes){let hierarchy=new Set,queue=[typeName];for(;queue.length>0&&hierarchy.size<50;){let current=queue.shift();if(hierarchy.has(current))continue;hierarchy.add(current);let defs=indexes.byName.get(current)??[];for(let def of defs)if(def.kind.includes("def"))for(let parent of def.typeNames)hierarchy.has(parent)||queue.push(parent)}return hierarchy}checkConstraintBodyReferences(text,uri,symbolsInUri,indexes){if(!text)return[];let diagnostics=[],blocks=this.extractConstraintBlocks(text);for(let b of blocks){if(this.hasDocumentationOnlyConstraintBody(b.body)){if(b.isRequire&&this.isInsideViewpoint(text,b.bodyOffset))continue;let trimStart=b.body.search(/\S/),startInBody=trimStart>=0?trimStart:0,trimmedLen=Math.max(1,b.body.trim().length);diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range:this.indexToRange(text,b.bodyOffset+startInBody,trimmedLen),message:"Invalid constraint body: expected expression, found documentation text",source:"sysml",code:"invalid-constraint-body",data:{uri}});continue}let parent=this.findConstraintScopeSymbol(symbolsInUri,indexes,b.startLine);if(!parent)continue;let parentMembers=indexes.byParent.get(parent.qualifiedName)??[];if(parentMembers.length===0)continue;let ignoredRanges=this.getIgnoredBodyRanges(b.body),idRe=/\b([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*)\b/g,im;for(;(im=idRe.exec(b.body))!==null;){if(this.isIndexInRanges(im.index,ignoredRanges))continue;let expr=im[1];if(this.isConstraintKeyword(expr)||/^\d/.test(expr))continue;let path=expr.split(".");if(this.resolvePathFromParent(path,parentMembers,indexes))continue;if(path.length===1){let root=path[0];if(indexes.byName.has(root)||resolveLibraryType(root)!==void 0)continue}let absoluteStart=b.bodyOffset+im.index;diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range:this.indexToRange(text,absoluteStart,expr.length),message:`Unresolved constraint reference '${expr}' in scope '${parent.name}'`,source:"sysml",code:"unresolved-constraint-reference",data:{uri}})}}return diagnostics}extractConstraintBlocks(text){let out=[],re=/\b(require\s+)?constraint\s*\{/g,m2;for(;(m2=re.exec(text))!==null;){let open=m2.index+m2[0].length-1,depth=1,i=open+1;for(;i<text.length&&depth>0;){let ch=text[i];ch==="{"&&depth++,ch==="}"&&depth--,i++}if(depth!==0)continue;let bodyStart=open+1,bodyEnd=i-1,body=text.slice(bodyStart,bodyEnd),startLine=text.slice(0,bodyStart).split(`
|
|
117
|
+
`).length-1;out.push({body,bodyOffset:bodyStart,startLine,isRequire:!!m2[1]})}return out}resolvePathFromParent(path,parentMembers,indexes){let root=parentMembers.find(s=>s.name===path[0]);if(!root)return!1;if(path.length===1)return!0;let typeName=root.typeNames[0]??root.typeName;for(let i=1;i<path.length;i++){if(!typeName)return!1;let typeDef=(indexes.definitionsByName.get(typeName)??[])[0];if(!typeDef)return!1;let member=(indexes.byParent.get(typeDef.qualifiedName)??[]).find(s=>s.name===path[i]);if(!member)return!1;typeName=member.typeNames[0]??member.typeName}return!0}findContainingSymbolByLine(symbols,line){return symbols.filter(s=>s.range.start.line<=line&&s.range.end.line>=line).sort((a,b)=>{let ar=(a.range.end.line-a.range.start.line)*1e3+(a.range.end.character-a.range.start.character),br=(b.range.end.line-b.range.start.line)*1e3+(b.range.end.character-b.range.start.character);return ar-br})[0]}findConstraintScopeSymbol(symbolsInUri,indexes,line){let scope=this.findContainingSymbolByLine(symbolsInUri,line);for(;scope;){if((indexes.byParent.get(scope.qualifiedName)??[]).length>0||!scope.parentQualifiedName)return scope;scope=indexes.byQualifiedName.get(scope.parentQualifiedName)}}getOrBuildIndexes(allSymbols){if(this.indexCache&&this.indexCache.symbols===allSymbols)return this.indexCache.indexes;let indexes=this.buildSymbolIndexes(allSymbols);return this.indexCache={symbols:allSymbols,indexes},indexes}buildSymbolIndexes(allSymbols){let byName=new Map,byParent=new Map,byQualifiedName=new Map,definitionsByName=new Map,portsByName=new Map;for(let s of allSymbols){let nameList=byName.get(s.name)??[];if(nameList.push(s),byName.set(s.name,nameList),byQualifiedName.set(s.qualifiedName,s),s.parentQualifiedName){let children=byParent.get(s.parentQualifiedName)??[];children.push(s),byParent.set(s.parentQualifiedName,children)}if(isDefinition(s.kind)){let defs=definitionsByName.get(s.name)??[];defs.push(s),definitionsByName.set(s.name,defs)}if(s.kind==="port"||s.kind==="port def"){let ports=portsByName.get(s.name)??[];ports.push(s),portsByName.set(s.name,ports)}}return{byName,byParent,byQualifiedName,definitionsByName,portsByName}}isConstraintKeyword(value){return CONSTRAINT_KEYWORDS.has(value)}getIgnoredBodyRanges(body){let ranges=[],blockRe=/\/\*[\s\S]*?\*\//g,m2;for(;(m2=blockRe.exec(body))!==null;)ranges.push({start:m2.index,end:m2.index+m2[0].length});let lineRe=/\/\/[^\n\r]*/g;for(;(m2=lineRe.exec(body))!==null;)ranges.push({start:m2.index,end:m2.index+m2[0].length});return ranges}isIndexInRanges(index2,ranges){for(let r of ranges)if(index2>=r.start&&index2<r.end)return!0;return!1}hasDocumentationOnlyConstraintBody(body){let trimmed=body.trim();if(!trimmed)return!1;let withoutComments=trimmed.replace(/\/\*[\s\S]*?\*\//g," ").replace(/\/\/[^\n\r]*/g," ").trim();if(withoutComments==="doc"||withoutComments==="comment")return!0;let hasExprSignal=/[<>=!+\-*/%()[\].,:]|\b(and|or|not)\b/.test(withoutComments);return/^[A-Za-z_\s]+$/.test(withoutComments)&&!hasExprSignal}isInsideViewpoint(text,offset){let preceding=text.slice(0,offset),depth=0;for(let i=preceding.length-1;i>=0;i--){let ch=preceding[i];if(ch==="}"&&depth++,ch==="{"){if(depth>0){depth--;continue}let before=preceding.slice(0,i).trimEnd();return/\bviewpoint\b/.test(before.slice(-40))}}return!1}lineOffsetCache;indexToRange(text,start,length){let offsets;if(this.lineOffsetCache&&this.lineOffsetCache.text===text)offsets=this.lineOffsetCache.offsets;else{offsets=[0];for(let i=0;i<text.length;i++)text[i]===`
|
|
118
118
|
`&&offsets.push(i+1);this.lineOffsetCache={text,offsets}}let lo=0,hi=offsets.length-1;for(;lo<hi;){let mid=lo+hi+1>>>1;offsets[mid]<=start?lo=mid:hi=mid-1}let startLine=lo,startChar=start-offsets[startLine];return{start:{line:startLine,character:startChar},end:{line:startLine,character:startChar+length}}}dedupeDiagnostics(diags){let seen=new Set,out=[];for(let d of diags){let key=[d.code??"no-code",d.range.start.line,d.range.start.character,d.range.end.line,d.range.end.character,d.message].join("|");seen.has(key)||(seen.add(key),out.push(d))}return out}checkDuplicateDefinitions(symbols){let diagnostics=[],definitionsByScope=new Map;for(let symbol2 of symbols){if(!isDefinition(symbol2.kind))continue;let scope=symbol2.parentQualifiedName??"__root__",scopeMap=definitionsByScope.get(scope);scopeMap||(scopeMap=new Map,definitionsByScope.set(scope,scopeMap));let defs=scopeMap.get(symbol2.name);defs||(defs=[],scopeMap.set(symbol2.name,defs)),defs.push(symbol2)}for(let scopeMap of definitionsByScope.values())for(let[name,defs]of scopeMap)if(defs.length>1)for(let def of defs)diagnostics.push({severity:import_node.DiagnosticSeverity.Warning,range:def.selectionRange,message:`Duplicate definition: '${name}' is defined ${defs.length} times in the same scope`,source:"sysml",code:"duplicate-definition"});return diagnostics}};var McpContext=class{symbolTable=new SymbolTable;loadedDocuments=new Map;loadedDocumentHashes=new Map;lastParseErrors=new Map;lastParseTiming=new Map};function hashTextFNV1a(text){let hash2=2166136261;for(let i=0;i<text.length;i++)hash2^=text.charCodeAt(i),hash2=Math.imul(hash2,16777619);return hash2>>>0}function ensureParsed(ctx2,uri,code){if(code!==void 0){parseAndBuild(ctx2,code,uri);return}if(ctx2.symbolTable.getSymbolsForUri(uri).length===0){let cached2=ctx2.loadedDocuments.get(uri);cached2&&parseAndBuild(ctx2,cached2,uri)}}function formatSymbol(sym){return{name:sym.name,kind:sym.kind,qualifiedName:sym.qualifiedName,...sym.typeNames.length>0?{type:sym.typeNames.join(", ")}:{},...sym.documentation?{documentation:sym.documentation}:{},...sym.parentQualifiedName?{parent:sym.parentQualifiedName}:{},...sym.children.length>0?{children:sym.children}:{},location:{uri:sym.uri,range:sym.range}}}function formatError2(err){return{line:err.line+1,column:err.column+1,message:err.message,length:err.length}}function parseAndBuild(ctx2,text,uri){let hash2=hashTextFNV1a(text),prevHash=ctx2.loadedDocumentHashes.get(uri),currentSymbolCount=ctx2.symbolTable.getSymbolsForUri(uri).length,cachedErrors=ctx2.lastParseErrors.get(uri)??[],canReuseCachedParse=currentSymbolCount>0||cachedErrors.length>0;if(prevHash===hash2&&ctx2.lastParseTiming.has(uri)&&canReuseCachedParse)return{errors:cachedErrors,symbolCount:currentSymbolCount,timingMs:ctx2.lastParseTiming.get(uri)??{lex:0,parse:0}};let result=parseDocument(text);ctx2.symbolTable.build(uri,result),ctx2.loadedDocuments.set(uri,text),ctx2.loadedDocumentHashes.set(uri,hash2),ctx2.lastParseErrors.set(uri,result.errors);let timing={lex:result.timing.lexMs,parse:result.timing.parseMs};return ctx2.lastParseTiming.set(uri,timing),{errors:result.errors,symbolCount:ctx2.symbolTable.getSymbolsForUri(uri).length,timingMs:timing}}function handleParse(ctx2,code,uri){let docUri=uri??"untitled.sysml",{errors,symbolCount,timingMs}=parseAndBuild(ctx2,code,docUri),summary={uri:docUri,symbolCount,errorCount:errors.length,timing:timingMs};errors.length>0&&(summary.errors=errors.map(formatError2));let topLevel=ctx2.symbolTable.getSymbolsForUri(docUri).filter(s=>!s.parentQualifiedName).map(s=>`${s.kind} ${s.qualifiedName}`);return topLevel.length>0&&(summary.topLevelElements=topLevel),summary}function handlePreview(ctx2,opts){let docUri=opts.uri??"preview.sysml",{errors}=parseAndBuild(ctx2,opts.code,docUri),allSymbols=ctx2.symbolTable.getSymbolsForUri(docUri),allNames=new Set(ctx2.symbolTable.getAllSymbols().map(s=>s.name)),semanticDiags=SemanticValidator.validateSymbols(allSymbols,allNames,{allSymbols:ctx2.symbolTable.getAllSymbols(),text:opts.code,uri:docUri}),renderSymbols=allSymbols;if(opts.focus){let focusName=opts.focus,focusSet=new Set,childrenOf=new Map;for(let s of allSymbols)if(s.parentQualifiedName){let list=childrenOf.get(s.parentQualifiedName)??[];list.push(s),childrenOf.set(s.parentQualifiedName,list)}let focused=allSymbols.filter(s=>s.name===focusName||s.qualifiedName===focusName||s.qualifiedName.endsWith(`::${focusName}`));for(let f of focused){focusSet.add(f.qualifiedName);let children=childrenOf.get(f.qualifiedName)??[];for(let child of children)focusSet.add(child.qualifiedName);f.parentQualifiedName&&focusSet.add(f.parentQualifiedName);let typeSource=[...f.typeNames];for(let child of children)typeSource.push(...child.typeNames);for(let tn of typeSource){let typed=allSymbols.find(s=>s.name===tn||s.qualifiedName===tn);if(typed){focusSet.add(typed.qualifiedName);for(let child of childrenOf.get(typed.qualifiedName)??[])focusSet.add(child.qualifiedName)}}}focusSet.size>0&&(renderSymbols=allSymbols.filter(s=>focusSet.has(s.qualifiedName)))}let mermaid=generateMermaidDiagram(renderSymbols,allSymbols,opts.diagramType),result={diagram:mermaid.diagram,diagramType:mermaid.diagramType,description:mermaid.description,elementCount:mermaid.elementCount,errors:errors.map(formatError2)};if(opts.originalCode){let origUri="preview-original.sysml",origResult=parseDocument(opts.originalCode),origTable=new SymbolTable;origTable.build(origUri,origResult);let origSymbols=origTable.getSymbolsForUri(origUri),diff=diffSymbols(origSymbols,allSymbols);result.diff={added:diff.added.map(s=>`${s.kind} ${s.qualifiedName}`),changed:diff.changed.map(s=>`${s.kind} ${s.qualifiedName}`),removed:diff.removed,unchangedCount:diff.unchanged.length}}return semanticDiags.length>0&&(result.semanticIssues=semanticDiags.map(d=>({line:d.range.start.line+1,column:d.range.start.character+1,message:d.message,severity:d.severity===1?"error":d.severity===2?"warning":"info"}))),result}function handleValidate(ctx2,code,uri){let docUri=uri??"untitled.sysml",{errors}=parseAndBuild(ctx2,code,docUri),symbols=ctx2.symbolTable.getSymbolsForUri(docUri),allNames=new Set(ctx2.symbolTable.getAllSymbols().map(s=>s.name)),semanticDiags=SemanticValidator.validateSymbols(symbols,allNames,{allSymbols:ctx2.symbolTable.getAllSymbols(),text:code,uri:docUri}),semanticIssues=semanticDiags.map(d=>({line:d.range.start.line+1,column:d.range.start.character+1,message:d.message,severity:d.severity===1?"error":d.severity===2?"warning":d.severity===3?"info":"hint",code:d.code}));return{valid:errors.length===0&&semanticDiags.filter(d=>d.severity===1).length===0,syntaxErrors:errors.map(formatError2),semanticIssues,totalIssues:errors.length+semanticDiags.length}}function handleGetDiagnostics(ctx2,uri,code){let docUri=uri??"untitled.sysml";ensureParsed(ctx2,docUri,code);let symbols=ctx2.symbolTable.getSymbolsForUri(docUri),allNames=new Set(ctx2.symbolTable.getAllSymbols().map(s=>s.name)),diags=SemanticValidator.validateSymbols(symbols,allNames,{allSymbols:ctx2.symbolTable.getAllSymbols(),text:code??ctx2.loadedDocuments.get(docUri),uri:docUri}),summary={};for(let d of diags){let code2=String(d.code??"unknown");summary[code2]=(summary[code2]??0)+1}return{uri:docUri,diagnostics:diags.map(d=>({line:d.range.start.line+1,column:d.range.start.character+1,message:d.message,severity:d.severity===1?"error":d.severity===2?"warning":d.severity===3?"info":"hint",code:d.code})),summary}}function handleGetSymbols(ctx2,opts){if(opts.code){let docUri=opts.uri??"untitled.sysml";ensureParsed(ctx2,docUri,opts.code)}let symbols=opts.uri?ctx2.symbolTable.getSymbolsForUri(opts.uri):ctx2.symbolTable.getAllSymbols();return opts.kind&&(symbols=symbols.filter(s=>s.kind.toLowerCase()===opts.kind.toLowerCase())),opts.definitionsOnly&&(symbols=symbols.filter(s=>isDefinition(s.kind))),opts.usagesOnly&&(symbols=symbols.filter(s=>isUsage(s.kind))),{count:symbols.length,symbols:symbols.map(formatSymbol)}}function handleGetDefinition(ctx2,name,code,uri){code&&ensureParsed(ctx2,uri??"untitled.sysml",code);let exact=ctx2.symbolTable.getSymbol(name);if(exact)return formatSymbol(exact);let matches=ctx2.symbolTable.findByName(name);return matches.length===0?{found:!1,message:`No symbol found with name "${name}"`}:{found:!0,count:matches.length,symbols:matches.map(formatSymbol)}}function handleGetReferences(ctx2,name,code,uri){code&&ensureParsed(ctx2,uri??"untitled.sysml",code);let refs=ctx2.symbolTable.findReferences(name);return{name,referenceCount:refs.length,references:refs.map(formatSymbol)}}function handleGetHierarchy(ctx2,name,code,uri){code&&ensureParsed(ctx2,uri??"untitled.sysml",code);let target=ctx2.symbolTable.getSymbol(name)??ctx2.symbolTable.findByName(name)[0];if(!target)return{found:!1,message:`No symbol "${name}" found`};let ancestors=[],current=target.parentQualifiedName;for(;current;){let parent=ctx2.symbolTable.getSymbol(current);if(!parent)break;ancestors.unshift({name:parent.name,kind:parent.kind,qualifiedName:parent.qualifiedName}),current=parent.parentQualifiedName}let children=target.children.map(qn=>ctx2.symbolTable.getSymbol(qn)).filter(s=>s!==void 0).map(s=>({name:s.name,kind:s.kind,qualifiedName:s.qualifiedName,...s.typeNames.length>0?{type:s.typeNames.join(", ")}:{}}));return{element:{name:target.name,kind:target.kind,qualifiedName:target.qualifiedName,...target.typeNames.length>0?{type:target.typeNames.join(", ")}:{}},ancestors,children}}function handleGetModelSummary(ctx2,code,uri){code&&ensureParsed(ctx2,uri??"untitled.sysml",code);let allSymbols=ctx2.symbolTable.getAllSymbols(),kindCounts={};for(let sym of allSymbols)kindCounts[sym.kind]=(kindCounts[sym.kind]??0)+1;let sorted=Object.entries(kindCounts).sort(([,a],[,b])=>b-a);return{totalSymbols:allSymbols.length,loadedDocuments:Array.from(ctx2.loadedDocuments.keys()),elementsByKind:Object.fromEntries(sorted),definitions:allSymbols.filter(s=>isDefinition(s.kind)).length,usages:allSymbols.filter(s=>isUsage(s.kind)).length}}function handleGetComplexity(ctx2,uri,code){code&&ensureParsed(ctx2,uri??"untitled.sysml",code);let symbols=uri?ctx2.symbolTable.getSymbolsForUri(uri):ctx2.symbolTable.getAllSymbols();return analyseComplexity(symbols)}function getElementKinds(){let kinds=Object.values(SysMLElementKind);return{definitions:kinds.filter(k=>isDefinition(k)),usages:kinds.filter(k=>isUsage(k)),other:kinds.filter(k=>!isDefinition(k)&&!isUsage(k)),total:kinds.length}}function handleResourceElementKinds(){return getElementKinds()}function handleResourceKeywords(){return{keywords:SYSML_KEYWORDS_ARRAY,count:SYSML_KEYWORDS_ARRAY.length}}function handleResourceGrammarOverview(){return"# SysML v2 Grammar Overview\n\n## Element Categories\n\n### Definitions (Types)\nDefinitions declare reusable types:\n- `part def` \u2014 structural element type\n- `attribute def` \u2014 value type\n- `port def` \u2014 interface point type\n- `connection def` \u2014 connection type\n- `interface def` \u2014 interface type\n- `action def` \u2014 behavior type\n- `state def` \u2014 state machine type\n- `requirement def` \u2014 requirement type\n- `constraint def` \u2014 constraint type\n- `item def` \u2014 general item type\n- `enum def` \u2014 enumeration type\n- `calc def` \u2014 calculation type\n- `use case def` \u2014 use case type\n- `allocation def` \u2014 allocation type\n- `view def` / `viewpoint def` \u2014 viewpoint types\n\n### Usages (Instances)\nUsages create instances of definitions:\n- `part` \u2014 structural instance\n- `attribute` \u2014 value instance\n- `port` \u2014 port instance\n- `action` \u2014 action step\n- `state` \u2014 state instance\n- `requirement` \u2014 requirement instance\n- `item` \u2014 item instance\n\n## Specialisation Syntax\n- `part car : Vehicle` \u2014 `car` specialises `Vehicle`\n- `part car :> baseVehicle` \u2014 `car` subsets `baseVehicle`\n- `part car :>> specificVehicle` \u2014 `car` redefines `specificVehicle`\n\n## Packages & Namespaces\n```sysml\npackage VehicleModel {\n part def Vehicle { ... }\n part car : Vehicle;\n}\n```\n\n## Documentation\n```sysml\npart def Vehicle {\n doc /* A general vehicle definition */\n attribute mass : Real;\n}\n```\n\n## Unrestricted Names\nNames with spaces use single quotes: `part 'Main Assembly' : Assembly;`\n"}function handlePromptReviewSysml(ctx2,code){let{errors,symbolCount}=parseAndBuild(ctx2,code,"review.sysml"),allSymbols=ctx2.symbolTable.getSymbolsForUri("review.sysml"),defs=allSymbols.filter(s=>isDefinition(s.kind)),usages=allSymbols.filter(s=>isUsage(s.kind)),context=[`Parsed: ${symbolCount} symbols, ${errors.length} syntax errors`,`Definitions: ${defs.map(d=>`${d.kind} ${d.name}`).join(", ")||"none"}`,`Usages: ${usages.map(u=>`${u.kind} ${u.name}`).join(", ")||"none"}`];return errors.length>0&&context.push(`Errors: ${errors.map(e=>`line ${e.line+1}: ${e.message}`).join("; ")}`),[{role:"user",content:{type:"text",text:`Please review the following SysML v2 model for correctness, completeness, and best practices.
|
|
119
119
|
|
|
120
120
|
## Parse Results
|