From a6a8b84690c50767f714b413496b5aeb45b31c21 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 14 Aug 2025 00:21:06 +0300 Subject: =?UTF-8?q?Revert=20"feat(lsp):=20gate=20completion=20by=20context?= =?UTF-8?q?=20=E2=80=94=20require=20preceding=20space=20and=20>=3D2s=20idl?= =?UTF-8?q?e=20since=20last=20change;=20add=20logging=20for=20gating=20dec?= =?UTF-8?q?isions"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit eb0bb96fd23cae6e92c5f8d77ef29db8b6d50dc1. --- internal/lsp/server.go | 141 ++++++++++++++----------------------------------- 1 file changed, 41 insertions(+), 100 deletions(-) (limited to 'internal/lsp/server.go') diff --git a/internal/lsp/server.go b/internal/lsp/server.go index 32ae7f2..3949680 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -12,7 +12,6 @@ import ( "strconv" "strings" "sync" - "time" ) // JSON-RPC 2.0 structures (minimal) @@ -79,11 +78,10 @@ type Server struct { mu sync.RWMutex docs map[string]*document logContext bool - lastChange map[string]time.Time } func NewServer(r io.Reader, w io.Writer, logger *log.Logger, logContext bool) *Server { - return &Server{in: bufio.NewReader(r), out: w, logger: logger, docs: make(map[string]*document), logContext: logContext, lastChange: make(map[string]time.Time)} + return &Server{in: bufio.NewReader(r), out: w, logger: logger, docs: make(map[string]*document), logContext: logContext} } func (s *Server) Run() error { @@ -135,30 +133,26 @@ func (s *Server) handle(req Request) { s.exited = true // No response per spec. os.Exit(0) - case "textDocument/didOpen": - var p DidOpenTextDocumentParams - if err := json.Unmarshal(req.Params, &p); err == nil { - s.setDocument(p.TextDocument.URI, p.TextDocument.Text) - s.setLastChange(p.TextDocument.URI, time.Now()) - } - case "textDocument/didChange": - var p DidChangeTextDocumentParams - if err := json.Unmarshal(req.Params, &p); err == nil { - if len(p.ContentChanges) > 0 { - s.setDocument(p.TextDocument.URI, p.ContentChanges[len(p.ContentChanges)-1].Text) - s.setLastChange(p.TextDocument.URI, time.Now()) - } - } - case "textDocument/didClose": - var p DidCloseTextDocumentParams - if err := json.Unmarshal(req.Params, &p); err == nil { - s.deleteDocument(p.TextDocument.URI) - s.clearLastChange(p.TextDocument.URI) - } + case "textDocument/didOpen": + var p DidOpenTextDocumentParams + if err := json.Unmarshal(req.Params, &p); err == nil { + s.setDocument(p.TextDocument.URI, p.TextDocument.Text) + } + case "textDocument/didChange": + var p DidChangeTextDocumentParams + if err := json.Unmarshal(req.Params, &p); err == nil { + if len(p.ContentChanges) > 0 { + s.setDocument(p.TextDocument.URI, p.ContentChanges[len(p.ContentChanges)-1].Text) + } + } + case "textDocument/didClose": + var p DidCloseTextDocumentParams + if err := json.Unmarshal(req.Params, &p); err == nil { + s.deleteDocument(p.TextDocument.URI) + } case "textDocument/completion": var p CompletionParams var docStr string - allowed := true if err := json.Unmarshal(req.Params, &p); err == nil { above, current, below, funcCtx := s.lineContext(p.TextDocument.URI, p.Position) docStr = fmt.Sprintf("file: %s\nline: %d\nabove: %s\ncurrent: %s\nbelow: %s\nfunction: %s", p.TextDocument.URI, p.Position.Line, trimLen(above), trimLen(current), trimLen(below), trimLen(funcCtx)) @@ -166,28 +160,15 @@ func (s *Server) handle(req Request) { s.logger.Printf("completion ctx uri=%s line=%d char=%d above=%q current=%q below=%q function=%q", p.TextDocument.URI, p.Position.Line, p.Position.Character, trimLen(above), trimLen(current), trimLen(below), trimLen(funcCtx)) } - // Apply gating: require space before cursor AND >=2s idle since last change - if !s.prevCharIsSpace(p.TextDocument.URI, p.Position) { - allowed = false - } - if since := s.sinceLastChange(p.TextDocument.URI); since >= 0 && since < 2*time.Second { - allowed = false - } - } - var items []CompletionItem - if allowed { - items = []CompletionItem{{ - Label: "hexai-complete", - Kind: 14, - Detail: "dummy completion", - InsertText: "hexai", - SortText: "0000", - Documentation: docStr, - }} - } else if s.logContext { - s.logger.Printf("completion gated: uri=%s allowed=%v idle=%v spaceBefore=%v", - p.TextDocument.URI, allowed, s.sinceLastChange(p.TextDocument.URI), s.prevCharIsSpace(p.TextDocument.URI, p.Position)) } + items := []CompletionItem{{ + Label: "hexai-complete", + Kind: 14, + Detail: "dummy completion", + InsertText: "hexai", + SortText: "0000", + Documentation: docStr, + }} s.reply(req.ID, CompletionList{IsIncomplete: false, Items: items}, nil) default: // Unknown method; reply with Method Not Found for requests that have an ID. @@ -264,9 +245,9 @@ type document struct { } func (s *Server) setDocument(uri, text string) { - s.mu.Lock() - defer s.mu.Unlock() - s.docs[uri] = &document{uri: uri, text: text, lines: splitLines(text)} + s.mu.Lock() + defer s.mu.Unlock() + s.docs[uri] = &document{uri: uri, text: text, lines: splitLines(text)} } func (s *Server) deleteDocument(uri string) { @@ -282,8 +263,8 @@ func (s *Server) getDocument(uri string) *document { } func splitLines(sx string) []string { - sx = strings.ReplaceAll(sx, "\r\n", "\n") - return strings.Split(sx, "\n") + sx = strings.ReplaceAll(sx, "\r\n", "\n") + return strings.Split(sx, "\n") } // LSP param types (subset) @@ -334,10 +315,10 @@ type CompletionParams struct { } func (s *Server) lineContext(uri string, pos Position) (above, current, below, funcCtx string) { - d := s.getDocument(uri) - if d == nil || len(d.lines) == 0 { - return "", "", "", "" - } + d := s.getDocument(uri) + if d == nil || len(d.lines) == 0 { + return "", "", "", "" + } idx := pos.Line if idx < 0 { idx = 0 @@ -359,7 +340,7 @@ func (s *Server) lineContext(uri string, pos Position) (above, current, below, f break } } - return + return } func hasAny(s string, needles []string) bool { @@ -372,49 +353,9 @@ func hasAny(s string, needles []string) bool { } func trimLen(s string) string { - s = strings.TrimSpace(s) - if len(s) > 200 { - return s[:200] + "…" - } - return s -} - -// --- Gating helpers --- -func (s *Server) setLastChange(uri string, t time.Time) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastChange[uri] = t -} - -func (s *Server) clearLastChange(uri string) { - s.mu.Lock() - defer s.mu.Unlock() - delete(s.lastChange, uri) -} - -func (s *Server) sinceLastChange(uri string) time.Duration { - s.mu.RLock() - t, ok := s.lastChange[uri] - s.mu.RUnlock() - if !ok { - return -1 - } - return time.Since(t) -} - -func (s *Server) prevCharIsSpace(uri string, pos Position) bool { - d := s.getDocument(uri) - if d == nil { - return false - } - if pos.Line < 0 || pos.Line >= len(d.lines) { - return false - } - line := d.lines[pos.Line] - // Convert to runes to be safe with multibyte - r := []rune(line) - if pos.Character <= 0 || pos.Character > len(r) { - return false - } - return r[pos.Character-1] == ' ' + s = strings.TrimSpace(s) + if len(s) > 200 { + return s[:200] + "…" + } + return s } -- cgit v1.2.3