mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
205 lines
4.6 KiB
Go
205 lines
4.6 KiB
Go
package docvalues
|
|
|
|
import (
|
|
"config-lsp/utils"
|
|
"fmt"
|
|
"strings"
|
|
|
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
)
|
|
|
|
type ArrayContainsDuplicatesError struct {
|
|
Value string
|
|
}
|
|
|
|
func (e ArrayContainsDuplicatesError) Error() string {
|
|
return fmt.Sprintf("'%s' is a duplicate value (and duplicates are not allowed)", e.Value)
|
|
}
|
|
|
|
var SimpleDuplicatesExtractor = func(value string) string {
|
|
return value
|
|
}
|
|
|
|
var ExtractKeyDuplicatesExtractor = func(separator string) func(string) string {
|
|
return func(value string) string {
|
|
splitted := strings.Split(value, separator)
|
|
|
|
if len(splitted) == 0 {
|
|
return ""
|
|
}
|
|
|
|
return splitted[0]
|
|
}
|
|
}
|
|
|
|
var DuplicatesAllowedExtractor func(string) string = nil
|
|
|
|
type ArrayValue struct {
|
|
SubValue DeprecatedValue
|
|
Separator string
|
|
// If this function is nil, no duplicate check is done.
|
|
// (value) => Extracted value
|
|
// This is used to extract the value from the user input,
|
|
// because you may want to preprocess the value before checking for duplicates
|
|
DuplicatesExtractor *(func(string) string)
|
|
|
|
// If true, array ArrayValue ignores the `Separator` if it's within quotes
|
|
RespectQuotes bool
|
|
}
|
|
|
|
func (v ArrayValue) GetTypeDescription() []string {
|
|
subValue := v.SubValue.(DeprecatedValue)
|
|
|
|
return append(
|
|
[]string{fmt.Sprintf("An Array separated by '%s' of:", v.Separator)},
|
|
subValue.GetTypeDescription()...,
|
|
)
|
|
}
|
|
|
|
// TODO: Add support for quotes
|
|
func (v ArrayValue) DeprecatedCheckIsValid(value string) []*InvalidValue {
|
|
errors := []*InvalidValue{}
|
|
values := strings.Split(value, v.Separator)
|
|
|
|
if *v.DuplicatesExtractor != nil {
|
|
valuesOccurrences := utils.SliceToMap(
|
|
utils.Map(values, *v.DuplicatesExtractor),
|
|
0,
|
|
)
|
|
|
|
// Only continue if there are actually duplicate values
|
|
if len(values) != len(valuesOccurrences) {
|
|
for _, duplicateRawValue := range values {
|
|
duplicateValue := (*v.DuplicatesExtractor)(duplicateRawValue)
|
|
valuesOccurrences[duplicateValue]++
|
|
}
|
|
|
|
duplicateValuesAsList := utils.FilterMapWhere(valuesOccurrences, func(_ string, value int) bool {
|
|
return value > 1
|
|
})
|
|
duplicateValues := utils.KeysAsSet(duplicateValuesAsList)
|
|
|
|
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 {
|
|
newErrors := v.SubValue.DeprecatedCheckIsValid(subValue)
|
|
|
|
if len(newErrors) > 0 {
|
|
ShiftInvalidValues(currentIndex, newErrors)
|
|
}
|
|
|
|
errors = append(errors, newErrors...)
|
|
|
|
currentIndex += uint32(len(subValue) + len(v.Separator))
|
|
}
|
|
|
|
return errors
|
|
}
|
|
|
|
func (v ArrayValue) getCurrentValue(line string, cursor uint32) (string, uint32) {
|
|
if line == "" {
|
|
return line, cursor
|
|
}
|
|
|
|
MIN := uint32(0)
|
|
MAX := uint32(len(line) - 1)
|
|
|
|
var cursorSearchStart = cursor
|
|
var cursorSearchEnd = cursor
|
|
|
|
var start uint32
|
|
var end uint32
|
|
|
|
// Hello,world,how,are,you
|
|
// Hello,"world,how",are,you
|
|
if v.RespectQuotes {
|
|
quotes := utils.GetQuoteRanges(line)
|
|
quote := quotes.GetQuoteForIndex(int(cursor))
|
|
|
|
if quote != nil {
|
|
cursorSearchStart = uint32(quote[0])
|
|
cursorSearchEnd = uint32(quote[1])
|
|
}
|
|
}
|
|
|
|
// hello,w[o]rld,and,more
|
|
// [h]ello,world
|
|
// hello,[w]orld
|
|
// hell[o],world
|
|
// hello,worl[d]
|
|
// hello,world[,]
|
|
// hello[,]world,how,are,you
|
|
relativePosition, found := utils.FindPreviousCharacter(
|
|
line,
|
|
v.Separator,
|
|
int(cursorSearchStart),
|
|
)
|
|
|
|
if found {
|
|
// + 1 to skip the separator
|
|
start = min(
|
|
MAX,
|
|
uint32(relativePosition)+1,
|
|
)
|
|
} else {
|
|
start = MIN
|
|
}
|
|
|
|
relativePosition, found = utils.FindNextCharacter(
|
|
line,
|
|
v.Separator,
|
|
int(cursorSearchEnd),
|
|
)
|
|
|
|
if found {
|
|
// - 1 to skip the separator
|
|
end = max(
|
|
MIN,
|
|
uint32(relativePosition)-1,
|
|
)
|
|
} else {
|
|
end = MAX
|
|
}
|
|
|
|
if cursor > end {
|
|
// The user is typing a new (yet empty) value
|
|
return "", 0
|
|
}
|
|
|
|
return line[start : end+1], cursor - start
|
|
}
|
|
|
|
func (v ArrayValue) DeprecatedFetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
|
|
value, cursor := v.getCurrentValue(line, cursor)
|
|
|
|
return v.SubValue.DeprecatedFetchCompletions(value, cursor)
|
|
}
|
|
|
|
func (v ArrayValue) DeprecatedFetchHoverInfo(line string, cursor uint32) []string {
|
|
value, cursor := v.getCurrentValue(line, cursor)
|
|
|
|
return v.SubValue.DeprecatedFetchHoverInfo(value, cursor)
|
|
}
|