diff options
| author | Paul Buetow <paul@buetow.org> | 2025-08-16 15:35:02 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-08-16 15:35:02 +0300 |
| commit | 6c8eb6876fe87553770de114ebd34649a0c6ec10 (patch) | |
| tree | 064517edaf9d59522bec7191a61362a853c195bd /internal/lsp/context.go | |
| parent | 1e1df8c204f6771719f85d8402128d72138bb863 (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.go | 80 |
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] +} + |
