From d56eef43bf4a082825db72fce20716aa157675b3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 18:02:34 +0200 Subject: [PATCH] feat(aliases): Add support for renaming --- handlers/aliases/handlers/go_to_definition.go | 2 +- handlers/aliases/handlers/rename.go | 33 ++++++++++ handlers/aliases/handlers/rename_test.go | 48 +++++++++++++++ handlers/aliases/indexes/indexes.go | 22 ++++++- handlers/aliases/indexes/indexes_test.go | 49 +++++++++++++++ handlers/aliases/lsp/text-document-rename.go | 60 +++++++++++++++++++ root-handler/handler.go | 1 + root-handler/text-document-rename.go | 36 +++++++++++ 8 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 handlers/aliases/handlers/rename.go create mode 100644 handlers/aliases/handlers/rename_test.go create mode 100644 handlers/aliases/indexes/indexes_test.go create mode 100644 handlers/aliases/lsp/text-document-rename.go create mode 100644 root-handler/text-document-rename.go diff --git a/handlers/aliases/handlers/go_to_definition.go b/handlers/aliases/handlers/go_to_definition.go index e7ab96c..31d43e7 100644 --- a/handlers/aliases/handlers/go_to_definition.go +++ b/handlers/aliases/handlers/go_to_definition.go @@ -23,7 +23,7 @@ func GetDefinitionLocationForValue( return []protocol.Location{ { URI: params.TextDocument.URI, - Range: entry.Location.ToLSPRange(), + Range: entry.Key.Location.ToLSPRange(), }, } } diff --git a/handlers/aliases/handlers/rename.go b/handlers/aliases/handlers/rename.go new file mode 100644 index 0000000..9b1cfab --- /dev/null +++ b/handlers/aliases/handlers/rename.go @@ -0,0 +1,33 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func RenameAlias( + i indexes.AliasesIndexes, + oldEntry *ast.AliasEntry, + newName string, +) []protocol.TextEdit { + occurrences := i.UserOccurrences[indexes.NormalizeKey(oldEntry.Key.Value)] + changes := make([]protocol.TextEdit, 0, len(occurrences)) + + // Own rename + changes = append(changes, protocol.TextEdit{ + Range: oldEntry.Key.Location.ToLSPRange(), + NewText: newName, + }) + + // Other AliasValueUser occurrences + for _, value := range occurrences { + changes = append(changes, protocol.TextEdit{ + Range: value.Location.ToLSPRange(), + NewText: newName, + }) + } + + return changes +} diff --git a/handlers/aliases/handlers/rename_test.go b/handlers/aliases/handlers/rename_test.go new file mode 100644 index 0000000..601a37a --- /dev/null +++ b/handlers/aliases/handlers/rename_test.go @@ -0,0 +1,48 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + "testing" +) + +func TestComplexExample( + t *testing.T, +) { + input := utils.Dedent(` +alice: alice +bob: root +support: alice, bob +`) + parser := ast.NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Unexpected errors: %v", errors) + } + + i, errors := indexes.CreateIndexes(parser) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, but got: %v", errors) + } + + edits := RenameAlias(i, i.Keys["alice"], "amelie") + + if !(len(edits) == 3) { + t.Errorf("Expected 2 edits, but got %v", len(edits)) + } + + if !(edits[0].Range.Start.Line == 0 && edits[0].Range.Start.Character == 0 && edits[0].Range.End.Line == 0 && edits[0].Range.End.Character == 5) { + t.Errorf("Unexpected edit: %v", edits[0]) + } + + if !(edits[1].Range.Start.Line == 0 && edits[1].Range.Start.Character == 7 && edits[1].Range.End.Line == 0 && edits[1].Range.End.Character == 12) { + t.Errorf("Unexpected edit: %v", edits[1]) + } + + if !(edits[2].Range.Start.Line == 2 && edits[2].Range.Start.Character == 9 && edits[2].Range.End.Line == 2 && edits[2].Range.End.Character == 14) { + t.Errorf("Unexpected edit: %v", edits[2]) + } +} diff --git a/handlers/aliases/indexes/indexes.go b/handlers/aliases/indexes/indexes.go index 359cf31..5d9cf10 100644 --- a/handlers/aliases/indexes/indexes.go +++ b/handlers/aliases/indexes/indexes.go @@ -8,7 +8,8 @@ import ( ) type AliasesIndexes struct { - Keys map[string]*ast.AliasKey + Keys map[string]*ast.AliasEntry + UserOccurrences map[string][]*ast.AliasValueUser } func NormalizeKey(key string) string { @@ -18,7 +19,8 @@ func NormalizeKey(key string) string { func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) { errors := make([]common.LSPError, 0) indexes := &AliasesIndexes{ - Keys: make(map[string]*ast.AliasKey), + Keys: make(map[string]*ast.AliasEntry), + UserOccurrences: make(map[string][]*ast.AliasValueUser), } it := parser.Aliases.Iterator() @@ -26,6 +28,20 @@ func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) for it.Next() { entry := it.Value().(*ast.AliasEntry) + if entry.Values != nil { + for _, value := range entry.Values.Values { + switch value.(type) { + case ast.AliasValueUser: + userValue := value.(ast.AliasValueUser) + + indexes.UserOccurrences[userValue.Value] = append( + indexes.UserOccurrences[userValue.Value], + &userValue, + ) + } + } + } + if entry.Key == nil || entry.Key.Value == "" { continue } @@ -44,7 +60,7 @@ func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) continue } - indexes.Keys[normalizedAlias] = entry.Key + indexes.Keys[normalizedAlias] = entry } return *indexes, errors diff --git a/handlers/aliases/indexes/indexes_test.go b/handlers/aliases/indexes/indexes_test.go new file mode 100644 index 0000000..d8d7891 --- /dev/null +++ b/handlers/aliases/indexes/indexes_test.go @@ -0,0 +1,49 @@ +package indexes + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/utils" + "testing" +) + +func TestComplexExample( + t *testing.T, +) { + input := utils.Dedent(` +postmaster: alice, bob +alice: root +bob: root +`) + parser := ast.NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Unexpected errors: %v", errors) + } + + indexes, errors := CreateIndexes(parser) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, but got: %v", errors) + } + + if !(len(indexes.Keys) == 3) { + t.Errorf("Expected 3 keys, but got %v", len(indexes.Keys)) + } + + if !(len(indexes.UserOccurrences) == 3) { + t.Errorf("Expected 3 user occurrences, but got %v", len(indexes.UserOccurrences)) + } + + if !(len(indexes.UserOccurrences["root"]) == 2) { + t.Errorf("Expected 2 occurrences of root, but got %v", len(indexes.UserOccurrences["root"])) + } + + if !(len(indexes.UserOccurrences["alice"]) == 1) { + t.Errorf("Expected 1 occurrence of alice, but got %v", len(indexes.UserOccurrences["alice"])) + } + + if !(len(indexes.UserOccurrences["bob"]) == 1) { + t.Errorf("Expected 1 occurrence of bob, but got %v", len(indexes.UserOccurrences["bob"])) + } +} diff --git a/handlers/aliases/lsp/text-document-rename.go b/handlers/aliases/lsp/text-document-rename.go new file mode 100644 index 0000000..be529ed --- /dev/null +++ b/handlers/aliases/lsp/text-document-rename.go @@ -0,0 +1,60 @@ +package lsp + +import ( + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/handlers" + "config-lsp/handlers/aliases/indexes" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, 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 character >= entry.Key.Location.Start.Character && character <= entry.Key.Location.End.Character { + changes := handlers.RenameAlias(*d.Indexes, entry, params.NewName) + + return &protocol.WorkspaceEdit{ + Changes: map[protocol.DocumentUri][]protocol.TextEdit{ + params.TextDocument.URI: changes, + }, + }, nil + } + + 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 + } + + switch (*rawValue).(type) { + case ast.AliasValueUser: + userValue := (*rawValue).(ast.AliasValueUser) + + definitionEntry := d.Indexes.Keys[indexes.NormalizeKey(userValue.Value)] + + changes := handlers.RenameAlias(*d.Indexes, definitionEntry, params.NewName) + + return &protocol.WorkspaceEdit{ + Changes: map[protocol.DocumentUri][]protocol.TextEdit{ + params.TextDocument.URI: changes, + }, + }, nil + } + } + + return nil, nil +} diff --git a/root-handler/handler.go b/root-handler/handler.go index ca19922..9b74ea4 100644 --- a/root-handler/handler.go +++ b/root-handler/handler.go @@ -29,6 +29,7 @@ func SetUpRootHandler() { TextDocumentCodeAction: TextDocumentCodeAction, TextDocumentDefinition: TextDocumentDefinition, WorkspaceExecuteCommand: WorkspaceExecuteCommand, + TextDocumentRename: TextDocumentRename, } server := server.NewServer(&lspHandler, lsName, false) diff --git a/root-handler/text-document-rename.go b/root-handler/text-document-rename.go new file mode 100644 index 0000000..5a41135 --- /dev/null +++ b/root-handler/text-document-rename.go @@ -0,0 +1,36 @@ +package roothandler + +import ( + aliases "config-lsp/handlers/aliases/lsp" + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, 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.TextDocumentRename(context, params) + } + + panic("root-handler/TextDocumentRename: unexpected language" + *language) +}