diff --git a/hcl/pos.go b/hcl/pos.go index 2ac16c7..39c183f 100644 --- a/hcl/pos.go +++ b/hcl/pos.go @@ -60,6 +60,40 @@ func RangeBetween(start, end Range) Range { } } +// RangeOver returns a new range that covers both of the given ranges and +// possibly additional content between them if the two ranges do not overlap. +// +// If either range is empty then it is ignored. The result is empty if both +// given ranges are empty. +// +// The result is meaningless if the two ranges to not belong to the same +// source file. +func RangeOver(a, b Range) Range { + if a.Empty() { + return b + } + if b.Empty() { + return a + } + + var start, end Pos + if a.Start.Byte < b.Start.Byte { + start = a.Start + } else { + start = b.Start + } + if a.End.Byte > b.End.Byte { + end = a.End + } else { + end = b.End + } + return Range{ + Filename: a.Filename, + Start: start, + End: end, + } +} + // ContainsOffset returns true if and only if the given byte offset is within // the receiving Range. func (r Range) ContainsOffset(offset int) bool { diff --git a/hcl/pos_test.go b/hcl/pos_test.go index 05703c9..32ad116 100644 --- a/hcl/pos_test.go +++ b/hcl/pos_test.go @@ -7,6 +7,156 @@ import ( "testing" ) +func TestRangeOver(t *testing.T) { + tests := []struct { + A Range + B Range + Want Range + }{ + { + Range{ // ## + Start: Pos{Byte: 2, Line: 1, Column: 3}, + End: Pos{Byte: 4, Line: 1, Column: 5}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + }, + { + Range{ // #### + Start: Pos{Byte: 0, Line: 1, Column: 1}, + End: Pos{Byte: 4, Line: 1, Column: 5}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // ##### + Start: Pos{Byte: 0, Line: 1, Column: 1}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + }, + { + Range{ // #### + Start: Pos{Byte: 2, Line: 1, Column: 3}, + End: Pos{Byte: 6, Line: 1, Column: 7}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // ##### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 6, Line: 1, Column: 7}, + }, + }, + { + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // ## + Start: Pos{Byte: 2, Line: 1, Column: 3}, + End: Pos{Byte: 4, Line: 1, Column: 5}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + }, + { + Range{ // ### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 4, Line: 1, Column: 5}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + }, + { + Range{ // ### + Start: Pos{Byte: 2, Line: 1, Column: 3}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // #### + Start: Pos{Byte: 1, Line: 1, Column: 2}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + }, + { + Range{ // #### + Start: Pos{Byte: 2, Line: 1, Column: 3}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // #### + Start: Pos{Byte: 2, Line: 1, Column: 3}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + Range{ // #### + Start: Pos{Byte: 2, Line: 1, Column: 3}, + End: Pos{Byte: 5, Line: 1, Column: 6}, + }, + }, + { + Range{ // ## + Start: Pos{Byte: 0, Line: 1, Column: 1}, + End: Pos{Byte: 2, Line: 1, Column: 3}, + }, + Range{ // ## + Start: Pos{Byte: 4, Line: 1, Column: 5}, + End: Pos{Byte: 6, Line: 1, Column: 7}, + }, + Range{ // ###### + Start: Pos{Byte: 0, Line: 1, Column: 1}, + End: Pos{Byte: 6, Line: 1, Column: 7}, + }, + }, + { + Range{ // ## + Start: Pos{Byte: 4, Line: 1, Column: 5}, + End: Pos{Byte: 6, Line: 1, Column: 7}, + }, + Range{ // ## + Start: Pos{Byte: 0, Line: 1, Column: 1}, + End: Pos{Byte: 2, Line: 1, Column: 3}, + }, + Range{ // ###### + Start: Pos{Byte: 0, Line: 1, Column: 1}, + End: Pos{Byte: 6, Line: 1, Column: 7}, + }, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%s<=>%s", test.A, test.B), func(t *testing.T) { + got := RangeOver(test.A, test.B) + if !reflect.DeepEqual(got, test.Want) { + t.Errorf( + "wrong result\nA : %-10s %s\nB : %-10s %s\ngot : %-10s %s\nwant: %-10s %s", + visRangeOffsets(test.A), test.A, + visRangeOffsets(test.B), test.B, + visRangeOffsets(got), got, + visRangeOffsets(test.Want), test.Want, + ) + } + }) + } +} + func TestPosOverlap(t *testing.T) { tests := []struct { A Range