mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 15:05:28 +02:00
refactor(fstab): Overall refactoring to new style
This commit is contained in:
parent
716440cf4c
commit
41239654c8
30
server/handlers/fstab/analyzer/analyzer.go
Normal file
30
server/handlers/fstab/analyzer/analyzer.go
Normal file
@ -0,0 +1,30 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/fstab/shared"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
type analyzerContext struct {
|
||||
document *shared.FstabDocument
|
||||
diagnostics []protocol.Diagnostic
|
||||
}
|
||||
|
||||
func Analyze(
|
||||
document *shared.FstabDocument,
|
||||
) []protocol.Diagnostic {
|
||||
ctx := analyzerContext{
|
||||
document: document,
|
||||
}
|
||||
|
||||
analyzeFieldAreFilled(&ctx)
|
||||
|
||||
if len(ctx.diagnostics) > 0 {
|
||||
return ctx.diagnostics
|
||||
}
|
||||
|
||||
analyzeValuesAreValid(&ctx)
|
||||
|
||||
return ctx.diagnostics
|
||||
}
|
131
server/handlers/fstab/analyzer/fields.go
Normal file
131
server/handlers/fstab/analyzer/fields.go
Normal file
@ -0,0 +1,131 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func analyzeFieldAreFilled(
|
||||
ctx *analyzerContext,
|
||||
) {
|
||||
it := ctx.document.Config.Entries.Iterator()
|
||||
for it.Next() {
|
||||
entry := it.Value().(*ast.FstabEntry)
|
||||
|
||||
if entry.Fields.Spec == nil || entry.Fields.Spec.Value.Value == "" {
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: 0,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: 0,
|
||||
},
|
||||
},
|
||||
Message: "The spec field is missing",
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.Fields.MountPoint == nil || entry.Fields.MountPoint.Value.Value == "" {
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.Spec.End.Character,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.Spec.End.Character,
|
||||
},
|
||||
},
|
||||
Message: "The mount point field is missing",
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.Fields.FilesystemType == nil || entry.Fields.FilesystemType.Value.Value == "" {
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.MountPoint.End.Character,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.MountPoint.End.Character,
|
||||
},
|
||||
},
|
||||
Message: "The file system type field is missing",
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.Fields.Options == nil || entry.Fields.Options.Value.Value == "" {
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.FilesystemType.End.Character,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.FilesystemType.End.Character,
|
||||
},
|
||||
},
|
||||
Message: "The options field is missing",
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.Fields.Freq == nil || entry.Fields.Freq.Value.Value == "" {
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.Options.End.Character,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.Options.End.Character,
|
||||
},
|
||||
},
|
||||
Message: "The freq field is missing",
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.Fields.Pass == nil || entry.Fields.Pass.Value.Value == "" {
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.Freq.End.Character,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: entry.Fields.Start.Line,
|
||||
Character: entry.Fields.Freq.End.Character,
|
||||
},
|
||||
},
|
||||
Message: "The pass field is missing",
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
38
server/handlers/fstab/analyzer/fields_test.go
Normal file
38
server/handlers/fstab/analyzer/fields_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
testutils_test "config-lsp/handlers/fstab/test_utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFieldsMissingMountPoint(t *testing.T) {
|
||||
document := testutils_test.DocumentFromInput(t, `
|
||||
LABEL=test
|
||||
`)
|
||||
|
||||
ctx := &analyzerContext{
|
||||
document: document,
|
||||
}
|
||||
|
||||
analyzeFieldAreFilled(ctx)
|
||||
|
||||
if len(ctx.diagnostics) != 1 {
|
||||
t.Fatalf("Expected 1 diagnostic, got %d", len(ctx.diagnostics))
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidExample(t *testing.T) {
|
||||
document := testutils_test.DocumentFromInput(t, `
|
||||
LABEL=test /mnt/test ext4 defaults 0 0
|
||||
`)
|
||||
|
||||
ctx := &analyzerContext{
|
||||
document: document,
|
||||
}
|
||||
|
||||
analyzeFieldAreFilled(ctx)
|
||||
|
||||
if len(ctx.diagnostics) != 0 {
|
||||
t.Fatalf("Expected 0 diagnostics, got %d", len(ctx.diagnostics))
|
||||
}
|
||||
}
|
46
server/handlers/fstab/analyzer/values.go
Normal file
46
server/handlers/fstab/analyzer/values.go
Normal file
@ -0,0 +1,46 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
docvalues "config-lsp/doc-values"
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
"config-lsp/handlers/fstab/fields"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func analyzeValuesAreValid(
|
||||
ctx *analyzerContext,
|
||||
) {
|
||||
it := ctx.document.Config.Entries.Iterator()
|
||||
|
||||
for it.Next() {
|
||||
entry := it.Value().(*ast.FstabEntry)
|
||||
|
||||
checkField(ctx, entry.Fields.Spec, fields.SpecField)
|
||||
checkField(ctx, entry.Fields.MountPoint, fields.MountPointField)
|
||||
checkField(ctx, entry.Fields.FilesystemType, fields.FileSystemTypeField)
|
||||
checkField(ctx, entry.Fields.Options, entry.GetMountOptionsField())
|
||||
checkField(ctx, entry.Fields.Freq, fields.FreqField)
|
||||
checkField(ctx, entry.Fields.Pass, fields.PassField)
|
||||
}
|
||||
}
|
||||
|
||||
func checkField(
|
||||
ctx *analyzerContext,
|
||||
field *ast.FstabField,
|
||||
docOption docvalues.DeprecatedValue,
|
||||
) {
|
||||
invalidValues := docOption.DeprecatedCheckIsValid(field.Value.Value)
|
||||
|
||||
for _, invalidValue := range invalidValues {
|
||||
err := docvalues.LSPErrorFromInvalidValue(field.Start.Line, *invalidValue)
|
||||
err.ShiftCharacter(field.Start.Character)
|
||||
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: err.Range.ToLSPRange(),
|
||||
Message: err.Err.Error(),
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
}
|
||||
}
|
61
server/handlers/fstab/analyzer/values_test.go
Normal file
61
server/handlers/fstab/analyzer/values_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
testutils_test "config-lsp/handlers/fstab/test_utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInvalidMountOptionsExample(
|
||||
t *testing.T,
|
||||
) {
|
||||
document := testutils_test.DocumentFromInput(t, `
|
||||
LABEL=test /mnt/test ext4 invalid 0 0
|
||||
`)
|
||||
|
||||
ctx := &analyzerContext{
|
||||
document: document,
|
||||
}
|
||||
|
||||
analyzeValuesAreValid(ctx)
|
||||
|
||||
if len(ctx.diagnostics) == 0 {
|
||||
t.Fatalf("Expected diagnostic, got %d", len(ctx.diagnostics))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExt4IsUsingBtrfsMountOption(
|
||||
t *testing.T,
|
||||
) {
|
||||
document := testutils_test.DocumentFromInput(t, `
|
||||
# Valid, but only for btrfs
|
||||
LABEL=test /mnt/test ext4 subvolid=1 0 0
|
||||
`)
|
||||
|
||||
ctx := &analyzerContext{
|
||||
document: document,
|
||||
}
|
||||
|
||||
analyzeValuesAreValid(ctx)
|
||||
|
||||
if len(ctx.diagnostics) == 0 {
|
||||
t.Fatalf("Expected diagnostic, got %d", len(ctx.diagnostics))
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidBtrfsIsUsingBtrfsMountOption(
|
||||
t *testing.T,
|
||||
) {
|
||||
document := testutils_test.DocumentFromInput(t, `
|
||||
LABEL=test /mnt/test btrfs subvolid=1 0 0
|
||||
`)
|
||||
|
||||
ctx := &analyzerContext{
|
||||
document: document,
|
||||
}
|
||||
|
||||
analyzeValuesAreValid(ctx)
|
||||
|
||||
if len(ctx.diagnostics) != 0 {
|
||||
t.Fatalf("Expected diagnostic, got %d", len(ctx.diagnostics))
|
||||
}
|
||||
}
|
@ -43,4 +43,3 @@ type FstabConfig struct {
|
||||
// [uint32]{} - line number to empty struct for comments
|
||||
CommentLines map[uint32]struct{}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
docvalues "config-lsp/doc-values"
|
||||
"config-lsp/handlers/fstab/fields"
|
||||
)
|
||||
|
||||
// func (c FstabConfig) GetEntry(line uint32) *FstabEntry {
|
||||
// entry, found := c.Entries.Get(line)
|
||||
//
|
||||
@ -10,26 +16,40 @@ package ast
|
||||
// return entry.(*FstabEntry)
|
||||
// }
|
||||
|
||||
func (e FstabEntry) GetFieldAtPosition(cursor uint32) FstabFieldName {
|
||||
if e.Fields.Spec == nil || (cursor >= e.Fields.Spec.Start.Character && cursor <= e.Fields.Spec.End.Character) {
|
||||
func (e FstabEntry) GetFieldAtPosition(position common.Position) FstabFieldName {
|
||||
if e.Fields.Spec == nil || (e.Fields.Spec.ContainsPosition(position)) {
|
||||
return FstabFieldSpec
|
||||
}
|
||||
|
||||
if e.Fields.MountPoint == nil || (cursor >= e.Fields.MountPoint.Start.Character && cursor <= e.Fields.MountPoint.End.Character) {
|
||||
if e.Fields.MountPoint == nil || (e.Fields.MountPoint.ContainsPosition(position)) {
|
||||
return FstabFieldMountPoint
|
||||
}
|
||||
|
||||
if e.Fields.FilesystemType == nil || (cursor >= e.Fields.FilesystemType.Start.Character && cursor <= e.Fields.FilesystemType.End.Character) {
|
||||
if e.Fields.FilesystemType == nil || (e.Fields.FilesystemType.ContainsPosition(position)) {
|
||||
return FstabFieldFileSystemType
|
||||
}
|
||||
|
||||
if e.Fields.Options == nil || (cursor >= e.Fields.Options.Start.Character && cursor <= e.Fields.Options.End.Character) {
|
||||
if e.Fields.Options == nil || (e.Fields.Options.ContainsPosition(position)) {
|
||||
return FstabFieldOptions
|
||||
}
|
||||
|
||||
if e.Fields.Freq == nil || (cursor >= e.Fields.Freq.Start.Character && cursor <= e.Fields.Freq.End.Character) {
|
||||
if e.Fields.Freq == nil || (e.Fields.Freq.ContainsPosition(position)) {
|
||||
return FstabFieldFreq
|
||||
}
|
||||
|
||||
return FstabFieldPass
|
||||
}
|
||||
|
||||
func (e FstabEntry) GetMountOptionsField() docvalues.DeprecatedValue {
|
||||
fileSystemType := e.Fields.FilesystemType.Value.Value
|
||||
|
||||
var optionsField docvalues.DeprecatedValue
|
||||
|
||||
if foundField, found := fields.MountOptionsMapField[fileSystemType]; found {
|
||||
optionsField = foundField
|
||||
} else {
|
||||
optionsField = fields.DefaultMountOptionsField
|
||||
}
|
||||
|
||||
return optionsField
|
||||
}
|
||||
|
@ -88,21 +88,45 @@ func (c *FstabConfig) parseStatement(
|
||||
fallthrough
|
||||
case 5:
|
||||
freq = parseField(line, input, fields[4])
|
||||
|
||||
if pass == nil && fields[4][1] < len(input) {
|
||||
pass = createPartialField(line, input, fields[4][1], len(input))
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case 4:
|
||||
options = parseField(line, input, fields[3])
|
||||
|
||||
if freq == nil && fields[3][1] < len(input) {
|
||||
freq = createPartialField(line, input, fields[3][1], len(input))
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case 3:
|
||||
filesystemType = parseField(line, input, fields[2])
|
||||
|
||||
if options == nil && fields[2][1] < len(input) {
|
||||
options = createPartialField(line, input, fields[2][1], len(input))
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case 2:
|
||||
mountPoint = parseField(line, input, fields[1])
|
||||
|
||||
if filesystemType == nil && fields[1][1] < len(input) {
|
||||
filesystemType = createPartialField(line, input, fields[1][1], len(input))
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case 1:
|
||||
spec = parseField(line, input, fields[0])
|
||||
|
||||
if mountPoint == nil && fields[0][1] < len(input) {
|
||||
mountPoint = createPartialField(line, input, fields[0][1], len(input))
|
||||
}
|
||||
}
|
||||
|
||||
fstabLine := FstabEntry{
|
||||
fstabLine := &FstabEntry{
|
||||
Fields: FstabFields{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
@ -157,3 +181,29 @@ func parseField(
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func createPartialField(
|
||||
line uint32,
|
||||
input string,
|
||||
start int,
|
||||
end int,
|
||||
) *FstabField {
|
||||
return nil
|
||||
return &FstabField{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: line,
|
||||
Character: uint32(start),
|
||||
},
|
||||
End: common.Location{
|
||||
Line: line,
|
||||
Character: uint32(end),
|
||||
},
|
||||
},
|
||||
Value: commonparser.ParseRawString(input[end:end], commonparser.ParseFeatures{
|
||||
ParseEscapedCharacters: true,
|
||||
ParseDoubleQuotes: true,
|
||||
Replacements: &map[string]string{},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/utils"
|
||||
"testing"
|
||||
)
|
||||
@ -24,7 +25,7 @@ LABEL=test /mnt/test ext4 defaults 0 0
|
||||
}
|
||||
|
||||
rawFirstEntry, _ := c.Entries.Get(uint32(0))
|
||||
firstEntry := rawFirstEntry.(FstabEntry)
|
||||
firstEntry := rawFirstEntry.(*FstabEntry)
|
||||
if !(firstEntry.Fields.Spec.Value.Value == "LABEL=test" && firstEntry.Fields.MountPoint.Value.Value == "/mnt/test" && firstEntry.Fields.FilesystemType.Value.Value == "ext4" && firstEntry.Fields.Options.Value.Value == "defaults" && firstEntry.Fields.Freq.Value.Value == "0" && firstEntry.Fields.Pass.Value.Value == "0") {
|
||||
t.Fatalf("Expected entry to be LABEL=test /mnt/test ext4 defaults 0 0, got %v", firstEntry)
|
||||
}
|
||||
@ -72,4 +73,66 @@ LABEL=test /mnt/test ext4 defaults 0 0
|
||||
if !(firstEntry.Fields.Pass.LocationRange.Start.Line == 0 && firstEntry.Fields.Pass.LocationRange.Start.Character == 37) {
|
||||
t.Errorf("Expected pass start to be 0:37, got %v", firstEntry.Fields.Pass.LocationRange.Start)
|
||||
}
|
||||
|
||||
field := firstEntry.GetFieldAtPosition(common.IndexPosition(0))
|
||||
if !(field == FstabFieldSpec) {
|
||||
t.Errorf("Expected field to be spec, got %v", field)
|
||||
}
|
||||
|
||||
field = firstEntry.GetFieldAtPosition(common.IndexPosition(11))
|
||||
if !(field == FstabFieldMountPoint) {
|
||||
t.Errorf("Expected field to be mountpoint, got %v", field)
|
||||
}
|
||||
|
||||
field = firstEntry.GetFieldAtPosition(common.IndexPosition(33))
|
||||
if !(field == FstabFieldOptions) {
|
||||
t.Errorf("Expected field to be spec, got %v", field)
|
||||
}
|
||||
|
||||
field = firstEntry.GetFieldAtPosition(common.IndexPosition(35))
|
||||
if !(field == FstabFieldFreq) {
|
||||
t.Errorf("Expected field to be freq, got %v", field)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncompleteExample(
|
||||
t *testing.T,
|
||||
) {
|
||||
input := utils.Dedent(`
|
||||
LABEL=test /mnt/test ext4 defaults
|
||||
`)
|
||||
c := NewFstabConfig()
|
||||
|
||||
errors := c.Parse(input)
|
||||
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("Expected no errors, got %v", errors)
|
||||
}
|
||||
|
||||
rawFirstEntry, _ := c.Entries.Get(uint32(0))
|
||||
firstEntry := rawFirstEntry.(*FstabEntry)
|
||||
|
||||
if !(firstEntry.Fields.Spec.Value.Raw == "LABEL=test" && firstEntry.Fields.MountPoint.Value.Raw == "/mnt/test" && firstEntry.Fields.FilesystemType.Value.Raw == "ext4" && firstEntry.Fields.Options.Value.Raw == "defaults") {
|
||||
t.Fatalf("Expected entry to be LABEL=test /mnt/test ext4 defaults, got %v", firstEntry)
|
||||
}
|
||||
|
||||
field := firstEntry.GetFieldAtPosition(common.IndexPosition(0))
|
||||
if !(field == FstabFieldSpec) {
|
||||
t.Errorf("Expected field to be spec, got %v", field)
|
||||
}
|
||||
|
||||
field = firstEntry.GetFieldAtPosition(common.IndexPosition(11))
|
||||
if !(field == FstabFieldMountPoint) {
|
||||
t.Errorf("Expected field to be mountpoint, got %v", field)
|
||||
}
|
||||
|
||||
field = firstEntry.GetFieldAtPosition(common.IndexPosition(33))
|
||||
if !(field == FstabFieldOptions) {
|
||||
t.Errorf("Expected field to be spec, got %v", field)
|
||||
}
|
||||
|
||||
field = firstEntry.GetFieldAtPosition(common.IndexPosition(35))
|
||||
if !(field == FstabFieldFreq) {
|
||||
t.Errorf("Expected field to be freq, got %v", field)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package fstabdocumentation
|
||||
package fields
|
||||
|
||||
import docvalues "config-lsp/doc-values"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package fstabdocumentation
|
||||
package fields
|
||||
|
||||
import (
|
||||
docvalues "config-lsp/doc-values"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package fstabdocumentation
|
||||
package fields
|
||||
|
||||
import (
|
||||
commondocumentation "config-lsp/common-documentation"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package fstabdocumentation
|
||||
package fields
|
||||
|
||||
import docvalues "config-lsp/doc-values"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package fstabdocumentation
|
||||
package fields
|
||||
|
||||
import (
|
||||
docvalues "config-lsp/doc-values"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package fstabdocumentation
|
||||
package fields
|
||||
|
||||
import (
|
||||
docvalues "config-lsp/doc-values"
|
||||
|
@ -1,9 +1,10 @@
|
||||
package fstab
|
||||
|
||||
import (
|
||||
fstabdocumentation "config-lsp/handlers/fstab/fields"
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
fields "config-lsp/handlers/fstab/fields"
|
||||
handlers "config-lsp/handlers/fstab/handlers"
|
||||
"config-lsp/handlers/fstab/parser"
|
||||
"config-lsp/utils"
|
||||
"testing"
|
||||
)
|
||||
@ -12,22 +13,21 @@ func TestValidBasicExample(t *testing.T) {
|
||||
input := utils.Dedent(`
|
||||
LABEL=test /mnt/test ext4 defaults 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatal("ParseFromContent failed with error", errors)
|
||||
t.Fatal("Parse failed with error", errors)
|
||||
}
|
||||
|
||||
// Get hover for first field
|
||||
rawEntry, _ := p.Entries.Get(uint32(0))
|
||||
entry := rawEntry.(parser.FstabEntry)
|
||||
entry := rawEntry.(*ast.FstabEntry)
|
||||
|
||||
println("Getting hover info")
|
||||
{
|
||||
hover, err := handlers.GetHoverInfo(&entry, uint32(0))
|
||||
hover, err := handlers.GetHoverInfo(uint32(0), common.IndexPosition(0), entry)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("getHoverInfo failed with error", err)
|
||||
@ -38,7 +38,7 @@ LABEL=test /mnt/test ext4 defaults 0 0
|
||||
}
|
||||
|
||||
// Get hover for second field
|
||||
hover, err = handlers.GetHoverInfo(&entry, uint32(11))
|
||||
hover, err = handlers.GetHoverInfo(uint32(0), common.IndexPosition(11), entry)
|
||||
if err != nil {
|
||||
t.Fatal("getHoverInfo failed with error", err)
|
||||
}
|
||||
@ -47,20 +47,16 @@ LABEL=test /mnt/test ext4 defaults 0 0
|
||||
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", handlers.MountPointHoverField.Contents)
|
||||
}
|
||||
|
||||
hover, err = handlers.GetHoverInfo(&entry, uint32(20))
|
||||
hover, err = handlers.GetHoverInfo(uint32(0), common.IndexPosition(20), entry)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("getHoverInfo failed with error", err)
|
||||
}
|
||||
|
||||
if hover.Contents != handlers.MountPointHoverField.Contents {
|
||||
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", handlers.MountPointHoverField.Contents)
|
||||
}
|
||||
}
|
||||
|
||||
println("Getting completions")
|
||||
{
|
||||
completions, err := handlers.GetCompletion(entry.Line, uint32(0))
|
||||
completions, err := handlers.GetCompletion(entry, common.CursorPosition(0))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("getCompletion failed with error", err)
|
||||
@ -79,50 +75,30 @@ LABEL=test /mnt/test ext4 defaults 0 0
|
||||
}
|
||||
|
||||
{
|
||||
completions, err := handlers.GetCompletion(entry.Line, uint32(21))
|
||||
completions, err := handlers.GetCompletion(entry, common.CursorPosition(23))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("getCompletion failed with error", err)
|
||||
}
|
||||
|
||||
expectedLength := len(utils.KeysOfMap(fstabdocumentation.MountOptionsMapField))
|
||||
expectedLength := len(utils.KeysOfMap(fields.MountOptionsMapField))
|
||||
if len(completions) != expectedLength {
|
||||
t.Fatal("getCompletion failed to return correct number of completions. Got:", len(completions), "but expected:", expectedLength)
|
||||
}
|
||||
}
|
||||
|
||||
println("Checking values")
|
||||
{
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Fatal("AnalyzeValues failed with error", diagnostics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidOptionsExample(t *testing.T) {
|
||||
input := utils.Dedent(`
|
||||
LABEL=test /mnt/test btrfs subvol=backup,fat=32 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatal("ParseFromContent returned error", errors)
|
||||
}
|
||||
|
||||
// Get hover for first field
|
||||
println("Checking values")
|
||||
{
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) == 0 {
|
||||
t.Fatal("AnalyzeValues should have returned error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func TestExample1(t *testing.T) {
|
||||
@ -150,20 +126,14 @@ UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1
|
||||
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0
|
||||
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
}
|
||||
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Errorf("AnalyzeValues failed with error %v", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchExample2(t *testing.T) {
|
||||
@ -173,10 +143,9 @@ func TestArchExample2(t *testing.T) {
|
||||
/dev/sda3 /home ext4 defaults 0 2
|
||||
/dev/sda4 none swap defaults 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
@ -190,20 +159,14 @@ LABEL=System / ext4 defaults 0 1
|
||||
LABEL=Data /home ext4 defaults 0 2
|
||||
LABEL=Swap none swap defaults 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
}
|
||||
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Errorf("AnalyzeValues failed with error %v", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchExample4(t *testing.T) {
|
||||
@ -213,20 +176,13 @@ UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1
|
||||
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2
|
||||
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
}
|
||||
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Errorf("AnalyzeValues failed with error %v", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchExample5(t *testing.T) {
|
||||
@ -236,20 +192,13 @@ PARTLABEL=GNU/Linux / ext4 defaults 0 1
|
||||
PARTLABEL=Home /home ext4 defaults 0 2
|
||||
PARTLABEL=Swap none swap defaults 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
}
|
||||
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Errorf("AnalyzeValues failed with error %v", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchExample6(t *testing.T) {
|
||||
@ -259,60 +208,39 @@ PARTUUID=98a81274-10f7-40db-872a-03df048df366 / ext4 defaults 0 1
|
||||
PARTUUID=7280201c-fc5d-40f2-a9b2-466611d3d49e /home ext4 defaults 0 2
|
||||
PARTUUID=039b6c1c-7553-4455-9537-1befbc9fbc5b none swap defaults 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
}
|
||||
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Errorf("AnalyzeValues failed with error %v", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinuxConfigExample(t *testing.T) {
|
||||
input := utils.Dedent(`
|
||||
UUID=80b496fa-ce2d-4dcf-9afc-bcaa731a67f1 /mnt/example ext4 defaults 0 2
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
}
|
||||
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Errorf("AnalyzeValues failed with error %v", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func Test1(t *testing.T) {
|
||||
input := utils.Dedent(`
|
||||
PARTLABEL="rootfs" / ext4 noatime,lazytime,rw 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
}
|
||||
|
||||
diagnostics := p.AnalyzeValues()
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
t.Errorf("AnalyzeValues failed with error %v", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func Test2(t *testing.T) {
|
||||
@ -323,10 +251,9 @@ func Test2(t *testing.T) {
|
||||
/dev/sdd /homeD xfs noauto,rw,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0
|
||||
/dev/sde /homeE xfs defaults 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
@ -344,10 +271,9 @@ func Test3(t *testing.T) {
|
||||
tmpfs /var tmpfs rw,nosuid,nodev,size=128M,mode=755 0 0
|
||||
tmpfs /tmp tmpfs rw,nosuid,nodev,size=150M,mode=1777 0 0
|
||||
`)
|
||||
p := parser.FstabParser{}
|
||||
p.Clear()
|
||||
p := ast.NewFstabConfig()
|
||||
|
||||
errors := p.ParseFromContent(input)
|
||||
errors := p.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/doc-values"
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
"config-lsp/handlers/fstab/fields"
|
||||
@ -10,7 +11,7 @@ import (
|
||||
|
||||
func GetCompletion(
|
||||
entry *ast.FstabEntry,
|
||||
cursor uint32,
|
||||
cursor common.CursorPosition,
|
||||
) ([]protocol.CompletionItem, error) {
|
||||
targetField := entry.GetFieldAtPosition(cursor)
|
||||
|
||||
@ -18,21 +19,21 @@ func GetCompletion(
|
||||
case ast.FstabFieldSpec:
|
||||
value, cursor := getFieldSafely(entry.Fields.Spec, cursor)
|
||||
|
||||
return fstabdocumentation.SpecField.DeprecatedFetchCompletions(
|
||||
return fields.SpecField.DeprecatedFetchCompletions(
|
||||
value,
|
||||
cursor,
|
||||
), nil
|
||||
case ast.FstabFieldMountPoint:
|
||||
value, cursor := getFieldSafely(entry.Fields.MountPoint, cursor)
|
||||
|
||||
return fstabdocumentation.MountPointField.DeprecatedFetchCompletions(
|
||||
return fields.MountPointField.DeprecatedFetchCompletions(
|
||||
value,
|
||||
cursor,
|
||||
), nil
|
||||
case ast.FstabFieldFileSystemType:
|
||||
value, cursor := getFieldSafely(entry.Fields.FilesystemType, cursor)
|
||||
|
||||
return fstabdocumentation.FileSystemTypeField.DeprecatedFetchCompletions(
|
||||
return fields.FileSystemTypeField.DeprecatedFetchCompletions(
|
||||
value,
|
||||
cursor,
|
||||
), nil
|
||||
@ -41,10 +42,10 @@ func GetCompletion(
|
||||
|
||||
var optionsField docvalues.DeprecatedValue
|
||||
|
||||
if foundField, found := fstabdocumentation.MountOptionsMapField[fileSystemType]; found {
|
||||
if foundField, found := fields.MountOptionsMapField[fileSystemType]; found {
|
||||
optionsField = foundField
|
||||
} else {
|
||||
optionsField = fstabdocumentation.DefaultMountOptionsField
|
||||
optionsField = fields.DefaultMountOptionsField
|
||||
}
|
||||
|
||||
value, cursor := getFieldSafely(entry.Fields.Options, cursor)
|
||||
@ -58,14 +59,14 @@ func GetCompletion(
|
||||
case ast.FstabFieldFreq:
|
||||
value, cursor := getFieldSafely(entry.Fields.Freq, cursor)
|
||||
|
||||
return fstabdocumentation.FreqField.DeprecatedFetchCompletions(
|
||||
return fields.FreqField.DeprecatedFetchCompletions(
|
||||
value,
|
||||
cursor,
|
||||
), nil
|
||||
case ast.FstabFieldPass:
|
||||
value, cursor := getFieldSafely(entry.Fields.Pass, cursor)
|
||||
|
||||
return fstabdocumentation.PassField.DeprecatedFetchCompletions(
|
||||
return fields.PassField.DeprecatedFetchCompletions(
|
||||
value,
|
||||
cursor,
|
||||
), nil
|
||||
@ -76,7 +77,7 @@ func GetCompletion(
|
||||
|
||||
// Safely get value and new cursor position
|
||||
// If field is nil, return empty string and 0
|
||||
func getFieldSafely(field *ast.FstabField, character uint32) (string, uint32) {
|
||||
func getFieldSafely(field *ast.FstabField, cursor common.CursorPosition) (string, uint32) {
|
||||
if field == nil {
|
||||
return "", 0
|
||||
}
|
||||
@ -85,5 +86,5 @@ func getFieldSafely(field *ast.FstabField, character uint32) (string, uint32) {
|
||||
return "", 0
|
||||
}
|
||||
|
||||
return field.Value.Raw, character - field.Start.Character
|
||||
return field.Value.Raw, common.CursorToCharacterIndex(uint32(cursor)) - field.Start.Character
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/doc-values"
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
"config-lsp/handlers/fstab/fields"
|
||||
@ -11,10 +12,10 @@ import (
|
||||
|
||||
func GetHoverInfo(
|
||||
line uint32,
|
||||
cursor uint32,
|
||||
index common.IndexPosition,
|
||||
entry *ast.FstabEntry,
|
||||
) (*protocol.Hover, error) {
|
||||
targetField := entry.GetFieldAtPosition(cursor)
|
||||
targetField := entry.GetFieldAtPosition(index)
|
||||
|
||||
switch targetField {
|
||||
case ast.FstabFieldSpec:
|
||||
@ -27,13 +28,13 @@ func GetHoverInfo(
|
||||
fileSystemType := entry.Fields.FilesystemType.Value.Value
|
||||
var optionsField docvalues.DeprecatedValue
|
||||
|
||||
if foundField, found := fstabdocumentation.MountOptionsMapField[fileSystemType]; found {
|
||||
if foundField, found := fields.MountOptionsMapField[fileSystemType]; found {
|
||||
optionsField = foundField
|
||||
} else {
|
||||
optionsField = fstabdocumentation.DefaultMountOptionsField
|
||||
optionsField = fields.DefaultMountOptionsField
|
||||
}
|
||||
|
||||
relativeCursor := cursor - entry.Fields.Options.Start.Character
|
||||
relativeCursor := uint32(index) - entry.Fields.Options.Start.Character
|
||||
fieldInfo := optionsField.DeprecatedFetchHoverInfo(entry.Fields.Options.Value.Value, relativeCursor)
|
||||
|
||||
hover := protocol.Hover{
|
||||
|
@ -12,9 +12,10 @@ import (
|
||||
)
|
||||
|
||||
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
||||
c := shared.DocumentParserMap[params.TextDocument.URI]
|
||||
d := shared.DocumentParserMap[params.TextDocument.URI]
|
||||
cursor := common.LSPCharacterAsCursorPosition(params.Position.Character)
|
||||
|
||||
rawEntry, found := c.Entries.Get(params.Position.Line)
|
||||
rawEntry, found := d.Config.Entries.Get(params.Position.Line)
|
||||
|
||||
if !found {
|
||||
// Empty line, return spec completions
|
||||
@ -26,7 +27,5 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
|
||||
|
||||
entry := rawEntry.(*ast.FstabEntry)
|
||||
|
||||
cursor := common.CursorToCharacterIndex(params.Position.Character)
|
||||
|
||||
return handlers.GetCompletion(entry, cursor)
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package lsp
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/fstab/analyzer"
|
||||
"config-lsp/handlers/fstab/shared"
|
||||
"config-lsp/utils"
|
||||
|
||||
"github.com/tliron/glsp"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
@ -15,11 +17,11 @@ func TextDocumentDidChange(
|
||||
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
|
||||
common.ClearDiagnostics(context, params.TextDocument.URI)
|
||||
|
||||
p := shared.DocumentParserMap[params.TextDocument.URI]
|
||||
p.Clear()
|
||||
d := shared.DocumentParserMap[params.TextDocument.URI]
|
||||
d.Config.Clear()
|
||||
|
||||
diagnostics := make([]protocol.Diagnostic, 0)
|
||||
errors := p.Parse(content)
|
||||
errors := d.Config.Parse(content)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(diagnostics, utils.Map(
|
||||
@ -29,7 +31,7 @@ func TextDocumentDidChange(
|
||||
},
|
||||
)...)
|
||||
} else {
|
||||
// diagnostics = append(diagnostics, p.AnalyzeValues()...)
|
||||
diagnostics = append(diagnostics, analyzer.Analyze(d)...)
|
||||
}
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
|
@ -2,6 +2,7 @@ package lsp
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/fstab/analyzer"
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
"config-lsp/handlers/fstab/shared"
|
||||
"config-lsp/utils"
|
||||
@ -16,13 +17,16 @@ func TextDocumentDidOpen(
|
||||
) error {
|
||||
common.ClearDiagnostics(context, params.TextDocument.URI)
|
||||
|
||||
p := ast.NewFstabConfig()
|
||||
shared.DocumentParserMap[params.TextDocument.URI] = p
|
||||
config := ast.NewFstabConfig()
|
||||
d := &shared.FstabDocument{
|
||||
Config: config,
|
||||
}
|
||||
shared.DocumentParserMap[params.TextDocument.URI] = d
|
||||
|
||||
content := params.TextDocument.Text
|
||||
|
||||
diagnostics := make([]protocol.Diagnostic, 0)
|
||||
errors := p.Parse(content)
|
||||
errors := d.Config.Parse(content)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(diagnostics, utils.Map(
|
||||
@ -32,7 +36,7 @@ func TextDocumentDidOpen(
|
||||
},
|
||||
)...)
|
||||
} else {
|
||||
// diagnostics = append(diagnostics, p.AnalyzeValues()...)
|
||||
diagnostics = append(diagnostics, analyzer.Analyze(d)...)
|
||||
}
|
||||
|
||||
if len(diagnostics) > 0 {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
"config-lsp/handlers/fstab/handlers"
|
||||
"config-lsp/handlers/fstab/shared"
|
||||
@ -11,11 +12,11 @@ import (
|
||||
|
||||
func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
|
||||
line := params.Position.Line
|
||||
cursor := params.Position.Character
|
||||
index := common.LSPCharacterAsIndexPosition(params.Position.Character)
|
||||
|
||||
p := shared.DocumentParserMap[params.TextDocument.URI]
|
||||
d := shared.DocumentParserMap[params.TextDocument.URI]
|
||||
|
||||
rawEntry, found := p.Entries.Get(params.Position.Line)
|
||||
rawEntry, found := d.Config.Entries.Get(params.Position.Line)
|
||||
|
||||
// Empty line
|
||||
if !found {
|
||||
@ -26,7 +27,7 @@ func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*pr
|
||||
|
||||
return handlers.GetHoverInfo(
|
||||
line,
|
||||
cursor,
|
||||
index,
|
||||
entry,
|
||||
)
|
||||
}
|
||||
|
@ -1,385 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
docvalues "config-lsp/doc-values"
|
||||
fstabdocumentation "config-lsp/handlers/fstab/fields"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/emirpasic/gods/maps/treemap"
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
|
||||
gods "github.com/emirpasic/gods/utils"
|
||||
)
|
||||
|
||||
var commentPattern = regexp.MustCompile(`^\s*#`)
|
||||
var ignoreLinePattern = regexp.MustCompile(`^\s*$`)
|
||||
var whitespacePattern = regexp.MustCompile(`\S+`)
|
||||
|
||||
type MalformedLineError struct{}
|
||||
|
||||
func (e MalformedLineError) Error() string {
|
||||
return "Malformed line"
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Value string
|
||||
Start uint32
|
||||
End uint32
|
||||
}
|
||||
|
||||
func (f Field) String() string {
|
||||
return fmt.Sprintf("%v [%v-%v]", f.Value, f.Start, f.End)
|
||||
}
|
||||
|
||||
func (f *Field) CreateRange(fieldLine uint32) protocol.Range {
|
||||
return protocol.Range{
|
||||
Start: protocol.Position{
|
||||
Line: fieldLine,
|
||||
Character: f.Start,
|
||||
},
|
||||
End: protocol.Position{
|
||||
Line: fieldLine,
|
||||
Character: f.End,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type FstabField string
|
||||
|
||||
const (
|
||||
FstabFieldSpec FstabField = "spec"
|
||||
FstabFieldMountPoint FstabField = "mountpoint"
|
||||
FstabFieldFileSystemType FstabField = "filesystemtype"
|
||||
FstabFieldOptions FstabField = "options"
|
||||
FstabFieldFreq FstabField = "freq"
|
||||
FstabFieldPass FstabField = "pass"
|
||||
)
|
||||
|
||||
type FstabFields struct {
|
||||
Spec *Field
|
||||
MountPoint *Field
|
||||
FilesystemType *Field
|
||||
Options *Field
|
||||
Freq *Field
|
||||
Pass *Field
|
||||
}
|
||||
|
||||
func (f FstabFields) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Spec: %s, MountPoint: %s, FilesystemType: %s, Options: %s, Freq: %s, Pass: %s",
|
||||
f.Spec,
|
||||
f.MountPoint,
|
||||
f.FilesystemType,
|
||||
f.Options,
|
||||
f.Freq,
|
||||
f.Pass,
|
||||
)
|
||||
}
|
||||
|
||||
type FstabLine struct {
|
||||
Line uint32
|
||||
Fields FstabFields
|
||||
}
|
||||
|
||||
func (e *FstabLine) CheckIsValid() []protocol.Diagnostic {
|
||||
diagnostics := make([]protocol.Diagnostic, 0)
|
||||
|
||||
if e.Fields.Spec != nil {
|
||||
errors := fstabdocumentation.SpecField.DeprecatedCheckIsValid(e.Fields.Spec.Value)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(
|
||||
diagnostics,
|
||||
docvalues.InvalidValuesToErrorDiagnostics(e.Line, e.Fields.Spec.Start, errors)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if e.Fields.MountPoint != nil {
|
||||
errors := fstabdocumentation.MountPointField.DeprecatedCheckIsValid(e.Fields.MountPoint.Value)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(
|
||||
diagnostics,
|
||||
docvalues.InvalidValuesToErrorDiagnostics(e.Line, e.Fields.MountPoint.Start, errors)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var fileSystemType string = ""
|
||||
|
||||
if e.Fields.FilesystemType != nil {
|
||||
errors := fstabdocumentation.FileSystemTypeField.DeprecatedCheckIsValid(e.Fields.FilesystemType.Value)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(
|
||||
diagnostics,
|
||||
docvalues.InvalidValuesToErrorDiagnostics(e.Line, e.Fields.FilesystemType.Start, errors)...,
|
||||
)
|
||||
} else {
|
||||
fileSystemType = e.Fields.FilesystemType.Value
|
||||
}
|
||||
}
|
||||
|
||||
if e.Fields.Options != nil && fileSystemType != "" {
|
||||
var optionsField docvalues.DeprecatedValue
|
||||
|
||||
if foundField, found := fstabdocumentation.MountOptionsMapField[fileSystemType]; found {
|
||||
optionsField = foundField
|
||||
} else {
|
||||
optionsField = fstabdocumentation.DefaultMountOptionsField
|
||||
}
|
||||
|
||||
errors := optionsField.DeprecatedCheckIsValid(e.Fields.Options.Value)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(
|
||||
diagnostics,
|
||||
docvalues.InvalidValuesToErrorDiagnostics(e.Line, e.Fields.Options.Start, errors)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if e.Fields.Freq != nil {
|
||||
errors := fstabdocumentation.FreqField.DeprecatedCheckIsValid(e.Fields.Freq.Value)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(
|
||||
diagnostics,
|
||||
docvalues.InvalidValuesToErrorDiagnostics(e.Line, e.Fields.Freq.Start, errors)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if e.Fields.Pass != nil {
|
||||
errors := fstabdocumentation.PassField.DeprecatedCheckIsValid(e.Fields.Pass.Value)
|
||||
|
||||
if len(errors) > 0 {
|
||||
diagnostics = append(
|
||||
diagnostics,
|
||||
docvalues.InvalidValuesToErrorDiagnostics(e.Line, e.Fields.Pass.Start, errors)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return diagnostics
|
||||
}
|
||||
|
||||
func (e FstabLine) GetFieldAtPosition(cursor uint32) FstabField {
|
||||
if e.Fields.Spec == nil || (cursor >= e.Fields.Spec.Start && cursor <= e.Fields.Spec.End) {
|
||||
return FstabFieldSpec
|
||||
}
|
||||
|
||||
if e.Fields.MountPoint == nil || (cursor >= e.Fields.MountPoint.Start && cursor <= e.Fields.MountPoint.End) {
|
||||
return FstabFieldMountPoint
|
||||
}
|
||||
|
||||
if e.Fields.FilesystemType == nil || (cursor >= e.Fields.FilesystemType.Start && cursor <= e.Fields.FilesystemType.End) {
|
||||
return FstabFieldFileSystemType
|
||||
}
|
||||
|
||||
if e.Fields.Options == nil || (cursor >= e.Fields.Options.Start && cursor <= e.Fields.Options.End) {
|
||||
return FstabFieldOptions
|
||||
}
|
||||
|
||||
if e.Fields.Freq == nil || (cursor >= e.Fields.Freq.Start && cursor <= e.Fields.Freq.End) {
|
||||
return FstabFieldFreq
|
||||
}
|
||||
|
||||
return FstabFieldPass
|
||||
}
|
||||
|
||||
type FstabEntryType string
|
||||
|
||||
const (
|
||||
FstabEntryTypeLine FstabEntryType = "line"
|
||||
FstabEntryTypeComment FstabEntryType = "comment"
|
||||
)
|
||||
|
||||
type FstabEntry struct {
|
||||
Type FstabEntryType
|
||||
Line FstabLine
|
||||
}
|
||||
|
||||
type FstabParser struct {
|
||||
// [uint32]FstabEntry - line number to entry mapping
|
||||
Entries *treemap.Map
|
||||
}
|
||||
|
||||
func (p *FstabParser) AddLine(line string, lineNumber int) error {
|
||||
fields := whitespacePattern.FindAllStringIndex(line, -1)
|
||||
|
||||
if len(fields) == 0 {
|
||||
return MalformedLineError{}
|
||||
}
|
||||
|
||||
var spec *Field
|
||||
var mountPoint *Field
|
||||
var filesystemType *Field
|
||||
var options *Field
|
||||
var freq *Field
|
||||
var pass *Field
|
||||
|
||||
switch len(fields) {
|
||||
case 6:
|
||||
field := fields[5]
|
||||
start := uint32(field[0])
|
||||
end := uint32(field[1])
|
||||
|
||||
pass = &Field{
|
||||
Value: line[start:end],
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
fallthrough
|
||||
case 5:
|
||||
field := fields[4]
|
||||
start := uint32(field[0])
|
||||
end := uint32(field[1])
|
||||
|
||||
freq = &Field{
|
||||
Value: line[start:end],
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
fallthrough
|
||||
case 4:
|
||||
field := fields[3]
|
||||
start := uint32(field[0])
|
||||
end := uint32(field[1])
|
||||
|
||||
options = &Field{
|
||||
Value: line[start:end],
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
fallthrough
|
||||
case 3:
|
||||
field := fields[2]
|
||||
start := uint32(field[0])
|
||||
end := uint32(field[1])
|
||||
|
||||
filesystemType = &Field{
|
||||
Value: line[start:end],
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
field := fields[1]
|
||||
start := uint32(field[0])
|
||||
end := uint32(field[1])
|
||||
|
||||
mountPoint = &Field{
|
||||
Value: line[start:end],
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
field := fields[0]
|
||||
start := uint32(field[0])
|
||||
end := uint32(field[1])
|
||||
|
||||
spec = &Field{
|
||||
Value: line[start:end],
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
}
|
||||
|
||||
entry := FstabEntry{
|
||||
Type: FstabEntryTypeLine,
|
||||
Line: FstabLine{
|
||||
Line: uint32(lineNumber),
|
||||
Fields: FstabFields{
|
||||
Spec: spec,
|
||||
MountPoint: mountPoint,
|
||||
FilesystemType: filesystemType,
|
||||
Options: options,
|
||||
Freq: freq,
|
||||
Pass: pass,
|
||||
},
|
||||
},
|
||||
}
|
||||
p.Entries.Put(entry.Line.Line, entry)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *FstabParser) AddCommentLine(line string, lineNumber int) {
|
||||
entry := FstabLine{
|
||||
Line: uint32(lineNumber),
|
||||
}
|
||||
p.Entries.Put(entry.Line, FstabEntry{
|
||||
Type: FstabEntryTypeComment,
|
||||
Line: entry,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *FstabParser) ParseFromContent(content string) []common.ParseError {
|
||||
errors := []common.ParseError{}
|
||||
lines := strings.Split(content, "\n")
|
||||
|
||||
for index, line := range lines {
|
||||
if ignoreLinePattern.MatchString(line) {
|
||||
continue
|
||||
}
|
||||
|
||||
if commentPattern.MatchString(line) {
|
||||
p.AddCommentLine(line, index)
|
||||
continue
|
||||
}
|
||||
|
||||
err := p.AddLine(line, index)
|
||||
|
||||
if err != nil {
|
||||
errors = append(errors, common.ParseError{
|
||||
Line: uint32(index),
|
||||
Err: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (p *FstabParser) GetEntry(line uint32) (*FstabEntry, bool) {
|
||||
rawEntry, found := p.Entries.Get(line)
|
||||
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
entry := rawEntry.(FstabEntry)
|
||||
|
||||
return &entry, true
|
||||
}
|
||||
|
||||
func (p *FstabParser) Clear() {
|
||||
p.Entries = treemap.NewWith(gods.UInt32Comparator)
|
||||
}
|
||||
|
||||
func (p *FstabParser) AnalyzeValues() []protocol.Diagnostic {
|
||||
diagnostics := []protocol.Diagnostic{}
|
||||
|
||||
it := p.Entries.Iterator()
|
||||
|
||||
for it.Next() {
|
||||
entry := it.Value().(FstabEntry)
|
||||
|
||||
switch entry.Type {
|
||||
case FstabEntryTypeLine:
|
||||
newDiagnostics := entry.Line.CheckIsValid()
|
||||
|
||||
if len(newDiagnostics) > 0 {
|
||||
diagnostics = append(diagnostics, newDiagnostics...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diagnostics
|
||||
}
|
@ -6,5 +6,8 @@ import (
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
var DocumentParserMap = map[protocol.DocumentUri]*ast.FstabConfig{}
|
||||
type FstabDocument struct {
|
||||
Config *ast.FstabConfig
|
||||
}
|
||||
|
||||
var DocumentParserMap = map[protocol.DocumentUri]*FstabDocument{}
|
||||
|
25
server/handlers/fstab/test_utils/input.go
Normal file
25
server/handlers/fstab/test_utils/input.go
Normal file
@ -0,0 +1,25 @@
|
||||
package testutils_test
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/fstab/ast"
|
||||
"config-lsp/handlers/fstab/shared"
|
||||
"config-lsp/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func DocumentFromInput(
|
||||
t *testing.T,
|
||||
content string,
|
||||
) *shared.FstabDocument {
|
||||
input := utils.Dedent(content)
|
||||
c := ast.NewFstabConfig()
|
||||
errors := c.Parse(input)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("Parse error: %v", errors)
|
||||
}
|
||||
|
||||
return &shared.FstabDocument{
|
||||
Config: c,
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user