-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
169 lines (135 loc) · 4.65 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package lsp
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"github.com/armsnyder/lsp/jsonrpc"
"github.com/armsnyder/lsp/types"
)
// Server is an LSP server. It handles the I/O and delegates handling of
// requests to a Handler.
type Server struct {
Reader io.Reader
Writer io.Writer
Handler Handler
ServerInfo types.ServerInfo
}
// Run is a blocking function that reads from the server's Reader, processes
// requests, and writes responses to the server's Writer. It returns an error
// if the server stops unexpectedly.
func (s *Server) Run() error {
scanner := bufio.NewScanner(s.Reader)
scanner.Split(jsonrpc.Split)
log.Println("LSP server started")
for scanner.Scan() {
if err := s.handleRequestPayload(scanner.Bytes()); err != nil {
if errors.Is(err, errShutdown) {
log.Println("LSP server shutting down")
return nil
}
return err
}
}
return scanner.Err()
}
func (s *Server) handleRequestPayload(payload []byte) (err error) {
var request types.RequestMessage
err = json.Unmarshal(payload, &request)
if err != nil {
return err
}
if request.JSONRPC != "2.0" {
return errors.New("unknown jsonrpc version")
}
if request.Method == "" {
return errors.New("request is missing a method")
}
return s.handleRequest(request)
}
var errShutdown = errors.New("shutdown")
func (s *Server) handleRequest(request types.RequestMessage) error {
switch request.Method {
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize
case "initialize":
var params types.InitializeParams
if err := json.Unmarshal(request.Params, ¶ms); err != nil {
return fmt.Errorf("invalid initialize params: %w", err)
}
log.Printf("Connected to: %s %s", params.ClientInfo.Name, params.ClientInfo.Version)
s.write(request, types.InitializeResult{
Capabilities: s.Handler.Capabilities(),
ServerInfo: s.ServerInfo,
})
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialized
case "initialized":
// No-op
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#shutdown
case "shutdown":
s.write(request, nil)
return errShutdown
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didOpen
case "textDocument/didOpen":
var params types.DidOpenTextDocumentParams
if err := json.Unmarshal(request.Params, ¶ms); err != nil {
return fmt.Errorf("invalid textDocument/didOpen params: %w", err)
}
if err := s.Handler.HandleOpen(params); err != nil {
return err
}
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didClose
case "textDocument/didClose":
var params types.DidCloseTextDocumentParams
if err := json.Unmarshal(request.Params, ¶ms); err != nil {
return fmt.Errorf("invalid textDocument/didClose params: %w", err)
}
if err := s.Handler.HandleClose(params); err != nil {
return err
}
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didChange
case "textDocument/didChange":
var params types.DidChangeTextDocumentParams
if err := json.Unmarshal(request.Params, ¶ms); err != nil {
return fmt.Errorf("invalid textDocument/didChange params: %w", err)
}
if err := s.Handler.HandleChange(params); err != nil {
return err
}
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition
case "textDocument/definition":
var params types.DefinitionParams
if err := json.Unmarshal(request.Params, ¶ms); err != nil {
return fmt.Errorf("invalid textDocument/definition params: %w", err)
}
location, err := s.Handler.HandleDefinition(params)
if err != nil {
return err
}
s.write(request, location)
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references
case "textDocument/references":
var params types.ReferenceParams
if err := json.Unmarshal(request.Params, ¶ms); err != nil {
return fmt.Errorf("invalid textDocument/references params: %w", err)
}
locations, err := s.Handler.HandleReferences(params)
if err != nil {
return err
}
s.write(request, locations)
default:
log.Printf("Warning: Request with unknown method %q", request.Method)
}
return nil
}
func (s *Server) write(request types.RequestMessage, result any) {
if err := jsonrpc.Write(s.Writer, types.ResponseMessage{
JSONRPC: "2.0",
ID: request.ID,
Result: result,
}); err != nil {
log.Printf("Error writing response: %v", err)
}
}