mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(ssh_config): Add rename support; Improvements
This commit is contained in:
parent
413d919719
commit
15b732e136
@ -5,6 +5,7 @@ import (
|
|||||||
"config-lsp/common/formatting"
|
"config-lsp/common/formatting"
|
||||||
sshconfig "config-lsp/handlers/ssh_config"
|
sshconfig "config-lsp/handlers/ssh_config"
|
||||||
"config-lsp/handlers/ssh_config/ast"
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
|
"config-lsp/handlers/ssh_config/indexes"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -18,9 +19,9 @@ func getTagCompletions(
|
|||||||
) ([]protocol.CompletionItem, error) {
|
) ([]protocol.CompletionItem, error) {
|
||||||
return utils.MapMapToSlice(
|
return utils.MapMapToSlice(
|
||||||
d.Indexes.Tags,
|
d.Indexes.Tags,
|
||||||
func(name string, block *ast.SSHMatchBlock) protocol.CompletionItem {
|
func(name string, info indexes.SSHIndexTagInfo) protocol.CompletionItem {
|
||||||
kind := protocol.CompletionItemKindModule
|
kind := protocol.CompletionItemKindModule
|
||||||
text := renderMatchBlock(block)
|
text := renderMatchBlock(info.Block)
|
||||||
return protocol.CompletionItem{
|
return protocol.CompletionItem{
|
||||||
Label: name,
|
Label: name,
|
||||||
Kind: &kind,
|
Kind: &kind,
|
||||||
|
42
handlers/ssh_config/handlers/rename.go
Normal file
42
handlers/ssh_config/handlers/rename.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
sshconfig "config-lsp/handlers/ssh_config"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RenameTag(
|
||||||
|
params *protocol.RenameParams,
|
||||||
|
d *sshconfig.SSHDocument,
|
||||||
|
oldName string,
|
||||||
|
newName string,
|
||||||
|
) (*protocol.WorkspaceEdit, error) {
|
||||||
|
changes := make([]protocol.TextEdit, 0)
|
||||||
|
|
||||||
|
// tag rename
|
||||||
|
info, found := d.Indexes.Tags[oldName]
|
||||||
|
if !found {
|
||||||
|
return nil, errors.New("Tag could not be found")
|
||||||
|
}
|
||||||
|
|
||||||
|
changes = append(changes, protocol.TextEdit{
|
||||||
|
Range: info.EntryValue.ToLSPRange(),
|
||||||
|
NewText: newName,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Rename all occurrences
|
||||||
|
for _, option := range d.Indexes.TagImports[oldName] {
|
||||||
|
changes = append(changes, protocol.TextEdit{
|
||||||
|
Range: option.OptionValue.ToLSPRange(),
|
||||||
|
NewText: newName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
params.TextDocument.URI: changes,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
"config-lsp/handlers/ssh_config/ast"
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
"config-lsp/handlers/ssh_config/fields"
|
"config-lsp/handlers/ssh_config/fields"
|
||||||
|
matchparser "config-lsp/handlers/ssh_config/match-parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ValidPath string
|
type ValidPath string
|
||||||
@ -35,6 +36,11 @@ type SSHIndexIgnoredUnknowns struct {
|
|||||||
IgnoredOptions map[fields.NormalizedOptionName]SSHIndexIgnoredUnknownInfo
|
IgnoredOptions map[fields.NormalizedOptionName]SSHIndexIgnoredUnknownInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SSHIndexTagInfo struct {
|
||||||
|
EntryValue *matchparser.MatchValue
|
||||||
|
Block *ast.SSHMatchBlock
|
||||||
|
}
|
||||||
|
|
||||||
type SSHIndexes struct {
|
type SSHIndexes struct {
|
||||||
AllOptionsPerName map[fields.NormalizedOptionName](map[ast.SSHBlock]([]*ast.SSHOption))
|
AllOptionsPerName map[fields.NormalizedOptionName](map[ast.SSHBlock]([]*ast.SSHOption))
|
||||||
|
|
||||||
@ -51,5 +57,6 @@ type SSHIndexes struct {
|
|||||||
// This is a map of <line> to <option>
|
// This is a map of <line> to <option>
|
||||||
UnknownOptions map[uint32]ast.AllOptionInfo
|
UnknownOptions map[uint32]ast.AllOptionInfo
|
||||||
|
|
||||||
Tags map[string]*ast.SSHMatchBlock
|
Tags map[string]SSHIndexTagInfo
|
||||||
|
TagImports map[string](map[ast.SSHBlock]*ast.SSHOption)
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,14 @@ func NewSSHIndexes() *SSHIndexes {
|
|||||||
Includes: make([]*SSHIndexIncludeLine, 0),
|
Includes: make([]*SSHIndexIncludeLine, 0),
|
||||||
IgnoredOptions: make(map[ast.SSHBlock]SSHIndexIgnoredUnknowns),
|
IgnoredOptions: make(map[ast.SSHBlock]SSHIndexIgnoredUnknowns),
|
||||||
UnknownOptions: make(map[uint32]ast.AllOptionInfo),
|
UnknownOptions: make(map[uint32]ast.AllOptionInfo),
|
||||||
Tags: make(map[string]*ast.SSHMatchBlock),
|
Tags: make(map[string]SSHIndexTagInfo),
|
||||||
|
TagImports: make(map[string](map[ast.SSHBlock]*ast.SSHOption)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var includeOption = fields.CreateNormalizedName("Include")
|
var includeOption = fields.CreateNormalizedName("Include")
|
||||||
var matchOption = fields.CreateNormalizedName("Match")
|
var matchOption = fields.CreateNormalizedName("Match")
|
||||||
|
var tagOption = fields.CreateNormalizedName("Tag")
|
||||||
|
|
||||||
func CreateIndexes(config ast.SSHConfig) (*SSHIndexes, []common.LSPError) {
|
func CreateIndexes(config ast.SSHConfig) (*SSHIndexes, []common.LSPError) {
|
||||||
errs := make([]common.LSPError, 0)
|
errs := make([]common.LSPError, 0)
|
||||||
@ -106,14 +108,45 @@ func CreateIndexes(config ast.SSHConfig) (*SSHIndexes, []common.LSPError) {
|
|||||||
// Tag already exists
|
// Tag already exists
|
||||||
errs = append(errs, common.LSPError{
|
errs = append(errs, common.LSPError{
|
||||||
Range: entry.LocationRange,
|
Range: entry.LocationRange,
|
||||||
Err: fmt.Errorf("Tag %s has already been defined on line %d", name, existingBlock.Start.Line+1),
|
Err: fmt.Errorf("Tag %s has already been defined on line %d", name, existingBlock.Block.Start.Line+1),
|
||||||
})
|
})
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tag
|
// Add tag
|
||||||
indexes.Tags[name] = block
|
indexes.Tags[name] = SSHIndexTagInfo{
|
||||||
|
EntryValue: value,
|
||||||
|
Block: block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Tag imports
|
||||||
|
for block, options := range indexes.AllOptionsPerName[tagOption] {
|
||||||
|
for _, option := range options {
|
||||||
|
if option.OptionValue == nil || option.OptionValue.Value.Value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tagName := option.OptionValue.Value.Value
|
||||||
|
|
||||||
|
if tagImport, found := indexes.TagImports[tagName]; found {
|
||||||
|
if definedOption, found := tagImport[block]; found {
|
||||||
|
errs = append(errs, common.LSPError{
|
||||||
|
Range: option.OptionValue.LocationRange,
|
||||||
|
Err: fmt.Errorf("Tag %s has already been imported on line %d", tagName, definedOption.Start.Line+1),
|
||||||
|
})
|
||||||
|
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
tagImport[block] = option
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
indexes.TagImports[tagName] = map[ast.SSHBlock]*ast.SSHOption{
|
||||||
|
block: option,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package indexes
|
|||||||
import (
|
import (
|
||||||
"config-lsp/handlers/ssh_config/ast"
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -128,6 +127,7 @@ Match tagged good_ip
|
|||||||
|
|
||||||
Match tagged myuser
|
Match tagged myuser
|
||||||
User root
|
User root
|
||||||
|
Tag good_ip
|
||||||
`)
|
`)
|
||||||
|
|
||||||
config := ast.NewSSHConfig()
|
config := ast.NewSSHConfig()
|
||||||
@ -150,14 +150,26 @@ Match tagged myuser
|
|||||||
|
|
||||||
rawFirstMatch, _ := config.Options.Get(uint32(0))
|
rawFirstMatch, _ := config.Options.Get(uint32(0))
|
||||||
firstMatch := rawFirstMatch.(*ast.SSHMatchBlock)
|
firstMatch := rawFirstMatch.(*ast.SSHMatchBlock)
|
||||||
println(fmt.Sprintf("%v", indexes.Tags["good_ip"]))
|
if !(indexes.Tags["good_ip"].Block.Start.Line == firstMatch.Start.Line) {
|
||||||
if !(indexes.Tags["good_ip"].Start.Line == firstMatch.Start.Line) {
|
|
||||||
t.Errorf("Expected first tag to be 'good_ip', but got %v", indexes.Tags)
|
t.Errorf("Expected first tag to be 'good_ip', but got %v", indexes.Tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawSecondMatch, _ := config.Options.Get(uint32(3))
|
_, secondBlock := config.FindOption(uint32(3))
|
||||||
secondMatch := rawSecondMatch.(*ast.SSHMatchBlock)
|
secondMatch := secondBlock.(*ast.SSHMatchBlock)
|
||||||
if !(indexes.Tags["myuser"].Start.Line == secondMatch.Start.Line) {
|
if !(indexes.Tags["myuser"].Block.Start.Line == secondMatch.Start.Line) {
|
||||||
t.Errorf("Expected second tag to be 'myuser', but got %v", indexes.Tags)
|
t.Errorf("Expected second tag to be 'myuser', but got %v", indexes.Tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !(len(indexes.TagImports) == 1) {
|
||||||
|
t.Errorf("Expected 1 tag import, but got %v", indexes.TagImports)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(indexes.TagImports["good_ip"]) == 1) {
|
||||||
|
t.Errorf("Expected 1 tag import for 'good_ip', but got %v", indexes.TagImports["good_ip"])
|
||||||
|
}
|
||||||
|
|
||||||
|
tagOption, _ := config.FindOption(uint32(5))
|
||||||
|
if !(indexes.TagImports["good_ip"][secondBlock].Start.Line == tagOption.Start.Line) {
|
||||||
|
t.Errorf("Expected first tag import to be 'good_ip', but got %v", indexes.TagImports)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
||||||
println("TextDocumentCodeAction sshconfig")
|
|
||||||
d := sshconfig.DocumentParserMap[params.TextDocument.URI]
|
d := sshconfig.DocumentParserMap[params.TextDocument.URI]
|
||||||
actions := handlers.FetchCodeActions(d, params)
|
actions := handlers.FetchCodeActions(d, params)
|
||||||
|
|
||||||
|
@ -18,11 +18,11 @@ func TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionPa
|
|||||||
option, _ := d.Config.FindOption(line)
|
option, _ := d.Config.FindOption(line)
|
||||||
|
|
||||||
if option != nil && option.Key.Key == tagOption && option.OptionValue != nil {
|
if option != nil && option.Key.Key == tagOption && option.OptionValue != nil {
|
||||||
if block, found := d.Indexes.Tags[option.OptionValue.Value.Value]; found {
|
if info, found := d.Indexes.Tags[option.OptionValue.Value.Value]; found {
|
||||||
return []protocol.Location{
|
return []protocol.Location{
|
||||||
{
|
{
|
||||||
URI: params.TextDocument.URI,
|
URI: params.TextDocument.URI,
|
||||||
Range: block.ToLSPRange(),
|
Range: info.Block.ToLSPRange(),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
45
handlers/ssh_config/lsp/text-document-prepare-rename.go
Normal file
45
handlers/ssh_config/lsp/text-document-prepare-rename.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
sshconfig "config-lsp/handlers/ssh_config"
|
||||||
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
|
|
||||||
|
"github.com/tliron/glsp"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRenameParams) (any, error) {
|
||||||
|
d := sshconfig.DocumentParserMap[params.TextDocument.URI]
|
||||||
|
line := params.Position.Line
|
||||||
|
|
||||||
|
option, block := d.Config.FindOption(line)
|
||||||
|
|
||||||
|
if option == nil || option.Key == nil {
|
||||||
|
// Empty line
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if option.Key.Key == tagOption && option.OptionValue != nil {
|
||||||
|
return option.OptionValue.ToLSPRange(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if option.Key.Key == matchOption {
|
||||||
|
matchBlock := block.(*ast.SSHMatchBlock)
|
||||||
|
entry := matchBlock.MatchValue.GetEntryAtPosition(common.LSPCharacterAsIndexPosition(params.Position.Character))
|
||||||
|
|
||||||
|
if entry == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value := entry.GetValueAtPosition(common.LSPCharacterAsIndexPosition(params.Position.Character))
|
||||||
|
|
||||||
|
if value == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.ToLSPRange(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
58
handlers/ssh_config/lsp/text-document-rename.go
Normal file
58
handlers/ssh_config/lsp/text-document-rename.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/common/formatting"
|
||||||
|
sshconfig "config-lsp/handlers/ssh_config"
|
||||||
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
|
"config-lsp/handlers/ssh_config/fields"
|
||||||
|
"config-lsp/handlers/ssh_config/handlers"
|
||||||
|
|
||||||
|
"github.com/tliron/glsp"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nameTemplate = formatting.FormatTemplate("/!'%s/!'")
|
||||||
|
var matchOption = fields.CreateNormalizedName("Match")
|
||||||
|
|
||||||
|
func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
|
||||||
|
d := sshconfig.DocumentParserMap[params.TextDocument.URI]
|
||||||
|
index := common.LSPCharacterAsIndexPosition(params.Position.Character)
|
||||||
|
line := params.Position.Line
|
||||||
|
|
||||||
|
option, block := d.Config.FindOption(line)
|
||||||
|
|
||||||
|
if option != nil && option.OptionValue != nil && option.OptionValue.Value.Value != "" {
|
||||||
|
newName := nameTemplate.Format(formatting.DefaultFormattingOptions, params.NewName)
|
||||||
|
|
||||||
|
if option.Key.Key == tagOption {
|
||||||
|
return handlers.RenameTag(
|
||||||
|
params,
|
||||||
|
d,
|
||||||
|
option.OptionValue.Value.Value,
|
||||||
|
newName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if option.Key.Key == matchOption {
|
||||||
|
matchBlock := block.(*ast.SSHMatchBlock)
|
||||||
|
|
||||||
|
entry := matchBlock.MatchValue.GetEntryAtPosition(index)
|
||||||
|
|
||||||
|
if entry != nil {
|
||||||
|
value := entry.GetValueAtPosition(index)
|
||||||
|
|
||||||
|
if value != nil {
|
||||||
|
return handlers.RenameTag(
|
||||||
|
params,
|
||||||
|
d,
|
||||||
|
value.Value.Value,
|
||||||
|
newName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
@ -2,6 +2,8 @@ package roothandler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
aliases "config-lsp/handlers/aliases/lsp"
|
aliases "config-lsp/handlers/aliases/lsp"
|
||||||
|
sshconfig "config-lsp/handlers/ssh_config/lsp"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
@ -26,7 +28,7 @@ func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRe
|
|||||||
case LanguageSSHDConfig:
|
case LanguageSSHDConfig:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case LanguageSSHConfig:
|
case LanguageSSHConfig:
|
||||||
return nil, nil
|
return sshconfig.TextDocumentPrepareRename(context, params)
|
||||||
case LanguageFstab:
|
case LanguageFstab:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case LanguageWireguard:
|
case LanguageWireguard:
|
||||||
|
@ -2,6 +2,8 @@ package roothandler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
aliases "config-lsp/handlers/aliases/lsp"
|
aliases "config-lsp/handlers/aliases/lsp"
|
||||||
|
sshconfig "config-lsp/handlers/ssh_config/lsp"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
@ -25,7 +27,7 @@ func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*
|
|||||||
case LanguageSSHDConfig:
|
case LanguageSSHDConfig:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case LanguageSSHConfig:
|
case LanguageSSHConfig:
|
||||||
return nil, nil
|
return sshconfig.TextDocumentRename(context, params)
|
||||||
case LanguageFstab:
|
case LanguageFstab:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case LanguageWireguard:
|
case LanguageWireguard:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user