Compare commits
3 Commits
599beb6f4f
...
71a3c40108
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71a3c40108 | ||
|
|
aedc715ef3 | ||
|
|
73cfc43f4b |
10
README.md
10
README.md
@@ -10,6 +10,16 @@
|
||||
- **Formatter**: Standardizes configuration file formatting.
|
||||
- **Validator**: Advanced semantic validation using [CUE](https://cuelang.org/) schemas, ensuring type safety and structural correctness.
|
||||
|
||||
### MARTe extended configuration language
|
||||
|
||||
Few additional features have been added to the standard MARTe configuration language:
|
||||
|
||||
- Multi file configuration support
|
||||
- Multi file definition merging
|
||||
- File level namespace / node
|
||||
- Doc-strings support
|
||||
- Pragmas for warning suppression / documentation
|
||||
|
||||
## Installation
|
||||
|
||||
### From Source
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/logger"
|
||||
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||
)
|
||||
|
||||
@@ -456,9 +455,7 @@ type QueryResult struct {
|
||||
}
|
||||
|
||||
func (pt *ProjectTree) Query(file string, line, col int) *QueryResult {
|
||||
logger.Printf("File: %s:%d:%d", file, line, col)
|
||||
for i := range pt.References {
|
||||
logger.Printf("%s", pt.Root.Name)
|
||||
ref := &pt.References[i]
|
||||
if ref.File == file {
|
||||
if line == ref.Position.Line && col >= ref.Position.Column && col < ref.Position.Column+len(ref.Name) {
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
type CompletionParams struct {
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
Position Position `json:"position"`
|
||||
Context CompletionContext `json:"context,omitempty"`
|
||||
Context CompletionContext `json:"context"`
|
||||
}
|
||||
|
||||
type CompletionContext struct {
|
||||
@@ -222,6 +222,12 @@ func readMessage(reader *bufio.Reader) (*JsonRpcMessage, error) {
|
||||
}
|
||||
|
||||
func HandleMessage(msg *JsonRpcMessage) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Printf("Panic in HandleMessage: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
switch msg.Method {
|
||||
case "initialize":
|
||||
var params InitializeParams
|
||||
@@ -395,7 +401,7 @@ func HandleFormatting(params DocumentFormattingParams) []TextEdit {
|
||||
}
|
||||
}
|
||||
|
||||
func runValidation(uri string) {
|
||||
func runValidation(_ string) {
|
||||
v := validator.NewValidator(Tree, ProjectRoot)
|
||||
v.ValidateProject()
|
||||
v.CheckUnused()
|
||||
@@ -580,10 +586,7 @@ func HandleCompletion(params CompletionParams) *CompletionList {
|
||||
}
|
||||
lineStr := lines[params.Position.Line]
|
||||
|
||||
col := params.Position.Character
|
||||
if col > len(lineStr) {
|
||||
col = len(lineStr)
|
||||
}
|
||||
col := min(params.Position.Character, len(lineStr))
|
||||
|
||||
prefix := lineStr[:col]
|
||||
|
||||
@@ -628,7 +631,7 @@ func HandleCompletion(params CompletionParams) *CompletionList {
|
||||
return nil
|
||||
}
|
||||
|
||||
func suggestGAMSignals(container *index.ProjectNode, direction string) *CompletionList {
|
||||
func suggestGAMSignals(_ *index.ProjectNode, direction string) *CompletionList {
|
||||
var items []CompletionItem
|
||||
|
||||
processNode := func(node *index.ProjectNode) {
|
||||
@@ -641,7 +644,7 @@ func suggestGAMSignals(container *index.ProjectNode, direction string) *Completi
|
||||
return
|
||||
}
|
||||
|
||||
dir := "INOUT"
|
||||
dir := "NIL"
|
||||
if GlobalSchema != nil {
|
||||
classPath := cue.ParsePath(fmt.Sprintf("#Classes.%s.direction", cls))
|
||||
val := GlobalSchema.Value.LookupPath(classPath)
|
||||
@@ -652,16 +655,14 @@ func suggestGAMSignals(container *index.ProjectNode, direction string) *Completi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compatible := false
|
||||
if direction == "Input" {
|
||||
if dir == "IN" || dir == "INOUT" {
|
||||
compatible = true
|
||||
}
|
||||
} else if direction == "Output" {
|
||||
if dir == "OUT" || dir == "INOUT" {
|
||||
compatible = true
|
||||
}
|
||||
switch direction {
|
||||
case "Input":
|
||||
compatible = dir == "IN" || dir == "INOUT"
|
||||
case "Output":
|
||||
compatible = dir == "OUT" || dir == "INOUT"
|
||||
default:
|
||||
compatible = false
|
||||
}
|
||||
|
||||
if !compatible {
|
||||
@@ -677,8 +678,8 @@ func suggestGAMSignals(container *index.ProjectNode, direction string) *Completi
|
||||
dsName := node.Name
|
||||
sigName := sig.Name
|
||||
|
||||
label := fmt.Sprintf("%s:%s", sigName, dsName)
|
||||
insertText := fmt.Sprintf("%s = { DataSource = %s }", sigName, dsName)
|
||||
label := fmt.Sprintf("%s:%s", dsName, sigName)
|
||||
insertText := fmt.Sprintf("%s = {\n DataSource = %s \n}", sigName, dsName)
|
||||
|
||||
items = append(items, CompletionItem{
|
||||
Label: label,
|
||||
@@ -902,14 +903,11 @@ func suggestObjects(root *index.ProjectNode, filter string) *CompletionList {
|
||||
var walk func(*index.ProjectNode)
|
||||
walk = func(node *index.ProjectNode) {
|
||||
match := false
|
||||
if filter == "GAM" {
|
||||
if isGAM(node) {
|
||||
match = true
|
||||
}
|
||||
} else if filter == "DataSource" {
|
||||
if isDataSource(node) {
|
||||
match = true
|
||||
}
|
||||
switch filter {
|
||||
case "GAM":
|
||||
match = isGAM(node)
|
||||
case "DataSource":
|
||||
match = isDataSource(node)
|
||||
}
|
||||
|
||||
if match {
|
||||
@@ -1334,10 +1332,10 @@ func HandleRename(params RenameParams) *WorkspaceEdit {
|
||||
}
|
||||
|
||||
func respond(id any, result any) {
|
||||
msg := JsonRpcMessage{
|
||||
Jsonrpc: "2.0",
|
||||
ID: id,
|
||||
Result: result,
|
||||
msg := map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": result,
|
||||
}
|
||||
send(msg)
|
||||
}
|
||||
|
||||
@@ -542,11 +542,6 @@ func isValidType(t string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *Validator) checkType(val parser.Value, expectedType string) bool {
|
||||
// Legacy function, replaced by CUE.
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *Validator) getFileForField(f *parser.Field, node *index.ProjectNode) string {
|
||||
for _, frag := range node.Fragments {
|
||||
for _, def := range frag.Definitions {
|
||||
|
||||
@@ -61,7 +61,7 @@ package schema
|
||||
found := false
|
||||
if listIn != nil {
|
||||
for _, item := range listIn.Items {
|
||||
if item.Label == "Sig:DS" {
|
||||
if item.Label == "DS:Sig" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ package schema
|
||||
found = false
|
||||
if listOut != nil {
|
||||
for _, item := range listOut.Items {
|
||||
if item.Label == "Sig:DS" {
|
||||
if item.Label == "DS:Sig" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ package schema
|
||||
foundIn := false
|
||||
foundOut := false
|
||||
for _, item := range listIn.Items {
|
||||
if item.Label == "InSig:InDS" {
|
||||
if item.Label == "InDS:InSig" {
|
||||
foundIn = true
|
||||
// Normalize spaces for check
|
||||
insert := strings.ReplaceAll(item.InsertText, " ", "")
|
||||
@@ -85,16 +85,16 @@ package schema
|
||||
t.Errorf("InsertText mismatch: %s", item.InsertText)
|
||||
}
|
||||
}
|
||||
if item.Label == "OutSig:OutDS" {
|
||||
if item.Label == "OutDS:OutSig" {
|
||||
foundOut = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundIn {
|
||||
t.Error("Did not find InSig:InDS")
|
||||
t.Error("Did not find InDS:InSig")
|
||||
}
|
||||
if foundOut {
|
||||
t.Error("Should not find OutSig:OutDS in InputSignals")
|
||||
t.Error("Should not find OutDS:OutSig in InputSignals")
|
||||
}
|
||||
|
||||
// 2. Suggest in OutputSignals
|
||||
@@ -111,18 +111,18 @@ package schema
|
||||
foundIn = false
|
||||
foundOut = false
|
||||
for _, item := range listOut.Items {
|
||||
if item.Label == "InSig:InDS" {
|
||||
if item.Label == "InDS:InSig" {
|
||||
foundIn = true
|
||||
}
|
||||
if item.Label == "OutSig:OutDS" {
|
||||
if item.Label == "OutDS:OutSig" {
|
||||
foundOut = true
|
||||
}
|
||||
}
|
||||
|
||||
if foundIn {
|
||||
t.Error("Should not find InSig:InDS in OutputSignals")
|
||||
t.Error("Should not find InDS:InSig in OutputSignals")
|
||||
}
|
||||
if !foundOut {
|
||||
t.Error("Did not find OutSig:OutDS in OutputSignals")
|
||||
t.Error("Did not find OutDS:OutSig in OutputSignals")
|
||||
}
|
||||
}
|
||||
|
||||
74
test/lsp_crash_test.go
Normal file
74
test/lsp_crash_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||
"github.com/marte-community/marte-dev-tools/internal/lsp"
|
||||
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||
)
|
||||
|
||||
func TestLSPCrashOnUndefinedReference(t *testing.T) {
|
||||
// Setup
|
||||
lsp.Tree = index.NewProjectTree()
|
||||
lsp.Documents = make(map[string]string)
|
||||
|
||||
content := `
|
||||
+App = {
|
||||
Class = RealTimeApplication
|
||||
+State = {
|
||||
Class = RealTimeState
|
||||
+Thread = {
|
||||
Class = RealTimeThread
|
||||
Functions = { UndefinedGAM }
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
uri := "file://crash.marte"
|
||||
lsp.Documents[uri] = content
|
||||
p := parser.NewParser(content)
|
||||
cfg, err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lsp.Tree.AddFile("crash.marte", cfg)
|
||||
lsp.Tree.ResolveReferences()
|
||||
|
||||
// Line 7: " Functions = { UndefinedGAM }"
|
||||
// 12 spaces + "Functions" (9) + " = { " (5) = 26 chars prefix.
|
||||
// UndefinedGAM starts at 26.
|
||||
params := lsp.DefinitionParams{
|
||||
TextDocument: lsp.TextDocumentIdentifier{URI: uri},
|
||||
Position: lsp.Position{Line: 7, Character: 27},
|
||||
}
|
||||
|
||||
// This should NOT panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("Recovered from panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
res := lsp.HandleDefinition(params)
|
||||
|
||||
if res != nil {
|
||||
t.Error("Expected nil for undefined reference definition")
|
||||
}
|
||||
|
||||
// 2. Hover
|
||||
hParams := lsp.HoverParams{
|
||||
TextDocument: lsp.TextDocumentIdentifier{URI: uri},
|
||||
Position: lsp.Position{Line: 7, Character: 27},
|
||||
}
|
||||
hover := lsp.HandleHover(hParams)
|
||||
if hover == nil {
|
||||
t.Error("Expected hover for unresolved reference")
|
||||
} else {
|
||||
content := hover.Contents.(lsp.MarkupContent).Value
|
||||
if !strings.Contains(content, "Unresolved") {
|
||||
t.Errorf("Expected 'Unresolved' in hover, got: %s", content)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user