blob: 8353f9276814f7cf69b45543684d132fe5d9e5e5 [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package lsppos_test
import (
"fmt"
"strings"
"testing"
. "golang.org/x/tools/internal/lsp/lsppos"
"golang.org/x/tools/internal/lsp/protocol"
)
type testCase struct {
content string // input text
substrOrOffset interface{} // explicit integer offset, or a substring
wantLine, wantChar int // expected LSP position information
}
// offset returns the test case byte offset
func (c testCase) offset() int {
switch x := c.substrOrOffset.(type) {
case int:
return x
case string:
i := strings.Index(c.content, x)
if i < 0 {
panic(fmt.Sprintf("%q does not contain substring %q", c.content, x))
}
return i
}
panic("substrOrIndex must be an integer or string")
}
var tests = []testCase{
{"a𐐀b", "a", 0, 0},
{"a𐐀b", "𐐀", 0, 1},
{"a𐐀b", "b", 0, 3},
{"a𐐀b\n", "\n", 0, 4},
{"a𐐀b\r\n", "\n", 0, 4}, // \r|\n is not a valid position, so we move back to the end of the first line.
{"a𐐀b\r\nx", "x", 1, 0},
{"a𐐀b\r\nx\ny", "y", 2, 0},
// Testing EOL and EOF positions
{"", 0, 0, 0}, // 0th position of an empty buffer is (0, 0)
{"abc", "c", 0, 2},
{"abc", 3, 0, 3},
{"abc\n", "\n", 0, 3},
{"abc\n", 4, 1, 0}, // position after a newline is on the next line
}
func TestLineChar(t *testing.T) {
for _, test := range tests {
m := NewMapper([]byte(test.content))
offset := test.offset()
gotLine, gotChar := m.LineColUTF16(offset)
if gotLine != test.wantLine || gotChar != test.wantChar {
t.Errorf("LineChar(%d) = (%d,%d), want (%d,%d)", offset, gotLine, gotChar, test.wantLine, test.wantChar)
}
}
}
func TestInvalidOffset(t *testing.T) {
content := []byte("a𐐀b\r\nx\ny")
m := NewMapper(content)
for _, offset := range []int{-1, 100} {
gotLine, gotChar := m.LineColUTF16(offset)
if gotLine != -1 {
t.Errorf("LineChar(%d) = (%d,%d), want (-1,-1)", offset, gotLine, gotChar)
}
}
}
func TestPosition(t *testing.T) {
for _, test := range tests {
m := NewMapper([]byte(test.content))
offset := test.offset()
got, ok := m.Position(offset)
if !ok {
t.Error("invalid position for", test.substrOrOffset)
continue
}
want := protocol.Position{Line: uint32(test.wantLine), Character: uint32(test.wantChar)}
if got != want {
t.Errorf("Position(%d) = %v, want %v", offset, got, want)
}
}
}
func TestRange(t *testing.T) {
for _, test := range tests {
m := NewMapper([]byte(test.content))
offset := test.offset()
got, err := m.Range(0, offset)
if err != nil {
t.Fatal(err)
}
want := protocol.Range{
End: protocol.Position{Line: uint32(test.wantLine), Character: uint32(test.wantChar)},
}
if got != want {
t.Errorf("Range(%d) = %v, want %v", offset, got, want)
}
}
}