feat: Add TimeFormatValue

This commit is contained in:
Myzel394 2024-07-30 23:59:42 +02:00
parent ac2d972d3d
commit 26514ed059
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
7 changed files with 171 additions and 20 deletions

View File

@ -47,11 +47,9 @@ func (p *SimpleConfigParser) AddLine(line string, lineNumber uint32) (string, er
}
optionIndex := re.SubexpIndex("OptionName")
if optionIndex == -1 {
return "", docvalues.MalformedLineError{}
}
option = matches[optionIndex]
if _, exists := (*p.Options.AvailableOptions)[option]; !exists {
@ -59,17 +57,15 @@ func (p *SimpleConfigParser) AddLine(line string, lineNumber uint32) (string, er
}
separatorIndex := re.SubexpIndex("Separator")
if separatorIndex == -1 {
return option, docvalues.MalformedLineError{}
}
separator = matches[separatorIndex]
valueIndex := re.SubexpIndex("Value")
if valueIndex == -1 {
return option, docvalues.MalformedLineError{}
}
value = matches[valueIndex]
if _, exists := p.Lines[option]; exists {

View File

@ -89,7 +89,6 @@ type groupInfo struct {
var _cachedGroupInfo []groupInfo
func fetchGroupInfo() ([]groupInfo, error) {
if len(_cachedGroupInfo) > 0 {
return _cachedGroupInfo, nil

View File

@ -90,6 +90,10 @@ func (v ArrayValue) CheckIsValid(value string) error {
}
func (v ArrayValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
if cursor == 0 {
return v.SubValue.FetchCompletions(line, cursor)
}
relativePosition, found := utils.FindPreviousCharacter(line, v.Separator, int(cursor-1))
if found {

View File

@ -55,10 +55,14 @@ func (v KeyValueAssignmentValue) CheckIsValid(value string) error {
}
func (v KeyValueAssignmentValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
if cursor == 0 {
return v.Key.FetchCompletions(line, cursor)
}
relativePosition, found := utils.FindPreviousCharacter(line, v.Separator, int(cursor-1))
if found {
line = line[uint32(relativePosition):]
line = line[uint32(relativePosition+len(v.Separator)):]
cursor -= uint32(relativePosition)
return v.Value.FetchCompletions(line, cursor)

38
doc-values/value-regex.go Normal file
View File

@ -0,0 +1,38 @@
package docvalues
import (
"fmt"
"regexp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
type RegexInvalidError struct {
Regex string
}
func (e RegexInvalidError) Error() string {
return fmt.Sprintf("This value does not match the regular expression (Pattern: `%s`)", e.Regex)
}
type RegexValue struct {
Regex regexp.Regexp
}
func (v RegexValue) GetTypeDescription() []string {
return []string{
fmt.Sprintf("String matching the regular expression (Pattern: `%s`)", v.Regex.String()),
}
}
func (v RegexValue) CheckIsValid(value string) error {
if value == "" {
return EmptyStringError{}
}
return nil
}
func (v RegexValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
return []protocol.CompletionItem{}
}

View File

@ -147,7 +147,8 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
SubValue: docvalues.ArrayValue{
Separator: ",",
DuplicatesExtractor: &docvalues.DuplicatesAllowedExtractor,
SubValue: docvalues.StringValue{},
// TODO: Add
SubValue: docvalues.StringValue{},
},
},
),
@ -182,7 +183,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
"x11-connection",
},
},
Value: docvalues.StringValue{},
Value: TimeFormatValue{},
},
},
),
@ -230,7 +231,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
},
},
),
"DenyGroups": common.NewOption(`This keyword can be followed by a list of group name patterns, separated by spaces. Login is disallowed for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny groups directives are processed in the following order: DenyGroups, AllowGroups.
"DenyGroups": common.NewOption(`This keyword can be followed by a list of group name patterns, separated by spaces. Login is disallowed for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny groups directives are processed in the following order: DenyGroups, AllowGroups.
See PATTERNS in ssh_config(5) for more information on patterns. This keyword may appear multiple times in sshd_config with each instance appending to the list.`,
docvalues.GroupValue(" ", false),
),
@ -430,7 +431,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
},
},
),
"LogVerbose": common.NewOption(`Specify one or more overrides to LogLevel. An override consists of a pattern lists that matches the source file, function and line number to force detailed logging for. For example, an override pattern of:
"LogVerbose": common.NewOption(`Specify one or more overrides to LogLevel. An override consists of a pattern lists that matches the source file, function and line number to force detailed logging for. For example, an override pattern of:
kex.c:*:1000,*:kex_exchange_identification():*,packet.c:*
would enable detailed logging for line 1000 of kex.c, everything in the kex_exchange_identification() function, and all code in the packet.c file. This option is intended for debugging and no overrides are enabled by default.`,
docvalues.StringValue{},
@ -605,16 +606,16 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
),
"SetEnv": common.NewOption(`Specifies one or more environment variables to set in child sessions started by sshd(8) as “NAME=VALUE”. The environment value may be quoted (e.g. if it contains whitespace characters). Environment variables set by SetEnv override the default environment and any variables specified by the user via AcceptEnv or PermitUserEnvironment.`,
docvalues.ArrayValue{
Separator: " ",
DuplicatesExtractor: &SetEnvExtractor,
SubValue: docvalues.KeyValueAssignmentValue{
Separator: "=",
Key: docvalues.StringValue{},
Value: docvalues.StringValue{},
docvalues.ArrayValue{
Separator: " ",
DuplicatesExtractor: &SetEnvExtractor,
SubValue: docvalues.KeyValueAssignmentValue{
Separator: "=",
Key: docvalues.StringValue{},
Value: docvalues.StringValue{},
},
},
},
),
),
"StreamLocalBindMask": common.NewOption(`Sets the octal file creation mode mask (umask) used when creating a Unix-domain socket file for local or remote port forwarding. This option is only used for port forwarding to a Unix-domain socket file.
The default value is 0177, which creates a Unix-domain socket file that is readable and writable only by the owner. Note that not all operating systems honor the file mode on Unix-domain socket files.`,
docvalues.PositiveNumberValue{},

View File

@ -0,0 +1,109 @@
package openssh
import (
docvalues "config-lsp/doc-values"
"config-lsp/utils"
"fmt"
"regexp"
"strconv"
protocol "github.com/tliron/glsp/protocol_3_16"
)
var timeFormatCompletionsPattern = regexp.MustCompile(`(?i)^(\d+)([smhdw])$`)
var timeFormatCheckPattern = regexp.MustCompile(`(?i)^(\d+)([smhdw]?)$`)
var isJustDigitsPattern = regexp.MustCompile(`^\d+$`)
type TimeFormatValue struct{}
func (v TimeFormatValue) GetTypeDescription() []string {
return []string{"Time value"}
}
func (v TimeFormatValue) CheckIsValid(value string) error {
if !timeFormatCheckPattern.MatchString(value) {
return docvalues.RegexInvalidError{Regex: timeFormatCheckPattern.String()}
}
return nil
}
func calculateInSeconds(value int, unit string) int {
switch unit {
case "s":
return value
case "m":
return value * 60
case "h":
return value * 60 * 60
case "d":
return value * 60 * 60 * 24
case "w":
return value * 60 * 60 * 24 * 7
default:
return 0
}
}
func (v TimeFormatValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
completions := make([]protocol.CompletionItem, 0)
if line != "" && !timeFormatCompletionsPattern.MatchString(line) {
completions = append(
completions,
utils.Map(
[]string{"s", "m", "h", "d", "w"},
func(unit string) protocol.CompletionItem {
kind := protocol.CompletionItemKindValue
unitName := map[string]string{
"s": "seconds",
"m": "minutes",
"h": "hours",
"d": "days",
"w": "weeks",
}[unit]
var detail string
value, err := strconv.Atoi(line)
if err == nil {
if unit == "s" {
detail = fmt.Sprintf("%d seconds", value)
} else {
detail = fmt.Sprintf("%d %s (%d seconds)", value, unitName, calculateInSeconds(value, unit))
}
}
return protocol.CompletionItem{
Label: line + unit,
Kind: &kind,
Detail: &detail,
}
},
)...,
)
}
if line == "" || isJustDigitsPattern.MatchString(line) {
completions = append(
completions,
utils.Map(
[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
func(index int) protocol.CompletionItem {
kind := protocol.CompletionItemKindValue
sortText := strconv.Itoa(index)
return protocol.CompletionItem{
Label: line + strconv.Itoa(index),
Kind: &kind,
SortText: &sortText,
}
},
)...,
)
}
return completions
}