summaryrefslogtreecommitdiff
path: root/internal/lsp/context.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-08-16 15:35:02 +0300
committerPaul Buetow <paul@buetow.org>2025-08-16 15:35:02 +0300
commit6c8eb6876fe87553770de114ebd34649a0c6ec10 (patch)
tree064517edaf9d59522bec7191a61362a853c195bd /internal/lsp/context.go
parent1e1df8c204f6771719f85d8402128d72138bb863 (diff)
lsp: split monolithic server.go into modules; add configurable max tokens and context strategies (minimal|window|file-on-new-func|always-full); provide flags/env fallbacks; add unit tests for helpers and context; update README; remove obsolete files
Diffstat (limited to 'internal/lsp/context.go')
-rw-r--r--internal/lsp/context.go80
1 files changed, 80 insertions, 0 deletions
diff --git a/internal/lsp/context.go b/internal/lsp/context.go
new file mode 100644
index 0000000..c08a865
--- /dev/null
+++ b/internal/lsp/context.go
@@ -0,0 +1,80 @@
+package lsp
+
+import (
+ "strings"
+)
+
+// buildAdditionalContext builds extra context messages based on the configured mode.
+// Modes:
+// - minimal: no extra context
+// - window: include a window of lines around the cursor
+// - file-on-new-func: include full file only when defining a new function
+// - always-full: always include the full file
+func (s *Server) buildAdditionalContext(newFunc bool, uri string, pos Position) (string, bool) {
+ mode := s.contextMode
+ switch mode {
+ case "minimal":
+ return "", false
+ case "window":
+ return s.windowContext(uri, pos), true
+ case "file-on-new-func":
+ if newFunc {
+ return s.fullFileContext(uri), true
+ }
+ return "", false
+ case "always-full":
+ return s.fullFileContext(uri), true
+ default:
+ // fallback to minimal if unknown
+ return "", false
+ }
+}
+
+func (s *Server) windowContext(uri string, pos Position) string {
+ d := s.getDocument(uri)
+ if d == nil || len(d.lines) == 0 {
+ return ""
+ }
+ n := len(d.lines)
+ half := s.windowLines / 2
+ start := pos.Line - half
+ if start < 0 {
+ start = 0
+ }
+ end := pos.Line + half + 1
+ if end > n {
+ end = n
+ }
+ text := strings.Join(d.lines[start:end], "\n")
+ return truncateToApproxTokens(text, s.maxContextTokens)
+}
+
+func (s *Server) fullFileContext(uri string) string {
+ d := s.getDocument(uri)
+ if d == nil {
+ return ""
+ }
+ return truncateToApproxTokens(d.text, s.maxContextTokens)
+}
+
+// truncateToApproxTokens naively truncates the input to fit approx N tokens.
+// Uses 4 chars/token heuristic for speed and determinism.
+func truncateToApproxTokens(text string, maxTokens int) string {
+ if maxTokens <= 0 {
+ return ""
+ }
+ maxChars := maxTokens * 4
+ if len(text) <= maxChars {
+ return text
+ }
+ // try to cut on a line boundary near maxChars
+ cut := maxChars
+ if cut > len(text) {
+ cut = len(text)
+ }
+ if i := strings.LastIndex(text[:cut], "\n"); i > 0 {
+ cut = i
+ }
+ return text[:cut]
+}
+