diff options
Diffstat (limited to 'internal/lsp/completion_state.go')
| -rw-r--r-- | internal/lsp/completion_state.go | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/internal/lsp/completion_state.go b/internal/lsp/completion_state.go new file mode 100644 index 0000000..9799561 --- /dev/null +++ b/internal/lsp/completion_state.go @@ -0,0 +1,167 @@ +package lsp + +import ( + "context" + "sync" + "time" +) + +type completionState struct { + stateMu sync.RWMutex + compCache map[string]string + compCacheOrder []string + pendingCompletions map[string][]CompletionItem + lastLLMCall time.Time + completionsDisabled bool +} + +func newCompletionState() completionState { + return completionState{ + compCache: make(map[string]string), + pendingCompletions: make(map[string][]CompletionItem), + } +} + +func (s *completionState) storePendingCompletion(key string, items []CompletionItem) { + if len(items) == 0 { + return + } + cpy := make([]CompletionItem, len(items)) + copy(cpy, items) + s.stateMu.Lock() + defer s.stateMu.Unlock() + if s.pendingCompletions == nil { + s.pendingCompletions = make(map[string][]CompletionItem) + } + s.pendingCompletions[key] = cpy +} + +func (s *completionState) setCompletionsDisabled(disabled bool) bool { + s.stateMu.Lock() + defer s.stateMu.Unlock() + prev := s.completionsDisabled + s.completionsDisabled = disabled + return prev +} + +func (s *completionState) completionDisabled() bool { + s.stateMu.RLock() + defer s.stateMu.RUnlock() + return s.completionsDisabled +} + +func (s *completionState) takePendingCompletion(key string) []CompletionItem { + s.stateMu.Lock() + defer s.stateMu.Unlock() + if len(s.pendingCompletions) == 0 { + return nil + } + items, ok := s.pendingCompletions[key] + if !ok { + return nil + } + delete(s.pendingCompletions, key) + cpy := make([]CompletionItem, len(items)) + copy(cpy, items) + return cpy +} + +func (s *completionState) cacheGet(key string) (string, bool) { + s.stateMu.Lock() + defer s.stateMu.Unlock() + v, ok := s.compCache[key] + if !ok { + return "", false + } + s.touchLocked(key) + return v, true +} + +func (s *completionState) cachePut(key, value string) { + s.stateMu.Lock() + defer s.stateMu.Unlock() + if s.compCache == nil { + s.compCache = make(map[string]string) + } + if _, exists := s.compCache[key]; !exists { + s.compCacheOrder = append(s.compCacheOrder, key) + s.compCache[key] = value + if len(s.compCacheOrder) > 10 { + old := s.compCacheOrder[0] + s.compCacheOrder = s.compCacheOrder[1:] + delete(s.compCache, old) + } + return + } + s.compCache[key] = value + s.touchLocked(key) +} + +func (s *completionState) touchLocked(key string) { + idx := -1 + for i, k := range s.compCacheOrder { + if k == key { + idx = i + break + } + } + if idx >= 0 { + s.compCacheOrder = append(append([]string{}, s.compCacheOrder[:idx]...), s.compCacheOrder[idx+1:]...) + } + s.compCacheOrder = append(s.compCacheOrder, key) +} + +func (s *completionState) waitForThrottle(ctx context.Context, interval time.Duration) bool { + if interval <= 0 { + return true + } + var wait time.Duration + for { + s.stateMu.Lock() + next := s.lastLLMCall.Add(interval) + now := time.Now() + if now.Before(next) { + wait = next.Sub(now) + s.stateMu.Unlock() + timer := time.NewTimer(wait) + select { + case <-ctx.Done(): + timer.Stop() + return false + case <-timer.C: + continue + } + } + s.lastLLMCall = now + s.stateMu.Unlock() + return true + } +} + +func (s *Server) storePendingCompletion(key string, items []CompletionItem) { + s.completionState.storePendingCompletion(key, items) +} + +func (s *Server) setCompletionsDisabled(disabled bool) bool { + return s.completionState.setCompletionsDisabled(disabled) +} + +func (s *Server) completionDisabled() bool { + return s.completionState.completionDisabled() +} + +func (s *Server) takePendingCompletion(key string) []CompletionItem { + return s.completionState.takePendingCompletion(key) +} + +func (s *Server) completionCacheGet(key string) (string, bool) { + return s.completionState.cacheGet(key) +} + +func (s *Server) completionCachePut(key, value string) { + s.completionState.cachePut(key, value) +} + +func (s *Server) waitForThrottle(ctx context.Context) bool { + return s.completionState.waitForThrottle(ctx, s.completionThrottle()) +} |
