Moved tests in test folder (and made methods public in server.go)

This commit is contained in:
Martino Ferrari
2026-01-23 14:04:24 +01:00
parent 4a515fd6c3
commit e3c84fcf60
3 changed files with 134 additions and 145 deletions

View File

@@ -47,10 +47,10 @@ type CompletionList struct {
Items []CompletionItem `json:"items"` Items []CompletionItem `json:"items"`
} }
var tree = index.NewProjectTree() var Tree = index.NewProjectTree()
var documents = make(map[string]string) var Documents = make(map[string]string)
var projectRoot string var ProjectRoot string
var globalSchema *schema.Schema var GlobalSchema *schema.Schema
type JsonRpcMessage struct { type JsonRpcMessage struct {
Jsonrpc string `json:"jsonrpc"` Jsonrpc string `json:"jsonrpc"`
@@ -184,7 +184,7 @@ func RunServer() {
continue continue
} }
handleMessage(msg) HandleMessage(msg)
} }
} }
@@ -214,7 +214,7 @@ func readMessage(reader *bufio.Reader) (*JsonRpcMessage, error) {
return &msg, err return &msg, err
} }
func handleMessage(msg *JsonRpcMessage) { func HandleMessage(msg *JsonRpcMessage) {
switch msg.Method { switch msg.Method {
case "initialize": case "initialize":
var params InitializeParams var params InitializeParams
@@ -227,13 +227,13 @@ func handleMessage(msg *JsonRpcMessage) {
} }
if root != "" { if root != "" {
projectRoot = root ProjectRoot = root
logger.Printf("Scanning workspace: %s\n", root) logger.Printf("Scanning workspace: %s\n", root)
if err := tree.ScanDirectory(root); err != nil { if err := Tree.ScanDirectory(root); err != nil {
logger.Printf("ScanDirectory failed: %v\n", err) logger.Printf("ScanDirectory failed: %v\n", err)
} }
tree.ResolveReferences() Tree.ResolveReferences()
globalSchema = schema.LoadFullSchema(projectRoot) GlobalSchema = schema.LoadFullSchema(ProjectRoot)
} }
} }
@@ -258,18 +258,18 @@ func handleMessage(msg *JsonRpcMessage) {
case "textDocument/didOpen": case "textDocument/didOpen":
var params DidOpenTextDocumentParams var params DidOpenTextDocumentParams
if err := json.Unmarshal(msg.Params, &params); err == nil { if err := json.Unmarshal(msg.Params, &params); err == nil {
handleDidOpen(params) HandleDidOpen(params)
} }
case "textDocument/didChange": case "textDocument/didChange":
var params DidChangeTextDocumentParams var params DidChangeTextDocumentParams
if err := json.Unmarshal(msg.Params, &params); err == nil { if err := json.Unmarshal(msg.Params, &params); err == nil {
handleDidChange(params) HandleDidChange(params)
} }
case "textDocument/hover": case "textDocument/hover":
var params HoverParams var params HoverParams
if err := json.Unmarshal(msg.Params, &params); err == nil { if err := json.Unmarshal(msg.Params, &params); err == nil {
logger.Printf("Hover: %s:%d", params.TextDocument.URI, params.Position.Line) logger.Printf("Hover: %s:%d", params.TextDocument.URI, params.Position.Line)
res := handleHover(params) res := HandleHover(params)
if res != nil { if res != nil {
logger.Printf("Res: %v", res.Contents) logger.Printf("Res: %v", res.Contents)
} else { } else {
@@ -283,22 +283,22 @@ func handleMessage(msg *JsonRpcMessage) {
case "textDocument/definition": case "textDocument/definition":
var params DefinitionParams var params DefinitionParams
if err := json.Unmarshal(msg.Params, &params); err == nil { if err := json.Unmarshal(msg.Params, &params); err == nil {
respond(msg.ID, handleDefinition(params)) respond(msg.ID, HandleDefinition(params))
} }
case "textDocument/references": case "textDocument/references":
var params ReferenceParams var params ReferenceParams
if err := json.Unmarshal(msg.Params, &params); err == nil { if err := json.Unmarshal(msg.Params, &params); err == nil {
respond(msg.ID, handleReferences(params)) respond(msg.ID, HandleReferences(params))
} }
case "textDocument/completion": case "textDocument/completion":
var params CompletionParams var params CompletionParams
if err := json.Unmarshal(msg.Params, &params); err == nil { if err := json.Unmarshal(msg.Params, &params); err == nil {
respond(msg.ID, handleCompletion(params)) respond(msg.ID, HandleCompletion(params))
} }
case "textDocument/formatting": case "textDocument/formatting":
var params DocumentFormattingParams var params DocumentFormattingParams
if err := json.Unmarshal(msg.Params, &params); err == nil { if err := json.Unmarshal(msg.Params, &params); err == nil {
respond(msg.ID, handleFormatting(params)) respond(msg.ID, HandleFormatting(params))
} }
} }
} }
@@ -307,9 +307,9 @@ func uriToPath(uri string) string {
return strings.TrimPrefix(uri, "file://") return strings.TrimPrefix(uri, "file://")
} }
func handleDidOpen(params DidOpenTextDocumentParams) { func HandleDidOpen(params DidOpenTextDocumentParams) {
path := uriToPath(params.TextDocument.URI) path := uriToPath(params.TextDocument.URI)
documents[params.TextDocument.URI] = params.TextDocument.Text Documents[params.TextDocument.URI] = params.TextDocument.Text
p := parser.NewParser(params.TextDocument.Text) p := parser.NewParser(params.TextDocument.Text)
config, err := p.Parse() config, err := p.Parse()
@@ -320,18 +320,18 @@ func handleDidOpen(params DidOpenTextDocumentParams) {
} }
if config != nil { if config != nil {
tree.AddFile(path, config) Tree.AddFile(path, config)
tree.ResolveReferences() Tree.ResolveReferences()
runValidation(params.TextDocument.URI) runValidation(params.TextDocument.URI)
} }
} }
func handleDidChange(params DidChangeTextDocumentParams) { func HandleDidChange(params DidChangeTextDocumentParams) {
if len(params.ContentChanges) == 0 { if len(params.ContentChanges) == 0 {
return return
} }
text := params.ContentChanges[0].Text text := params.ContentChanges[0].Text
documents[params.TextDocument.URI] = text Documents[params.TextDocument.URI] = text
path := uriToPath(params.TextDocument.URI) path := uriToPath(params.TextDocument.URI)
p := parser.NewParser(text) p := parser.NewParser(text)
config, err := p.Parse() config, err := p.Parse()
@@ -343,15 +343,15 @@ func handleDidChange(params DidChangeTextDocumentParams) {
} }
if config != nil { if config != nil {
tree.AddFile(path, config) Tree.AddFile(path, config)
tree.ResolveReferences() Tree.ResolveReferences()
runValidation(params.TextDocument.URI) runValidation(params.TextDocument.URI)
} }
} }
func handleFormatting(params DocumentFormattingParams) []TextEdit { func HandleFormatting(params DocumentFormattingParams) []TextEdit {
uri := params.TextDocument.URI uri := params.TextDocument.URI
text, ok := documents[uri] text, ok := Documents[uri]
if !ok { if !ok {
return nil return nil
} }
@@ -383,7 +383,7 @@ func handleFormatting(params DocumentFormattingParams) []TextEdit {
} }
func runValidation(uri string) { func runValidation(uri string) {
v := validator.NewValidator(tree, projectRoot) v := validator.NewValidator(Tree, ProjectRoot)
v.ValidateProject() v.ValidateProject()
v.CheckUnused() v.CheckUnused()
@@ -392,7 +392,7 @@ func runValidation(uri string) {
// Collect all known files to ensure we clear diagnostics for fixed files // Collect all known files to ensure we clear diagnostics for fixed files
knownFiles := make(map[string]bool) knownFiles := make(map[string]bool)
collectFiles(tree.Root, knownFiles) collectFiles(Tree.Root, knownFiles)
// Initialize all known files with empty diagnostics // Initialize all known files with empty diagnostics
for f := range knownFiles { for f := range knownFiles {
@@ -501,12 +501,12 @@ func mustMarshal(v any) json.RawMessage {
return b return b
} }
func handleHover(params HoverParams) *Hover { func HandleHover(params HoverParams) *Hover {
path := uriToPath(params.TextDocument.URI) path := uriToPath(params.TextDocument.URI)
line := params.Position.Line + 1 line := params.Position.Line + 1
col := params.Position.Character + 1 col := params.Position.Character + 1
res := tree.Query(path, line, col) res := Tree.Query(path, line, col)
if res == nil { if res == nil {
logger.Printf("No object/node/reference found") logger.Printf("No object/node/reference found")
return nil return nil
@@ -553,10 +553,10 @@ func handleHover(params HoverParams) *Hover {
} }
} }
func handleCompletion(params CompletionParams) *CompletionList { func HandleCompletion(params CompletionParams) *CompletionList {
uri := params.TextDocument.URI uri := params.TextDocument.URI
path := uriToPath(uri) path := uriToPath(uri)
text, ok := documents[uri] text, ok := Documents[uri]
if !ok { if !ok {
return nil return nil
} }
@@ -591,7 +591,7 @@ func handleCompletion(params CompletionParams) *CompletionList {
return suggestClasses() return suggestClasses()
} }
container := tree.GetNodeContaining(path, parser.Position{Line: params.Position.Line + 1, Column: col + 1}) container := Tree.GetNodeContaining(path, parser.Position{Line: params.Position.Line + 1, Column: col + 1})
if container != nil { if container != nil {
return suggestFieldValues(container, key, path) return suggestFieldValues(container, key, path)
} }
@@ -599,7 +599,7 @@ func handleCompletion(params CompletionParams) *CompletionList {
} }
// Case 2: Typing a key inside an object // Case 2: Typing a key inside an object
container := tree.GetNodeContaining(path, parser.Position{Line: params.Position.Line + 1, Column: col + 1}) container := Tree.GetNodeContaining(path, parser.Position{Line: params.Position.Line + 1, Column: col + 1})
if container != nil { if container != nil {
return suggestFields(container) return suggestFields(container)
} }
@@ -608,11 +608,11 @@ func handleCompletion(params CompletionParams) *CompletionList {
} }
func suggestClasses() *CompletionList { func suggestClasses() *CompletionList {
if globalSchema == nil { if GlobalSchema == nil {
return nil return nil
} }
classesVal := globalSchema.Value.LookupPath(cue.ParsePath("#Classes")) classesVal := GlobalSchema.Value.LookupPath(cue.ParsePath("#Classes"))
if classesVal.Err() != nil { if classesVal.Err() != nil {
return nil return nil
} }
@@ -647,11 +647,11 @@ func suggestFields(container *index.ProjectNode) *CompletionList {
}}} }}}
} }
if globalSchema == nil { if GlobalSchema == nil {
return nil return nil
} }
classPath := cue.ParsePath(fmt.Sprintf("#Classes.%s", cls)) classPath := cue.ParsePath(fmt.Sprintf("#Classes.%s", cls))
classVal := globalSchema.Value.LookupPath(classPath) classVal := GlobalSchema.Value.LookupPath(classPath)
if classVal.Err() != nil { if classVal.Err() != nil {
return nil return nil
} }
@@ -711,10 +711,10 @@ func suggestFields(container *index.ProjectNode) *CompletionList {
func suggestFieldValues(container *index.ProjectNode, field string, path string) *CompletionList { func suggestFieldValues(container *index.ProjectNode, field string, path string) *CompletionList {
var root *index.ProjectNode var root *index.ProjectNode
if iso, ok := tree.IsolatedFiles[path]; ok { if iso, ok := Tree.IsolatedFiles[path]; ok {
root = iso root = iso
} else { } else {
root = tree.Root root = Tree.Root
} }
if field == "DataSource" { if field == "DataSource" {
@@ -779,12 +779,12 @@ func isDataSource(node *index.ProjectNode) bool {
return hasSignals return hasSignals
} }
func handleDefinition(params DefinitionParams) any { func HandleDefinition(params DefinitionParams) any {
path := uriToPath(params.TextDocument.URI) path := uriToPath(params.TextDocument.URI)
line := params.Position.Line + 1 line := params.Position.Line + 1
col := params.Position.Character + 1 col := params.Position.Character + 1
res := tree.Query(path, line, col) res := Tree.Query(path, line, col)
if res == nil { if res == nil {
return nil return nil
} }
@@ -819,12 +819,12 @@ func handleDefinition(params DefinitionParams) any {
return nil return nil
} }
func handleReferences(params ReferenceParams) []Location { func HandleReferences(params ReferenceParams) []Location {
path := uriToPath(params.TextDocument.URI) path := uriToPath(params.TextDocument.URI)
line := params.Position.Line + 1 line := params.Position.Line + 1
col := params.Position.Character + 1 col := params.Position.Character + 1
res := tree.Query(path, line, col) res := Tree.Query(path, line, col)
if res == nil { if res == nil {
return nil return nil
} }
@@ -862,7 +862,7 @@ func handleReferences(params ReferenceParams) []Location {
} }
// 1. References from index (Aliases) // 1. References from index (Aliases)
for _, ref := range tree.References { for _, ref := range Tree.References {
if ref.Target == canonical { if ref.Target == canonical {
locations = append(locations, Location{ locations = append(locations, Location{
URI: "file://" + ref.File, URI: "file://" + ref.File,
@@ -875,7 +875,7 @@ func handleReferences(params ReferenceParams) []Location {
} }
// 2. References from Node Targets (Direct References) // 2. References from Node Targets (Direct References)
tree.Walk(func(node *index.ProjectNode) { Tree.Walk(func(node *index.ProjectNode) {
if node.Target == canonical { if node.Target == canonical {
for _, frag := range node.Fragments { for _, frag := range node.Fragments {
if frag.IsObject { if frag.IsObject {
@@ -929,9 +929,9 @@ func formatNodeInfo(node *index.ProjectNode) string {
// Find references // Find references
var refs []string var refs []string
for _, ref := range tree.References { for _, ref := range Tree.References {
if ref.Target == node { if ref.Target == node {
container := tree.GetNodeContaining(ref.File, ref.Position) container := Tree.GetNodeContaining(ref.File, ref.Position)
if container != nil { if container != nil {
threadName := "" threadName := ""
stateName := "" stateName := ""

View File

@@ -1,21 +1,21 @@
package lsp package integration
import ( import (
"strings" "strings"
"testing" "testing"
"github.com/marte-community/marte-dev-tools/internal/index" "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" "github.com/marte-community/marte-dev-tools/internal/parser"
"github.com/marte-community/marte-dev-tools/internal/schema" "github.com/marte-community/marte-dev-tools/internal/schema"
) )
func TestHandleCompletion(t *testing.T) { func TestHandleCompletion(t *testing.T) {
setup := func() { setup := func() {
tree = index.NewProjectTree() lsp.Tree = index.NewProjectTree()
documents = make(map[string]string) lsp.Documents = make(map[string]string)
lsp.ProjectRoot = "."
projectRoot = "." lsp.GlobalSchema = schema.NewSchema()
globalSchema = schema.NewSchema()
} }
uri := "file://test.marte" uri := "file://test.marte"
@@ -24,14 +24,14 @@ func TestHandleCompletion(t *testing.T) {
t.Run("Suggest Classes", func(t *testing.T) { t.Run("Suggest Classes", func(t *testing.T) {
setup() setup()
content := "+Obj = { Class = " content := "+Obj = { Class = "
documents[uri] = content lsp.Documents[uri] = content
params := CompletionParams{ params := lsp.CompletionParams{
TextDocument: TextDocumentIdentifier{URI: uri}, TextDocument: lsp.TextDocumentIdentifier{URI: uri},
Position: Position{Line: 0, Character: len(content)}, Position: lsp.Position{Line: 0, Character: len(content)},
} }
list := handleCompletion(params) list := lsp.HandleCompletion(params)
if list == nil || len(list.Items) == 0 { if list == nil || len(list.Items) == 0 {
t.Fatal("Expected class suggestions, got none") t.Fatal("Expected class suggestions, got none")
} }
@@ -56,18 +56,18 @@ func TestHandleCompletion(t *testing.T) {
} }
` `
documents[uri] = content lsp.Documents[uri] = content
p := parser.NewParser(content) p := parser.NewParser(content)
cfg, _ := p.Parse() cfg, _ := p.Parse()
tree.AddFile(path, cfg) lsp.Tree.AddFile(path, cfg)
// Position at line 3 (empty line inside MyApp) // Position at line 3 (empty line inside MyApp)
params := CompletionParams{ params := lsp.CompletionParams{
TextDocument: TextDocumentIdentifier{URI: uri}, TextDocument: lsp.TextDocumentIdentifier{URI: uri},
Position: Position{Line: 3, Character: 4}, Position: lsp.Position{Line: 3, Character: 4},
} }
list := handleCompletion(params) list := lsp.HandleCompletion(params)
if list == nil || len(list.Items) == 0 { if list == nil || len(list.Items) == 0 {
t.Fatal("Expected field suggestions, got none") t.Fatal("Expected field suggestions, got none")
} }
@@ -106,19 +106,19 @@ $App = {
} }
} }
` `
documents[uri] = content lsp.Documents[uri] = content
p := parser.NewParser(content) p := parser.NewParser(content)
cfg, _ := p.Parse() cfg, _ := p.Parse()
tree.AddFile(path, cfg) lsp.Tree.AddFile(path, cfg)
tree.ResolveReferences() lsp.Tree.ResolveReferences()
// Position at end of "DataSource = " // Position at end of "DataSource = "
params := CompletionParams{ params := lsp.CompletionParams{
TextDocument: TextDocumentIdentifier{URI: uri}, TextDocument: lsp.TextDocumentIdentifier{URI: uri},
Position: Position{Line: 14, Character: 28}, Position: lsp.Position{Line: 14, Character: 28},
} }
list := handleCompletion(params) list := lsp.HandleCompletion(params)
if list == nil || len(list.Items) == 0 { if list == nil || len(list.Items) == 0 {
t.Fatal("Expected DataSource suggestions, got none") t.Fatal("Expected DataSource suggestions, got none")
} }
@@ -144,18 +144,18 @@ $App = {
} }
` `
documents[uri] = content lsp.Documents[uri] = content
p := parser.NewParser(content) p := parser.NewParser(content)
cfg, _ := p.Parse() cfg, _ := p.Parse()
tree.AddFile(path, cfg) lsp.Tree.AddFile(path, cfg)
// Position at line 4 // Position at line 4
params := CompletionParams{ params := lsp.CompletionParams{
TextDocument: TextDocumentIdentifier{URI: uri}, TextDocument: lsp.TextDocumentIdentifier{URI: uri},
Position: Position{Line: 4, Character: 4}, Position: lsp.Position{Line: 4, Character: 4},
} }
list := handleCompletion(params) list := lsp.HandleCompletion(params)
for _, item := range list.Items { for _, item := range list.Items {
if item.Label == "Functions" || item.Label == "Class" { if item.Label == "Functions" || item.Label == "Class" {
t.Errorf("Did not expect already defined field %s in suggestions", item.Label) t.Errorf("Did not expect already defined field %s in suggestions", item.Label)
@@ -163,27 +163,27 @@ $App = {
} }
}) })
t.Run("Scope-aware suggestions", func(t *testing.T) { t.Run("Scope-aware suggestions", func(t *testing.T) {
setup() setup()
// Define a project DataSource in one file // Define a project DataSource in one file
cfg1, _ := parser.NewParser("#package MYPROJ.Data\n+ProjectDS = { Class = FileReader +Signals = { S1 = { Type = int32 } } }").Parse() cfg1, _ := parser.NewParser("#package MYPROJ.Data\n+ProjectDS = { Class = FileReader +Signals = { S1 = { Type = int32 } } }").Parse()
tree.AddFile("project_ds.marte", cfg1) lsp.Tree.AddFile("project_ds.marte", cfg1)
// Define an isolated file // Define an isolated file
contentIso := "+MyGAM = { Class = IOGAM +InputSignals = { S1 = { DataSource = } } }" contentIso := "+MyGAM = { Class = IOGAM +InputSignals = { S1 = { DataSource = } } }"
documents["file://iso.marte"] = contentIso lsp.Documents["file://iso.marte"] = contentIso
cfg2, _ := parser.NewParser(contentIso).Parse() cfg2, _ := parser.NewParser(contentIso).Parse()
tree.AddFile("iso.marte", cfg2) lsp.Tree.AddFile("iso.marte", cfg2)
tree.ResolveReferences() lsp.Tree.ResolveReferences()
// Completion in isolated file // Completion in isolated file
params := CompletionParams{ params := lsp.CompletionParams{
TextDocument: TextDocumentIdentifier{URI: "file://iso.marte"}, TextDocument: lsp.TextDocumentIdentifier{URI: "file://iso.marte"},
Position: Position{Line: 0, Character: strings.Index(contentIso, "DataSource = ") + len("DataSource = ") + 1}, Position: lsp.Position{Line: 0, Character: strings.Index(contentIso, "DataSource = ") + len("DataSource = ") + 1},
} }
list := handleCompletion(params) list := lsp.HandleCompletion(params)
foundProjectDS := false foundProjectDS := false
if list != nil { if list != nil {
for _, item := range list.Items { for _, item := range list.Items {
@@ -200,21 +200,21 @@ $App = {
// Completion in a project file // Completion in a project file
lineContent := "+MyGAM = { Class = IOGAM +InputSignals = { S1 = { DataSource = Dummy } } }" lineContent := "+MyGAM = { Class = IOGAM +InputSignals = { S1 = { DataSource = Dummy } } }"
contentPrj := "#package MYPROJ.App\n" + lineContent contentPrj := "#package MYPROJ.App\n" + lineContent
documents["file://prj.marte"] = contentPrj lsp.Documents["file://prj.marte"] = contentPrj
pPrj := parser.NewParser(contentPrj) pPrj := parser.NewParser(contentPrj)
cfg3, err := pPrj.Parse() cfg3, err := pPrj.Parse()
if err != nil { if err != nil {
t.Logf("Parser error in contentPrj: %v", err) t.Logf("Parser error in contentPrj: %v", err)
} }
tree.AddFile("prj.marte", cfg3) lsp.Tree.AddFile("prj.marte", cfg3)
tree.ResolveReferences() lsp.Tree.ResolveReferences()
paramsPrj := CompletionParams{ paramsPrj := lsp.CompletionParams{
TextDocument: TextDocumentIdentifier{URI: "file://prj.marte"}, TextDocument: lsp.TextDocumentIdentifier{URI: "file://prj.marte"},
Position: Position{Line: 1, Character: strings.Index(lineContent, "Dummy")}, Position: lsp.Position{Line: 1, Character: strings.Index(lineContent, "Dummy")},
} }
listPrj := handleCompletion(paramsPrj) listPrj := lsp.HandleCompletion(paramsPrj)
foundProjectDS = false foundProjectDS = false
if listPrj != nil { if listPrj != nil {
for _, item := range listPrj.Items { for _, item := range listPrj.Items {

View File

@@ -1,4 +1,4 @@
package lsp package integration
import ( import (
"encoding/json" "encoding/json"
@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/marte-community/marte-dev-tools/internal/index" "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" "github.com/marte-community/marte-dev-tools/internal/parser"
) )
@@ -24,50 +25,38 @@ func TestInitProjectScan(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// File 2: Reference // File 2: Reference
// +Source = { Class = C Link = Target }
// Link = Target starts at index ...
// #package Test.Common (21 chars including newline)
// +Source = { Class = C Link = Target }
// 012345678901234567890123456789012345
// Previous offset was 29.
// Now add 21?
// #package Test.Common\n
// +Source = ...
// So add 21 to Character? Or Line 1?
// It's on Line 1 (0-based 1).
if err := os.WriteFile(filepath.Join(tmpDir, "ref.marte"), []byte("#package Test.Common\n+Source = { Class = C Link = Target }"), 0644); err != nil { if err := os.WriteFile(filepath.Join(tmpDir, "ref.marte"), []byte("#package Test.Common\n+Source = { Class = C Link = Target }"), 0644); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// 2. Initialize // 2. Initialize
tree = index.NewProjectTree() // Reset global tree lsp.Tree = index.NewProjectTree() // Reset global tree
initParams := InitializeParams{RootPath: tmpDir} initParams := lsp.InitializeParams{RootPath: tmpDir}
paramsBytes, _ := json.Marshal(initParams) paramsBytes, _ := json.Marshal(initParams)
msg := &JsonRpcMessage{ msg := &lsp.JsonRpcMessage{
Method: "initialize", Method: "initialize",
Params: paramsBytes, Params: paramsBytes,
ID: 1, ID: 1,
} }
handleMessage(msg) lsp.HandleMessage(msg)
// Query the reference in ref.marte at "Target" // Query the reference in ref.marte at "Target"
// Target starts at index 29 (0-based) on Line 1 defParams := lsp.DefinitionParams{
defParams := DefinitionParams{ TextDocument: lsp.TextDocumentIdentifier{URI: "file://" + filepath.Join(tmpDir, "ref.marte")},
TextDocument: TextDocumentIdentifier{URI: "file://" + filepath.Join(tmpDir, "ref.marte")}, Position: lsp.Position{Line: 1, Character: 29},
Position: Position{Line: 1, Character: 29},
} }
res := handleDefinition(defParams) res := lsp.HandleDefinition(defParams)
if res == nil { if res == nil {
t.Fatal("Definition not found via LSP after initialization") t.Fatal("Definition not found via LSP after initialization")
} }
locs, ok := res.([]Location) locs, ok := res.([]lsp.Location)
if !ok { if !ok {
t.Fatalf("Expected []Location, got %T", res) t.Fatalf("Expected []lsp.Location, got %T", res)
} }
if len(locs) == 0 { if len(locs) == 0 {
@@ -83,7 +72,7 @@ func TestInitProjectScan(t *testing.T) {
func TestHandleDefinition(t *testing.T) { func TestHandleDefinition(t *testing.T) {
// Reset tree for test // Reset tree for test
tree = index.NewProjectTree() lsp.Tree = index.NewProjectTree()
content := ` content := `
+MyObject = { +MyObject = {
@@ -100,28 +89,28 @@ func TestHandleDefinition(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Parse failed: %v", err) t.Fatalf("Parse failed: %v", err)
} }
tree.AddFile(path, config) lsp.Tree.AddFile(path, config)
tree.ResolveReferences() lsp.Tree.ResolveReferences()
t.Logf("Refs: %d", len(tree.References)) t.Logf("Refs: %d", len(lsp.Tree.References))
for _, r := range tree.References { for _, r := range lsp.Tree.References {
t.Logf(" %s at %d:%d", r.Name, r.Position.Line, r.Position.Column) t.Logf(" %s at %d:%d", r.Name, r.Position.Line, r.Position.Column)
} }
// Test Go to Definition on MyObject reference // Test Go to Definition on MyObject reference
params := DefinitionParams{ params := lsp.DefinitionParams{
TextDocument: TextDocumentIdentifier{URI: "file://" + path}, TextDocument: lsp.TextDocumentIdentifier{URI: "file://" + path},
Position: Position{Line: 6, Character: 15}, // "MyObject" in RefField = MyObject Position: lsp.Position{Line: 6, Character: 15}, // "MyObject" in RefField = MyObject
} }
result := handleDefinition(params) result := lsp.HandleDefinition(params)
if result == nil { if result == nil {
t.Fatal("handleDefinition returned nil") t.Fatal("HandleDefinition returned nil")
} }
locations, ok := result.([]Location) locations, ok := result.([]lsp.Location)
if !ok { if !ok {
t.Fatalf("Expected []Location, got %T", result) t.Fatalf("Expected []lsp.Location, got %T", result)
} }
if len(locations) != 1 { if len(locations) != 1 {
@@ -135,7 +124,7 @@ func TestHandleDefinition(t *testing.T) {
func TestHandleReferences(t *testing.T) { func TestHandleReferences(t *testing.T) {
// Reset tree for test // Reset tree for test
tree = index.NewProjectTree() lsp.Tree = index.NewProjectTree()
content := ` content := `
+MyObject = { +MyObject = {
@@ -155,17 +144,17 @@ func TestHandleReferences(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Parse failed: %v", err) t.Fatalf("Parse failed: %v", err)
} }
tree.AddFile(path, config) lsp.Tree.AddFile(path, config)
tree.ResolveReferences() lsp.Tree.ResolveReferences()
// Test Find References for MyObject (triggered from its definition) // Test Find References for MyObject (triggered from its definition)
params := ReferenceParams{ params := lsp.ReferenceParams{
TextDocument: TextDocumentIdentifier{URI: "file://" + path}, TextDocument: lsp.TextDocumentIdentifier{URI: "file://" + path},
Position: Position{Line: 1, Character: 1}, // "+MyObject" Position: lsp.Position{Line: 1, Character: 1}, // "+MyObject"
Context: ReferenceContext{IncludeDeclaration: true}, Context: lsp.ReferenceContext{IncludeDeclaration: true},
} }
locations := handleReferences(params) locations := lsp.HandleReferences(params)
if len(locations) != 3 { // 1 declaration + 2 references if len(locations) != 3 { // 1 declaration + 2 references
t.Fatalf("Expected 3 locations, got %d", len(locations)) t.Fatalf("Expected 3 locations, got %d", len(locations))
} }
@@ -181,15 +170,15 @@ Field=1
` `
uri := "file:///test.marte" uri := "file:///test.marte"
// Open (populate documents map) // Open (populate Documents map)
documents[uri] = content lsp.Documents[uri] = content
// Format // Format
params := DocumentFormattingParams{ params := lsp.DocumentFormattingParams{
TextDocument: TextDocumentIdentifier{URI: uri}, TextDocument: lsp.TextDocumentIdentifier{URI: uri},
} }
edits := handleFormatting(params) edits := lsp.HandleFormatting(params)
if len(edits) != 1 { if len(edits) != 1 {
t.Fatalf("Expected 1 edit, got %d", len(edits)) t.Fatalf("Expected 1 edit, got %d", len(edits))