feat: enhance completion service with session management and language support

- Introduced session management using Redis for tracking active sessions.
- Added session claiming and releasing functionality in the completion manager.
- Enhanced HTTP and WebSocket completion endpoints to support multiple languages.
- Implemented request timeout and maximum body size configurations for API routes.
- Updated client-side code to handle session IDs and language parameters in completion requests.
- Improved error handling for unsupported languages and session conflicts.
- Added tests for the completion manager to ensure proper session handling and cleanup.
This commit is contained in:
2026-02-15 16:22:01 +08:00
parent 23decb8687
commit 57afb90bc0
14 changed files with 1334 additions and 138 deletions

View File

@@ -33,6 +33,8 @@ type Client struct {
nextID atomic.Int64
workspaceDir string
languageID string
clientName string
pendingMu sync.Mutex
pending map[string]chan rpcResponse
@@ -74,19 +76,33 @@ type lspCompletionList struct {
Items []lspCompletionItem `json:"items"`
}
func NewClient(parent context.Context, goplsPath, rootPath string) (*Client, error) {
if goplsPath == "" {
goplsPath = "gopls"
type Config struct {
Command string
Args []string
RootPath string
LanguageID string
ClientName string
}
func NewClient(parent context.Context, cfg Config) (*Client, error) {
if cfg.Command == "" {
cfg.Command = "gopls"
}
if rootPath == "" {
if cfg.RootPath == "" {
cwd, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("get working directory: %w", err)
}
rootPath = cwd
cfg.RootPath = cwd
}
if cfg.LanguageID == "" {
cfg.LanguageID = "go"
}
if cfg.ClientName == "" {
cfg.ClientName = "monica-lsp-gateway"
}
cmd := exec.Command(goplsPath)
cmd := exec.Command(cfg.Command, cfg.Args...)
stdin, err := cmd.StdinPipe()
if err != nil {
return nil, fmt.Errorf("create stdin pipe: %w", err)
@@ -98,13 +114,15 @@ func NewClient(parent context.Context, goplsPath, rootPath string) (*Client, err
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("start gopls: %w", err)
return nil, fmt.Errorf("start language server %q: %w", cfg.Command, err)
}
client := &Client{
cmd: cmd,
stdin: stdin,
workspaceDir: rootPath,
workspaceDir: cfg.RootPath,
languageID: cfg.LanguageID,
clientName: cfg.ClientName,
pending: make(map[string]chan rpcResponse),
exitCh: make(chan error, 1),
}
@@ -117,7 +135,7 @@ func NewClient(parent context.Context, goplsPath, rootPath string) (*Client, err
initCtx, cancel := context.WithTimeout(parent, 10*time.Second)
defer cancel()
if err := client.initialize(initCtx, rootPath); err != nil {
if err := client.initialize(initCtx, cfg.RootPath); err != nil {
_ = client.Close()
return nil, err
}
@@ -150,7 +168,7 @@ func (c *Client) initialize(ctx context.Context, rootPath string) error {
},
},
"clientInfo": map[string]string{
"name": "monica-go-completion-backend",
"name": c.clientName,
"version": "0.1.0",
},
}
@@ -174,7 +192,7 @@ func (c *Client) DidOpen(ctx context.Context, uri, text string, version int) err
params := map[string]any{
"textDocument": map[string]any{
"uri": normalizedURI,
"languageId": "go",
"languageId": c.languageID,
"version": version,
"text": text,
},