diff --git a/lsp/server_test.go b/lsp/server_test.go index f1fc6c2..bdad5b4 100644 --- a/lsp/server_test.go +++ b/lsp/server_test.go @@ -274,6 +274,105 @@ func TestSemanticTokens(t *testing.T) { } } +func TestReferences(t *testing.T) { + in := session("file:///f_amd64.s", cleanDoc) + + frame(6, "textDocument/references", map[string]any{ + "textDocument": map[string]any{"uri": "file:///f_amd64.s"}, + "position": map[string]any{"line": 4, "character": 5}, // on "loop" in JMP loop + "context": map[string]any{"includeDeclaration": true}, + }) + frame(nil, "exit", nil) + msgs := run(t, in) + resp := findByID(msgs, 6) + if resp == nil { + t.Fatal("no references response") + } + var locs []Location + if err := json.Unmarshal(mustResult(t, resp), &locs); err != nil { + t.Fatal(err) + } + if len(locs) < 2 { + t.Fatalf("want at least 2 references (definition + use), got %d", len(locs)) + } +} + +func TestRename(t *testing.T) { + in := session("file:///f_amd64.s", cleanDoc) + + frame(7, "textDocument/rename", map[string]any{ + "textDocument": map[string]any{"uri": "file:///f_amd64.s"}, + "position": map[string]any{"line": 3, "character": 0}, // on "loop:" label + "newName": "newloop", + }) + frame(nil, "exit", nil) + msgs := run(t, in) + resp := findByID(msgs, 7) + if resp == nil { + t.Fatal("no rename response") + } + var edit WorkspaceEdit + if err := json.Unmarshal(mustResult(t, resp), &edit); err != nil { + t.Fatal(err) + } + edits := edit.Changes["file:///f_amd64.s"] + if len(edits) < 2 { + t.Fatalf("want at least 2 edits (definition + use), got %d", len(edits)) + } + for _, e := range edits { + if e.NewText != "newloop" { + t.Errorf("edit newText = %q, want newloop", e.NewText) + } + } +} + +func TestFormatting(t *testing.T) { + in := session("file:///f_amd64.s", cleanDoc) + + frame(8, "textDocument/formatting", map[string]any{ + "textDocument": map[string]any{"uri": "file:///f_amd64.s"}, + "options": map[string]any{"tabSize": 4, "insertSpaces": false}, + }) + frame(nil, "exit", nil) + msgs := run(t, in) + resp := findByID(msgs, 8) + if resp == nil { + t.Fatal("no formatting response") + } + var edits []TextEdit + if err := json.Unmarshal(mustResult(t, resp), &edits); err != nil { + t.Fatal(err) + } + // Formatting should return at most one whole-document edit. + if len(edits) > 1 { + t.Fatalf("formatting returned %d edits, want 0 or 1", len(edits)) + } +} + +func TestInlayHints(t *testing.T) { + docWithFrame := "#include \"textflag.h\"\n" + + "TEXT ·foo(SB), NOSPLIT, $16-24\n" + + "\tMOVQ AX, CX\n" + + "\tRET\n" + in := session("file:///f_amd64.s", docWithFrame) + + frame(9, "textDocument/inlayHint", map[string]any{ + "textDocument": map[string]any{"uri": "file:///f_amd64.s"}, + "range": map[string]any{ + "start": map[string]any{"line": 0, "character": 0}, + "end": map[string]any{"line": 3, "character": 0}, + }, + }) + frame(nil, "exit", nil) + msgs := run(t, in) + resp := findByID(msgs, 9) + if resp == nil { + t.Fatal("no inlayHint response") + } + var hints []InlayHint + if err := json.Unmarshal(mustResult(t, resp), &hints); err != nil { + t.Fatal(err) + } + if len(hints) == 0 { + t.Fatal("want at least 1 inlay hint for frame size") + } + if !strings.Contains(hints[0].Label, "frame=") { + t.Errorf("hint label = %q, want frame hint", hints[0].Label) + } +} + func TestMethodNotFound(t *testing.T) { in := frame(9, "bogus/method", map[string]any{}) + frame(nil, "exit", nil) msgs := run(t, in)