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
import (
"config-lsp/utils"
protocol "github.com/tliron/glsp/protocol_3_16"
)
type Value interface {
GetTypeDescription() []string
CheckIsValid(value string) error
CheckIsValid(value string) []*InvalidValue
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 (
"config-lsp/utils"
protocol "github.com/tliron/glsp/protocol_3_16"
"golang.org/x/exp/slices"
)
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 {
Duplicates []string
Value 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 {
@ -52,7 +52,9 @@ func (v ArrayValue) GetTypeDescription() []string {
subValue.GetTypeDescription()...,
)
}
func (v ArrayValue) CheckIsValid(value string) error {
func (v ArrayValue) CheckIsValid(value string) []*InvalidValue {
errors := []*InvalidValue{}
values := strings.Split(value, v.Separator)
if *v.DuplicatesExtractor != nil {
@ -68,25 +70,48 @@ func (v ArrayValue) CheckIsValid(value string) error {
valuesOccurrences[duplicateValue]++
}
duplicateValues := utils.FilterMapWhere(valuesOccurrences, func(_ string, value int) bool {
duplicateValuesAsList := utils.FilterMapWhere(valuesOccurrences, func(_ string, value int) bool {
return value > 1
})
duplicateValues := utils.KeysAsSet(duplicateValuesAsList)
return ArrayContainsDuplicatesError{
Duplicates: utils.KeysOfMap(duplicateValues),
duplicateIndexStart := uint32(0)
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 {
err := v.SubValue.CheckIsValid(subValue)
newErrors := v.SubValue.CheckIsValid(subValue)
if err != nil {
return err
if len(newErrors) > 0 {
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 {

View File

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

View File

@ -14,7 +14,11 @@ type ValueNotInEnumError struct {
}
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 {
@ -63,7 +67,7 @@ func (v EnumValue) GetTypeDescription() []string {
return lines
}
func (v EnumValue) CheckIsValid(value string) error {
func (v EnumValue) CheckIsValid(value string) []*InvalidValue {
if !v.EnforceValues {
return nil
}
@ -75,9 +79,15 @@ func (v EnumValue) CheckIsValid(value string) error {
}
return ValueNotInEnumError{
ProvidedValue: value,
AvailableValues: utils.Map(v.Values, func(value EnumString) string { return value.InsertText }),
return []*InvalidValue{
{
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 {

View File

@ -63,14 +63,20 @@ func (v IPAddressValue) GetTypeDescription() []string {
return []string{"An IP Address"}
}
func (v IPAddressValue) CheckIsValid(value string) error {
func (v IPAddressValue) CheckIsValid(value string) []*InvalidValue {
var ip net.Prefix
if v.AllowRange {
rawIP, err := net.ParsePrefix(value)
if err != nil {
return InvalidIPAddress{}
return []*InvalidValue{
{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
}
ip = rawIP
@ -78,14 +84,24 @@ func (v IPAddressValue) CheckIsValid(value string) error {
rawIP, err := net.ParseAddr(value)
if err != nil {
return InvalidIPAddress{}
return []*InvalidValue{{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
}
ip = net.PrefixFrom(rawIP, 32)
}
if !ip.IsValid() {
return InvalidIPAddress{}
return []*InvalidValue{{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
}
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 {
for _, disallowedIP := range *v.DisallowedIPs {
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 InvalidIPAddress{}
return []*InvalidValue{{
Err: InvalidIPAddress{},
Start: 0,
End: uint32(len(value)),
},
}
}
func (v IPAddressValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {

View File

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

View File

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

View File

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

View File

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

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 == "" {
return EmptyStringError{}
return []*InvalidValue{{
Err: EmptyStringError{},
Start: 0,
End: uint32(len(value)),
},
}
}
return nil

View File

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