Moved tests in test folder (and made methods public in server.go)
This commit is contained in:
@@ -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, ¶ms); err == nil {
|
if err := json.Unmarshal(msg.Params, ¶ms); err == nil {
|
||||||
handleDidOpen(params)
|
HandleDidOpen(params)
|
||||||
}
|
}
|
||||||
case "textDocument/didChange":
|
case "textDocument/didChange":
|
||||||
var params DidChangeTextDocumentParams
|
var params DidChangeTextDocumentParams
|
||||||
if err := json.Unmarshal(msg.Params, ¶ms); err == nil {
|
if err := json.Unmarshal(msg.Params, ¶ms); err == nil {
|
||||||
handleDidChange(params)
|
HandleDidChange(params)
|
||||||
}
|
}
|
||||||
case "textDocument/hover":
|
case "textDocument/hover":
|
||||||
var params HoverParams
|
var params HoverParams
|
||||||
if err := json.Unmarshal(msg.Params, ¶ms); err == nil {
|
if err := json.Unmarshal(msg.Params, ¶ms); 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, ¶ms); err == nil {
|
if err := json.Unmarshal(msg.Params, ¶ms); 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, ¶ms); err == nil {
|
if err := json.Unmarshal(msg.Params, ¶ms); 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, ¶ms); err == nil {
|
if err := json.Unmarshal(msg.Params, ¶ms); 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, ¶ms); err == nil {
|
if err := json.Unmarshal(msg.Params, ¶ms); 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 := ""
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
@@ -53,21 +53,21 @@ func TestHandleCompletion(t *testing.T) {
|
|||||||
content := `
|
content := `
|
||||||
+MyApp = {
|
+MyApp = {
|
||||||
Class = RealTimeApplication
|
Class = RealTimeApplication
|
||||||
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
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")
|
||||||
}
|
}
|
||||||
@@ -141,21 +141,21 @@ $App = {
|
|||||||
+MyThread = {
|
+MyThread = {
|
||||||
Class = RealTimeThread
|
Class = RealTimeThread
|
||||||
Functions = { }
|
Functions = { }
|
||||||
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
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 {
|
||||||
@@ -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))
|
||||||
Reference in New Issue
Block a user