feat(common): Add Position to common

This commit is contained in:
Myzel394 2024-09-21 15:30:58 +02:00
parent dbd82e4939
commit 5b660d9b60
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
3 changed files with 252 additions and 6 deletions

View File

@ -12,11 +12,60 @@ type Location struct {
Character uint32
}
func (l Location) GetRelativeIndexPosition(i IndexPosition) IndexPosition {
return i - IndexPosition(l.Character)
}
// LocationRange: Represents a range of characters in a document
// Locations are zero-based, start-inclusive and end-exclusive
// This approach is preferred over using an index-based range, because
// it allows to check very easily for cursor positions, as well as for
// index-based ranges.
type LocationRange struct {
Start Location
End Location
}
func (l LocationRange) ContainsPosition(p Position) bool {
return l.IsPositionAfterStart(p) && l.IsPositionBeforeEnd(p)
}
// Check if the given position is after the start of the range
// It's like: Position >= Start
// This checks inclusively
func (l LocationRange) IsPositionAfterStart(p Position) bool {
return p.getValue() >= l.Start.Character
}
func (l LocationRange) IsPositionBeforeStart(p Position) bool {
return p.getValue() < l.Start.Character
}
// Check if the given position is before the end of the range
// It's like: Position <= End
// This checks inclusively
func (l LocationRange) IsPositionBeforeEnd(p Position) bool {
switch p.(type) {
case CursorPosition:
return p.getValue() <= l.End.Character
case IndexPosition:
return p.getValue() < l.End.Character
}
return false
}
func (l LocationRange) IsPositionAfterEnd(p Position) bool {
switch p.(type) {
case CursorPosition:
return p.getValue() > l.End.Character
case IndexPosition:
return p.getValue() >= l.End.Character
}
return false
}
func (l LocationRange) ShiftHorizontal(offset uint32) LocationRange {
return LocationRange{
Start: Location{
@ -57,7 +106,7 @@ func (l LocationRange) ToLSPRange() protocol.Range {
},
End: protocol.Position{
Line: l.End.Line,
Character: l.End.Character + 1,
Character: l.End.Character,
},
}
}
@ -67,10 +116,6 @@ func (l *LocationRange) ChangeBothLines(newLine uint32) {
l.End.Line = newLine
}
func (l LocationRange) ContainsCursor(line uint32, character uint32) bool {
return line == l.Start.Line && character >= l.Start.Character && character <= l.End.Character
}
func (l LocationRange) ContainsCursorByCharacter(character uint32) bool {
return character >= l.Start.Character && character <= l.End.Character
}
@ -115,7 +160,57 @@ func CharacterRangeFromCtx(
},
End: Location{
Line: line,
Character: end,
Character: end + 1,
},
}
}
type Position interface {
getValue() uint32
}
// Use this type if you want to use a cursor based position
// A cursor based position is a position that represents a cursor
// Given the example:
// "PermitRootLogin yes"
// Taking a look at the first character "P" - the index is 0.
// However, the cursor can either be at:
//
// "|P" - 0 or
// "P|" - 1
//
// This is used for example for textDocument/completion or textDocument/signature
type CursorPosition uint32
func (c CursorPosition) getValue() uint32 {
return uint32(c)
}
func (c CursorPosition) shiftHorizontal(offset uint32) CursorPosition {
return CursorPosition(uint32(c) + offset)
}
func LSPCharacterAsCursorPosition(character uint32) CursorPosition {
return CursorPosition(character)
}
func (c CursorPosition) IsBeforeIndexPosition(i IndexPosition) bool {
// |H[e]llo
return uint32(c) < uint32(i)
}
func (c CursorPosition) IsAfterIndexPosition(i IndexPosition) bool {
// H[e]|llo
return uint32(c) > uint32(i)+1
}
// Use this type if you want to use an index based position
type IndexPosition uint32
func (i IndexPosition) getValue() uint32 {
return uint32(i)
}
func LSPCharacterAsIndexPosition(character uint32) IndexPosition {
return IndexPosition(character)
}

137
common/location_test.go Normal file
View File

@ -0,0 +1,137 @@
package common
import (
"testing"
)
func TestCursorPosition(
t *testing.T,
) {
// Contains fictive range for the name "Test" in the code:
// func Test() {}
locationRange := LocationRange{
Start: Location{
Line: 0,
Character: 5,
},
End: Location{
Line: 0,
Character: 9,
},
}
if !(locationRange.ContainsPosition(LSPCharacterAsCursorPosition(5)) == true) {
t.Errorf("Expected 5 to be in range, but it wasn't")
}
if !(locationRange.ContainsPosition(LSPCharacterAsCursorPosition(6)) == true) {
t.Errorf("Expected 6 to be in range, but it wasn't")
}
if !(locationRange.ContainsPosition(LSPCharacterAsCursorPosition(9)) == true) {
t.Errorf("Expected 9 to be in range, but it wasn't")
}
if !(locationRange.ContainsPosition(LSPCharacterAsCursorPosition(10)) == false) {
t.Errorf("Expected 10 to not be in range, but it was")
}
if !(locationRange.ContainsPosition(LSPCharacterAsCursorPosition(4)) == false) {
t.Errorf("Expected 4 to not be in range, but it was")
}
if !(locationRange.IsPositionBeforeStart(LSPCharacterAsCursorPosition(0)) == true) {
t.Errorf("Expected 0 to be before start, but it wasn't")
}
if !(locationRange.IsPositionBeforeStart(LSPCharacterAsCursorPosition(4)) == true) {
t.Errorf("Expected 5 to be before start, but it wasn't")
}
if !(locationRange.IsPositionBeforeStart(LSPCharacterAsCursorPosition(5)) == false) {
t.Errorf("Expected 5 to not be before start, but it was")
}
if !(locationRange.IsPositionBeforeStart(LSPCharacterAsCursorPosition(10)) == false) {
t.Errorf("Expected 10 to not be before start, but it was")
}
if !(locationRange.IsPositionAfterEnd(LSPCharacterAsCursorPosition(10)) == true) {
t.Errorf("Expected 10 to be after end, but it wasn't")
}
if !(locationRange.IsPositionAfterEnd(LSPCharacterAsCursorPosition(11)) == true) {
t.Errorf("Expected 11 to be after end, but it wasn't")
}
if !(locationRange.IsPositionAfterEnd(LSPCharacterAsCursorPosition(9)) == false) {
t.Errorf("Expected 9 to not be after end, but it was")
}
if !(locationRange.IsPositionAfterEnd(LSPCharacterAsCursorPosition(5)) == false) {
t.Errorf("Expected 5 to not be after end, but it was")
}
}
func TestIndexPosition(t *testing.T) {
// Contains fictive range for the name "Test" in the code:
// func Test() {}
locationRange := LocationRange{
Start: Location{
Line: 0,
Character: 5,
},
End: Location{
Line: 0,
Character: 9,
},
}
if !(locationRange.ContainsPosition(LSPCharacterAsIndexPosition(5)) == true) {
t.Errorf("Expected index position 5 to be in range, but it wasn't")
}
if !(locationRange.ContainsPosition(LSPCharacterAsIndexPosition(6)) == true) {
t.Errorf("Expected index position 6 to be in range, but it wasn't")
}
if !(locationRange.ContainsPosition(LSPCharacterAsIndexPosition(8)) == true) {
t.Errorf("Expected index position 6 to be in range, but it wasn't")
}
if !(locationRange.ContainsPosition(LSPCharacterAsIndexPosition(9)) == false) {
t.Errorf("Expected index position 9 to not be in range, but it was")
}
if !(locationRange.ContainsPosition(LSPCharacterAsIndexPosition(10)) == false) {
t.Errorf("Expected index position 10 to not be in range, but it was")
}
if !(locationRange.ContainsPosition(LSPCharacterAsIndexPosition(4)) == false) {
t.Errorf("Expected index position 4 to not be in range, but it was")
}
if !(locationRange.IsPositionBeforeStart(LSPCharacterAsIndexPosition(4)) == true) {
t.Errorf("Expected index position 4 to be before start, but it wasn't")
}
if !(locationRange.IsPositionBeforeStart(LSPCharacterAsIndexPosition(5)) == false) {
t.Errorf("Expected index position 5 to not be before start, but it was")
}
if !(locationRange.IsPositionBeforeStart(LSPCharacterAsIndexPosition(10)) == false) {
t.Errorf("Expected index position 10 to not be before start, but it wasn't")
}
if !(locationRange.IsPositionAfterEnd(LSPCharacterAsIndexPosition(10)) == true) {
t.Errorf("Expected index position 10 to be after end, but it wasn't")
}
if !(locationRange.IsPositionAfterEnd(LSPCharacterAsIndexPosition(9)) == true) {
t.Errorf("Expected index position 9 to be after end, but it wasn't")
}
if !(locationRange.IsPositionAfterEnd(LSPCharacterAsIndexPosition(5)) == false) {
t.Errorf("Expected index position 5 to not be after end, but it was")
}
}

View File

@ -1,5 +1,19 @@
package common
// LSPCharacterAsCursorPosition:
// @deprecated
func CursorToCharacterIndex(cursor uint32) uint32 {
return max(1, cursor) - 1
}
func DeprecatedImprovedCursorToIndex(
c CursorPosition,
line string,
offset uint32,
) uint32 {
if len(line) == 0 {
return 0
}
return min(uint32(len(line)-1), uint32(c)-offset+1)
}