MCPcopy Create free account
hub / github.com/diillson/chatcli / Acquire

Method Acquire

cli/agent/lsp/pool.go:103–159  ·  view source on GitHub ↗

Acquire returns a ready Session for absPath: the pooled client for the file's project root and language server (spawned and initialized on first use), with the document opened or re-synchronized when its content changed since the last call. Callers must pass an absolute, cleaned path.

(ctx context.Context, absPath string)

Source from the content-addressed store, hash-verified

101// use), with the document opened or re-synchronized when its content changed
102// since the last call. Callers must pass an absolute, cleaned path.
103func (p *Pool) Acquire(ctx context.Context, absPath string) (*Session, error) {
104 spec, ok := ServerForFile(absPath)
105 if !ok {
106 return nil, fmt.Errorf("no language server configured for %q files (supported: %s)",
107 filepath.Ext(absPath), strings.Join(SupportedExtensions(), " "))
108 }
109
110 data, err := os.ReadFile(absPath) //#nosec G304 -- agent-requested source file, same trust boundary as @read
111 if err != nil {
112 return nil, err
113 }
114 if len(data) > maxDocumentBytes {
115 return nil, fmt.Errorf("file too large for language-server analysis (%d bytes, limit %d)", len(data), maxDocumentBytes)
116 }
117
118 root := findProjectRoot(absPath)
119 key := root + "\x00" + strings.Join(spec.Command, " ")
120
121 p.mu.Lock()
122 defer p.mu.Unlock()
123 p.reapIdleLocked()
124
125 entry, ok := p.entries[key]
126 if !ok {
127 // The server must OUTLIVE this call: exec.CommandContext ties the
128 // subprocess lifetime to its context, so spawning with the request
129 // ctx (or any ctx we cancel) kills the server the moment we return —
130 // the E2E symptom is an instant "broken pipe" on initialize. The pool
131 // owns the lifetime (idle reap + Close); only inherit values.
132 client, spawnErr := p.spawn(context.WithoutCancel(ctx), spec, p.logger)
133 if spawnErr != nil {
134 return nil, fmt.Errorf("start %s: %w (install it or override via %s)", spec.Command[0], spawnErr, spec.EnvKey)
135 }
136 rootURI := "file://" + root
137 if initErr := client.Initialize(rootURI); initErr != nil {
138 client.Shutdown()
139 return nil, fmt.Errorf("initialize %s: %w", spec.Command[0], initErr)
140 }
141 entry = &poolEntry{
142 client: client,
143 docs: map[string]docState{},
144 rootURI: rootURI,
145 spec: spec,
146 poolKey: key,
147 }
148 p.entries[key] = entry
149 p.logger.Info("lsp: language server started",
150 zap.String("server", spec.Command[0]), zap.String("root", root))
151 }
152 entry.lastUse = p.now()
153
154 uri := "file://" + absPath
155 if syncErr := entry.syncDocument(uri, spec.LanguageID, string(data)); syncErr != nil {
156 return nil, syncErr
157 }
158 return &Session{Client: entry.client, URI: uri, Root: root}, nil
159}
160

Callers 6

TestPoolReapsIdleServersFunction · 0.95
acquireMethod · 0.45

Calls 13

reapIdleLockedMethod · 0.95
ServerForFileFunction · 0.85
SupportedExtensionsFunction · 0.85
findProjectRootFunction · 0.85
ErrorfMethod · 0.80
LockMethod · 0.80
UnlockMethod · 0.80
ShutdownMethod · 0.80
nowMethod · 0.80
syncDocumentMethod · 0.80
InfoMethod · 0.65
InitializeMethod · 0.45