using any instead of interface
This commit is contained in:
@@ -16,8 +16,8 @@ type JsonRpcMessage struct {
|
|||||||
Jsonrpc string `json:"jsonrpc"`
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
Method string `json:"method,omitempty"`
|
Method string `json:"method,omitempty"`
|
||||||
Params json.RawMessage `json:"params,omitempty"`
|
Params json.RawMessage `json:"params,omitempty"`
|
||||||
ID interface{} `json:"id,omitempty"`
|
ID any `json:"id,omitempty"`
|
||||||
Result interface{} `json:"result,omitempty"`
|
Result any `json:"result,omitempty"`
|
||||||
Error *JsonRpcError `json:"error,omitempty"`
|
Error *JsonRpcError `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ type Position struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Hover struct {
|
type Hover struct {
|
||||||
Contents interface{} `json:"contents"`
|
Contents any `json:"contents"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MarkupContent struct {
|
type MarkupContent struct {
|
||||||
@@ -121,8 +121,8 @@ func readMessage(reader *bufio.Reader) (*JsonRpcMessage, error) {
|
|||||||
func handleMessage(msg *JsonRpcMessage) {
|
func handleMessage(msg *JsonRpcMessage) {
|
||||||
switch msg.Method {
|
switch msg.Method {
|
||||||
case "initialize":
|
case "initialize":
|
||||||
respond(msg.ID, map[string]interface{}{
|
respond(msg.ID, map[string]any{
|
||||||
"capabilities": map[string]interface{}{
|
"capabilities": map[string]any{
|
||||||
"textDocumentSync": 1, // Full sync
|
"textDocumentSync": 1, // Full sync
|
||||||
"hoverProvider": true,
|
"hoverProvider": true,
|
||||||
"definitionProvider": true,
|
"definitionProvider": true,
|
||||||
@@ -254,7 +254,7 @@ func formatNodeInfo(node *index.ProjectNode) string {
|
|||||||
|
|
||||||
// Size
|
// Size
|
||||||
dims := node.Metadata["NumberOfDimensions"]
|
dims := node.Metadata["NumberOfDimensions"]
|
||||||
elems := node.Metadata["NumberOfElements"]
|
elems := node.Metadata["NumberOfElements"]
|
||||||
if dims != "" || elems != "" {
|
if dims != "" || elems != "" {
|
||||||
sigInfo += fmt.Sprintf("**Size**: `[%s]`, `%s` dims ", elems, dims)
|
sigInfo += fmt.Sprintf("**Size**: `[%s]`, `%s` dims ", elems, dims)
|
||||||
}
|
}
|
||||||
@@ -267,7 +267,7 @@ elems := node.Metadata["NumberOfElements"]
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func respond(id interface{}, result interface{}) {
|
func respond(id any, result any) {
|
||||||
msg := JsonRpcMessage{
|
msg := JsonRpcMessage{
|
||||||
Jsonrpc: "2.0",
|
Jsonrpc: "2.0",
|
||||||
ID: id,
|
ID: id,
|
||||||
@@ -276,7 +276,7 @@ func respond(id interface{}, result interface{}) {
|
|||||||
send(msg)
|
send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func send(msg interface{}) {
|
func send(msg any) {
|
||||||
body, _ := json.Marshal(msg)
|
body, _ := json.Marshal(msg)
|
||||||
fmt.Printf("Content-Length: %d\r\n\r\n%s", len(body), body)
|
fmt.Printf("Content-Length: %d\r\n\r\n%s", len(body), body)
|
||||||
}
|
}
|
||||||
5
test/integration/signal_no_class.marte
Normal file
5
test/integration/signal_no_class.marte
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#package TEST.SIGNAL
|
||||||
|
+MySignal = {
|
||||||
|
Type = uint32
|
||||||
|
NumberOfElements = 1
|
||||||
|
}
|
||||||
58
test/lsp_doc_test.go
Normal file
58
test/lsp_doc_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/index"
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLSPHoverDoc(t *testing.T) {
|
||||||
|
content := `
|
||||||
|
//# Object Documentation
|
||||||
|
//# Second line
|
||||||
|
+MyObject = {
|
||||||
|
Class = Type
|
||||||
|
}
|
||||||
|
|
||||||
|
+RefObject = {
|
||||||
|
Class = Type
|
||||||
|
RefField = MyObject
|
||||||
|
}
|
||||||
|
`
|
||||||
|
p := parser.NewParser(content)
|
||||||
|
config, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := index.NewProjectTree()
|
||||||
|
file := "doc.marte"
|
||||||
|
idx.AddFile(file, config)
|
||||||
|
idx.ResolveReferences()
|
||||||
|
|
||||||
|
// Test 1: Hover over +MyObject definition
|
||||||
|
res := idx.Query(file, 4, 2) // Line 4: +MyObject
|
||||||
|
if res == nil || res.Node == nil {
|
||||||
|
t.Fatal("Query failed for definition")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedDoc := "Object Documentation\nSecond line"
|
||||||
|
if res.Node.Doc != expectedDoc {
|
||||||
|
t.Errorf("Expected definition doc:\n%q\nGot:\n%q", expectedDoc, res.Node.Doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Hover over MyObject reference
|
||||||
|
resRef := idx.Query(file, 10, 16) // Line 10: RefField = MyObject
|
||||||
|
if resRef == nil || resRef.Reference == nil {
|
||||||
|
t.Fatal("Query failed for reference")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resRef.Reference.Target == nil {
|
||||||
|
t.Fatal("Reference target not resolved")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resRef.Reference.Target.Doc != expectedDoc {
|
||||||
|
t.Errorf("Expected reference target definition doc:\n%q\nGot:\n%q", expectedDoc, resRef.Reference.Target.Doc)
|
||||||
|
}
|
||||||
|
}
|
||||||
49
test/lsp_signal_test.go
Normal file
49
test/lsp_signal_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/index"
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLSPSignalMetadata(t *testing.T) {
|
||||||
|
content := `
|
||||||
|
+MySignal = {
|
||||||
|
Class = Signal
|
||||||
|
Type = uint32
|
||||||
|
NumberOfElements = 10
|
||||||
|
NumberOfDimensions = 1
|
||||||
|
DataSource = DDB1
|
||||||
|
}
|
||||||
|
`
|
||||||
|
p := parser.NewParser(content)
|
||||||
|
config, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := index.NewProjectTree()
|
||||||
|
file := "signal.marte"
|
||||||
|
idx.AddFile(file, config)
|
||||||
|
|
||||||
|
res := idx.Query(file, 2, 2) // Query +MySignal
|
||||||
|
if res == nil || res.Node == nil {
|
||||||
|
t.Fatal("Query failed for signal definition")
|
||||||
|
}
|
||||||
|
|
||||||
|
meta := res.Node.Metadata
|
||||||
|
if meta["Class"] != "Signal" {
|
||||||
|
t.Errorf("Expected Class Signal, got %s", meta["Class"])
|
||||||
|
}
|
||||||
|
if meta["Type"] != "uint32" {
|
||||||
|
t.Errorf("Expected Type uint32, got %s", meta["Type"])
|
||||||
|
}
|
||||||
|
if meta["NumberOfElements"] != "10" {
|
||||||
|
t.Errorf("Expected 10 elements, got %s", meta["NumberOfElements"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since handleHover logic is in internal/lsp which we can't easily test directly without
|
||||||
|
// exposing formatNodeInfo, we rely on the fact that Metadata is populated correctly.
|
||||||
|
// If Metadata is correct, server.go logic (verified by code review) should display it.
|
||||||
|
}
|
||||||
142
test/lsp_test.go
Normal file
142
test/lsp_test.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/index"
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/parser"
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helper to load and parse a file
|
||||||
|
func loadConfig(t *testing.T, filename string) *parser.Configuration {
|
||||||
|
content, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read %s: %v", filename, err)
|
||||||
|
}
|
||||||
|
p := parser.NewParser(string(content))
|
||||||
|
config, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLSPDiagnostics(t *testing.T) {
|
||||||
|
inputFile := "integration/check_dup.marte"
|
||||||
|
config := loadConfig(t, inputFile)
|
||||||
|
|
||||||
|
// Simulate LSP logic: Build Index -> Validate
|
||||||
|
idx := index.NewProjectTree()
|
||||||
|
idx.AddFile(inputFile, config)
|
||||||
|
|
||||||
|
v := validator.NewValidator(idx)
|
||||||
|
v.ValidateProject()
|
||||||
|
|
||||||
|
// Check for expected diagnostics
|
||||||
|
found := false
|
||||||
|
for _, d := range v.Diagnostics {
|
||||||
|
if d.Message == "Duplicate Field Definition: 'Field' is already defined in integration/check_dup.marte" {
|
||||||
|
found = true
|
||||||
|
if d.Position.Line != 5 {
|
||||||
|
t.Errorf("Expected diagnostic at line 5, got %d", d.Position.Line)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Error("LSP Diagnostic for duplicate field not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GoToDefinition and References, we need to test the Indexer's ability to resolve symbols.
|
||||||
|
// Currently, my Indexer (ProjectTree) stores structure but doesn't explicitly track
|
||||||
|
// "references" in a way that maps a source position to a target symbol yet.
|
||||||
|
// The ProjectTree is built for structure merging.
|
||||||
|
// To support LSP "Go To Definition", we need to map usage -> definition.
|
||||||
|
|
||||||
|
// Let's verify what we have implemented.
|
||||||
|
// `internal/index/index.go`:
|
||||||
|
// ProjectTree has Fragments.
|
||||||
|
// It does NOT have a "Lookup(position)" method or a reference map.
|
||||||
|
// Previously (before rewrite), `index.go` had `References []Reference`.
|
||||||
|
// I removed it during the rewrite to ProjectTree!
|
||||||
|
|
||||||
|
// I need to re-implement reference tracking in `ProjectTree` or a parallel structure
|
||||||
|
// to support LSP features.
|
||||||
|
func TestLSPDefinition(t *testing.T) {
|
||||||
|
// Create a virtual file content with a definition and a reference
|
||||||
|
content := `
|
||||||
|
+MyObject = {
|
||||||
|
Class = Type
|
||||||
|
}
|
||||||
|
+RefObject = {
|
||||||
|
Class = Type
|
||||||
|
RefField = MyObject
|
||||||
|
}
|
||||||
|
`
|
||||||
|
p := parser.NewParser(content)
|
||||||
|
config, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := index.NewProjectTree()
|
||||||
|
idx.AddFile("memory.marte", config)
|
||||||
|
idx.ResolveReferences()
|
||||||
|
|
||||||
|
// Find the reference to "MyObject"
|
||||||
|
var foundRef *index.Reference
|
||||||
|
for _, ref := range idx.References {
|
||||||
|
if ref.Name == "MyObject" {
|
||||||
|
foundRef = &ref
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundRef == nil {
|
||||||
|
t.Fatal("Reference to MyObject not found in index")
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundRef.Target == nil {
|
||||||
|
t.Fatal("Reference to MyObject was not resolved to a target")
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundRef.Target.RealName != "+MyObject" {
|
||||||
|
t.Errorf("Expected target to be +MyObject, got %s", foundRef.Target.RealName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLSPHover(t *testing.T) {
|
||||||
|
content := `
|
||||||
|
+MyObject = {
|
||||||
|
Class = Type
|
||||||
|
}
|
||||||
|
`
|
||||||
|
p := parser.NewParser(content)
|
||||||
|
config, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := index.NewProjectTree()
|
||||||
|
file := "hover.marte"
|
||||||
|
idx.AddFile(file, config)
|
||||||
|
|
||||||
|
// +MyObject is at line 2.
|
||||||
|
// Query at line 2, col 2 (on 'M' of MyObject)
|
||||||
|
res := idx.Query(file, 2, 2)
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
t.Fatal("Query returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Node == nil {
|
||||||
|
t.Fatal("Expected Node result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Node.RealName != "+MyObject" {
|
||||||
|
t.Errorf("Expected +MyObject, got %s", res.Node.RealName)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user