mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(ssh_config): Add include functionality
This commit is contained in:
parent
584250ddbf
commit
fdb005a75d
@ -35,6 +35,25 @@ func Analyze(
|
|||||||
|
|
||||||
d.Indexes = i
|
d.Indexes = i
|
||||||
|
|
||||||
|
analyzeIncludeValues(ctx)
|
||||||
|
|
||||||
|
if len(ctx.diagnostics) == 0 {
|
||||||
|
for _, include := range d.Indexes.Includes {
|
||||||
|
for _, value := range include.Values {
|
||||||
|
for _, path := range value.Paths {
|
||||||
|
_, err := parseFile(string(path))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Range: value.LocationRange.ToLSPRange(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
analyzeValuesAreValid(ctx)
|
analyzeValuesAreValid(ctx)
|
||||||
analyzeIgnoreUnknownHasNoUnnecessary(ctx)
|
analyzeIgnoreUnknownHasNoUnnecessary(ctx)
|
||||||
analyzeDependents(ctx)
|
analyzeDependents(ctx)
|
||||||
|
110
server/handlers/ssh_config/analyzer/include.go
Normal file
110
server/handlers/ssh_config/analyzer/include.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
sshconfig "config-lsp/handlers/ssh_config"
|
||||||
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
|
"config-lsp/handlers/ssh_config/indexes"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
var whitespacePattern = regexp.MustCompile(`\S+`)
|
||||||
|
|
||||||
|
func analyzeIncludeValues(
|
||||||
|
ctx *analyzerContext,
|
||||||
|
) {
|
||||||
|
for _, include := range ctx.document.Indexes.Includes {
|
||||||
|
for _, value := range include.Values {
|
||||||
|
validPaths, err := createIncludePaths(value.Value)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Range: value.LocationRange.ToLSPRange(),
|
||||||
|
Message: err.Error(),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
value.Paths = validPaths
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIncludePaths(
|
||||||
|
suggestedPath string,
|
||||||
|
) ([]indexes.ValidPath, error) {
|
||||||
|
var absolutePath string
|
||||||
|
|
||||||
|
if path.IsAbs(suggestedPath) {
|
||||||
|
absolutePath = suggestedPath
|
||||||
|
} else {
|
||||||
|
homeFolder, err := os.UserHomeDir()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(fmt.Sprintf("Could not find home folder (error: %s)", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
absolutePath = path.Join(homeFolder, ".ssh", suggestedPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := filepath.Glob(absolutePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(fmt.Sprintf("Could not find file %s (error: %s)", absolutePath, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
return nil, errors.New(fmt.Sprintf("Could not find file %s", absolutePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.Map(
|
||||||
|
files,
|
||||||
|
func(file string) indexes.ValidPath {
|
||||||
|
return indexes.ValidPath(file)
|
||||||
|
},
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFile(
|
||||||
|
filePath string,
|
||||||
|
) (*sshconfig.SSHDocument, error) {
|
||||||
|
if d, ok := sshconfig.DocumentParserMap[filePath]; ok {
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := ast.NewSSHConfig()
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parseErrors := c.Parse(string(content))
|
||||||
|
|
||||||
|
if len(parseErrors) > 0 {
|
||||||
|
return nil, errors.New(fmt.Sprintf("Errors in %s", filePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &sshconfig.SSHDocument{
|
||||||
|
Config: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := Analyze(d)
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil, errors.New(fmt.Sprintf("Errors in %s", filePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
sshconfig.DocumentParserMap[filePath] = d
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
@ -44,7 +44,7 @@ type SSHIndexTagInfo struct {
|
|||||||
type SSHIndexes struct {
|
type SSHIndexes struct {
|
||||||
AllOptionsPerName map[fields.NormalizedOptionName](map[ast.SSHBlock]([]*ast.SSHOption))
|
AllOptionsPerName map[fields.NormalizedOptionName](map[ast.SSHBlock]([]*ast.SSHOption))
|
||||||
|
|
||||||
Includes []*SSHIndexIncludeLine
|
Includes map[uint32]*SSHIndexIncludeLine
|
||||||
|
|
||||||
BlockRanges map[uint32]ast.SSHBlock
|
BlockRanges map[uint32]ast.SSHBlock
|
||||||
|
|
||||||
@ -57,6 +57,8 @@ 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]SSHIndexTagInfo
|
// Map of available tags
|
||||||
|
Tags map[string]SSHIndexTagInfo
|
||||||
|
// Map of <Tag name> to <option per block>
|
||||||
TagImports map[string](map[ast.SSHBlock]*ast.SSHOption)
|
TagImports map[string](map[ast.SSHBlock]*ast.SSHOption)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ var whitespacePattern = regexp.MustCompile(`\S+`)
|
|||||||
func NewSSHIndexes() *SSHIndexes {
|
func NewSSHIndexes() *SSHIndexes {
|
||||||
return &SSHIndexes{
|
return &SSHIndexes{
|
||||||
AllOptionsPerName: make(map[fields.NormalizedOptionName](map[ast.SSHBlock]([]*ast.SSHOption))),
|
AllOptionsPerName: make(map[fields.NormalizedOptionName](map[ast.SSHBlock]([]*ast.SSHOption))),
|
||||||
Includes: make([]*SSHIndexIncludeLine, 0),
|
Includes: make(map[uint32]*SSHIndexIncludeLine),
|
||||||
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]SSHIndexTagInfo),
|
Tags: make(map[string]SSHIndexTagInfo),
|
||||||
|
@ -173,3 +173,48 @@ Match tagged myuser
|
|||||||
t.Errorf("Expected first tag import to be 'good_ip', but got %v", indexes.TagImports)
|
t.Errorf("Expected first tag import to be 'good_ip', but got %v", indexes.TagImports)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIncludeExample(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
PermitRootLogin yes
|
||||||
|
Include /etc/ssh/sshd_config.d/*.conf hello_world
|
||||||
|
`)
|
||||||
|
config := ast.NewSSHConfig()
|
||||||
|
errors := config.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("Expected no errors, but got %v", len(errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
indexes, errors := CreateIndexes(*config)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("Expected no errors, but got %v", len(errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(indexes.Includes) == 1) {
|
||||||
|
t.Fatalf("Expected 1 include, but got %v", len(indexes.Includes))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(indexes.Includes[1].Values) == 2) {
|
||||||
|
t.Fatalf("Expected 2 include path, but got %v", len(indexes.Includes[1].Values))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(indexes.Includes[1].Values[0].Value == "/etc/ssh/sshd_config.d/*.conf" &&
|
||||||
|
indexes.Includes[1].Values[0].Start.Line == 1 &&
|
||||||
|
indexes.Includes[1].Values[0].End.Line == 1 &&
|
||||||
|
indexes.Includes[1].Values[0].Start.Character == 8 &&
|
||||||
|
indexes.Includes[1].Values[0].End.Character == 37) {
|
||||||
|
t.Errorf("Expected '/etc/ssh/sshd_config.d/*.conf' on line 1, but got %v on line %v", indexes.Includes[1].Values[0].Value, indexes.Includes[1].Values[0].Start.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(indexes.Includes[1].Values[1].Value == "hello_world" &&
|
||||||
|
indexes.Includes[1].Values[1].Start.Line == 1 &&
|
||||||
|
indexes.Includes[1].Values[1].End.Line == 1 &&
|
||||||
|
indexes.Includes[1].Values[1].Start.Character == 38 &&
|
||||||
|
indexes.Includes[1].Values[1].End.Character == 49) {
|
||||||
|
t.Errorf("Expected 'hello_world' on line 1, but got %v on line %v", indexes.Includes[1].Values[1].Value, indexes.Includes[1].Values[1].Start.Line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user