diff --git a/doc-values/utils.go b/doc-values/utils.go new file mode 100644 index 0000000..a016cda --- /dev/null +++ b/doc-values/utils.go @@ -0,0 +1,20 @@ +package docvalues + +import ( + "config-lsp/utils" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func GenerateBase10Completions(prefix string) []protocol.CompletionItem { + kind := protocol.CompletionItemKindValue + + return utils.Map( + []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, + func(index string) protocol.CompletionItem { + return protocol.CompletionItem{ + Label: prefix + index, + Kind: &kind, + } + }, + ) +} diff --git a/doc-values/value-key-value-assignment.go b/doc-values/value-key-value-assignment.go index 59ff8af..72236ab 100644 --- a/doc-values/value-key-value-assignment.go +++ b/doc-values/value-key-value-assignment.go @@ -22,10 +22,19 @@ type KeyValueAssignmentValue struct { } func (v KeyValueAssignmentValue) GetTypeDescription() []string { - return []string{ - fmt.Sprintf("Key-Value pair in form of 'key%svalue'", v.Separator), - fmt.Sprintf("#### Key\n%s", strings.Join(v.Key.GetTypeDescription(), "\n")), - fmt.Sprintf("#### Value:\n%s", strings.Join(v.Value.GetTypeDescription(), "\n")), + keyDescription := v.Key.GetTypeDescription() + valueDescription := v.Value.GetTypeDescription() + + if len(keyDescription) == 1 && len(valueDescription) == 1 { + return []string{ + fmt.Sprintf("Key-Value pair in form of '<%s>%s<%s>'", keyDescription[0], v.Separator, valueDescription[0]), + } + } else { + return []string{ + fmt.Sprintf("Key-Value pair in form of 'key%svalue'", v.Separator), + fmt.Sprintf("#### Key\n%s", strings.Join(v.Key.GetTypeDescription(), "\n")), + fmt.Sprintf("#### Value:\n%s", strings.Join(v.Value.GetTypeDescription(), "\n")), + } } } diff --git a/doc-values/value-number.go b/doc-values/value-number.go index c64beec..6ceba66 100644 --- a/doc-values/value-number.go +++ b/doc-values/value-number.go @@ -10,7 +10,7 @@ import ( type NotANumberError struct{} func (e NotANumberError) Error() string { - return "This must be number" + return "This must be a number" } type NumberNotInRangeError struct { diff --git a/handlers/openssh/documentation.go b/handlers/openssh/documentation.go index cc0169b..ad03639 100644 --- a/handlers/openssh/documentation.go +++ b/handlers/openssh/documentation.go @@ -755,7 +755,14 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may "PubkeyAuthentication": common.NewOption(`Specifies whether public key authentication is allowed. The default is yes.`, BooleanEnumValue, ), - // "RekeyLimit": `Specifies the maximum amount of data that may be transmitted or received before the session key is renegotiated, optionally followed by a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of ‘K’, ‘M’, or ‘G’ to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between ‘1G’ and ‘4G’, depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the “TIME FORMATS” section. The default value for RekeyLimit is default none, which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done.`, + "RekeyLimit": common.NewOption(`Specifies the maximum amount of data that may be transmitted or received before the session key is renegotiated, optionally followed by a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of ‘K’, ‘M’, or ‘G’ to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between ‘1G’ and ‘4G’, depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the “TIME FORMATS” section. The default value for RekeyLimit is default none, which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done.`, + docvalues.KeyValueAssignmentValue{ + Separator: " ", + ValueIsOptional: true, + Key: DataAmountValue{}, + Value: TimeFormatValue{}, + }, + ), "RequiredRSASize": common.NewOption(`Specifies the minimum RSA key size (in bits) that sshd(8) will accept. User and host-based authentication keys smaller than this limit will be refused. The default is 1024 bits. Note that this limit may only be raised from the default.`, docvalues.NumberValue{Min: &ZERO}, ), diff --git a/handlers/openssh/utils.go b/handlers/openssh/utils.go new file mode 100644 index 0000000..9ef0d5f --- /dev/null +++ b/handlers/openssh/utils.go @@ -0,0 +1,5 @@ +package openssh + +import "regexp" + +var isJustDigitsPattern = regexp.MustCompile(`^\d+$`) diff --git a/handlers/openssh/value-data-amount-value.go b/handlers/openssh/value-data-amount-value.go new file mode 100644 index 0000000..933e3da --- /dev/null +++ b/handlers/openssh/value-data-amount-value.go @@ -0,0 +1,87 @@ +package openssh + +import ( + docvalues "config-lsp/doc-values" + "fmt" + "regexp" + "strconv" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +var dataAmountCheckPattern = regexp.MustCompile(`(?i)^(\d+)([KMG])$`) + +type InvalidDataAmountError struct{} + +func (e InvalidDataAmountError) Error() string { + return "Data amount is invalid. It must be in the form of: [K|M|G]" +} + +type DataAmountValue struct{} + +func (v DataAmountValue) GetTypeDescription() []string { + return []string{"Data amount"} +} + +func (v DataAmountValue) CheckIsValid(value string) error { + if !dataAmountCheckPattern.MatchString(value) { + return InvalidDataAmountError{} + } + + return nil +} + +func calculateLineToKilobyte(value string, unit string) string { + val, err := strconv.Atoi(value) + + if err != nil { + return "" + } + + switch unit { + case "K": + return strconv.Itoa(val) + case "M": + return strconv.Itoa(val * 1000) + case "G": + return strconv.Itoa(val * 1000 * 1000) + default: + return "" + } +} + +func (v DataAmountValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { + completions := make([]protocol.CompletionItem, 0) + + if line != "" && !dataAmountCheckPattern.MatchString(line) { + kind := protocol.CompletionItemKindValue + + completions = append( + completions, + protocol.CompletionItem{ + Label: line + "K", + Kind: &kind, + Documentation: line + " kilobytes", + }, + protocol.CompletionItem{ + Label: line + "M", + Kind: &kind, + Documentation: fmt.Sprintf("%s megabytes (%s kilobytes)", line, calculateLineToKilobyte(line, "M")), + }, + protocol.CompletionItem{ + Label: line + "G", + Kind: &kind, + Documentation: fmt.Sprintf("%s gigabytes (%s kilobytes)", line, calculateLineToKilobyte(line, "G")), + }, + ) + } + + if line == "" || isJustDigitsPattern.MatchString(line) { + completions = append( + completions, + docvalues.GenerateBase10Completions(line)..., + ) + } + + return completions +} diff --git a/handlers/openssh/value-time-format.go b/handlers/openssh/value-time-format.go index 7cee919..b6d63b9 100644 --- a/handlers/openssh/value-time-format.go +++ b/handlers/openssh/value-time-format.go @@ -12,7 +12,12 @@ import ( var timeFormatCompletionsPattern = regexp.MustCompile(`(?i)^(\d+)([smhdw])$`) var timeFormatCheckPattern = regexp.MustCompile(`(?i)^(\d+)([smhdw]?)$`) -var isJustDigitsPattern = regexp.MustCompile(`^\d+$`) + +type InvalidTimeFormatError struct{} + +func (e InvalidTimeFormatError) Error() string { + return "Time format is invalid. It must be in the form of: [s|m|h|d|w]" +} type TimeFormatValue struct{} @@ -22,7 +27,7 @@ func (v TimeFormatValue) GetTypeDescription() []string { func (v TimeFormatValue) CheckIsValid(value string) error { if !timeFormatCheckPattern.MatchString(value) { - return docvalues.RegexInvalidError{Regex: timeFormatCheckPattern.String()} + return InvalidTimeFormatError{} } return nil @@ -88,20 +93,7 @@ func (v TimeFormatValue) FetchCompletions(line string, cursor uint32) []protocol 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, - } - }, - )..., + docvalues.GenerateBase10Completions(line)..., ) }