diff --git a/common/fetchers.go b/common/fetchers.go index 040e61d..9243d45 100644 --- a/common/fetchers.go +++ b/common/fetchers.go @@ -10,6 +10,7 @@ type PasswdInfo struct { UID string GID string HomePath string + Line uint32 } var _cachedPasswdInfo []PasswdInfo @@ -28,7 +29,7 @@ func FetchPasswdInfo() ([]PasswdInfo, error) { lines := strings.Split(string(readBytes), "\n") infos := make([]PasswdInfo, 0) - for _, line := range lines { + for lineNumber, line := range lines { splitted := strings.Split(line, ":") if len(splitted) < 6 { @@ -40,6 +41,7 @@ func FetchPasswdInfo() ([]PasswdInfo, error) { UID: splitted[2], GID: splitted[3], HomePath: splitted[5], + Line: uint32(lineNumber), } infos = append(infos, info) diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index 61ca11d..91f424b 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -164,7 +164,7 @@ func getUsersFromEntry( switch (value).(type) { case ast.AliasValueUser: userValue := value.(ast.AliasValueUser) - + users[indexes.NormalizeKey(userValue.Value)] = struct{}{} } } @@ -172,4 +172,3 @@ func getUsersFromEntry( return users } - diff --git a/handlers/aliases/handlers/go_to_definition.go b/handlers/aliases/handlers/go_to_definition.go new file mode 100644 index 0000000..e7ab96c --- /dev/null +++ b/handlers/aliases/handlers/go_to_definition.go @@ -0,0 +1,94 @@ +package handlers + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func GetDefinitionLocationForValue( + i indexes.AliasesIndexes, + value ast.AliasValueInterface, + params *protocol.DefinitionParams, +) []protocol.Location { + switch value.(type) { + case ast.AliasValueUser: + userValue := value.(ast.AliasValueUser) + + // Own defined alias + if entry, found := i.Keys[indexes.NormalizeKey(userValue.Value)]; found { + return []protocol.Location{ + { + URI: params.TextDocument.URI, + Range: entry.Location.ToLSPRange(), + }, + } + } + + // System user + systemUsers, _ := getSystemUserMap() + if user, found := systemUsers[userValue.Value]; found { + return []protocol.Location{ + { + URI: "file:///etc/passwd", + Range: protocol.Range{ + Start: protocol.Position{ + Line: user.Line, + Character: 0, + }, + End: protocol.Position{ + Line: user.Line, + Character: uint32(len(user.Name)), + }, + }, + }, + } + } + case ast.AliasValueFile: + fileValue := value.(ast.AliasValueFile) + path := string(fileValue.Path) + + if utils.DoesPathExist(path) { + return []protocol.Location{ + { + URI: "file://" + path, + }, + } + } + case ast.AliasValueInclude: + includeValue := value.(ast.AliasValueInclude) + + if includeValue.Path != nil { + path := string(includeValue.Path.Path) + + if utils.DoesPathExist(path) { + return []protocol.Location{ + { + URI: "file://" + path, + }, + } + } + } + } + + return nil +} + +func getSystemUserMap() (map[string]common.PasswdInfo, error) { + users, err := common.FetchPasswdInfo() + + if err != nil { + return nil, err + } + + userMap := make(map[string]common.PasswdInfo) + + for _, user := range users { + userMap[user.Name] = user + } + + return userMap, nil +} diff --git a/handlers/aliases/lsp/text-document-definition.go b/handlers/aliases/lsp/text-document-definition.go new file mode 100644 index 0000000..67454ae --- /dev/null +++ b/handlers/aliases/lsp/text-document-definition.go @@ -0,0 +1,40 @@ +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 TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionParams) ([]protocol.Location, error) { + d := aliases.DocumentParserMap[params.TextDocument.URI] + character := params.Position.Character + line := params.Position.Line + + rawEntry, found := d.Parser.Aliases.Get(line) + + if !found { + return nil, nil + } + + entry := rawEntry.(*ast.AliasEntry) + + if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character { + rawValue := handlers.GetValueAtCursor(character, entry) + + if rawValue == nil { + return nil, nil + } + + return handlers.GetDefinitionLocationForValue( + *d.Indexes, + *rawValue, + params, + ), nil + } + + return nil, nil +} diff --git a/root-handler/handler.go b/root-handler/handler.go index 565413e..ca19922 100644 --- a/root-handler/handler.go +++ b/root-handler/handler.go @@ -27,6 +27,7 @@ func SetUpRootHandler() { TextDocumentHover: TextDocumentHover, TextDocumentDidClose: TextDocumentDidClose, TextDocumentCodeAction: TextDocumentCodeAction, + TextDocumentDefinition: TextDocumentDefinition, WorkspaceExecuteCommand: WorkspaceExecuteCommand, } diff --git a/root-handler/text-document-definition.go b/root-handler/text-document-definition.go new file mode 100644 index 0000000..194bcb8 --- /dev/null +++ b/root-handler/text-document-definition.go @@ -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 TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionParams) (any, 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.TextDocumentDefinition(context, params) + } + + panic("root-handler/TextDocumentDefinition: unexpected language" + *language) +}