feat(aliases): Add hover info

This commit is contained in:
Myzel394 2024-09-05 23:24:48 +02:00
parent 3a1b38400c
commit 3538a37203
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
6 changed files with 216 additions and 66 deletions

View File

@ -42,7 +42,10 @@
}; };
}; };
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
buildInputs = inputs; buildInputs = inputs ++ (with pkgs; [
mailutils
swaks
]);
}; };
} }
); );

View File

@ -6,6 +6,7 @@ import (
"config-lsp/handlers/aliases/fields" "config-lsp/handlers/aliases/fields"
"config-lsp/utils" "config-lsp/utils"
"fmt" "fmt"
"strconv"
) )
type AliasValueInterface interface { type AliasValueInterface interface {
@ -102,6 +103,16 @@ type AliasValueErrorCode struct {
AliasValue AliasValue
} }
func (a AliasValueErrorCode) ErrorCodeAsInt() uint16 {
code, err := strconv.Atoi(a.Value)
if err != nil {
return 0
}
return uint16(code)
}
type AliasValueErrorMessage struct { type AliasValueErrorMessage struct {
AliasValue AliasValue
} }

View File

@ -48,7 +48,7 @@ func GetCompletionsForEntry(
return completions, nil return completions, nil
} }
value := getValueAtCursor(cursor, entry) value := GetValueAtCursor(cursor, entry)
relativeCursor := cursor - entry.Key.Location.Start.Character relativeCursor := cursor - entry.Key.Location.Start.Character
if value == nil { if value == nil {

View File

@ -5,7 +5,7 @@ import (
"slices" "slices"
) )
func getValueAtCursor( func GetValueAtCursor(
cursor uint32, cursor uint32,
entry *ast.AliasEntry, entry *ast.AliasEntry,
) *ast.AliasValueInterface { ) *ast.AliasValueInterface {

View File

@ -0,0 +1,158 @@
package handlers
import (
"config-lsp/handlers/aliases/ast"
"config-lsp/handlers/aliases/indexes"
"config-lsp/utils"
"fmt"
"strings"
)
// Get hover information for an alias entry
// Expects `entry` to contain at least a key
func GetAliasHoverInfo(
i indexes.AliasesIndexes,
entry ast.AliasEntry,
) string {
header := []string{
fmt.Sprintf("Emails targeted for `%s` will be passed to:", entry.Key.Value),
"",
}
var forwards []string
if entry.Values == nil {
forwards = []string{
"No forwards configured",
}
} else {
if len(entry.Values.Values) == 1 {
forwards = []string{
GetAliasValueHoverInfo(
i,
entry.Values.Values[0],
),
}
} else {
forwards = utils.Map(
entry.Values.Values,
func(value ast.AliasValueInterface) string {
return fmt.Sprintf(
"* %s",
GetAliasValueHoverInfo(
i,
value,
),
)
},
)
}
}
content := append(header, forwards...)
return strings.Join(
content,
"\n",
)
}
func GetAliasValueHoverInfo(
i indexes.AliasesIndexes,
value ast.AliasValueInterface,
) string {
switch value.(type) {
case ast.AliasValueUser:
return fmt.Sprintf("User: **%s**", value.GetAliasValue().Value)
case ast.AliasValueEmail:
return fmt.Sprintf("Email: **%s**", value.GetAliasValue().Value)
case ast.AliasValueInclude:
includeValue := value.(ast.AliasValueInclude)
return fmt.Sprintf("Included file: `%s`", string(includeValue.Path.Path))
case ast.AliasValueFile:
fileValue := value.(ast.AliasValueFile)
return fmt.Sprintf("File: Email will be written to `%s`", string(fileValue.Path))
case ast.AliasValueCommand:
commandValue := value.(ast.AliasValueCommand)
return fmt.Sprintf("Command: Will be passed as stdin to `%s`", commandValue.Command)
case ast.AliasValueError:
errorValue := value.(ast.AliasValueError)
if errorValue.Code == nil || errorValue.Message == nil {
return "Error: An error will show up"
}
return fmt.Sprintf(
"Error: An error will show up; code: **%s** (%s), message: '%s'",
errorValue.Code.Value,
getErrorCodeInfo(errorValue.Code.ErrorCodeAsInt()),
errorValue.Message.Value,
)
}
panic("Unknown value type")
}
func GetAliasValueTypeInfo(
value ast.AliasValueInterface,
) []string {
switch value.(type) {
case ast.AliasValueUser:
return []string{
"### User",
"`user`",
"",
"A user on the host machine. The user must have a valid entry in the passwd(5) database file.",
}
case ast.AliasValueEmail:
return []string{
"### Email",
"`user-part@domain-part`",
"",
"An email address in RFC 5322 format. If an address extension is appended to the user-part, it is first compared for an exact match. It is then stripped so that an address such as user+ext@example.com will only use the part that precedes + as a key.",
}
case ast.AliasValueInclude:
return []string{
"### Include",
"`include:/path/to/file`",
"",
"Include any definitions in file as alias entries. The format of the file is identical to this one.",
}
case ast.AliasValueFile:
return []string{
"### File",
"`/path/to/file`",
"",
"Append messages to file, specified by its absolute pathname.",
}
case ast.AliasValueCommand:
return []string{
"### Command",
"`|command`",
"",
"Pipe the message to command on its standard input. The command is run under the privileges of the daemon's unprivileged account.",
}
case ast.AliasValueError:
return []string{
"### Error",
"`error:code message`",
"",
"A status code and message to return. The code must be 3 digits, starting 4XX (TempFail) or 5XX (PermFail). The message must be present and can be freely chosen.",
}
}
panic("Unknown value type")
}
func getErrorCodeInfo(
code uint16,
) string {
if code >= 400 && code <= 499 {
return "4XX: TempFail"
}
if code >= 500 && code <= 599 {
return "5XX: PermFail"
}
return "Unknown code"
}

View File

@ -1,7 +1,10 @@
package lsp package lsp
import ( import (
"config-lsp/handlers/hosts" "config-lsp/handlers/aliases"
"config-lsp/handlers/aliases/ast"
"config-lsp/handlers/aliases/handlers"
"strings"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
@ -11,76 +14,51 @@ func TextDocumentHover(
context *glsp.Context, context *glsp.Context,
params *protocol.HoverParams, params *protocol.HoverParams,
) (*protocol.Hover, error) { ) (*protocol.Hover, error) {
document := hosts.DocumentParserMap[params.TextDocument.URI] document := aliases.DocumentParserMap[params.TextDocument.URI]
line := params.Position.Line line := params.Position.Line
// character := params.Position.Character character := params.Position.Character
if _, found := document.Parser.CommentLines[line]; found { if _, found := document.Parser.CommentLines[line]; found {
// Comment // Comment
return nil, nil return nil, nil
} }
// entry, found := document.Parser.Tree.Entries[line] rawEntry, found := document.Parser.Aliases.Get(line)
//
// if !found { if !found {
// // Empty line return nil, nil
// return nil, nil }
// }
// entry := rawEntry.(*ast.AliasEntry)
// target := handlers.GetHoverTargetInEntry(*entry, character)
// if entry.Key != nil && character >= entry.Key.Location.Start.Character && character <= entry.Key.Location.End.Character {
// var hostname *ast.HostsHostname text := handlers.GetAliasHoverInfo(*document.Indexes, *entry)
//
// switch *target { return &protocol.Hover{
// case handlers.HoverTargetIPAddress: Contents: text,
// relativeCursor := character - entry.IPAddress.Location.Start.Character }, nil
// hover := fields.IPAddressField.FetchHoverInfo(entry.IPAddress.Value.String(), relativeCursor) }
//
// return &protocol.Hover{ if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character {
// Contents: hover, value := handlers.GetValueAtCursor(character, entry)
// }, nil
// case handlers.HoverTargetHostname: if value == nil {
// hostname = entry.Hostname return nil, nil
// case handlers.HoverTargetAlias: }
// for _, alias := range entry.Aliases {
// if alias.Location.Start.Character <= character && character <= alias.Location.End.Character { contents := []string{}
// hostname = alias contents = append(contents, handlers.GetAliasValueTypeInfo(*value)...)
// break contents = append(contents, "")
// } contents = append(contents, "#### Value")
// } contents = append(contents, handlers.GetAliasValueHoverInfo(*document.Indexes, *value))
// }
// text := strings.Join(contents, "\n")
// if hostname != nil {
// contents := []string{ return &protocol.Hover{
// "## Hostname", Contents: text,
// } }, nil
// contents = append( }
// contents,
// fields.HostnameField.GetTypeDescription()...,
// )
// contents = append(
// contents,
// []string{
// "",
// }...,
// )
// contents = append(
// contents,
// fields.HostnameField.Documentation,
// )
// contents = append(
// contents,
// handlers.GetHoverInfoForHostname(*document, *hostname, character)...,
// )
//
// return &protocol.Hover{
// Contents: &protocol.MarkupContent{
// Kind: protocol.MarkupKindMarkdown,
// Value: strings.Join(contents, "\n"),
// },
// }, nil
// }
return nil, nil return nil, nil
} }