transport.go

  1package lsp
  2
  3import (
  4	"bufio"
  5	"context"
  6	"encoding/json"
  7	"fmt"
  8	"io"
  9	"log"
 10	"os"
 11	"strings"
 12)
 13
 14var debug = os.Getenv("DEBUG") != ""
 15
 16// Write writes an LSP message to the given writer
 17func WriteMessage(w io.Writer, msg *Message) error {
 18	data, err := json.Marshal(msg)
 19	if err != nil {
 20		return fmt.Errorf("failed to marshal message: %w", err)
 21	}
 22
 23	if debug {
 24		log.Printf("%v", msg.Method)
 25		log.Printf("-> Sending: %s", string(data))
 26	}
 27
 28	_, err = fmt.Fprintf(w, "Content-Length: %d\r\n\r\n", len(data))
 29	if err != nil {
 30		return fmt.Errorf("failed to write header: %w", err)
 31	}
 32
 33	_, err = w.Write(data)
 34	if err != nil {
 35		return fmt.Errorf("failed to write message: %w", err)
 36	}
 37
 38	return nil
 39}
 40
 41// ReadMessage reads a single LSP message from the given reader
 42func ReadMessage(r *bufio.Reader) (*Message, error) {
 43	// Read headers
 44	var contentLength int
 45	for {
 46		line, err := r.ReadString('\n')
 47		if err != nil {
 48			return nil, fmt.Errorf("failed to read header: %w", err)
 49		}
 50		line = strings.TrimSpace(line)
 51
 52		if debug {
 53			log.Printf("<- Header: %s", line)
 54		}
 55
 56		if line == "" {
 57			break // End of headers
 58		}
 59
 60		if strings.HasPrefix(line, "Content-Length: ") {
 61			_, err := fmt.Sscanf(line, "Content-Length: %d", &contentLength)
 62			if err != nil {
 63				return nil, fmt.Errorf("invalid Content-Length: %w", err)
 64			}
 65		}
 66	}
 67
 68	if debug {
 69		log.Printf("<- Reading content with length: %d", contentLength)
 70	}
 71
 72	// Read content
 73	content := make([]byte, contentLength)
 74	_, err := io.ReadFull(r, content)
 75	if err != nil {
 76		return nil, fmt.Errorf("failed to read content: %w", err)
 77	}
 78
 79	if debug {
 80		log.Printf("<- Received: %s", string(content))
 81	}
 82
 83	// Parse message
 84	var msg Message
 85	if err := json.Unmarshal(content, &msg); err != nil {
 86		return nil, fmt.Errorf("failed to unmarshal message: %w", err)
 87	}
 88
 89	return &msg, nil
 90}
 91
 92// handleMessages reads and dispatches messages in a loop
 93func (c *Client) handleMessages() {
 94	for {
 95		msg, err := ReadMessage(c.stdout)
 96		if err != nil {
 97			if debug {
 98				log.Printf("Error reading message: %v", err)
 99			}
100			return
101		}
102
103		// Handle server->client request (has both Method and ID)
104		if msg.Method != "" && msg.ID != 0 {
105			if debug {
106				log.Printf("Received request from server: method=%s id=%d", msg.Method, msg.ID)
107			}
108
109			response := &Message{
110				JSONRPC: "2.0",
111				ID:      msg.ID,
112			}
113
114			// Look up handler for this method
115			c.serverHandlersMu.RLock()
116			handler, ok := c.serverRequestHandlers[msg.Method]
117			c.serverHandlersMu.RUnlock()
118
119			if ok {
120				result, err := handler(msg.Params)
121				if err != nil {
122					response.Error = &ResponseError{
123						Code:    -32603,
124						Message: err.Error(),
125					}
126				} else {
127					rawJSON, err := json.Marshal(result)
128					if err != nil {
129						response.Error = &ResponseError{
130							Code:    -32603,
131							Message: fmt.Sprintf("failed to marshal response: %v", err),
132						}
133					} else {
134						response.Result = rawJSON
135					}
136				}
137			} else {
138				response.Error = &ResponseError{
139					Code:    -32601,
140					Message: fmt.Sprintf("method not found: %s", msg.Method),
141				}
142			}
143
144			// Send response back to server
145			if err := WriteMessage(c.stdin, response); err != nil {
146				log.Printf("Error sending response to server: %v", err)
147			}
148
149			continue
150		}
151
152		// Handle notification (has Method but no ID)
153		if msg.Method != "" && msg.ID == 0 {
154			c.notificationMu.RLock()
155			handler, ok := c.notificationHandlers[msg.Method]
156			c.notificationMu.RUnlock()
157
158			if ok {
159				if debug {
160					log.Printf("Handling notification: %s", msg.Method)
161				}
162				go handler(msg.Params)
163			} else if debug {
164				log.Printf("No handler for notification: %s", msg.Method)
165			}
166			continue
167		}
168
169		// Handle response to our request (has ID but no Method)
170		if msg.ID != 0 && msg.Method == "" {
171			c.handlersMu.RLock()
172			ch, ok := c.handlers[msg.ID]
173			c.handlersMu.RUnlock()
174
175			if ok {
176				if debug {
177					log.Printf("Sending response for ID %d to handler", msg.ID)
178				}
179				ch <- msg
180				close(ch)
181			} else if debug {
182				log.Printf("No handler for response ID: %d", msg.ID)
183			}
184		}
185	}
186}
187
188// Call makes a request and waits for the response
189func (c *Client) Call(ctx context.Context, method string, params any, result any) error {
190	id := c.nextID.Add(1)
191
192	if debug {
193		log.Printf("Making call: method=%s id=%d", method, id)
194	}
195
196	msg, err := NewRequest(id, method, params)
197	if err != nil {
198		return fmt.Errorf("failed to create request: %w", err)
199	}
200
201	// Create response channel
202	ch := make(chan *Message, 1)
203	c.handlersMu.Lock()
204	c.handlers[id] = ch
205	c.handlersMu.Unlock()
206
207	defer func() {
208		c.handlersMu.Lock()
209		delete(c.handlers, id)
210		c.handlersMu.Unlock()
211	}()
212
213	// Send request
214	if err := WriteMessage(c.stdin, msg); err != nil {
215		return fmt.Errorf("failed to send request: %w", err)
216	}
217
218	if debug {
219		log.Printf("Waiting for response to request ID: %d", id)
220	}
221
222	// Wait for response
223	resp := <-ch
224
225	if debug {
226		log.Printf("Received response for request ID: %d", id)
227	}
228
229	if resp.Error != nil {
230		return fmt.Errorf("request failed: %s (code: %d)", resp.Error.Message, resp.Error.Code)
231	}
232
233	if result != nil {
234		// If result is a json.RawMessage, just copy the raw bytes
235		if rawMsg, ok := result.(*json.RawMessage); ok {
236			*rawMsg = resp.Result
237			return nil
238		}
239		// Otherwise unmarshal into the provided type
240		if err := json.Unmarshal(resp.Result, result); err != nil {
241			return fmt.Errorf("failed to unmarshal result: %w", err)
242		}
243	}
244
245	return nil
246}
247
248// Notify sends a notification (a request without an ID that doesn't expect a response)
249func (c *Client) Notify(ctx context.Context, method string, params any) error {
250	if debug {
251		log.Printf("Sending notification: method=%s", method)
252	}
253
254	msg, err := NewNotification(method, params)
255	if err != nil {
256		return fmt.Errorf("failed to create notification: %w", err)
257	}
258
259	if err := WriteMessage(c.stdin, msg); err != nil {
260		return fmt.Errorf("failed to send notification: %w", err)
261	}
262
263	return nil
264}
265
266type (
267	NotificationHandler  func(params json.RawMessage)
268	ServerRequestHandler func(params json.RawMessage) (any, error)
269)