@@ -247,14 +247,18 @@ func ApplyWorkspaceEdit(edit protocol.WorkspaceEdit) error {
return nil
}
+// rangesOverlap checks if two LSP ranges overlap.
+// Per the LSP specification, ranges are half-open intervals [start, end),
+// so adjacent ranges where one's end equals another's start do NOT overlap.
+// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#range
func rangesOverlap(r1, r2 protocol.Range) bool {
if r1.Start.Line > r2.End.Line || r2.Start.Line > r1.End.Line {
return false
}
- if r1.Start.Line == r2.End.Line && r1.Start.Character > r2.End.Character {
+ if r1.Start.Line == r2.End.Line && r1.Start.Character >= r2.End.Character {
return false
}
- if r2.Start.Line == r1.End.Line && r2.Start.Character > r1.End.Character {
+ if r2.Start.Line == r1.End.Line && r2.Start.Character >= r1.End.Character {
return false
}
return true
@@ -0,0 +1,68 @@
+package util
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
+)
+
+func TestRangesOverlap(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ r1 protocol.Range
+ r2 protocol.Range
+ want bool
+ }{
+ {
+ name: "adjacent ranges do not overlap",
+ r1: protocol.Range{
+ Start: protocol.Position{Line: 0, Character: 0},
+ End: protocol.Position{Line: 0, Character: 5},
+ },
+ r2: protocol.Range{
+ Start: protocol.Position{Line: 0, Character: 5},
+ End: protocol.Position{Line: 0, Character: 10},
+ },
+ want: false,
+ },
+ {
+ name: "overlapping ranges",
+ r1: protocol.Range{
+ Start: protocol.Position{Line: 0, Character: 0},
+ End: protocol.Position{Line: 0, Character: 8},
+ },
+ r2: protocol.Range{
+ Start: protocol.Position{Line: 0, Character: 5},
+ End: protocol.Position{Line: 0, Character: 10},
+ },
+ want: true,
+ },
+ {
+ name: "non-overlapping with gap",
+ r1: protocol.Range{
+ Start: protocol.Position{Line: 0, Character: 0},
+ End: protocol.Position{Line: 0, Character: 3},
+ },
+ r2: protocol.Range{
+ Start: protocol.Position{Line: 0, Character: 7},
+ End: protocol.Position{Line: 0, Character: 10},
+ },
+ want: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ got := rangesOverlap(tt.r1, tt.r2)
+ require.Equal(t, tt.want, got, "rangesOverlap(r1, r2)")
+ // Overlap should be symmetric
+ got2 := rangesOverlap(tt.r2, tt.r1)
+ require.Equal(t, tt.want, got2, "rangesOverlap(r2, r1) symmetry")
+ })
+ }
+}