summaryrefslogtreecommitdiff
path: root/internal/lsp/completion_state.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/lsp/completion_state.go')
-rw-r--r--internal/lsp/completion_state.go167
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())
+}