summaryrefslogtreecommitdiff
path: root/internal/lsp/handlers_utils.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-16 04:03:44 +0200
committerPaul Buetow <paul@buetow.org>2026-03-16 04:03:44 +0200
commit8f31040cc388943601cfd8a026ea85f0790e66c2 (patch)
tree682ab9d8e0cf698f754866ec63e4eb955eb684bd /internal/lsp/handlers_utils.go
parent030ce454f330871a6be729d7ca8c6ac33e1bbbb5 (diff)
Add bounds checks to extractRangeText and split into helper functions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/lsp/handlers_utils.go')
-rw-r--r--internal/lsp/handlers_utils.go57
1 files changed, 43 insertions, 14 deletions
diff --git a/internal/lsp/handlers_utils.go b/internal/lsp/handlers_utils.go
index 4a5bf4a..620b3a9 100644
--- a/internal/lsp/handlers_utils.go
+++ b/internal/lsp/handlers_utils.go
@@ -550,23 +550,52 @@ func labelForCompletion(cleaned, filter string) string {
}
// extractRangeText returns the exact text within the given document range.
+// It performs bounds checks on line indices and character offsets, returning
+// an empty string when the range is invalid (e.g. negative lines, out-of-bounds
+// lines, or an empty document).
func extractRangeText(d *document, r Range) string {
+ if d == nil || len(d.lines) == 0 {
+ return ""
+ }
+ // Clamp line indices to valid bounds.
+ if r.Start.Line < 0 || r.End.Line < 0 || r.Start.Line >= len(d.lines) {
+ return ""
+ }
+ if r.End.Line >= len(d.lines) {
+ r.End.Line = len(d.lines) - 1
+ r.End.Character = len(d.lines[r.End.Line])
+ }
+ if r.Start.Line > r.End.Line {
+ return ""
+ }
+
if r.Start.Line == r.End.Line {
- line := d.lines[r.Start.Line]
- if r.Start.Character < 0 {
- r.Start.Character = 0
- }
- if r.End.Character > len(line) {
- r.End.Character = len(line)
- }
- if r.Start.Character > r.End.Character {
- return ""
- }
- return line[r.Start.Character:r.End.Character]
+ return extractSingleLineRange(d.lines[r.Start.Line], r)
+ }
+ return extractMultiLineRange(d.lines, r)
+}
+
+// extractSingleLineRange handles the case where start and end are on the same line.
+// Character offsets are clamped to the line length.
+func extractSingleLineRange(line string, r Range) string {
+ if r.Start.Character < 0 {
+ r.Start.Character = 0
}
+ if r.End.Character > len(line) {
+ r.End.Character = len(line)
+ }
+ if r.Start.Character > r.End.Character {
+ return ""
+ }
+ return line[r.Start.Character:r.End.Character]
+}
+
+// extractMultiLineRange handles ranges spanning multiple lines, clamping
+// character offsets on the first and last lines.
+func extractMultiLineRange(lines []string, r Range) string {
var b strings.Builder
// first line
- first := d.lines[r.Start.Line]
+ first := lines[r.Start.Line]
if r.Start.Character < 0 {
r.Start.Character = 0
}
@@ -577,13 +606,13 @@ func extractRangeText(d *document, r Range) string {
b.WriteString("\n")
// middle lines
for i := r.Start.Line + 1; i < r.End.Line; i++ {
- b.WriteString(d.lines[i])
+ b.WriteString(lines[i])
if i+1 <= r.End.Line {
b.WriteString("\n")
}
}
// last line
- last := d.lines[r.End.Line]
+ last := lines[r.End.Line]
if r.End.Character < 0 {
r.End.Character = 0
}