Skip to content

Commit c261e7c

Browse files
authored
Merge pull request isaacphi#9 from virtuald/call-hierarchy
Add incoming_calls and outgoing_calls tools
2 parents 5effe60 + 6d598f5 commit c261e7c

File tree

10 files changed

+452
-0
lines changed

10 files changed

+452
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ This is an [MCP](https://modelcontextprotocol.io/introduction) server that runs
178178
- `hover`: Display documentation, type hints, or other hover information for a given location.
179179
- `rename_symbol`: Rename a symbol across a project.
180180
- `edit_file`: Allows making multiple text edits to a file based on line numbers. Provides a more reliable and context-economical way to edit files compared to search and replace based edit tools.
181+
- `callers`: Shows all locations that call a given symbol
182+
- `callees`: Shows all functions that a given symbol calls
181183

182184
## About
183185

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
---
3+
Name: HelperFunction
4+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacehelper.go
5+
/TEST_OUTPUT/workspace/helper.go
6+
Range: L4:C6 - L4:C20
7+
- Called By: AnotherConsumer
8+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspaceanother_consumer.go
9+
/TEST_OUTPUT/workspace/another_consumer.go
10+
Range: L6:C6 - L6:C21
11+
- Called By: ConsumerFunction
12+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspaceconsumer.go
13+
/TEST_OUTPUT/workspace/consumer.go
14+
Range: L6:C6 - L6:C22
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
---
3+
Name: FooBar
4+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacemain.go
5+
/TEST_OUTPUT/workspace/main.go
6+
Range: L6:C6 - L6:C12
7+
- Called By: main
8+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacemain.go
9+
/TEST_OUTPUT/workspace/main.go
10+
Range: L12:C6 - L12:C10
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
---
3+
Name: ConsumerFunction
4+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspaceconsumer.go
5+
/TEST_OUTPUT/workspace/consumer.go
6+
Range: L6:C6 - L6:C22
7+
- Calls: GetName
8+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacetypes.go
9+
/TEST_OUTPUT/workspace/types.go
10+
Range: L21:C2 - L21:C9
11+
- Calls: HelperFunction
12+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacehelper.go
13+
/TEST_OUTPUT/workspace/helper.go
14+
Range: L4:C6 - L4:C20
15+
- Calls: Method
16+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacetypes.go
17+
/TEST_OUTPUT/workspace/types.go
18+
Range: L14:C24 - L14:C30
19+
- Calls: Println
20+
Detail: fmtprint.go
21+
/GOROOT/src/fmt/print.go
22+
Range: L313:C6 - L313:C13
23+
- Calls: Process
24+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacetypes.go
25+
/TEST_OUTPUT/workspace/types.go
26+
Range: L31:C24 - L31:C31
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
---
3+
Name: main
4+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacemain.go
5+
/TEST_OUTPUT/workspace/main.go
6+
Range: L12:C6 - L12:C10
7+
- Calls: FooBar
8+
Detail: github.com/isaacphi/mcp-language-server/integrationtests/test-output/go/workspacemain.go
9+
/TEST_OUTPUT/workspace/main.go
10+
Range: L6:C6 - L6:C12
11+
- Calls: Println
12+
Detail: fmtprint.go
13+
/GOROOT/src/fmt/print.go
14+
Range: L313:C6 - L313:C13

integrationtests/tests/common/helpers.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package common
22

33
import (
4+
"bytes"
45
"fmt"
56
"io"
67
"os"
8+
"os/exec"
79
"path/filepath"
810
"strings"
911
"testing"
@@ -91,10 +93,25 @@ func CleanupTestSuites(suites ...*TestSuite) {
9193
}
9294
}
9395

96+
// use instead of runtime.GOROOT which is deprecated
97+
func getGoRoot() string {
98+
cmd := exec.Command("go", "env", "GOROOT")
99+
var out bytes.Buffer
100+
cmd.Stdout = &out
101+
err := cmd.Run()
102+
if err != nil {
103+
panic(err)
104+
}
105+
return strings.TrimSpace(out.String())
106+
}
107+
94108
// normalizePaths replaces absolute paths in the result with placeholder paths for consistent snapshots
95109
func normalizePaths(_ *testing.T, input string) string {
96110
// No need to get the repo root - we're just looking for patterns
97111

112+
// But this is useful
113+
goroot := getGoRoot()
114+
98115
// Simple approach: just replace any path segments that contain workspace/
99116
lines := strings.Split(input, "\n")
100117
for i, line := range lines {
@@ -116,6 +133,13 @@ func normalizePaths(_ *testing.T, input string) string {
116133
lines[i] = "/TEST_OUTPUT/workspace/" + parts[1]
117134
}
118135
}
136+
if strings.Contains(line, goroot) {
137+
parts := strings.Split(line, goroot)
138+
if len(parts) > 1 {
139+
// Replace with a simple placeholder path
140+
lines[i] = "/GOROOT" + parts[1]
141+
}
142+
}
119143
}
120144

121145
return strings.Join(lines, "\n")
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package callhierarchy_test
2+
3+
import (
4+
"context"
5+
"strings"
6+
"testing"
7+
"time"
8+
9+
"github.com/isaacphi/mcp-language-server/integrationtests/tests/common"
10+
"github.com/isaacphi/mcp-language-server/integrationtests/tests/go/internal"
11+
"github.com/isaacphi/mcp-language-server/internal/tools"
12+
)
13+
14+
func TestIncomingCalls(t *testing.T) {
15+
suite := internal.GetTestSuite(t)
16+
17+
ctx, cancel := context.WithTimeout(suite.Context, 10*time.Second)
18+
defer cancel()
19+
20+
tests := []struct {
21+
name string
22+
symbolName string
23+
expectedText string
24+
snapshotName string
25+
}{
26+
{
27+
name: "Function with calls in same file",
28+
symbolName: "FooBar",
29+
expectedText: ": main",
30+
snapshotName: "incoming-same-file",
31+
},
32+
{
33+
name: "Function with calls in other file",
34+
symbolName: "HelperFunction",
35+
expectedText: "ConsumerFunction",
36+
snapshotName: "incoming-other-file",
37+
},
38+
}
39+
40+
for _, tc := range tests {
41+
t.Run(tc.name, func(t *testing.T) {
42+
// Call the GetIncomingCalls tool
43+
result, err := tools.GetCallers(ctx, suite.Client, tc.symbolName, 1)
44+
if err != nil {
45+
t.Fatalf("Failed to find incoming calls: %v", err)
46+
}
47+
48+
// Check that the result contains relevant information
49+
if !strings.Contains(result, tc.expectedText) {
50+
t.Errorf("Incoming calls do not contain expected text: %s", tc.expectedText)
51+
}
52+
53+
// Use snapshot testing to verify exact output
54+
common.SnapshotTest(t, "go", "call_hierarchy", tc.snapshotName, result)
55+
})
56+
}
57+
58+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package callhierarchy_test
2+
3+
import (
4+
"context"
5+
"strings"
6+
"testing"
7+
"time"
8+
9+
"github.com/isaacphi/mcp-language-server/integrationtests/tests/common"
10+
"github.com/isaacphi/mcp-language-server/integrationtests/tests/go/internal"
11+
"github.com/isaacphi/mcp-language-server/internal/tools"
12+
)
13+
14+
func TestOutgoingCalls(t *testing.T) {
15+
suite := internal.GetTestSuite(t)
16+
17+
ctx, cancel := context.WithTimeout(suite.Context, 10*time.Second)
18+
defer cancel()
19+
20+
tests := []struct {
21+
name string
22+
symbolName string
23+
expectedText string
24+
snapshotName string
25+
}{
26+
{
27+
name: "Function with calls in other file",
28+
symbolName: "ConsumerFunction",
29+
expectedText: "HelperFunction",
30+
snapshotName: "outgoing-other-file",
31+
},
32+
{
33+
name: "Function with calls in same file",
34+
symbolName: "main",
35+
expectedText: "FooBar",
36+
snapshotName: "outgoing-same-file",
37+
},
38+
}
39+
40+
for _, tc := range tests {
41+
t.Run(tc.name, func(t *testing.T) {
42+
// Call the GetOutgoingCalls tool
43+
result, err := tools.GetCallees(ctx, suite.Client, tc.symbolName, 1)
44+
if err != nil {
45+
t.Fatalf("Failed to find outgoing calls: %v", err)
46+
}
47+
48+
// Check that the result contains relevant information
49+
if !strings.Contains(result, tc.expectedText) {
50+
t.Errorf("Outgoing calls do not contain expected text: %s", tc.expectedText)
51+
}
52+
53+
// Use snapshot testing to verify exact output
54+
common.SnapshotTest(t, "go", "call_hierarchy", tc.snapshotName, result)
55+
})
56+
}
57+
58+
}

0 commit comments

Comments
 (0)