mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(aliases): Add signature help support; Improvements
This commit is contained in:
parent
e1af64f2c0
commit
2768f99d2f
@ -5,14 +5,50 @@ import (
|
||||
docvalues "config-lsp/doc-values"
|
||||
)
|
||||
|
||||
var UserField = docvalues.UserValue("", false)
|
||||
|
||||
var PathField = docvalues.PathValue{
|
||||
RequiredType: docvalues.PathTypeFile,
|
||||
var UserField = docvalues.DocumentationValue{
|
||||
Documentation: "A user on the host machine. The user must have a valid entry in the passwd(5) database file.",
|
||||
Value: docvalues.UserValue("", false),
|
||||
}
|
||||
|
||||
var CommandField = docvalues.StringValue{}
|
||||
var UserDeclaration = "`user`"
|
||||
|
||||
var EmailField = docvalues.RegexValue{
|
||||
Regex: *commondocumentation.EmailRegex,
|
||||
var PathField = docvalues.DocumentationValue{
|
||||
Documentation: "Append messages to file, specified by its absolute pathname",
|
||||
Value: docvalues.PathValue{
|
||||
RequiredType: docvalues.PathTypeFile,
|
||||
},
|
||||
}
|
||||
|
||||
var PathDeclaration = "`/path/to/file`"
|
||||
|
||||
var CommandField = docvalues.DocumentationValue{
|
||||
Documentation: "Pipe the message to command on its standard input. The command is run under the privileges of the daemon's unprivileged account.",
|
||||
Value: docvalues.StringValue{},
|
||||
}
|
||||
|
||||
var CommandDeclaration = "`|command`"
|
||||
|
||||
var EmailField = docvalues.DocumentationValue{
|
||||
Documentation: "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.",
|
||||
Value: docvalues.RegexValue{
|
||||
Regex: *commondocumentation.EmailRegex,
|
||||
},
|
||||
}
|
||||
|
||||
var EmailDeclaration = "`user-part@domain-part`"
|
||||
|
||||
var IncludeField = docvalues.DocumentationValue{
|
||||
Documentation: "Include any definitions in file as alias entries. The format of the file is identical to this one.",
|
||||
Value: docvalues.PathValue{
|
||||
RequiredType: docvalues.PathTypeFile,
|
||||
},
|
||||
}
|
||||
|
||||
var IncludeDeclaration = "`include:/path/to/file`"
|
||||
|
||||
var ErrorMessageField = docvalues.DocumentationValue{
|
||||
Documentation: "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.",
|
||||
Value: docvalues.StringValue{},
|
||||
}
|
||||
|
||||
var ErrorDeclaration = "`error:code message`"
|
||||
|
@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
"config-lsp/handlers/aliases/fields"
|
||||
"config-lsp/handlers/aliases/indexes"
|
||||
"config-lsp/utils"
|
||||
"fmt"
|
||||
@ -100,44 +101,44 @@ func GetAliasValueTypeInfo(
|
||||
case ast.AliasValueUser:
|
||||
return []string{
|
||||
"### User",
|
||||
"`user`",
|
||||
fields.UserDeclaration,
|
||||
"",
|
||||
"A user on the host machine. The user must have a valid entry in the passwd(5) database file.",
|
||||
fields.UserField.Documentation,
|
||||
}
|
||||
case ast.AliasValueEmail:
|
||||
return []string{
|
||||
"### Email",
|
||||
"`user-part@domain-part`",
|
||||
fields.EmailDeclaration,
|
||||
"",
|
||||
"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.",
|
||||
fields.EmailField.Documentation,
|
||||
}
|
||||
case ast.AliasValueInclude:
|
||||
return []string{
|
||||
"### Include",
|
||||
"`include:/path/to/file`",
|
||||
fields.IncludeDeclaration,
|
||||
"",
|
||||
"Include any definitions in file as alias entries. The format of the file is identical to this one.",
|
||||
fields.IncludeField.Documentation,
|
||||
}
|
||||
case ast.AliasValueFile:
|
||||
return []string{
|
||||
"### File",
|
||||
"`/path/to/file`",
|
||||
fields.PathDeclaration,
|
||||
"",
|
||||
"Append messages to file, specified by its absolute pathname.",
|
||||
fields.PathField.Documentation,
|
||||
}
|
||||
case ast.AliasValueCommand:
|
||||
return []string{
|
||||
"### Command",
|
||||
"`|command`",
|
||||
fields.CommandDeclaration,
|
||||
"",
|
||||
"Pipe the message to command on its standard input. The command is run under the privileges of the daemon's unprivileged account.",
|
||||
fields.CommandField.Documentation,
|
||||
}
|
||||
case ast.AliasValueError:
|
||||
return []string{
|
||||
"### Error",
|
||||
"`error:code message`",
|
||||
fields.ErrorDeclaration,
|
||||
"",
|
||||
"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.",
|
||||
fields.ErrorMessageField.Documentation,
|
||||
}
|
||||
}
|
||||
|
||||
|
309
handlers/aliases/handlers/signature_help.go
Normal file
309
handlers/aliases/handlers/signature_help.go
Normal file
@ -0,0 +1,309 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
"strings"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func GetRootSignatureHelp(
|
||||
activeParameter uint32,
|
||||
) *protocol.SignatureHelp {
|
||||
index := uint32(0)
|
||||
return &protocol.SignatureHelp{
|
||||
ActiveSignature: &index,
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "<alias>: <value1>, <value2>, ...",
|
||||
ActiveParameter: &activeParameter,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<alias>") + 1),
|
||||
},
|
||||
Documentation: "The alias to define",
|
||||
},
|
||||
{
|
||||
Label: []uint32{
|
||||
uint32(len("<alias>:")),
|
||||
uint32(len("<alias>:") + len("<value1>")),
|
||||
},
|
||||
Documentation: "A value to associate with the alias",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func GetAllValuesSignatureHelp() *protocol.SignatureHelp {
|
||||
index := uint32(0)
|
||||
return &protocol.SignatureHelp{
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "<user>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<user>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: "<user>@<host>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<user>")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: []uint32{
|
||||
uint32(len("<user>@")),
|
||||
uint32(len("<user>@<host>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: "<file>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<file>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: ":include:<file>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len(":include:")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: "|<command>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: "error:<code> <message>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("error")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func GetValueSignatureHelp(
|
||||
value ast.AliasValueInterface,
|
||||
cursor uint32,
|
||||
) *protocol.SignatureHelp {
|
||||
switch value.(type) {
|
||||
case ast.AliasValueUser:
|
||||
index := uint32(0)
|
||||
return &protocol.SignatureHelp{
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "<user>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<user>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: "<user>@<host>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<user>")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: []uint32{
|
||||
uint32(len("<user>@")),
|
||||
uint32(len("<user>@") + len("<host>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
case ast.AliasValueEmail:
|
||||
isBeforeAtSymbol := cursor <= uint32(strings.Index(value.GetAliasValue().Value, "@"))
|
||||
|
||||
var index uint32
|
||||
|
||||
if isBeforeAtSymbol {
|
||||
index = 0
|
||||
} else {
|
||||
index = 1
|
||||
}
|
||||
|
||||
return &protocol.SignatureHelp{
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "<user>@<host>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<user>")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: []uint32{
|
||||
uint32(len("<user>@")),
|
||||
uint32(len("<user>@") + len("<host>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
case ast.AliasValueFile:
|
||||
index := uint32(0)
|
||||
return &protocol.SignatureHelp{
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "<file>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("<file>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
case ast.AliasValueInclude:
|
||||
index := uint32(0)
|
||||
return &protocol.SignatureHelp{
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "include:<file>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
uint32(len("include:")),
|
||||
uint32(len("include:<file>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
case ast.AliasValueCommand:
|
||||
var index uint32
|
||||
|
||||
if cursor == 0 {
|
||||
index = 0
|
||||
} else {
|
||||
index = 1
|
||||
}
|
||||
|
||||
return &protocol.SignatureHelp{
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "|<command>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: []uint32{
|
||||
1,
|
||||
uint32(1 + len("<command>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
case ast.AliasValueError:
|
||||
errorValue := value.(ast.AliasValueError)
|
||||
var index uint32
|
||||
|
||||
if errorValue.Code == nil || cursor <= errorValue.Code.Location.End.Character {
|
||||
index = 1
|
||||
} else {
|
||||
index = 2
|
||||
}
|
||||
|
||||
return &protocol.SignatureHelp{
|
||||
Signatures: []protocol.SignatureInformation{
|
||||
{
|
||||
Label: "error:<code> <message>",
|
||||
ActiveParameter: &index,
|
||||
Parameters: []protocol.ParameterInformation{
|
||||
{
|
||||
Label: []uint32{
|
||||
0,
|
||||
uint32(len("error:")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: []uint32{
|
||||
uint32(len("error:")),
|
||||
uint32(len("error:<code>")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: []uint32{
|
||||
uint32(len("error:<code> ")),
|
||||
uint32(len("error:<code> <message>")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
52
handlers/aliases/lsp/text-document-signature-help.go
Normal file
52
handlers/aliases/lsp/text-document-signature-help.go
Normal file
@ -0,0 +1,52 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/aliases"
|
||||
"config-lsp/handlers/aliases/ast"
|
||||
"config-lsp/handlers/aliases/handlers"
|
||||
|
||||
"github.com/tliron/glsp"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func TextDocumentSignatureHelp(context *glsp.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
|
||||
document := aliases.DocumentParserMap[params.TextDocument.URI]
|
||||
|
||||
line := params.Position.Line
|
||||
character := params.Position.Character
|
||||
|
||||
if _, found := document.Parser.CommentLines[line]; found {
|
||||
// Comment
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rawEntry, found := document.Parser.Aliases.Get(line)
|
||||
|
||||
if !found {
|
||||
return handlers.GetRootSignatureHelp(0), nil
|
||||
}
|
||||
|
||||
entry := rawEntry.(*ast.AliasEntry)
|
||||
|
||||
if entry.Key != nil && character >= entry.Key.Location.Start.Character && character <= entry.Key.Location.End.Character {
|
||||
return handlers.GetRootSignatureHelp(0), nil
|
||||
}
|
||||
|
||||
if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character {
|
||||
value := handlers.GetValueAtCursor(character, entry)
|
||||
|
||||
if value == nil {
|
||||
// For some reason, this does not really work,
|
||||
// When we return all, and then a user value is entered
|
||||
// and the `GetValueSignatureHelp` is called, still the old
|
||||
// signatures with all signature are shown
|
||||
// return handlers.GetAllValuesSignatureHelp(), nil
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return handlers.GetValueSignatureHelp(*value, character), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
@ -17,20 +17,21 @@ var lspHandler protocol.Handler
|
||||
func SetUpRootHandler() {
|
||||
rootHandler = NewRootHandler()
|
||||
lspHandler = protocol.Handler{
|
||||
Initialize: initialize,
|
||||
Initialized: initialized,
|
||||
Shutdown: shutdown,
|
||||
SetTrace: setTrace,
|
||||
TextDocumentDidOpen: TextDocumentDidOpen,
|
||||
TextDocumentDidChange: TextDocumentDidChange,
|
||||
TextDocumentCompletion: TextDocumentCompletion,
|
||||
TextDocumentHover: TextDocumentHover,
|
||||
TextDocumentDidClose: TextDocumentDidClose,
|
||||
TextDocumentCodeAction: TextDocumentCodeAction,
|
||||
TextDocumentDefinition: TextDocumentDefinition,
|
||||
WorkspaceExecuteCommand: WorkspaceExecuteCommand,
|
||||
TextDocumentRename: TextDocumentRename,
|
||||
Initialize: initialize,
|
||||
Initialized: initialized,
|
||||
Shutdown: shutdown,
|
||||
SetTrace: setTrace,
|
||||
TextDocumentDidOpen: TextDocumentDidOpen,
|
||||
TextDocumentDidChange: TextDocumentDidChange,
|
||||
TextDocumentCompletion: TextDocumentCompletion,
|
||||
TextDocumentHover: TextDocumentHover,
|
||||
TextDocumentDidClose: TextDocumentDidClose,
|
||||
TextDocumentCodeAction: TextDocumentCodeAction,
|
||||
TextDocumentDefinition: TextDocumentDefinition,
|
||||
WorkspaceExecuteCommand: WorkspaceExecuteCommand,
|
||||
TextDocumentRename: TextDocumentRename,
|
||||
TextDocumentPrepareRename: TextDocumentPrepareRename,
|
||||
TextDocumentSignatureHelp: TextDocumentSignatureHelp,
|
||||
}
|
||||
|
||||
server := server.NewServer(&lspHandler, lsName, false)
|
||||
@ -41,6 +42,15 @@ func SetUpRootHandler() {
|
||||
func initialize(context *glsp.Context, params *protocol.InitializeParams) (any, error) {
|
||||
capabilities := lspHandler.CreateServerCapabilities()
|
||||
capabilities.TextDocumentSync = protocol.TextDocumentSyncKindFull
|
||||
capabilities.SignatureHelpProvider = &protocol.SignatureHelpOptions{
|
||||
TriggerCharacters: []string{
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"_", "-", ".", "/", ":", "@", "#", "!", "$", "%", "^", "&", "*", "(", ")", "+", "=", "[", "]", "{", "}", "<", ">", "?", ";", ",", "|",
|
||||
" ",
|
||||
},
|
||||
}
|
||||
|
||||
if (*params.Capabilities.TextDocument.Rename.PrepareSupport) == true {
|
||||
// Client supports rename preparation
|
||||
|
@ -1,8 +1,8 @@
|
||||
package roothandler
|
||||
|
||||
import (
|
||||
"github.com/tliron/glsp"
|
||||
aliases "config-lsp/handlers/aliases/lsp"
|
||||
"github.com/tliron/glsp"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
@ -35,4 +35,3 @@ func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRe
|
||||
|
||||
panic("root-handler/TextDocumentPrepareRename: unexpected language" + *language)
|
||||
}
|
||||
|
||||
|
37
root-handler/text-document-signature-help.go
Normal file
37
root-handler/text-document-signature-help.go
Normal file
@ -0,0 +1,37 @@
|
||||
package roothandler
|
||||
|
||||
import (
|
||||
aliases "config-lsp/handlers/aliases/lsp"
|
||||
|
||||
"github.com/tliron/glsp"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func TextDocumentSignatureHelp(context *glsp.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
|
||||
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI)
|
||||
|
||||
if language == nil {
|
||||
showParseError(
|
||||
context,
|
||||
params.TextDocument.URI,
|
||||
undetectableError,
|
||||
)
|
||||
|
||||
return nil, undetectableError.Err
|
||||
}
|
||||
|
||||
switch *language {
|
||||
case LanguageHosts:
|
||||
return nil, nil
|
||||
case LanguageSSHDConfig:
|
||||
return nil, nil
|
||||
case LanguageFstab:
|
||||
return nil, nil
|
||||
case LanguageWireguard:
|
||||
return nil, nil
|
||||
case LanguageAliases:
|
||||
return aliases.TextDocumentSignatureHelp(context, params)
|
||||
}
|
||||
|
||||
panic("root-handler/TextDocumentSignatureHelp: unexpected language" + *language)
|
||||
}
|
@ -51,3 +51,9 @@ func CountCharacterOccurrences(line string, character rune) int {
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
var emptyRegex = regexp.MustCompile(`^\s*$`)
|
||||
|
||||
func IsEmpty(s string) bool {
|
||||
return emptyRegex.MatchString(s)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user