mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(ssh_config): Add code actiont o add option to unknown option
This commit is contained in:
parent
bf54705e93
commit
cd23ffcb7f
@ -7,6 +7,12 @@ import (
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
var DefaultFormattingOptions = protocol.FormattingOptions{
|
||||
"tabSize": float64(4),
|
||||
"insertSpaces": false,
|
||||
"trimTrailingWhitespace": true,
|
||||
}
|
||||
|
||||
type FormatTemplate string
|
||||
|
||||
func (f FormatTemplate) Format(
|
||||
@ -84,7 +90,10 @@ func surroundWithQuotes(s string) string {
|
||||
|
||||
innerValue := value[startPosition:endPosition]
|
||||
|
||||
if strings.Contains(innerValue, " ") {
|
||||
if innerValue[0] == '"' && innerValue[len(innerValue)-1] == '"' && (len(innerValue) >= 2 || innerValue[len(innerValue)-2] != '\\') {
|
||||
// Already surrounded
|
||||
value = value[:startPosition-3] + innerValue + value[endPosition+3:]
|
||||
} else if strings.Contains(innerValue, " ") {
|
||||
value = value[:startPosition-3] + "\"" + innerValue + "\"" + value[endPosition+3:]
|
||||
} else {
|
||||
value = value[:startPosition-3] + innerValue + value[endPosition+3:]
|
||||
|
@ -116,3 +116,22 @@ func TestSurroundWithQuotesButNoSpaceExample(
|
||||
t.Errorf("Expected %q but got %q", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSurroundWithQuotesButAlreadySurrounded(
|
||||
t *testing.T,
|
||||
) {
|
||||
template := FormatTemplate("%s /!'%s/!'")
|
||||
|
||||
options := protocol.FormattingOptions{
|
||||
"tabSize": float64(4),
|
||||
"insertSpaces": false,
|
||||
"trimTrailingWhitespace": true,
|
||||
}
|
||||
|
||||
result := template.Format(options, "PermitRootLogin", `"Hello World"`)
|
||||
expected := `PermitRootLogin "Hello World"`
|
||||
|
||||
if result != expected {
|
||||
t.Errorf("Expected %q but got %q", expected, result)
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func analyzeValuesAreValid(
|
||||
Severity: &common.SeverityError,
|
||||
},
|
||||
)
|
||||
ctx.document.Indexes.UnknownOptions = append(ctx.document.Indexes.UnknownOptions, info)
|
||||
ctx.document.Indexes.UnknownOptions[info.Option.Start.Line] = info
|
||||
|
||||
continue
|
||||
}
|
||||
|
184
handlers/ssh_config/handlers/code-actions.go
Normal file
184
handlers/ssh_config/handlers/code-actions.go
Normal file
@ -0,0 +1,184 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"config-lsp/common/formatting"
|
||||
sshconfig "config-lsp/handlers/ssh_config"
|
||||
"config-lsp/handlers/ssh_config/ast"
|
||||
"fmt"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
type codeActionName string
|
||||
|
||||
const (
|
||||
CodeActionAddToUnknown codeActionName = "addToUnknown"
|
||||
)
|
||||
|
||||
type codeAction interface {
|
||||
RunCommand(*sshconfig.SSHDocument) (*protocol.ApplyWorkspaceEditParams, error)
|
||||
}
|
||||
|
||||
type codeActionArgs interface{}
|
||||
|
||||
type codeActionAddToUnknownArgs struct {
|
||||
URI protocol.DocumentUri
|
||||
// Where the option is defined
|
||||
OptionLine uint32
|
||||
// Where the block is defined, if nil, option is globally defined
|
||||
BlockLine *uint32
|
||||
}
|
||||
|
||||
func CodeActionAddToUnknownArgsFromArguments(arguments map[string]interface{}) codeActionAddToUnknownArgs {
|
||||
var blockLine *uint32
|
||||
|
||||
if arguments["BlockLine"] != nil {
|
||||
blockLineValue := uint32(arguments["BlockLine"].(float64))
|
||||
blockLine = &blockLineValue
|
||||
}
|
||||
|
||||
return codeActionAddToUnknownArgs{
|
||||
URI: arguments["URI"].(protocol.DocumentUri),
|
||||
OptionLine: uint32(arguments["OptionLine"].(float64)),
|
||||
BlockLine: blockLine,
|
||||
}
|
||||
}
|
||||
|
||||
var addToUnknownOptionTemplate = formatting.FormatTemplate("/!'%s/!'")
|
||||
|
||||
func (args codeActionAddToUnknownArgs) RunCommand(d *sshconfig.SSHDocument) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||
var option *ast.SSHOption
|
||||
var block ast.SSHBlock
|
||||
|
||||
// Either this or `insertionLine` must be set
|
||||
// `ignoreUnknownOption` is used if an `IgnoreUnknown` option is set already
|
||||
// `insertionLine` is used if no `IgnoreUnknown` option is set
|
||||
var ignoreUnknownOption *ast.SSHOption
|
||||
var insertionLine uint32
|
||||
|
||||
if args.BlockLine == nil {
|
||||
// Global
|
||||
rawOption, found := d.Config.Options.Get(args.OptionLine)
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("No option found at line %d", args.OptionLine)
|
||||
}
|
||||
|
||||
option = rawOption.(*ast.SSHOption)
|
||||
|
||||
if ignoreOption, found := d.Indexes.IgnoredOptions[nil]; found {
|
||||
ignoreUnknownOption = ignoreOption.OptionValue
|
||||
} else {
|
||||
insertionLine = 0
|
||||
}
|
||||
} else {
|
||||
// Block
|
||||
rawBlock, found := d.Config.Options.Get(*args.BlockLine)
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("No block found at line %d", *args.BlockLine)
|
||||
}
|
||||
|
||||
block = rawBlock.(ast.SSHBlock)
|
||||
|
||||
rawOption, found := block.GetOptions().Get(args.OptionLine)
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("No option found at line %d", args.OptionLine)
|
||||
}
|
||||
|
||||
option = rawOption.(*ast.SSHOption)
|
||||
|
||||
if ignoreOption, found := d.Indexes.IgnoredOptions[block]; found {
|
||||
ignoreUnknownOption = ignoreOption.OptionValue
|
||||
} else {
|
||||
insertionLine = block.GetEntryOption().Start.Line + 1
|
||||
}
|
||||
}
|
||||
|
||||
rawOptionName := option.Key.Value.Raw
|
||||
println("rawOption:")
|
||||
println(rawOptionName)
|
||||
optionName := addToUnknownOptionTemplate.Format(formatting.DefaultFormattingOptions, rawOptionName)
|
||||
|
||||
// We got everything, let's build the edit!
|
||||
if ignoreUnknownOption == nil {
|
||||
// Insert a completely new IgnoreUnknown option
|
||||
|
||||
if block == nil {
|
||||
// Global
|
||||
label := fmt.Sprintf("Add %s to unknown options", option.Key.Key)
|
||||
return &protocol.ApplyWorkspaceEditParams{
|
||||
Label: &label,
|
||||
Edit: protocol.WorkspaceEdit{
|
||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||
args.URI: {
|
||||
{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: insertionLine,
|
||||
Character: 0,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: insertionLine,
|
||||
Character: 0,
|
||||
},
|
||||
},
|
||||
NewText: fmt.Sprintf("IgnoreUnknown %s\n", optionName),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
label := fmt.Sprintf("Add %s to unknown options", option.Key.Key)
|
||||
return &protocol.ApplyWorkspaceEditParams{
|
||||
Label: &label,
|
||||
Edit: protocol.WorkspaceEdit{
|
||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||
args.URI: {
|
||||
{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: insertionLine,
|
||||
Character: 0,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: insertionLine,
|
||||
Character: 0,
|
||||
},
|
||||
},
|
||||
NewText: fmt.Sprintf(" IgnoreUnknown %s\n", optionName),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
// Append to the existing IgnoreUnknown option
|
||||
label := fmt.Sprintf("Add %s to unknown options", option.Key.Key)
|
||||
return &protocol.ApplyWorkspaceEditParams{
|
||||
Label: &label,
|
||||
Edit: protocol.WorkspaceEdit{
|
||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||
args.URI: {
|
||||
{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: ignoreUnknownOption.Start.Line,
|
||||
Character: ignoreUnknownOption.End.Character,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: ignoreUnknownOption.Start.Line,
|
||||
Character: ignoreUnknownOption.End.Character,
|
||||
},
|
||||
},
|
||||
NewText: fmt.Sprintf(" %s", optionName),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
47
handlers/ssh_config/handlers/fetch-code-actions.go
Normal file
47
handlers/ssh_config/handlers/fetch-code-actions.go
Normal file
@ -0,0 +1,47 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
sshconfig "config-lsp/handlers/ssh_config"
|
||||
"fmt"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func FetchCodeActions(
|
||||
d *sshconfig.SSHDocument,
|
||||
params *protocol.CodeActionParams,
|
||||
) []protocol.CodeAction {
|
||||
line := params.Range.Start.Line
|
||||
|
||||
if unknownOption, found := d.Indexes.UnknownOptions[line]; found {
|
||||
var blockLine *uint32
|
||||
|
||||
if unknownOption.Block != nil {
|
||||
blockLineValue := uint32(unknownOption.Block.GetLocation().Start.Line)
|
||||
blockLine = &blockLineValue
|
||||
}
|
||||
|
||||
commandID := "sshconfig." + CodeActionAddToUnknown
|
||||
command := protocol.Command{
|
||||
Title: fmt.Sprintf("Add %s to unknown options", unknownOption.Option.Key.Key),
|
||||
Command: string(commandID),
|
||||
Arguments: []any{
|
||||
codeActionAddToUnknownArgs{
|
||||
URI: params.TextDocument.URI,
|
||||
OptionLine: unknownOption.Option.Start.Line,
|
||||
BlockLine: blockLine,
|
||||
},
|
||||
},
|
||||
}
|
||||
codeAction := &protocol.CodeAction{
|
||||
Title: fmt.Sprintf("Add %s to unknown options", unknownOption.Option.Key.Key),
|
||||
Command: &command,
|
||||
}
|
||||
|
||||
return []protocol.CodeAction{
|
||||
*codeAction,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -44,5 +44,6 @@ type SSHIndexes struct {
|
||||
// This is used for code actions.
|
||||
// This stores a list of unknown option, so that we can provide
|
||||
// a code action to add them to a "IgnoreUnknown" option
|
||||
UnknownOptions []ast.AllOptionInfo
|
||||
// This is a map of <line> to <option>
|
||||
UnknownOptions map[uint32]ast.AllOptionInfo
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ func NewSSHIndexes() *SSHIndexes {
|
||||
AllOptionsPerName: make(map[fields.NormalizedOptionName](map[ast.SSHBlock]([]*ast.SSHOption)), 0),
|
||||
Includes: make([]*SSHIndexIncludeLine, 0),
|
||||
IgnoredOptions: make(map[ast.SSHBlock]SSHIndexIgnoredUnknowns),
|
||||
UnknownOptions: make([]ast.AllOptionInfo, 0),
|
||||
UnknownOptions: make(map[uint32]ast.AllOptionInfo, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
# /sshd_config/lsp
|
||||
|
||||
This folder is the glue between our language server and the LSP
|
||||
clients.
|
||||
This folder only contains LSP commands.
|
||||
It only handles very little actual logic, and instead calls
|
||||
the handlers from `../handlers`.
|
17
handlers/ssh_config/lsp/text-document-code-action.go
Normal file
17
handlers/ssh_config/lsp/text-document-code-action.go
Normal file
@ -0,0 +1,17 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
sshconfig "config-lsp/handlers/ssh_config"
|
||||
"config-lsp/handlers/ssh_config/handlers"
|
||||
|
||||
"github.com/tliron/glsp"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
||||
println("TextDocumentCodeAction sshconfig")
|
||||
d := sshconfig.DocumentParserMap[params.TextDocument.URI]
|
||||
actions := handlers.FetchCodeActions(d, params)
|
||||
|
||||
return actions, nil
|
||||
}
|
25
handlers/ssh_config/lsp/workspace-execute-command.go
Normal file
25
handlers/ssh_config/lsp/workspace-execute-command.go
Normal file
@ -0,0 +1,25 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
sshconfig "config-lsp/handlers/ssh_config"
|
||||
"config-lsp/handlers/ssh_config/handlers"
|
||||
"strings"
|
||||
|
||||
"github.com/tliron/glsp"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteCommandParams) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||
_, command, _ := strings.Cut(params.Command, ".")
|
||||
|
||||
switch command {
|
||||
case string(handlers.CodeActionAddToUnknown):
|
||||
args := handlers.CodeActionAddToUnknownArgsFromArguments(params.Arguments[0].(map[string]any))
|
||||
|
||||
d := sshconfig.DocumentParserMap[args.URI]
|
||||
|
||||
return args.RunCommand(d)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
@ -3,6 +3,7 @@ package roothandler
|
||||
import (
|
||||
aliases "config-lsp/handlers/aliases/lsp"
|
||||
hosts "config-lsp/handlers/hosts/lsp"
|
||||
sshconfig "config-lsp/handlers/ssh_config/lsp"
|
||||
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||
|
||||
"github.com/tliron/glsp"
|
||||
@ -30,7 +31,7 @@ func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionPa
|
||||
case LanguageSSHDConfig:
|
||||
return nil, nil
|
||||
case LanguageSSHConfig:
|
||||
return nil, nil
|
||||
return sshconfig.TextDocumentCodeAction(context, params)
|
||||
case LanguageWireguard:
|
||||
return wireguard.TextDocumentCodeAction(context, params)
|
||||
case LanguageAliases:
|
||||
|
@ -3,6 +3,7 @@ package roothandler
|
||||
import (
|
||||
aliases "config-lsp/handlers/aliases/lsp"
|
||||
hosts "config-lsp/handlers/hosts/lsp"
|
||||
sshconfig "config-lsp/handlers/ssh_config/lsp"
|
||||
wireguard "config-lsp/handlers/wireguard/lsp"
|
||||
|
||||
"strings"
|
||||
@ -24,6 +25,8 @@ func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteComm
|
||||
edit, err = hosts.WorkspaceExecuteCommand(context, params)
|
||||
case "aliases":
|
||||
edit, err = aliases.WorkspaceExecuteCommand(context, params)
|
||||
case "sshconfig":
|
||||
edit, err = sshconfig.WorkspaceExecuteCommand(context, params)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user