mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
388 lines
8.1 KiB
Go
388 lines
8.1 KiB
Go
package parser
|
|
|
|
import (
|
|
"config-lsp/common"
|
|
docvalues "config-lsp/doc-values"
|
|
fstabdocumentation "config-lsp/handlers/fstab/documentation"
|
|
"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...)
|
|
}
|
|
case FstabEntryTypeComment:
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
return diagnostics
|
|
}
|