-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
enhancementNew feature or requestNew feature or request
Description
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"log/slog"
"os/exec"
"github.com/sourcegraph/jsonrpc2"
lsp "github.com/toitware/go-lsp"
"github.com/toitware/go-lsp/lspext"
)
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
type CallHierarchyItem struct {
Name string `json:"name"`
Kind int `json:"kind"`
Tags []int `json:"tags,omitempty"`
Detail string `json:"detail,omitempty"`
URI lsp.DocumentURI `json:"uri"`
Range lsp.Range `json:"range"`
SelectionRange lsp.Range `json:"selectionRange"`
Data interface{} `json:"data,omitempty"`
}
type CallHierarchyPrepareParams struct {
lsp.TextDocumentPositionParams
WorkDoneToken interface{} `json:"workDoneToken,omitempty"`
}
type CallHierarchyIncomingCall struct {
From CallHierarchyItem `json:"from"`
FromRanges []lsp.Range `json:"fromRanges"`
}
type CallHierarchyOutgoingCall struct {
To CallHierarchyItem `json:"to"`
FromRanges []lsp.Range `json:"fromRanges"`
}
// Minimal stream wrapper for jsonrpc2
type stdioStream struct {
io.Reader
io.Writer
io.Closer
}
var _ interface {
io.Reader
io.Writer
io.Closer
} = (*stdioStream)(nil)
func hailait() {
ctx := context.Background()
// Start gopls in stdio mode
cmd := exec.Command("bob", "tools", "langserver")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout
if err := cmd.Start(); err != nil {
log.Fatal("failed to start gopls:", err)
}
stream := jsonrpc2.NewBufferedStream(&stdioStream{stdout, stdin, stdin}, jsonrpc2.VSCodeObjectCodec{})
conn := jsonrpc2.NewConn(ctx, stream, jsonrpc2.HandlerWithError(func(ctx context.Context, c *jsonrpc2.Conn, r *jsonrpc2.Request) (result interface{}, err error) {
switch r.Method {
case "window/showMessage":
logMessageParams := struct {
Message string `json:"message"`
Type int `json:"type"`
}{}
if err := json.Unmarshal(*r.Params, &logMessageParams); err != nil {
panic(err)
}
slog.Info("LSP server", "kind", "show", "msg", logMessageParams.Message, "type", logMessageParams.Type)
return nil, nil
case "window/logMessage":
logMessageParams := struct {
Message string `json:"message"`
Type int `json:"type"`
}{}
if err := json.Unmarshal(*r.Params, &logMessageParams); err != nil {
panic(err)
}
slog.Info("LSP server", "kind", "log", "msg", logMessageParams.Message, "type", logMessageParams.Type)
return nil, nil
default:
slog.Warn("returning error", "request", r.Method, "params", string(*r.Params))
return nil, errors.New("requests not implemented")
}
}))
defer stream.Close()
// ---- Initialize ----
initReq := lsp.InitializeParams{
RootURI: "file:///workspace",
}
var initResp lsp.InitializeResult
if err := conn.Call(ctx, "initialize", initReq, &initResp); err != nil {
log.Fatal("initialize error:", err)
}
// Tell server we're ready
_ = conn.Notify(ctx, "initialized", struct{}{})
// --- Prepare call hierarchy ---
params := CallHierarchyPrepareParams{
TextDocumentPositionParams: lsp.TextDocumentPositionParams{
TextDocument: lsp.TextDocumentIdentifier{
URI: initReq.RootURI + "/pkg/mypkg/example.go",
},
Position: lsp.Position{ // -1 because positions are 0-based
Line: 114 - 1,
Character: 6 - 1,
},
},
}
var items []CallHierarchyItem
if err := conn.Call(ctx, "textDocument/prepareCallHierarchy", params, &items); err != nil {
log.Fatal("prepareCallHierarchy:", err)
}
if len(items) == 0 {
log.Println("No call hierarchy items found at location")
return
}
item := items[0]
fmt.Println("Item:", item.Name)
_ = lspext.CacheSetParams{}
// --- Incoming calls ---
var incoming []CallHierarchyIncomingCall
if err := conn.Call(ctx, "callHierarchy/incomingCalls", struct {
Item CallHierarchyItem `json:"item"`
}{item}, &incoming); err != nil {
log.Fatal("incomingCalls: ", err)
}
fmt.Println("\nIncoming calls:")
for _, c := range incoming {
fmt.Println(" from:", c.From.Name)
}
// --- Outgoing calls ---
// var outgoing []CallHierarchyOutgoingCall
// if err := conn.Call(ctx, "callHierarchy/outgoingCalls", item, &outgoing); err != nil {
// log.Fatal("outgoingCalls:", err)
// }
// fmt.Println("\nOutgoing calls:")
// for _, c := range outgoing {
// fmt.Println(" to:", c.To.Name)
// }
}go.mod:
github.com/sourcegraph/jsonrpc2 v0.2.1 // indirect
github.com/toitware/go-lsp v0.0.0-20240220075859-6b0ca01316f0 // indirect
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request