- Add Nacos registry for service registration and deregistration. - Implement Redis registry for session management with heartbeat and session claiming. - Improve completion service with session handling and request validation. - Enhance WebSocket handling for completion requests with JSON-RPC support. - Add tests for new registry implementations and completion manager functionalities. - Refactor existing code for better readability and maintainability.
105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
package completion
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"sync"
|
||
)
|
||
|
||
var ErrInvalidRequest = errors.New("invalid completion request")
|
||
|
||
// Request 是统一的补全请求模型。
|
||
type Request struct {
|
||
Language string `json:"language,omitempty"`
|
||
SessionID string `json:"sessionId,omitempty"`
|
||
URI string `json:"uri"`
|
||
Text string `json:"text"`
|
||
Line int `json:"line"`
|
||
Character int `json:"character"`
|
||
}
|
||
|
||
// Item 对应一个补全候选项。
|
||
type Item struct {
|
||
Label string `json:"label"`
|
||
Kind int `json:"kind,omitempty"`
|
||
Detail string `json:"detail,omitempty"`
|
||
Documentation string `json:"documentation,omitempty"`
|
||
InsertText string `json:"insertText,omitempty"`
|
||
SortText string `json:"sortText,omitempty"`
|
||
FilterText string `json:"filterText,omitempty"`
|
||
}
|
||
|
||
// Response 是补全结果集合。
|
||
type Response struct {
|
||
Items []Item `json:"items"`
|
||
IsIncomplete bool `json:"isIncomplete"`
|
||
}
|
||
|
||
// Client 抽象 LSP 客户端所需的最小能力。
|
||
type Client interface {
|
||
DidOpen(ctx context.Context, uri, text string, version int) error
|
||
DidChange(ctx context.Context, uri, text string, version int) error
|
||
Completion(ctx context.Context, uri string, line, character int) (Response, error)
|
||
}
|
||
|
||
type documentState struct {
|
||
version int
|
||
}
|
||
|
||
// Service 负责文档生命周期同步(didOpen/didChange)与补全调用。
|
||
type Service struct {
|
||
client Client
|
||
|
||
mu sync.Mutex
|
||
documents map[string]*documentState
|
||
}
|
||
|
||
func NewService(client Client) *Service {
|
||
return &Service{
|
||
client: client,
|
||
documents: make(map[string]*documentState),
|
||
}
|
||
}
|
||
|
||
// Complete 根据 URI 的历史状态决定发送 didOpen 或 didChange,再请求补全。
|
||
func (s *Service) Complete(ctx context.Context, req Request) (Response, error) {
|
||
if err := validateRequest(req); err != nil {
|
||
return Response{}, err
|
||
}
|
||
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
|
||
state, opened := s.documents[req.URI]
|
||
if !opened {
|
||
if err := s.client.DidOpen(ctx, req.URI, req.Text, 1); err != nil {
|
||
return Response{}, fmt.Errorf("didOpen failed: %w", err)
|
||
}
|
||
s.documents[req.URI] = &documentState{version: 1}
|
||
} else {
|
||
nextVersion := state.version + 1
|
||
if err := s.client.DidChange(ctx, req.URI, req.Text, nextVersion); err != nil {
|
||
return Response{}, fmt.Errorf("didChange failed: %w", err)
|
||
}
|
||
state.version = nextVersion
|
||
}
|
||
|
||
resp, err := s.client.Completion(ctx, req.URI, req.Line, req.Character)
|
||
if err != nil {
|
||
return Response{}, fmt.Errorf("completion failed: %w", err)
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// validateRequest 做基础参数校验,避免向 LSP 发送非法请求。
|
||
func validateRequest(req Request) error {
|
||
if req.URI == "" {
|
||
return ErrInvalidRequest
|
||
}
|
||
if req.Line < 0 || req.Character < 0 {
|
||
return ErrInvalidRequest
|
||
}
|
||
return nil
|
||
}
|