feat(dovalues): Return multiple InvalidValues from now

This commit is contained in:
Myzel394 2024-08-05 20:35:23 +02:00
parent 7e0e6650eb
commit 433085443a
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
15 changed files with 343 additions and 69 deletions

View File

@ -1,11 +1,68 @@
package docvalues package docvalues
import ( import (
"config-lsp/utils"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
type Value interface { type Value interface {
GetTypeDescription() []string GetTypeDescription() []string
CheckIsValid(value string) error CheckIsValid(value string) []*InvalidValue
FetchCompletions(line string, cursor uint32) []protocol.CompletionItem FetchCompletions(line string, cursor uint32) []protocol.CompletionItem
} }
type InvalidValue struct {
Err error
Start uint32
End uint32
}
func (v *InvalidValue) Shift(offset uint32) {
v.Start += offset
v.End += offset
}
func (v *InvalidValue) GetRange(line uint32, characterStart uint32) protocol.Range {
return protocol.Range{
Start: protocol.Position{
Line: line,
Character: characterStart + v.Start,
},
End: protocol.Position{
Line: line,
Character: characterStart + v.End,
},
}
}
func (v *InvalidValue) GetMessage() string {
return v.Err.Error()
}
func ShiftInvalidValues(offset uint32, invalidValues []*InvalidValue) {
if len(invalidValues) > 0 {
for _, invalidValue := range invalidValues {
invalidValue.Shift(offset)
}
}
}
func InvalidValuesToErrorDiagnostics(
line uint32,
offset uint32,
values []*InvalidValue,
) []protocol.Diagnostic {
severity := protocol.DiagnosticSeverityError
return utils.Map(
values,
func(value *InvalidValue) protocol.Diagnostic {
return protocol.Diagnostic{
Range: value.GetRange(line, offset),
Severity: &severity,
Message: value.GetMessage(),
}
},
)
}

View File

@ -2,7 +2,9 @@ package docvalues
import ( import (
"config-lsp/utils" "config-lsp/utils"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
"golang.org/x/exp/slices"
) )
func GenerateBase10Completions(prefix string) []protocol.CompletionItem { func GenerateBase10Completions(prefix string) []protocol.CompletionItem {
@ -18,3 +20,24 @@ func GenerateBase10Completions(prefix string) []protocol.CompletionItem {
}, },
) )
} }
func MergeKeyEnumAssignmentMaps(maps ...map[EnumString]Value) map[EnumString]Value {
existingEnums := make(map[string]interface{})
result := make(map[EnumString]Value)
slices.Reverse(maps)
for _, m := range maps {
for key, value := range m {
if _, ok := existingEnums[key.InsertText]; !ok {
continue
}
existingEnums[key.InsertText] = nil
result[key] = value
}
}
return result
}

View File

@ -9,11 +9,11 @@ import (
) )
type ArrayContainsDuplicatesError struct { type ArrayContainsDuplicatesError struct {
Duplicates []string Value string
} }
func (e ArrayContainsDuplicatesError) Error() string { func (e ArrayContainsDuplicatesError) Error() string {
return fmt.Sprintf("The following values are duplicated: %s", strings.Join(e.Duplicates, ",")) return fmt.Sprintf("'%s' is a duplicate value (and duplicates are not allowed)", e.Value)
} }
var SimpleDuplicatesExtractor = func(value string) string { var SimpleDuplicatesExtractor = func(value string) string {
@ -52,7 +52,9 @@ func (v ArrayValue) GetTypeDescription() []string {
subValue.GetTypeDescription()..., subValue.GetTypeDescription()...,
) )
} }
func (v ArrayValue) CheckIsValid(value string) error {
func (v ArrayValue) CheckIsValid(value string) []*InvalidValue {
errors := []*InvalidValue{}
values := strings.Split(value, v.Separator) values := strings.Split(value, v.Separator)
if *v.DuplicatesExtractor != nil { if *v.DuplicatesExtractor != nil {
@ -68,25 +70,48 @@ func (v ArrayValue) CheckIsValid(value string) error {
valuesOccurrences[duplicateValue]++ valuesOccurrences[duplicateValue]++
} }
duplicateValues := utils.FilterMapWhere(valuesOccurrences, func(_ string, value int) bool { duplicateValuesAsList := utils.FilterMapWhere(valuesOccurrences, func(_ string, value int) bool {
return value > 1 return value > 1
}) })
duplicateValues := utils.KeysAsSet(duplicateValuesAsList)
return ArrayContainsDuplicatesError{ duplicateIndexStart := uint32(0)
Duplicates: utils.KeysOfMap(duplicateValues), duplicateIndexEnd := uint32(0)
currentIndex := uint32(0)
for _, rawValue := range values {
if _, found := duplicateValues[rawValue]; found {
duplicateIndexStart = currentIndex
duplicateIndexEnd = currentIndex + uint32(len(rawValue))
errors = append(errors, &InvalidValue{
Err: ArrayContainsDuplicatesError{
Value: rawValue,
},
Start: duplicateIndexStart,
End: duplicateIndexEnd,
})
}
} }
return errors
} }
} }
currentIndex := uint32(0)
for _, subValue := range values { for _, subValue := range values {
err := v.SubValue.CheckIsValid(subValue) newErrors := v.SubValue.CheckIsValid(subValue)
if err != nil { if len(newErrors) > 0 {
return err ShiftInvalidValues(currentIndex, newErrors)
} }
errors = append(errors, newErrors...)
currentIndex += uint32(len(subValue) + len(v.Separator))
} }
return nil return errors
} }
func (v ArrayValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { func (v ArrayValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {

View File

@ -24,7 +24,7 @@ func (v CustomValue) GetTypeDescription() []string {
return []string{"Custom"} return []string{"Custom"}
} }
func (v CustomValue) CheckIsValid(value string) error { func (v CustomValue) CheckIsValid(value string) []*InvalidValue {
return v.FetchValue(EmptyValueContextInstance).CheckIsValid(value) return v.FetchValue(EmptyValueContextInstance).CheckIsValid(value)
} }

View File

@ -14,7 +14,11 @@ type ValueNotInEnumError struct {
} }
func (e ValueNotInEnumError) Error() string { func (e ValueNotInEnumError) Error() string {
return fmt.Sprintf("This value is not valid. Select one from: %s", strings.Join(e.AvailableValues, ",")) if len(e.AvailableValues) <= 6 {
return fmt.Sprintf("This value is not valid. Select one from: %s", strings.Join(e.AvailableValues, ","))
} else {
return fmt.Sprintf("This value is not valid")
}
} }
type EnumString struct { type EnumString struct {
@ -63,7 +67,7 @@ func (v EnumValue) GetTypeDescription() []string {
return lines return lines
} }
func (v EnumValue) CheckIsValid(value string) error { func (v EnumValue) CheckIsValid(value string) []*InvalidValue {
if !v.EnforceValues { if !v.EnforceValues {
return nil return nil
} }
@ -75,9 +79,15 @@ func (v EnumValue) CheckIsValid(value string) error {
} }
return ValueNotInEnumError{ return []*InvalidValue{
ProvidedValue: value, {
AvailableValues: utils.Map(v.Values, func(value EnumString) string { return value.InsertText }), Err: ValueNotInEnumError{
ProvidedValue: value,
AvailableValues: utils.Map(v.Values, func(value EnumString) string { return value.InsertText }),
},
Start: 0,
End: uint32(len(value)),
},
} }
} }
func (v EnumValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { func (v EnumValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {

View File

@ -63,14 +63,20 @@ func (v IPAddressValue) GetTypeDescription() []string {
return []string{"An IP Address"} return []string{"An IP Address"}
} }
func (v IPAddressValue) CheckIsValid(value string) error { func (v IPAddressValue) CheckIsValid(value string) []*InvalidValue {
var ip net.Prefix var ip net.Prefix
if v.AllowRange { if v.AllowRange {
rawIP, err := net.ParsePrefix(value) rawIP, err := net.ParsePrefix(value)
if err != nil { if err != nil {
return InvalidIPAddress{} return []*InvalidValue{
{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
} }
ip = rawIP ip = rawIP
@ -78,14 +84,24 @@ func (v IPAddressValue) CheckIsValid(value string) error {
rawIP, err := net.ParseAddr(value) rawIP, err := net.ParseAddr(value)
if err != nil { if err != nil {
return InvalidIPAddress{} return []*InvalidValue{{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
} }
ip = net.PrefixFrom(rawIP, 32) ip = net.PrefixFrom(rawIP, 32)
} }
if !ip.IsValid() { if !ip.IsValid() {
return InvalidIPAddress{} return []*InvalidValue{{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
} }
if v.AllowedIPs != nil { if v.AllowedIPs != nil {
@ -95,13 +111,23 @@ func (v IPAddressValue) CheckIsValid(value string) error {
} }
} }
return IpRangeNotAllowedError{} return []*InvalidValue{{
Err: IPAddressNotAllowedError{},
Start: 0,
End: uint32(len(value)),
},
}
} }
if v.DisallowedIPs != nil { if v.DisallowedIPs != nil {
for _, disallowedIP := range *v.DisallowedIPs { for _, disallowedIP := range *v.DisallowedIPs {
if disallowedIP.Contains(ip.Addr()) { if disallowedIP.Contains(ip.Addr()) {
return IPAddressNotAllowedError{} return []*InvalidValue{{
Err: IPAddressNotAllowedError{},
Start: 0,
End: uint32(len(value)),
},
}
} }
} }
} }
@ -114,7 +140,12 @@ func (v IPAddressValue) CheckIsValid(value string) error {
return nil return nil
} }
return InvalidIPAddress{} return []*InvalidValue{{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
} }
func (v IPAddressValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { func (v IPAddressValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {

View File

@ -9,8 +9,8 @@ import (
) )
type KeyEnumAssignmentValue struct { type KeyEnumAssignmentValue struct {
Values map[EnumString]Value Values map[EnumString]Value
Separator string Separator string
ValueIsOptional bool ValueIsOptional bool
} }
@ -19,14 +19,13 @@ func (v KeyEnumAssignmentValue) GetTypeDescription() []string {
firstKey := utils.KeysOfMap(v.Values)[0] firstKey := utils.KeysOfMap(v.Values)[0]
valueDescription := v.Values[firstKey].GetTypeDescription() valueDescription := v.Values[firstKey].GetTypeDescription()
if (len(valueDescription) == 1) { if len(valueDescription) == 1 {
return []string{ return []string{
fmt.Sprintf("Key-Value pair in form of '<%s>%s<%s>'", firstKey.DescriptionText, v.Separator, valueDescription[0]), fmt.Sprintf("Key-Value pair in form of '<%s>%s<%s>'", firstKey.DescriptionText, v.Separator, valueDescription[0]),
} }
} }
} }
var result []string var result []string
for key, value := range v.Values { for key, value := range v.Values {
result = append(result, key.Documentation) result = append(result, key.Documentation)
@ -42,17 +41,17 @@ func (v KeyEnumAssignmentValue) getValue(findKey string) (*Value, bool) {
for key, value := range v.Values { for key, value := range v.Values {
if key.InsertText == findKey { if key.InsertText == findKey {
switch value.(type) { switch value.(type) {
case CustomValue: case CustomValue:
customValue := value.(CustomValue) customValue := value.(CustomValue)
context := KeyValueAssignmentContext{ context := KeyValueAssignmentContext{
SelectedKey: findKey, SelectedKey: findKey,
} }
fetchedValue := customValue.FetchValue(context) fetchedValue := customValue.FetchValue(context)
return &fetchedValue, true return &fetchedValue, true
default: default:
return &value, true return &value, true
} }
} }
} }
@ -60,7 +59,7 @@ func (v KeyEnumAssignmentValue) getValue(findKey string) (*Value, bool) {
return nil, false return nil, false
} }
func (v KeyEnumAssignmentValue) CheckIsValid(value string) error { func (v KeyEnumAssignmentValue) CheckIsValid(value string) []*InvalidValue {
parts := strings.Split(value, v.Separator) parts := strings.Split(value, v.Separator)
if len(parts) == 0 || parts[0] == "" { if len(parts) == 0 || parts[0] == "" {
@ -73,22 +72,35 @@ func (v KeyEnumAssignmentValue) CheckIsValid(value string) error {
return nil return nil
} }
return KeyValueAssignmentError{} return []*InvalidValue{
{
Err: KeyValueAssignmentError{},
Start: 0,
End: uint32(len(parts[0]) + len(v.Separator)),
},
}
} }
checkValue, found := v.getValue(parts[0]) checkValue, found := v.getValue(parts[0])
if !found { if !found {
return ValueNotInEnumError{ return []*InvalidValue{
AvailableValues: utils.Map(utils.KeysOfMap(v.Values), func(key EnumString) string { return key.InsertText }), {
ProvidedValue: parts[0], Err: ValueNotInEnumError{
AvailableValues: utils.Map(utils.KeysOfMap(v.Values), func(key EnumString) string { return key.InsertText }),
ProvidedValue: parts[0],
},
Start: 0,
End: uint32(len(parts[0])),
},
} }
} }
err := (*checkValue).CheckIsValid(parts[1]) errors := (*checkValue).CheckIsValid(parts[1])
if err != nil { if len(errors) > 0 {
return err ShiftInvalidValues(uint32(len(parts[0])+len(v.Separator)), errors)
return errors
} }
return nil return nil

View File

@ -65,7 +65,7 @@ func (v KeyValueAssignmentValue) getValue(selectedKey string) Value {
} }
} }
func (v KeyValueAssignmentValue) CheckIsValid(value string) error { func (v KeyValueAssignmentValue) CheckIsValid(value string) []*InvalidValue {
parts := strings.Split(value, v.Separator) parts := strings.Split(value, v.Separator)
if len(parts) == 0 || parts[0] == "" { if len(parts) == 0 || parts[0] == "" {
@ -84,13 +84,20 @@ func (v KeyValueAssignmentValue) CheckIsValid(value string) error {
return nil return nil
} }
return KeyValueAssignmentError{} return []*InvalidValue{
{
Err: KeyValueAssignmentError{},
Start: 0,
End: uint32(len(parts[0]) + len(v.Separator)),
},
}
} }
err = v.getValue(parts[0]).CheckIsValid(parts[1]) errors := v.getValue(parts[0]).CheckIsValid(parts[1])
if err != nil { if len(errors) > 0 {
return err ShiftInvalidValues(uint32(len(parts[0])+len(v.Separator)), errors)
return errors
} }
return nil return nil

View File

@ -48,15 +48,27 @@ func (v NumberValue) GetTypeDescription() []string {
return []string{"A number"} return []string{"A number"}
} }
func (v NumberValue) CheckIsValid(value string) error { func (v NumberValue) CheckIsValid(value string) []*InvalidValue {
number, err := strconv.Atoi(value) number, err := strconv.Atoi(value)
if err != nil { if err != nil {
return NotANumberError{} return []*InvalidValue{
{
Err: NotANumberError{},
Start: 0,
End: uint32(len(value)),
},
}
} }
if (v.Min != nil && number < *v.Min) || (v.Max != nil && number > *v.Max) { if (v.Min != nil && number < *v.Min) || (v.Max != nil && number > *v.Max) {
return NumberNotInRangeError{v.Min, v.Max} return []*InvalidValue{
{
Err: NumberNotInRangeError{v.Min, v.Max},
Start: 0,
End: uint32(len(value)),
},
}
} }
return nil return nil

View File

@ -33,20 +33,20 @@ func (v OrValue) GetTypeDescription() []string {
lines..., lines...,
) )
} }
func (v OrValue) CheckIsValid(value string) error { func (v OrValue) CheckIsValid(value string) []*InvalidValue {
var lastError error = nil errors := make([]*InvalidValue, 0)
for _, subValue := range v.Values { for _, subValue := range v.Values {
err := subValue.CheckIsValid(value) valueErrors := subValue.CheckIsValid(value)
if err == nil { if len(valueErrors) == 0 {
return nil return nil
} else {
lastError = err
} }
errors = append(errors, valueErrors...)
} }
return lastError return errors
} }
func (v OrValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { func (v OrValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
completions := make([]protocol.CompletionItem, 0) completions := make([]protocol.CompletionItem, 0)

View File

@ -46,9 +46,14 @@ func (v PathValue) GetTypeDescription() []string {
return []string{strings.Join(hints, ", ")} return []string{strings.Join(hints, ", ")}
} }
func (v PathValue) CheckIsValid(value string) error { func (v PathValue) CheckIsValid(value string) []*InvalidValue {
if !utils.DoesPathExist(value) { if !utils.DoesPathExist(value) {
return PathDoesNotExistError{} return []*InvalidValue{{
Err: PathDoesNotExistError{},
Start: 0,
End: uint32(len(value)),
},
}
} }
isValid := false isValid := false
@ -65,7 +70,12 @@ func (v PathValue) CheckIsValid(value string) error {
return nil return nil
} }
return PathInvalidError{} return []*InvalidValue{{
Err: PathInvalidError{},
Start: 0,
End: uint32(len(value)),
},
}
} }
func (v PathValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { func (v PathValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {

View File

@ -0,0 +1,77 @@
package docvalues
import (
"config-lsp/utils"
"strconv"
protocol "github.com/tliron/glsp/protocol_3_16"
)
type NotAPowerOfTwoError struct{}
func (e NotAPowerOfTwoError) Error() string {
return "This must be a power of 2 (e.g. 32, 64, 128, 265, 512, 1024,, 1024, ...)"
}
type PowerOfTwoValue struct{}
func (v PowerOfTwoValue) GetTypeDescription() []string {
return []string{"A power of 2"}
}
func isPowerOfTwo(number int) bool {
count := 0
for number > 0 {
count += number & 1
number >>= 1
if count > 1 {
return false
}
}
return true
}
func (v PowerOfTwoValue) CheckIsValid(value string) []*InvalidValue {
number, err := strconv.Atoi(value)
if err != nil {
return []*InvalidValue{{
Err: NotANumberError{},
Start: 0,
End: uint32(len(value)),
},
}
}
if number <= 0 || !isPowerOfTwo(number) {
return []*InvalidValue{{
Err: NotAPowerOfTwoError{},
Start: 0,
End: uint32(len(value)),
},
}
}
return nil
}
var powers = []int{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536}
func (v PowerOfTwoValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
textFormat := protocol.InsertTextFormatPlainText
kind := protocol.CompletionItemKindEnum
return utils.Map(
powers,
func(power int) protocol.CompletionItem {
return protocol.CompletionItem{
Label: strconv.Itoa(power),
InsertTextFormat: &textFormat,
Kind: &kind,
}
},
)
}

View File

@ -30,7 +30,7 @@ func (v PrefixWithMeaningValue) GetTypeDescription() []string {
) )
} }
func (v PrefixWithMeaningValue) CheckIsValid(value string) error { func (v PrefixWithMeaningValue) CheckIsValid(value string) []*InvalidValue {
return v.SubValue.CheckIsValid(value) return v.SubValue.CheckIsValid(value)
} }

View File

@ -25,9 +25,14 @@ func (v RegexValue) GetTypeDescription() []string {
} }
} }
func (v RegexValue) CheckIsValid(value string) error { func (v RegexValue) CheckIsValid(value string) []*InvalidValue {
if value == "" { if value == "" {
return EmptyStringError{} return []*InvalidValue{{
Err: EmptyStringError{},
Start: 0,
End: uint32(len(value)),
},
}
} }
return nil return nil

View File

@ -16,9 +16,14 @@ func (v StringValue) GetTypeDescription() []string {
return []string{"String"} return []string{"String"}
} }
func (v StringValue) CheckIsValid(value string) error { func (v StringValue) CheckIsValid(value string) []*InvalidValue {
if value == "" { if value == "" {
return EmptyStringError{} return []*InvalidValue{{
Err: EmptyStringError{},
Start: 0,
End: uint32(len(value)),
},
}
} }
return nil return nil