mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
commit
418c549a15
@ -20,7 +20,7 @@ var BtrfsDocumentationAssignable = map[docvalues.EnumString]docvalues.Deprecated
|
|||||||
Values: []docvalues.DeprecatedValue{
|
Values: []docvalues.DeprecatedValue{
|
||||||
docvalues.EnumValue{
|
docvalues.EnumValue{
|
||||||
Values: []docvalues.EnumString{
|
Values: []docvalues.EnumString{
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"no",
|
"no",
|
||||||
"No compression, used for remounting.",
|
"No compression, used for remounting.",
|
||||||
),
|
),
|
||||||
@ -38,7 +38,7 @@ var BtrfsDocumentationAssignable = map[docvalues.EnumString]docvalues.Deprecated
|
|||||||
15,
|
15,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Separator: ":",
|
Separator: ":",
|
||||||
ValueIsOptional: true,
|
ValueIsOptional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
type ParseFeatures struct {
|
type ParseFeatures struct {
|
||||||
ParseDoubleQuotes bool
|
ParseDoubleQuotes bool
|
||||||
ParseEscapedCharacters bool
|
ParseEscapedCharacters bool
|
||||||
|
Replacements *map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
var FullFeatures = ParseFeatures{
|
var FullFeatures = ParseFeatures{
|
||||||
ParseDoubleQuotes: true,
|
ParseDoubleQuotes: true,
|
||||||
ParseEscapedCharacters: true,
|
ParseEscapedCharacters: true,
|
||||||
|
Replacements: &map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParsedString struct {
|
type ParsedString struct {
|
||||||
@ -21,6 +25,10 @@ func ParseRawString(
|
|||||||
) ParsedString {
|
) ParsedString {
|
||||||
value := raw
|
value := raw
|
||||||
|
|
||||||
|
if len(*features.Replacements) > 0 {
|
||||||
|
value = ParseReplacements(value, *features.Replacements)
|
||||||
|
}
|
||||||
|
|
||||||
// Parse double quotes
|
// Parse double quotes
|
||||||
if features.ParseDoubleQuotes {
|
if features.ParseDoubleQuotes {
|
||||||
value = ParseDoubleQuotes(value)
|
value = ParseDoubleQuotes(value)
|
||||||
@ -85,6 +93,19 @@ func ParseEscapedCharacters(
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseReplacements(
|
||||||
|
raw string,
|
||||||
|
replacements map[string]string,
|
||||||
|
) string {
|
||||||
|
value := raw
|
||||||
|
|
||||||
|
for key, replacement := range replacements {
|
||||||
|
value = strings.ReplaceAll(value, key, replacement)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
func modifyString(
|
func modifyString(
|
||||||
input string,
|
input string,
|
||||||
start int,
|
start int,
|
||||||
|
@ -165,3 +165,25 @@ func TestStringsIncompleteQuotes3FullFeatures(
|
|||||||
t.Errorf("Expected %v, got %v", expected, actual)
|
t.Errorf("Expected %v, got %v", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStringsReplacements(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
input := `Hello\\040World`
|
||||||
|
expected := ParsedString{
|
||||||
|
Raw: input,
|
||||||
|
Value: `Hello World`,
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := ParseRawString(input, ParseFeatures{
|
||||||
|
ParseDoubleQuotes: true,
|
||||||
|
ParseEscapedCharacters: true,
|
||||||
|
Replacements: &map[string]string{
|
||||||
|
`\\040`: " ",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if !(cmp.Equal(expected, actual)) {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -132,7 +132,6 @@ func (v ArrayValue) getCurrentValue(line string, cursor uint32) (string, uint32)
|
|||||||
// hello,worl[d]
|
// hello,worl[d]
|
||||||
// hello,world[,]
|
// hello,world[,]
|
||||||
// hello[,]world,how,are,you
|
// hello[,]world,how,are,you
|
||||||
|
|
||||||
relativePosition, found := utils.FindPreviousCharacter(
|
relativePosition, found := utils.FindPreviousCharacter(
|
||||||
line,
|
line,
|
||||||
v.Separator,
|
v.Separator,
|
||||||
|
@ -170,13 +170,14 @@ func (v KeyEnumAssignmentValue) DeprecatedFetchCompletions(line string, cursor u
|
|||||||
return v.FetchEnumCompletions()
|
return v.FetchEnumCompletions()
|
||||||
}
|
}
|
||||||
|
|
||||||
relativePosition, found := utils.FindPreviousCharacter(
|
foundPosition, found := utils.FindPreviousCharacter(
|
||||||
line,
|
line,
|
||||||
v.Separator,
|
v.Separator,
|
||||||
int(cursor),
|
int(cursor),
|
||||||
)
|
)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
|
relativePosition := max(1, foundPosition) - 1
|
||||||
selectedKey := line[:uint32(relativePosition)]
|
selectedKey := line[:uint32(relativePosition)]
|
||||||
line = line[uint32(relativePosition+len(v.Separator)):]
|
line = line[uint32(relativePosition+len(v.Separator)):]
|
||||||
cursor -= uint32(relativePosition)
|
cursor -= uint32(relativePosition)
|
||||||
|
@ -108,13 +108,14 @@ func (v KeyValueAssignmentValue) DeprecatedFetchCompletions(line string, cursor
|
|||||||
return v.Key.DeprecatedFetchCompletions(line, cursor)
|
return v.Key.DeprecatedFetchCompletions(line, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
relativePosition, found := utils.FindPreviousCharacter(
|
foundPosition, found := utils.FindPreviousCharacter(
|
||||||
line,
|
line,
|
||||||
v.Separator,
|
v.Separator,
|
||||||
int(cursor),
|
int(cursor),
|
||||||
)
|
)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
|
relativePosition := max(1, foundPosition) - 1
|
||||||
selectedKey := line[:uint32(relativePosition)]
|
selectedKey := line[:uint32(relativePosition)]
|
||||||
line = line[uint32(relativePosition+len(v.Separator)):]
|
line = line[uint32(relativePosition+len(v.Separator)):]
|
||||||
cursor -= uint32(relativePosition)
|
cursor -= uint32(relativePosition)
|
||||||
|
76
server/handlers/fstab/Fstab.g4
Normal file
76
server/handlers/fstab/Fstab.g4
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
grammar Fstab;
|
||||||
|
|
||||||
|
entry
|
||||||
|
:
|
||||||
|
WHITESPACE? spec?
|
||||||
|
WHITESPACE? mountPoint?
|
||||||
|
WHITESPACE? fileSystem?
|
||||||
|
WHITESPACE? mountOptions?
|
||||||
|
WHITESPACE? freq?
|
||||||
|
WHITESPACE? pass? WHITESPACE?
|
||||||
|
EOF
|
||||||
|
;
|
||||||
|
|
||||||
|
spec
|
||||||
|
: QUOTED_STRING | STRING
|
||||||
|
;
|
||||||
|
|
||||||
|
mountPoint
|
||||||
|
: QUOTED_STRING | STRING
|
||||||
|
;
|
||||||
|
|
||||||
|
fileSystem
|
||||||
|
: ADFS | AFFS | BTRFS | EXFAT
|
||||||
|
// Still match unknown file systems
|
||||||
|
| STRING | QUOTED_STRING
|
||||||
|
;
|
||||||
|
|
||||||
|
mountOptions
|
||||||
|
: QUOTED_STRING | STRING
|
||||||
|
;
|
||||||
|
|
||||||
|
freq
|
||||||
|
: DIGITS
|
||||||
|
;
|
||||||
|
|
||||||
|
pass
|
||||||
|
: DIGITS
|
||||||
|
;
|
||||||
|
|
||||||
|
DIGITS
|
||||||
|
: [0-9]+
|
||||||
|
;
|
||||||
|
|
||||||
|
WHITESPACE
|
||||||
|
: [ \t]+
|
||||||
|
;
|
||||||
|
|
||||||
|
HASH
|
||||||
|
: '#'
|
||||||
|
;
|
||||||
|
|
||||||
|
STRING
|
||||||
|
: ~(' ' | '\t' | '#')+
|
||||||
|
;
|
||||||
|
|
||||||
|
QUOTED_STRING
|
||||||
|
: '"' WHITESPACE? (STRING WHITESPACE)* STRING? ('"')?
|
||||||
|
;
|
||||||
|
|
||||||
|
// ///// Supported file systems /////
|
||||||
|
|
||||||
|
ADFS
|
||||||
|
: ('A' | 'a') ('D' | 'd') ('F' | 'f') ('S' | 's')
|
||||||
|
;
|
||||||
|
|
||||||
|
AFFS
|
||||||
|
: ('A' | 'a') ('F' | 'f') ('F' | 'f') ('S' | 's')
|
||||||
|
;
|
||||||
|
|
||||||
|
BTRFS
|
||||||
|
: ('B' | 'b') ('T' | 't') ('R' | 'r') ('F' | 'f') ('S' | 's')
|
||||||
|
;
|
||||||
|
|
||||||
|
EXFAT
|
||||||
|
: ('E' | 'e') ('X' | 'x') ('F' | 'f') ('A' | 'a') ('T' | 't')
|
||||||
|
;
|
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
|
||||||
|
}
|
93
server/handlers/fstab/analyzer/fields.go
Normal file
93
server/handlers/fstab/analyzer/fields.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
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 empty. The usual convention is to use at least "defaults" keyword there.`,
|
||||||
|
Severity: &common.SeverityWarning,
|
||||||
|
})
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
58
server/handlers/fstab/analyzer/values.go
Normal file
58
server/handlers/fstab/analyzer/values.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
if entry.Fields.Options != nil {
|
||||||
|
mountOptions := entry.FetchMountOptionsField(true)
|
||||||
|
|
||||||
|
if mountOptions != nil {
|
||||||
|
checkField(ctx, entry.Fields.Options, mountOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Fields.Freq != nil {
|
||||||
|
checkField(ctx, entry.Fields.Freq, fields.FreqField)
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Fields.Pass != nil {
|
||||||
|
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).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))
|
||||||
|
}
|
||||||
|
}
|
45
server/handlers/fstab/ast/error-listener.go
Normal file
45
server/handlers/fstab/ast/error-listener.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
|
||||||
|
"github.com/antlr4-go/antlr/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type errorListenerContext struct {
|
||||||
|
line uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorListener struct {
|
||||||
|
*antlr.DefaultErrorListener
|
||||||
|
Errors []common.LSPError
|
||||||
|
context errorListenerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *errorListener) SyntaxError(
|
||||||
|
recognizer antlr.Recognizer,
|
||||||
|
offendingSymbol interface{},
|
||||||
|
_ int,
|
||||||
|
character int,
|
||||||
|
message string,
|
||||||
|
error antlr.RecognitionException,
|
||||||
|
) {
|
||||||
|
line := d.context.line
|
||||||
|
d.Errors = append(d.Errors, common.LSPError{
|
||||||
|
Range: common.CreateSingleCharRange(uint32(line), uint32(character)),
|
||||||
|
Err: common.SyntaxError{
|
||||||
|
Message: message,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func createErrorListener(
|
||||||
|
line uint32,
|
||||||
|
) errorListener {
|
||||||
|
return errorListener{
|
||||||
|
Errors: make([]common.LSPError, 0),
|
||||||
|
context: errorListenerContext{
|
||||||
|
line: line,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
45
server/handlers/fstab/ast/fstab.go
Normal file
45
server/handlers/fstab/ast/fstab.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
commonparser "config-lsp/common/parser"
|
||||||
|
"github.com/emirpasic/gods/maps/treemap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FstabFieldName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
FstabFieldSpec FstabFieldName = "spec"
|
||||||
|
FstabFieldMountPoint FstabFieldName = "mountpoint"
|
||||||
|
FstabFieldFileSystemType FstabFieldName = "filesystemtype"
|
||||||
|
FstabFieldOptions FstabFieldName = "options"
|
||||||
|
FstabFieldFreq FstabFieldName = "freq"
|
||||||
|
FstabFieldPass FstabFieldName = "pass"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FstabField struct {
|
||||||
|
common.LocationRange
|
||||||
|
Value commonparser.ParsedString
|
||||||
|
}
|
||||||
|
|
||||||
|
type FstabFields struct {
|
||||||
|
common.LocationRange
|
||||||
|
Spec *FstabField
|
||||||
|
MountPoint *FstabField
|
||||||
|
FilesystemType *FstabField
|
||||||
|
Options *FstabField
|
||||||
|
Freq *FstabField
|
||||||
|
Pass *FstabField
|
||||||
|
}
|
||||||
|
|
||||||
|
type FstabEntry struct {
|
||||||
|
Fields *FstabFields
|
||||||
|
}
|
||||||
|
|
||||||
|
type FstabConfig struct {
|
||||||
|
// [uint32]FstabEntry - line number to line mapping
|
||||||
|
Entries *treemap.Map
|
||||||
|
|
||||||
|
// [uint32]{} - line number to empty struct for comments
|
||||||
|
CommentLines map[uint32]struct{}
|
||||||
|
}
|
172
server/handlers/fstab/ast/fstab_fields.go
Normal file
172
server/handlers/fstab/ast/fstab_fields.go
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
docvalues "config-lsp/doc-values"
|
||||||
|
"config-lsp/handlers/fstab/fields"
|
||||||
|
"config-lsp/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// func (c FstabConfig) GetEntry(line uint32) *FstabEntry {
|
||||||
|
// entry, found := c.Entries.Get(line)
|
||||||
|
//
|
||||||
|
// if !found {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return entry.(*FstabEntry)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// LABEL=test ext4 defaults 0 0
|
||||||
|
func (e FstabEntry) GetFieldAtPosition(position common.Position) FstabFieldName {
|
||||||
|
// No fields defined, empty line
|
||||||
|
if e.Fields.Spec == nil && e.Fields.MountPoint == nil && e.Fields.FilesystemType == nil && e.Fields.Options == nil && e.Fields.Freq == nil && e.Fields.Pass == nil {
|
||||||
|
return FstabFieldSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, try if out of the existing fields the user wants to edit one of them
|
||||||
|
|
||||||
|
if e.Fields.Spec != nil && e.Fields.Spec.ContainsPosition(position) {
|
||||||
|
return FstabFieldSpec
|
||||||
|
}
|
||||||
|
if e.Fields.MountPoint != nil && e.Fields.MountPoint.ContainsPosition(position) {
|
||||||
|
return FstabFieldMountPoint
|
||||||
|
}
|
||||||
|
if e.Fields.FilesystemType != nil && e.Fields.FilesystemType.ContainsPosition(position) {
|
||||||
|
return FstabFieldFileSystemType
|
||||||
|
}
|
||||||
|
if e.Fields.Options != nil && e.Fields.Options.ContainsPosition(position) {
|
||||||
|
return FstabFieldOptions
|
||||||
|
}
|
||||||
|
if e.Fields.Freq != nil && e.Fields.Freq.ContainsPosition(position) {
|
||||||
|
return FstabFieldFreq
|
||||||
|
}
|
||||||
|
if e.Fields.Pass != nil && e.Fields.Pass.ContainsPosition(position) {
|
||||||
|
return FstabFieldPass
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okay let's try to fetch the field by assuming the user is typing from left to right normally
|
||||||
|
|
||||||
|
if e.Fields.Spec != nil && e.Fields.Spec.IsPositionAfterEnd(position) && (e.Fields.MountPoint == nil || e.Fields.MountPoint.IsPositionBeforeEnd(position)) {
|
||||||
|
return FstabFieldMountPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.MountPoint != nil && e.Fields.MountPoint.IsPositionAfterEnd(position) && (e.Fields.FilesystemType == nil || e.Fields.FilesystemType.IsPositionBeforeEnd(position)) {
|
||||||
|
return FstabFieldFileSystemType
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.FilesystemType != nil && e.Fields.FilesystemType.IsPositionAfterEnd(position) && (e.Fields.Options == nil || e.Fields.Options.IsPositionBeforeEnd(position)) {
|
||||||
|
return FstabFieldOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.Options != nil && e.Fields.Options.IsPositionAfterEnd(position) && (e.Fields.Freq == nil || e.Fields.Freq.IsPositionBeforeEnd(position)) {
|
||||||
|
return FstabFieldFreq
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.Freq != nil && e.Fields.Freq.IsPositionAfterEnd(position) && (e.Fields.Pass == nil || e.Fields.Pass.IsPositionBeforeEnd(position)) {
|
||||||
|
return FstabFieldPass
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okay shit no idea, let's just give whatever is missing
|
||||||
|
|
||||||
|
if e.Fields.Spec == nil {
|
||||||
|
return FstabFieldSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.MountPoint == nil {
|
||||||
|
return FstabFieldMountPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.FilesystemType == nil {
|
||||||
|
return FstabFieldFileSystemType
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.Options == nil {
|
||||||
|
return FstabFieldOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Fields.Freq == nil {
|
||||||
|
return FstabFieldFreq
|
||||||
|
}
|
||||||
|
|
||||||
|
return FstabFieldPass
|
||||||
|
}
|
||||||
|
|
||||||
|
// LABEL=test /mnt/test btrfs subvol=backup,fat=32 [0] [0]
|
||||||
|
func (e FstabEntry) getCursorIndex() uint8 {
|
||||||
|
definedAmount := e.getDefinedFieldsAmount()
|
||||||
|
|
||||||
|
switch definedAmount {
|
||||||
|
case 5:
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e FstabEntry) getDefinedFieldsAmount() uint8 {
|
||||||
|
var definedAmount uint8 = 0
|
||||||
|
|
||||||
|
if e.Fields.Spec != nil {
|
||||||
|
definedAmount++
|
||||||
|
}
|
||||||
|
if e.Fields.MountPoint != nil {
|
||||||
|
definedAmount++
|
||||||
|
}
|
||||||
|
if e.Fields.FilesystemType != nil {
|
||||||
|
definedAmount++
|
||||||
|
}
|
||||||
|
if e.Fields.Options != nil {
|
||||||
|
definedAmount++
|
||||||
|
}
|
||||||
|
if e.Fields.Freq != nil {
|
||||||
|
definedAmount++
|
||||||
|
}
|
||||||
|
if e.Fields.Pass != nil {
|
||||||
|
definedAmount++
|
||||||
|
}
|
||||||
|
|
||||||
|
return definedAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a mount options field for the entry
|
||||||
|
func (e FstabEntry) FetchMountOptionsField(includeDefaults bool) docvalues.DeprecatedValue {
|
||||||
|
if e.Fields.FilesystemType == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
option, found := fields.MountOptionsMapField[e.Fields.FilesystemType.Value.Value]
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var enums []docvalues.EnumString
|
||||||
|
var assignable map[docvalues.EnumString]docvalues.DeprecatedValue
|
||||||
|
|
||||||
|
if includeDefaults {
|
||||||
|
enums = append(option.Enums, fields.DefaultOptions...)
|
||||||
|
assignable = utils.MergeMaps(option.Assignable, fields.DefaultAssignOptions)
|
||||||
|
} else {
|
||||||
|
enums = option.Enums
|
||||||
|
assignable = option.Assignable
|
||||||
|
}
|
||||||
|
|
||||||
|
return &docvalues.ArrayValue{
|
||||||
|
Separator: ",",
|
||||||
|
DuplicatesExtractor: &fields.MountOptionsExtractor,
|
||||||
|
SubValue: docvalues.OrValue{
|
||||||
|
Values: []docvalues.DeprecatedValue{
|
||||||
|
docvalues.KeyEnumAssignmentValue{
|
||||||
|
Values: assignable,
|
||||||
|
ValueIsOptional: false,
|
||||||
|
Separator: "=",
|
||||||
|
},
|
||||||
|
docvalues.EnumValue{
|
||||||
|
EnforceValues: true,
|
||||||
|
Values: enums,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
135
server/handlers/fstab/ast/listener.go
Normal file
135
server/handlers/fstab/ast/listener.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/fstab/ast/parser"
|
||||||
|
|
||||||
|
commonparser "config-lsp/common/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fstabListenerContext struct {
|
||||||
|
line uint32
|
||||||
|
currentEntry *FstabEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func createListenerContext() *fstabListenerContext {
|
||||||
|
context := new(fstabListenerContext)
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
type fstabParserListener struct {
|
||||||
|
*parser.BaseFstabListener
|
||||||
|
Config *FstabConfig
|
||||||
|
Errors []common.LSPError
|
||||||
|
fstabContext *fstabListenerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func createListener(
|
||||||
|
config *FstabConfig,
|
||||||
|
context *fstabListenerContext,
|
||||||
|
) fstabParserListener {
|
||||||
|
return fstabParserListener{
|
||||||
|
Config: config,
|
||||||
|
Errors: make([]common.LSPError, 0),
|
||||||
|
fstabContext: context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) EnterEntry(ctx *parser.EntryContext) {
|
||||||
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
|
location.ChangeBothLines(s.fstabContext.line)
|
||||||
|
|
||||||
|
s.fstabContext.currentEntry = &FstabEntry{
|
||||||
|
Fields: &FstabFields{
|
||||||
|
LocationRange: location,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Config.Entries.Put(
|
||||||
|
s.fstabContext.line,
|
||||||
|
s.fstabContext.currentEntry,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) ExitEntry(ctx *parser.EntryContext) {
|
||||||
|
s.fstabContext.currentEntry = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) EnterSpec(ctx *parser.SpecContext) {
|
||||||
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
|
location.ChangeBothLines(s.fstabContext.line)
|
||||||
|
|
||||||
|
text := ctx.GetText()
|
||||||
|
value := commonparser.ParseRawString(text, commonparser.FullFeatures)
|
||||||
|
|
||||||
|
s.fstabContext.currentEntry.Fields.Spec = &FstabField{
|
||||||
|
Value: value,
|
||||||
|
LocationRange: location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) EnterMountPoint(ctx *parser.MountPointContext) {
|
||||||
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
|
location.ChangeBothLines(s.fstabContext.line)
|
||||||
|
|
||||||
|
text := ctx.GetText()
|
||||||
|
value := commonparser.ParseRawString(text, commonparser.FullFeatures)
|
||||||
|
|
||||||
|
s.fstabContext.currentEntry.Fields.MountPoint = &FstabField{
|
||||||
|
LocationRange: location,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) EnterFileSystem(ctx *parser.FileSystemContext) {
|
||||||
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
|
location.ChangeBothLines(s.fstabContext.line)
|
||||||
|
|
||||||
|
text := ctx.GetText()
|
||||||
|
value := commonparser.ParseRawString(text, commonparser.FullFeatures)
|
||||||
|
|
||||||
|
s.fstabContext.currentEntry.Fields.FilesystemType = &FstabField{
|
||||||
|
LocationRange: location,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) EnterMountOptions(ctx *parser.MountOptionsContext) {
|
||||||
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
|
location.ChangeBothLines(s.fstabContext.line)
|
||||||
|
|
||||||
|
text := ctx.GetText()
|
||||||
|
value := commonparser.ParseRawString(text, commonparser.FullFeatures)
|
||||||
|
|
||||||
|
s.fstabContext.currentEntry.Fields.Options = &FstabField{
|
||||||
|
LocationRange: location,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) EnterFreq(ctx *parser.FreqContext) {
|
||||||
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
|
location.ChangeBothLines(s.fstabContext.line)
|
||||||
|
|
||||||
|
text := ctx.GetText()
|
||||||
|
value := commonparser.ParseRawString(text, commonparser.FullFeatures)
|
||||||
|
|
||||||
|
s.fstabContext.currentEntry.Fields.Freq = &FstabField{
|
||||||
|
LocationRange: location,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fstabParserListener) EnterPass(ctx *parser.PassContext) {
|
||||||
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
|
location.ChangeBothLines(s.fstabContext.line)
|
||||||
|
|
||||||
|
text := ctx.GetText()
|
||||||
|
value := commonparser.ParseRawString(text, commonparser.FullFeatures)
|
||||||
|
|
||||||
|
s.fstabContext.currentEntry.Fields.Pass = &FstabField{
|
||||||
|
LocationRange: location,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
}
|
201
server/handlers/fstab/ast/parser.go
Normal file
201
server/handlers/fstab/ast/parser.go
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
commonparser "config-lsp/common/parser"
|
||||||
|
"config-lsp/handlers/fstab/ast/parser"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/antlr4-go/antlr/v4"
|
||||||
|
"github.com/emirpasic/gods/maps/treemap"
|
||||||
|
|
||||||
|
gods "github.com/emirpasic/gods/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewFstabConfig() *FstabConfig {
|
||||||
|
config := &FstabConfig{}
|
||||||
|
config.Clear()
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FstabConfig) Clear() {
|
||||||
|
c.Entries = treemap.NewWith(gods.UInt32Comparator)
|
||||||
|
c.CommentLines = map[uint32]struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var commentPattern = regexp.MustCompile(`^\s*#`)
|
||||||
|
var emptyPattern = regexp.MustCompile(`^\s*$`)
|
||||||
|
var leadingCommentPattern = regexp.MustCompile(`^(.+?)#`)
|
||||||
|
|
||||||
|
func (c *FstabConfig) Parse(input string) []common.LSPError {
|
||||||
|
errors := make([]common.LSPError, 0)
|
||||||
|
lines := utils.SplitIntoLines(input)
|
||||||
|
context := createListenerContext()
|
||||||
|
|
||||||
|
for rawLineNumber, rawLine := range lines {
|
||||||
|
line := rawLine
|
||||||
|
lineNumber := uint32(rawLineNumber)
|
||||||
|
context.line = lineNumber
|
||||||
|
|
||||||
|
if emptyPattern.MatchString(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if commentPattern.MatchString(line) {
|
||||||
|
c.CommentLines[lineNumber] = struct{}{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(line, "#") {
|
||||||
|
matches := leadingCommentPattern.FindStringSubmatch(line)
|
||||||
|
line = matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
errors = append(
|
||||||
|
errors,
|
||||||
|
c.parseStatement(context, line)...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FstabConfig) parseStatement(
|
||||||
|
context *fstabListenerContext,
|
||||||
|
input string,
|
||||||
|
) []common.LSPError {
|
||||||
|
stream := antlr.NewInputStream(input)
|
||||||
|
|
||||||
|
lexerErrorListener := createErrorListener(context.line)
|
||||||
|
lexer := parser.NewFstabLexer(stream)
|
||||||
|
lexer.RemoveErrorListeners()
|
||||||
|
lexer.AddErrorListener(&lexerErrorListener)
|
||||||
|
|
||||||
|
tokenStream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
|
||||||
|
|
||||||
|
parserErrorListener := createErrorListener(context.line)
|
||||||
|
antlrParser := parser.NewFstabParser(tokenStream)
|
||||||
|
antlrParser.RemoveErrorListeners()
|
||||||
|
antlrParser.AddErrorListener(&parserErrorListener)
|
||||||
|
|
||||||
|
listener := createListener(c, context)
|
||||||
|
antlr.ParseTreeWalkerDefault.Walk(
|
||||||
|
&listener,
|
||||||
|
antlrParser.Entry(),
|
||||||
|
)
|
||||||
|
|
||||||
|
errors := lexerErrorListener.Errors
|
||||||
|
errors = append(errors, parserErrorListener.Errors...)
|
||||||
|
errors = append(errors, listener.Errors...)
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (c *FstabConfig) parseStatement(
|
||||||
|
// line uint32,
|
||||||
|
// input string,
|
||||||
|
// ) []common.LSPError {
|
||||||
|
// fields := whitespacePattern.FindAllStringIndex(input, -1)
|
||||||
|
//
|
||||||
|
// if len(fields) == 0 {
|
||||||
|
// return []common.LSPError{
|
||||||
|
// {
|
||||||
|
// Range: common.LocationRange{
|
||||||
|
// Start: common.Location{
|
||||||
|
// Line: line,
|
||||||
|
// Character: 0,
|
||||||
|
// },
|
||||||
|
// End: common.Location{
|
||||||
|
// Line: line,
|
||||||
|
// Character: 0,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var spec *FstabField
|
||||||
|
// var mountPoint *FstabField
|
||||||
|
// var filesystemType *FstabField
|
||||||
|
// var options *FstabField
|
||||||
|
// var freq *FstabField
|
||||||
|
// var pass *FstabField
|
||||||
|
//
|
||||||
|
// switch len(fields) {
|
||||||
|
// case 6:
|
||||||
|
// pass = parseField(line, input, fields[5])
|
||||||
|
// fallthrough
|
||||||
|
// case 5:
|
||||||
|
// freq = parseField(line, input, fields[4])
|
||||||
|
// fallthrough
|
||||||
|
// case 4:
|
||||||
|
// options = parseField(line, input, fields[3])
|
||||||
|
// fallthrough
|
||||||
|
// case 3:
|
||||||
|
// filesystemType = parseField(line, input, fields[2])
|
||||||
|
// fallthrough
|
||||||
|
// case 2:
|
||||||
|
// mountPoint = parseField(line, input, fields[1])
|
||||||
|
// fallthrough
|
||||||
|
// case 1:
|
||||||
|
// spec = parseField(line, input, fields[0])
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fstabLine := &FstabEntry{
|
||||||
|
// Fields: FstabFields{
|
||||||
|
// LocationRange: common.LocationRange{
|
||||||
|
// Start: common.Location{
|
||||||
|
// Line: line,
|
||||||
|
// Character: 0,
|
||||||
|
// },
|
||||||
|
// End: common.Location{
|
||||||
|
// Line: line,
|
||||||
|
// Character: uint32(len(input)),
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// Spec: spec,
|
||||||
|
// MountPoint: mountPoint,
|
||||||
|
// FilesystemType: filesystemType,
|
||||||
|
// Options: options,
|
||||||
|
// Freq: freq,
|
||||||
|
// Pass: pass,
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// c.Entries.Put(line, fstabLine)
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func parseField(
|
||||||
|
line uint32,
|
||||||
|
input string,
|
||||||
|
field []int,
|
||||||
|
) *FstabField {
|
||||||
|
start := uint32(field[0])
|
||||||
|
end := uint32(field[1])
|
||||||
|
value := input[start:end]
|
||||||
|
|
||||||
|
return &FstabField{
|
||||||
|
LocationRange: common.LocationRange{
|
||||||
|
Start: common.Location{
|
||||||
|
Line: line,
|
||||||
|
Character: start,
|
||||||
|
},
|
||||||
|
End: common.Location{
|
||||||
|
Line: line,
|
||||||
|
Character: end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Value: commonparser.ParseRawString(value, commonparser.ParseFeatures{
|
||||||
|
ParseEscapedCharacters: true,
|
||||||
|
ParseDoubleQuotes: true,
|
||||||
|
Replacements: &map[string]string{
|
||||||
|
`\\040`: " ",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
36
server/handlers/fstab/ast/parser/Fstab.interp
Normal file
36
server/handlers/fstab/ast/parser/Fstab.interp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
token literal names:
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
'#'
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
|
||||||
|
token symbolic names:
|
||||||
|
null
|
||||||
|
DIGITS
|
||||||
|
WHITESPACE
|
||||||
|
HASH
|
||||||
|
STRING
|
||||||
|
QUOTED_STRING
|
||||||
|
ADFS
|
||||||
|
AFFS
|
||||||
|
BTRFS
|
||||||
|
EXFAT
|
||||||
|
|
||||||
|
rule names:
|
||||||
|
entry
|
||||||
|
spec
|
||||||
|
mountPoint
|
||||||
|
fileSystem
|
||||||
|
mountOptions
|
||||||
|
freq
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
atn:
|
||||||
|
[4, 1, 9, 68, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 3, 0, 16, 8, 0, 1, 0, 3, 0, 19, 8, 0, 1, 0, 3, 0, 22, 8, 0, 1, 0, 3, 0, 25, 8, 0, 1, 0, 3, 0, 28, 8, 0, 1, 0, 3, 0, 31, 8, 0, 1, 0, 3, 0, 34, 8, 0, 1, 0, 3, 0, 37, 8, 0, 1, 0, 3, 0, 40, 8, 0, 1, 0, 3, 0, 43, 8, 0, 1, 0, 3, 0, 46, 8, 0, 1, 0, 3, 0, 49, 8, 0, 1, 0, 3, 0, 52, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 0, 0, 7, 0, 2, 4, 6, 8, 10, 12, 0, 2, 1, 0, 4, 5, 1, 0, 4, 9, 73, 0, 15, 1, 0, 0, 0, 2, 55, 1, 0, 0, 0, 4, 57, 1, 0, 0, 0, 6, 59, 1, 0, 0, 0, 8, 61, 1, 0, 0, 0, 10, 63, 1, 0, 0, 0, 12, 65, 1, 0, 0, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 19, 3, 2, 1, 0, 18, 17, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 21, 1, 0, 0, 0, 20, 22, 5, 2, 0, 0, 21, 20, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1, 0, 0, 0, 23, 25, 3, 4, 2, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, 0, 0, 0, 26, 28, 5, 2, 0, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 30, 1, 0, 0, 0, 29, 31, 3, 6, 3, 0, 30, 29, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 33, 1, 0, 0, 0, 32, 34, 5, 2, 0, 0, 33, 32, 1, 0, 0, 0, 33, 34, 1, 0, 0, 0, 34, 36, 1, 0, 0, 0, 35, 37, 3, 8, 4, 0, 36, 35, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 39, 1, 0, 0, 0, 38, 40, 5, 2, 0, 0, 39, 38, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 1, 0, 0, 0, 41, 43, 3, 10, 5, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 45, 1, 0, 0, 0, 44, 46, 5, 2, 0, 0, 45, 44, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 48, 1, 0, 0, 0, 47, 49, 3, 12, 6, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 0, 0, 0, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 53, 1, 0, 0, 0, 53, 54, 5, 0, 0, 1, 54, 1, 1, 0, 0, 0, 55, 56, 7, 0, 0, 0, 56, 3, 1, 0, 0, 0, 57, 58, 7, 0, 0, 0, 58, 5, 1, 0, 0, 0, 59, 60, 7, 1, 0, 0, 60, 7, 1, 0, 0, 0, 61, 62, 7, 0, 0, 0, 62, 9, 1, 0, 0, 0, 63, 64, 5, 1, 0, 0, 64, 11, 1, 0, 0, 0, 65, 66, 5, 1, 0, 0, 66, 13, 1, 0, 0, 0, 13, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51]
|
10
server/handlers/fstab/ast/parser/Fstab.tokens
Normal file
10
server/handlers/fstab/ast/parser/Fstab.tokens
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
DIGITS=1
|
||||||
|
WHITESPACE=2
|
||||||
|
HASH=3
|
||||||
|
STRING=4
|
||||||
|
QUOTED_STRING=5
|
||||||
|
ADFS=6
|
||||||
|
AFFS=7
|
||||||
|
BTRFS=8
|
||||||
|
EXFAT=9
|
||||||
|
'#'=3
|
44
server/handlers/fstab/ast/parser/FstabLexer.interp
Normal file
44
server/handlers/fstab/ast/parser/FstabLexer.interp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
token literal names:
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
'#'
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
|
||||||
|
token symbolic names:
|
||||||
|
null
|
||||||
|
DIGITS
|
||||||
|
WHITESPACE
|
||||||
|
HASH
|
||||||
|
STRING
|
||||||
|
QUOTED_STRING
|
||||||
|
ADFS
|
||||||
|
AFFS
|
||||||
|
BTRFS
|
||||||
|
EXFAT
|
||||||
|
|
||||||
|
rule names:
|
||||||
|
DIGITS
|
||||||
|
WHITESPACE
|
||||||
|
HASH
|
||||||
|
STRING
|
||||||
|
QUOTED_STRING
|
||||||
|
ADFS
|
||||||
|
AFFS
|
||||||
|
BTRFS
|
||||||
|
EXFAT
|
||||||
|
|
||||||
|
channel names:
|
||||||
|
DEFAULT_TOKEN_CHANNEL
|
||||||
|
HIDDEN
|
||||||
|
|
||||||
|
mode names:
|
||||||
|
DEFAULT_MODE
|
||||||
|
|
||||||
|
atn:
|
||||||
|
[4, 0, 9, 76, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 1, 0, 4, 0, 21, 8, 0, 11, 0, 12, 0, 22, 1, 1, 4, 1, 26, 8, 1, 11, 1, 12, 1, 27, 1, 2, 1, 2, 1, 3, 4, 3, 33, 8, 3, 11, 3, 12, 3, 34, 1, 4, 1, 4, 3, 4, 39, 8, 4, 1, 4, 1, 4, 1, 4, 5, 4, 44, 8, 4, 10, 4, 12, 4, 47, 9, 4, 1, 4, 3, 4, 50, 8, 4, 1, 4, 3, 4, 53, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 0, 0, 9, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 1, 0, 12, 1, 0, 48, 57, 2, 0, 9, 9, 32, 32, 3, 0, 9, 9, 32, 32, 35, 35, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 70, 70, 102, 102, 2, 0, 83, 83, 115, 115, 2, 0, 66, 66, 98, 98, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 69, 69, 101, 101, 2, 0, 88, 88, 120, 120, 82, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 1, 20, 1, 0, 0, 0, 3, 25, 1, 0, 0, 0, 5, 29, 1, 0, 0, 0, 7, 32, 1, 0, 0, 0, 9, 36, 1, 0, 0, 0, 11, 54, 1, 0, 0, 0, 13, 59, 1, 0, 0, 0, 15, 64, 1, 0, 0, 0, 17, 70, 1, 0, 0, 0, 19, 21, 7, 0, 0, 0, 20, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 20, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 2, 1, 0, 0, 0, 24, 26, 7, 1, 0, 0, 25, 24, 1, 0, 0, 0, 26, 27, 1, 0, 0, 0, 27, 25, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 4, 1, 0, 0, 0, 29, 30, 5, 35, 0, 0, 30, 6, 1, 0, 0, 0, 31, 33, 8, 2, 0, 0, 32, 31, 1, 0, 0, 0, 33, 34, 1, 0, 0, 0, 34, 32, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 8, 1, 0, 0, 0, 36, 38, 5, 34, 0, 0, 37, 39, 3, 3, 1, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 45, 1, 0, 0, 0, 40, 41, 3, 7, 3, 0, 41, 42, 3, 3, 1, 0, 42, 44, 1, 0, 0, 0, 43, 40, 1, 0, 0, 0, 44, 47, 1, 0, 0, 0, 45, 43, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 49, 1, 0, 0, 0, 47, 45, 1, 0, 0, 0, 48, 50, 3, 7, 3, 0, 49, 48, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 50, 52, 1, 0, 0, 0, 51, 53, 5, 34, 0, 0, 52, 51, 1, 0, 0, 0, 52, 53, 1, 0, 0, 0, 53, 10, 1, 0, 0, 0, 54, 55, 7, 3, 0, 0, 55, 56, 7, 4, 0, 0, 56, 57, 7, 5, 0, 0, 57, 58, 7, 6, 0, 0, 58, 12, 1, 0, 0, 0, 59, 60, 7, 3, 0, 0, 60, 61, 7, 5, 0, 0, 61, 62, 7, 5, 0, 0, 62, 63, 7, 6, 0, 0, 63, 14, 1, 0, 0, 0, 64, 65, 7, 7, 0, 0, 65, 66, 7, 8, 0, 0, 66, 67, 7, 9, 0, 0, 67, 68, 7, 5, 0, 0, 68, 69, 7, 6, 0, 0, 69, 16, 1, 0, 0, 0, 70, 71, 7, 10, 0, 0, 71, 72, 7, 11, 0, 0, 72, 73, 7, 5, 0, 0, 73, 74, 7, 3, 0, 0, 74, 75, 7, 8, 0, 0, 75, 18, 1, 0, 0, 0, 8, 0, 22, 27, 34, 38, 45, 49, 52, 0]
|
10
server/handlers/fstab/ast/parser/FstabLexer.tokens
Normal file
10
server/handlers/fstab/ast/parser/FstabLexer.tokens
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
DIGITS=1
|
||||||
|
WHITESPACE=2
|
||||||
|
HASH=3
|
||||||
|
STRING=4
|
||||||
|
QUOTED_STRING=5
|
||||||
|
ADFS=6
|
||||||
|
AFFS=7
|
||||||
|
BTRFS=8
|
||||||
|
EXFAT=9
|
||||||
|
'#'=3
|
64
server/handlers/fstab/ast/parser/fstab_base_listener.go
Normal file
64
server/handlers/fstab/ast/parser/fstab_base_listener.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Code generated from Fstab.g4 by ANTLR 4.13.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
package parser // Fstab
|
||||||
|
|
||||||
|
import "github.com/antlr4-go/antlr/v4"
|
||||||
|
|
||||||
|
// BaseFstabListener is a complete listener for a parse tree produced by FstabParser.
|
||||||
|
type BaseFstabListener struct{}
|
||||||
|
|
||||||
|
var _ FstabListener = &BaseFstabListener{}
|
||||||
|
|
||||||
|
// VisitTerminal is called when a terminal node is visited.
|
||||||
|
func (s *BaseFstabListener) VisitTerminal(node antlr.TerminalNode) {}
|
||||||
|
|
||||||
|
// VisitErrorNode is called when an error node is visited.
|
||||||
|
func (s *BaseFstabListener) VisitErrorNode(node antlr.ErrorNode) {}
|
||||||
|
|
||||||
|
// EnterEveryRule is called when any rule is entered.
|
||||||
|
func (s *BaseFstabListener) EnterEveryRule(ctx antlr.ParserRuleContext) {}
|
||||||
|
|
||||||
|
// ExitEveryRule is called when any rule is exited.
|
||||||
|
func (s *BaseFstabListener) ExitEveryRule(ctx antlr.ParserRuleContext) {}
|
||||||
|
|
||||||
|
// EnterEntry is called when production entry is entered.
|
||||||
|
func (s *BaseFstabListener) EnterEntry(ctx *EntryContext) {}
|
||||||
|
|
||||||
|
// ExitEntry is called when production entry is exited.
|
||||||
|
func (s *BaseFstabListener) ExitEntry(ctx *EntryContext) {}
|
||||||
|
|
||||||
|
// EnterSpec is called when production spec is entered.
|
||||||
|
func (s *BaseFstabListener) EnterSpec(ctx *SpecContext) {}
|
||||||
|
|
||||||
|
// ExitSpec is called when production spec is exited.
|
||||||
|
func (s *BaseFstabListener) ExitSpec(ctx *SpecContext) {}
|
||||||
|
|
||||||
|
// EnterMountPoint is called when production mountPoint is entered.
|
||||||
|
func (s *BaseFstabListener) EnterMountPoint(ctx *MountPointContext) {}
|
||||||
|
|
||||||
|
// ExitMountPoint is called when production mountPoint is exited.
|
||||||
|
func (s *BaseFstabListener) ExitMountPoint(ctx *MountPointContext) {}
|
||||||
|
|
||||||
|
// EnterFileSystem is called when production fileSystem is entered.
|
||||||
|
func (s *BaseFstabListener) EnterFileSystem(ctx *FileSystemContext) {}
|
||||||
|
|
||||||
|
// ExitFileSystem is called when production fileSystem is exited.
|
||||||
|
func (s *BaseFstabListener) ExitFileSystem(ctx *FileSystemContext) {}
|
||||||
|
|
||||||
|
// EnterMountOptions is called when production mountOptions is entered.
|
||||||
|
func (s *BaseFstabListener) EnterMountOptions(ctx *MountOptionsContext) {}
|
||||||
|
|
||||||
|
// ExitMountOptions is called when production mountOptions is exited.
|
||||||
|
func (s *BaseFstabListener) ExitMountOptions(ctx *MountOptionsContext) {}
|
||||||
|
|
||||||
|
// EnterFreq is called when production freq is entered.
|
||||||
|
func (s *BaseFstabListener) EnterFreq(ctx *FreqContext) {}
|
||||||
|
|
||||||
|
// ExitFreq is called when production freq is exited.
|
||||||
|
func (s *BaseFstabListener) ExitFreq(ctx *FreqContext) {}
|
||||||
|
|
||||||
|
// EnterPass is called when production pass is entered.
|
||||||
|
func (s *BaseFstabListener) EnterPass(ctx *PassContext) {}
|
||||||
|
|
||||||
|
// ExitPass is called when production pass is exited.
|
||||||
|
func (s *BaseFstabListener) ExitPass(ctx *PassContext) {}
|
143
server/handlers/fstab/ast/parser/fstab_lexer.go
Normal file
143
server/handlers/fstab/ast/parser/fstab_lexer.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// Code generated from Fstab.g4 by ANTLR 4.13.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/antlr4-go/antlr/v4"
|
||||||
|
"sync"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Suppress unused import error
|
||||||
|
var _ = fmt.Printf
|
||||||
|
var _ = sync.Once{}
|
||||||
|
var _ = unicode.IsLetter
|
||||||
|
|
||||||
|
type FstabLexer struct {
|
||||||
|
*antlr.BaseLexer
|
||||||
|
channelNames []string
|
||||||
|
modeNames []string
|
||||||
|
// TODO: EOF string
|
||||||
|
}
|
||||||
|
|
||||||
|
var FstabLexerLexerStaticData struct {
|
||||||
|
once sync.Once
|
||||||
|
serializedATN []int32
|
||||||
|
ChannelNames []string
|
||||||
|
ModeNames []string
|
||||||
|
LiteralNames []string
|
||||||
|
SymbolicNames []string
|
||||||
|
RuleNames []string
|
||||||
|
PredictionContextCache *antlr.PredictionContextCache
|
||||||
|
atn *antlr.ATN
|
||||||
|
decisionToDFA []*antlr.DFA
|
||||||
|
}
|
||||||
|
|
||||||
|
func fstablexerLexerInit() {
|
||||||
|
staticData := &FstabLexerLexerStaticData
|
||||||
|
staticData.ChannelNames = []string{
|
||||||
|
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
|
||||||
|
}
|
||||||
|
staticData.ModeNames = []string{
|
||||||
|
"DEFAULT_MODE",
|
||||||
|
}
|
||||||
|
staticData.LiteralNames = []string{
|
||||||
|
"", "", "", "'#'",
|
||||||
|
}
|
||||||
|
staticData.SymbolicNames = []string{
|
||||||
|
"", "DIGITS", "WHITESPACE", "HASH", "STRING", "QUOTED_STRING", "ADFS",
|
||||||
|
"AFFS", "BTRFS", "EXFAT",
|
||||||
|
}
|
||||||
|
staticData.RuleNames = []string{
|
||||||
|
"DIGITS", "WHITESPACE", "HASH", "STRING", "QUOTED_STRING", "ADFS", "AFFS",
|
||||||
|
"BTRFS", "EXFAT",
|
||||||
|
}
|
||||||
|
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
|
||||||
|
staticData.serializedATN = []int32{
|
||||||
|
4, 0, 9, 76, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
|
||||||
|
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 1, 0, 4, 0, 21,
|
||||||
|
8, 0, 11, 0, 12, 0, 22, 1, 1, 4, 1, 26, 8, 1, 11, 1, 12, 1, 27, 1, 2, 1,
|
||||||
|
2, 1, 3, 4, 3, 33, 8, 3, 11, 3, 12, 3, 34, 1, 4, 1, 4, 3, 4, 39, 8, 4,
|
||||||
|
1, 4, 1, 4, 1, 4, 5, 4, 44, 8, 4, 10, 4, 12, 4, 47, 9, 4, 1, 4, 3, 4, 50,
|
||||||
|
8, 4, 1, 4, 3, 4, 53, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1,
|
||||||
|
6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1,
|
||||||
|
8, 1, 8, 1, 8, 0, 0, 9, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15,
|
||||||
|
8, 17, 9, 1, 0, 12, 1, 0, 48, 57, 2, 0, 9, 9, 32, 32, 3, 0, 9, 9, 32, 32,
|
||||||
|
35, 35, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 70, 70, 102,
|
||||||
|
102, 2, 0, 83, 83, 115, 115, 2, 0, 66, 66, 98, 98, 2, 0, 84, 84, 116, 116,
|
||||||
|
2, 0, 82, 82, 114, 114, 2, 0, 69, 69, 101, 101, 2, 0, 88, 88, 120, 120,
|
||||||
|
82, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0,
|
||||||
|
0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0,
|
||||||
|
0, 0, 0, 17, 1, 0, 0, 0, 1, 20, 1, 0, 0, 0, 3, 25, 1, 0, 0, 0, 5, 29, 1,
|
||||||
|
0, 0, 0, 7, 32, 1, 0, 0, 0, 9, 36, 1, 0, 0, 0, 11, 54, 1, 0, 0, 0, 13,
|
||||||
|
59, 1, 0, 0, 0, 15, 64, 1, 0, 0, 0, 17, 70, 1, 0, 0, 0, 19, 21, 7, 0, 0,
|
||||||
|
0, 20, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 20, 1, 0, 0, 0, 22, 23,
|
||||||
|
1, 0, 0, 0, 23, 2, 1, 0, 0, 0, 24, 26, 7, 1, 0, 0, 25, 24, 1, 0, 0, 0,
|
||||||
|
26, 27, 1, 0, 0, 0, 27, 25, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 4, 1, 0,
|
||||||
|
0, 0, 29, 30, 5, 35, 0, 0, 30, 6, 1, 0, 0, 0, 31, 33, 8, 2, 0, 0, 32, 31,
|
||||||
|
1, 0, 0, 0, 33, 34, 1, 0, 0, 0, 34, 32, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0,
|
||||||
|
35, 8, 1, 0, 0, 0, 36, 38, 5, 34, 0, 0, 37, 39, 3, 3, 1, 0, 38, 37, 1,
|
||||||
|
0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 45, 1, 0, 0, 0, 40, 41, 3, 7, 3, 0, 41,
|
||||||
|
42, 3, 3, 1, 0, 42, 44, 1, 0, 0, 0, 43, 40, 1, 0, 0, 0, 44, 47, 1, 0, 0,
|
||||||
|
0, 45, 43, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 49, 1, 0, 0, 0, 47, 45,
|
||||||
|
1, 0, 0, 0, 48, 50, 3, 7, 3, 0, 49, 48, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0,
|
||||||
|
50, 52, 1, 0, 0, 0, 51, 53, 5, 34, 0, 0, 52, 51, 1, 0, 0, 0, 52, 53, 1,
|
||||||
|
0, 0, 0, 53, 10, 1, 0, 0, 0, 54, 55, 7, 3, 0, 0, 55, 56, 7, 4, 0, 0, 56,
|
||||||
|
57, 7, 5, 0, 0, 57, 58, 7, 6, 0, 0, 58, 12, 1, 0, 0, 0, 59, 60, 7, 3, 0,
|
||||||
|
0, 60, 61, 7, 5, 0, 0, 61, 62, 7, 5, 0, 0, 62, 63, 7, 6, 0, 0, 63, 14,
|
||||||
|
1, 0, 0, 0, 64, 65, 7, 7, 0, 0, 65, 66, 7, 8, 0, 0, 66, 67, 7, 9, 0, 0,
|
||||||
|
67, 68, 7, 5, 0, 0, 68, 69, 7, 6, 0, 0, 69, 16, 1, 0, 0, 0, 70, 71, 7,
|
||||||
|
10, 0, 0, 71, 72, 7, 11, 0, 0, 72, 73, 7, 5, 0, 0, 73, 74, 7, 3, 0, 0,
|
||||||
|
74, 75, 7, 8, 0, 0, 75, 18, 1, 0, 0, 0, 8, 0, 22, 27, 34, 38, 45, 49, 52,
|
||||||
|
0,
|
||||||
|
}
|
||||||
|
deserializer := antlr.NewATNDeserializer(nil)
|
||||||
|
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
|
||||||
|
atn := staticData.atn
|
||||||
|
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
|
||||||
|
decisionToDFA := staticData.decisionToDFA
|
||||||
|
for index, state := range atn.DecisionToState {
|
||||||
|
decisionToDFA[index] = antlr.NewDFA(state, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FstabLexerInit initializes any static state used to implement FstabLexer. By default the
|
||||||
|
// static state used to implement the lexer is lazily initialized during the first call to
|
||||||
|
// NewFstabLexer(). You can call this function if you wish to initialize the static state ahead
|
||||||
|
// of time.
|
||||||
|
func FstabLexerInit() {
|
||||||
|
staticData := &FstabLexerLexerStaticData
|
||||||
|
staticData.once.Do(fstablexerLexerInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFstabLexer produces a new lexer instance for the optional input antlr.CharStream.
|
||||||
|
func NewFstabLexer(input antlr.CharStream) *FstabLexer {
|
||||||
|
FstabLexerInit()
|
||||||
|
l := new(FstabLexer)
|
||||||
|
l.BaseLexer = antlr.NewBaseLexer(input)
|
||||||
|
staticData := &FstabLexerLexerStaticData
|
||||||
|
l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache)
|
||||||
|
l.channelNames = staticData.ChannelNames
|
||||||
|
l.modeNames = staticData.ModeNames
|
||||||
|
l.RuleNames = staticData.RuleNames
|
||||||
|
l.LiteralNames = staticData.LiteralNames
|
||||||
|
l.SymbolicNames = staticData.SymbolicNames
|
||||||
|
l.GrammarFileName = "Fstab.g4"
|
||||||
|
// TODO: l.EOF = antlr.TokenEOF
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// FstabLexer tokens.
|
||||||
|
const (
|
||||||
|
FstabLexerDIGITS = 1
|
||||||
|
FstabLexerWHITESPACE = 2
|
||||||
|
FstabLexerHASH = 3
|
||||||
|
FstabLexerSTRING = 4
|
||||||
|
FstabLexerQUOTED_STRING = 5
|
||||||
|
FstabLexerADFS = 6
|
||||||
|
FstabLexerAFFS = 7
|
||||||
|
FstabLexerBTRFS = 8
|
||||||
|
FstabLexerEXFAT = 9
|
||||||
|
)
|
52
server/handlers/fstab/ast/parser/fstab_listener.go
Normal file
52
server/handlers/fstab/ast/parser/fstab_listener.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Code generated from Fstab.g4 by ANTLR 4.13.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
package parser // Fstab
|
||||||
|
|
||||||
|
import "github.com/antlr4-go/antlr/v4"
|
||||||
|
|
||||||
|
// FstabListener is a complete listener for a parse tree produced by FstabParser.
|
||||||
|
type FstabListener interface {
|
||||||
|
antlr.ParseTreeListener
|
||||||
|
|
||||||
|
// EnterEntry is called when entering the entry production.
|
||||||
|
EnterEntry(c *EntryContext)
|
||||||
|
|
||||||
|
// EnterSpec is called when entering the spec production.
|
||||||
|
EnterSpec(c *SpecContext)
|
||||||
|
|
||||||
|
// EnterMountPoint is called when entering the mountPoint production.
|
||||||
|
EnterMountPoint(c *MountPointContext)
|
||||||
|
|
||||||
|
// EnterFileSystem is called when entering the fileSystem production.
|
||||||
|
EnterFileSystem(c *FileSystemContext)
|
||||||
|
|
||||||
|
// EnterMountOptions is called when entering the mountOptions production.
|
||||||
|
EnterMountOptions(c *MountOptionsContext)
|
||||||
|
|
||||||
|
// EnterFreq is called when entering the freq production.
|
||||||
|
EnterFreq(c *FreqContext)
|
||||||
|
|
||||||
|
// EnterPass is called when entering the pass production.
|
||||||
|
EnterPass(c *PassContext)
|
||||||
|
|
||||||
|
// ExitEntry is called when exiting the entry production.
|
||||||
|
ExitEntry(c *EntryContext)
|
||||||
|
|
||||||
|
// ExitSpec is called when exiting the spec production.
|
||||||
|
ExitSpec(c *SpecContext)
|
||||||
|
|
||||||
|
// ExitMountPoint is called when exiting the mountPoint production.
|
||||||
|
ExitMountPoint(c *MountPointContext)
|
||||||
|
|
||||||
|
// ExitFileSystem is called when exiting the fileSystem production.
|
||||||
|
ExitFileSystem(c *FileSystemContext)
|
||||||
|
|
||||||
|
// ExitMountOptions is called when exiting the mountOptions production.
|
||||||
|
ExitMountOptions(c *MountOptionsContext)
|
||||||
|
|
||||||
|
// ExitFreq is called when exiting the freq production.
|
||||||
|
ExitFreq(c *FreqContext)
|
||||||
|
|
||||||
|
// ExitPass is called when exiting the pass production.
|
||||||
|
ExitPass(c *PassContext)
|
||||||
|
}
|
1171
server/handlers/fstab/ast/parser/fstab_parser.go
Normal file
1171
server/handlers/fstab/ast/parser/fstab_parser.go
Normal file
File diff suppressed because it is too large
Load Diff
145
server/handlers/fstab/ast/parser_test.go
Normal file
145
server/handlers/fstab/ast/parser_test.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExample1(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
LABEL=test /mnt/test ext4 defaults 0 0
|
||||||
|
LABEL=example /mnt/example fat32 defaults 0 2
|
||||||
|
`)
|
||||||
|
c := NewFstabConfig()
|
||||||
|
|
||||||
|
errors := c.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("Expected no errors, got %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Entries.Size() != 2 {
|
||||||
|
t.Fatalf("Expected 2 entry, got %d", c.Entries.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
rawFirstEntry, _ := c.Entries.Get(uint32(0))
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.Spec.LocationRange.Start.Line == 0 && firstEntry.Fields.Spec.LocationRange.Start.Character == 0) {
|
||||||
|
t.Errorf("Expected spec start to be 0:0, got %v", firstEntry.Fields.Spec.LocationRange.Start)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.Spec.LocationRange.End.Line == 0 && firstEntry.Fields.Spec.LocationRange.End.Character == 10) {
|
||||||
|
t.Errorf("Expected spec end to be 0:10, got %v", firstEntry.Fields.Spec.LocationRange.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.MountPoint.LocationRange.Start.Line == 0 && firstEntry.Fields.MountPoint.LocationRange.Start.Character == 11) {
|
||||||
|
t.Errorf("Expected mountpoint start to be 0:11, got %v", firstEntry.Fields.MountPoint.LocationRange.Start)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.MountPoint.LocationRange.End.Line == 0 && firstEntry.Fields.MountPoint.LocationRange.End.Character == 20) {
|
||||||
|
t.Errorf("Expected mountpoint end to be 0:20, got %v", firstEntry.Fields.MountPoint.LocationRange.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.FilesystemType.LocationRange.Start.Line == 0 && firstEntry.Fields.FilesystemType.LocationRange.Start.Character == 21) {
|
||||||
|
t.Errorf("Expected filesystemtype start to be 0:21, got %v", firstEntry.Fields.FilesystemType.LocationRange.Start)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.FilesystemType.LocationRange.End.Line == 0 && firstEntry.Fields.FilesystemType.LocationRange.End.Character == 25) {
|
||||||
|
t.Errorf("Expected filesystemtype end to be 0:25, got %v", firstEntry.Fields.FilesystemType.LocationRange.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.Options.LocationRange.Start.Line == 0 && firstEntry.Fields.Options.LocationRange.Start.Character == 26) {
|
||||||
|
t.Errorf("Expected options start to be 0:26, got %v", firstEntry.Fields.Options.LocationRange.Start)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.Options.LocationRange.End.Line == 0 && firstEntry.Fields.Options.LocationRange.End.Character == 34) {
|
||||||
|
t.Errorf("Expected options end to be 0:34, got %v", firstEntry.Fields.Options.LocationRange.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.Freq.LocationRange.Start.Line == 0 && firstEntry.Fields.Freq.LocationRange.Start.Character == 35) {
|
||||||
|
t.Errorf("Expected freq start to be 0:35, got %v", firstEntry.Fields.Freq.LocationRange.Start)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(firstEntry.Fields.Freq.LocationRange.End.Line == 0 && firstEntry.Fields.Freq.LocationRange.End.Character == 36) {
|
||||||
|
t.Errorf("Expected freq end to be 0:36, got %v", firstEntry.Fields.Freq.LocationRange.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawSecondEntry, _ := c.Entries.Get(uint32(1))
|
||||||
|
secondEntry := rawSecondEntry.(*FstabEntry)
|
||||||
|
if !(secondEntry.Fields.Start.Line == 1) {
|
||||||
|
t.Errorf("Expected start line to be 1, got %d", secondEntry.Fields.Start.Line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
import docvalues "config-lsp/doc-values"
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package fstabdocumentation
|
package fields
|
||||||
|
|
||||||
import (
|
import (
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
@ -1,4 +1,4 @@
|
|||||||
package fstabdocumentation
|
package fields
|
||||||
|
|
||||||
import (
|
import (
|
||||||
commondocumentation "config-lsp/common-documentation"
|
commondocumentation "config-lsp/common-documentation"
|
||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mountOptionsExtractor = func(value string) string {
|
var MountOptionsExtractor = func(value string) string {
|
||||||
separatorIndex := strings.Index(value, "=")
|
separatorIndex := strings.Index(value, "=")
|
||||||
|
|
||||||
if separatorIndex == -1 {
|
if separatorIndex == -1 {
|
||||||
@ -17,7 +17,7 @@ var mountOptionsExtractor = func(value string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// From https://www.man7.org/linux/man-pages/man8/mount.8.html
|
// From https://www.man7.org/linux/man-pages/man8/mount.8.html
|
||||||
var defaultOptions = []docvalues.EnumString{
|
var DefaultOptions = []docvalues.EnumString{
|
||||||
// Default options
|
// Default options
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"async",
|
"async",
|
||||||
@ -39,22 +39,6 @@ var defaultOptions = []docvalues.EnumString{
|
|||||||
"noauto",
|
"noauto",
|
||||||
"Can only be mounted explicitly (i.e., the -a option will not cause the filesystem to be mounted).",
|
"Can only be mounted explicitly (i.e., the -a option will not cause the filesystem to be mounted).",
|
||||||
),
|
),
|
||||||
docvalues.CreateEnumStringWithDoc(
|
|
||||||
"context",
|
|
||||||
"The context= option is useful when mounting filesystems that do not support extended attributes, such as a floppy or hard disk formatted with VFAT, or systems that are not normally running under SELinux, such as an ext3 or ext4 formatted disk from a non-SELinux workstation. You can also use context= on filesystems you do not trust, such as a floppy. It also helps in compatibility with xattr-supporting filesystems on earlier 2.4.<x> kernel versions. Even where xattrs are supported, you can save time not having to label every file by assigning the entire disk one security context.",
|
|
||||||
),
|
|
||||||
docvalues.CreateEnumStringWithDoc(
|
|
||||||
"fscontext",
|
|
||||||
"The fscontext= option works for all filesystems, regardless of their xattr support. The fscontext option sets the overarching filesystem label to a specific security context. This filesystem label is separate from the individual labels on the files. It represents the entire filesystem for certain kinds of permission checks, such as during mount or file creation. Individual file labels are still obtained from the xattrs on the files themselves. The context option actually sets the aggregate context that fscontext provides, in addition to supplying the same label for individual files.",
|
|
||||||
),
|
|
||||||
docvalues.CreateEnumStringWithDoc(
|
|
||||||
"defcontext",
|
|
||||||
"You can set the default security context for unlabeled files using defcontext= option. This overrides the value set for unlabeled files in the policy and requires a filesystem that supports xattr labeling.",
|
|
||||||
),
|
|
||||||
docvalues.CreateEnumStringWithDoc(
|
|
||||||
"rootcontext",
|
|
||||||
"The rootcontext= option allows you to explicitly label the root inode of a FS being mounted before that FS or inode becomes visible to userspace. This was found to be useful for things like stateless Linux. The special value @target can be used to assign the current context of the target mountpoint location.",
|
|
||||||
),
|
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"defaults",
|
"defaults",
|
||||||
"Use the default options: rw, suid, dev, exec, auto, nouser, and async. Note that the real set of all default mount options depends on the kernel and filesystem type. See the beginning of this section for more details.",
|
"Use the default options: rw, suid, dev, exec, auto, nouser, and async. Note that the real set of all default mount options depends on the kernel and filesystem type. See the beginning of this section for more details.",
|
||||||
@ -193,13 +177,13 @@ var defaultOptions = []docvalues.EnumString{
|
|||||||
),
|
),
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.automount",
|
"x-systemd.automount",
|
||||||
`An automount unit will be created for the file system. See systemd.automount(5) for details.
|
`An automount unit will be created for the file system. See systemd.automount(5) for details.
|
||||||
|
|
||||||
Added in version 215.`,
|
Added in version 215.`,
|
||||||
),
|
),
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.makefs",
|
"x-systemd.makefs",
|
||||||
`The file system will be initialized on the device. If the device is not "empty", i.e. it contains any signature, the operation will be skipped. It is hence expected that this option remains set even after the device has been initialized.
|
`The file system will be initialized on the device. If the device is not "empty", i.e. it contains any signature, the operation will be skipped. It is hence expected that this option remains set even after the device has been initialized.
|
||||||
|
|
||||||
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
||||||
|
|
||||||
@ -211,7 +195,7 @@ Added in version 236.`,
|
|||||||
),
|
),
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.growfs",
|
"x-systemd.growfs",
|
||||||
`The file system will be grown to occupy the full block device. If the file system is already at maximum size, no action will be performed. It is hence expected that this option remains set even after the file system has been grown. Only certain file system types are supported, see systemd-makefs@.service(8) for details.
|
`The file system will be grown to occupy the full block device. If the file system is already at maximum size, no action will be performed. It is hence expected that this option remains set even after the file system has been grown. Only certain file system types are supported, see systemd-makefs@.service(8) for details.
|
||||||
|
|
||||||
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
||||||
|
|
||||||
@ -219,7 +203,7 @@ Added in version 236.`,
|
|||||||
),
|
),
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.pcrfs",
|
"x-systemd.pcrfs",
|
||||||
`Measures file system identity information (mount point, type, label, UUID, partition label, partition UUID) into PCR 15 after the file system has been mounted. This ensures the systemd-pcrfs@.service(8) or systemd-pcrfs-root.service services are pulled in by the mount unit.
|
`Measures file system identity information (mount point, type, label, UUID, partition label, partition UUID) into PCR 15 after the file system has been mounted. This ensures the systemd-pcrfs@.service(8) or systemd-pcrfs-root.service services are pulled in by the mount unit.
|
||||||
|
|
||||||
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file. It is also implied for the root and /usr/ partitions discovered by systemd-gpt-auto-generator(8).
|
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file. It is also implied for the root and /usr/ partitions discovered by systemd-gpt-auto-generator(8).
|
||||||
|
|
||||||
@ -227,13 +211,13 @@ Added in version 253.`,
|
|||||||
),
|
),
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.rw-only",
|
"x-systemd.rw-only",
|
||||||
`If a mount operation fails to mount the file system read-write, it normally tries mounting the file system read-only instead. This option disables that behaviour, and causes the mount to fail immediately instead. This option is translated into the ReadWriteOnly= setting in a unit file.
|
`If a mount operation fails to mount the file system read-write, it normally tries mounting the file system read-only instead. This option disables that behaviour, and causes the mount to fail immediately instead. This option is translated into the ReadWriteOnly= setting in a unit file.
|
||||||
|
|
||||||
Added in version 246.`,
|
Added in version 246.`,
|
||||||
),
|
),
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-initrd.mount",
|
"x-initrd.mount",
|
||||||
`An additional filesystem to be mounted in the initrd. See initrd-fs.target description in systemd.special(7). This is both an indicator to the initrd to mount this partition early and an indicator to the host to leave the partition mounted until final shutdown. Or in other words, if this flag is set it is assumed the mount shall be active during the entire regular runtime of the system, i.e. established before the initrd transitions into the host all the way until the host transitions to the final shutdown phase.
|
`An additional filesystem to be mounted in the initrd. See initrd-fs.target description in systemd.special(7). This is both an indicator to the initrd to mount this partition early and an indicator to the host to leave the partition mounted until final shutdown. Or in other words, if this flag is set it is assumed the mount shall be active during the entire regular runtime of the system, i.e. established before the initrd transitions into the host all the way until the host transitions to the final shutdown phase.
|
||||||
|
|
||||||
Added in version 215.`,
|
Added in version 215.`,
|
||||||
),
|
),
|
||||||
@ -244,7 +228,7 @@ type assignOption struct {
|
|||||||
Handler func(context docvalues.KeyValueAssignmentContext) docvalues.DeprecatedValue
|
Handler func(context docvalues.KeyValueAssignmentContext) docvalues.DeprecatedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultAssignOptions = map[docvalues.EnumString]docvalues.DeprecatedValue{
|
var DefaultAssignOptions = map[docvalues.EnumString]docvalues.DeprecatedValue{
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"context",
|
"context",
|
||||||
"The context= option is useful when mounting filesystems that do not support extended attributes, such as a floppy or hard disk formatted with VFAT, or systems that are not normally running under SELinux, such as an ext3 or ext4 formatted disk from a non-SELinux workstation. You can also use context= on filesystems you do not trust, such as a floppy. It also helps in compatibility with xattr-supporting filesystems on earlier 2.4.<x> kernel versions. Even where xattrs are supported, you can save time not having to label every file by assigning the entire disk one security context. A commonly used option for removable media is context=\"system_u:object_r:removable_t\".",
|
"The context= option is useful when mounting filesystems that do not support extended attributes, such as a floppy or hard disk formatted with VFAT, or systems that are not normally running under SELinux, such as an ext3 or ext4 formatted disk from a non-SELinux workstation. You can also use context= on filesystems you do not trust, such as a floppy. It also helps in compatibility with xattr-supporting filesystems on earlier 2.4.<x> kernel versions. Even where xattrs are supported, you can save time not having to label every file by assigning the entire disk one security context. A commonly used option for removable media is context=\"system_u:object_r:removable_t\".",
|
||||||
@ -299,19 +283,19 @@ Added in version 245.`,
|
|||||||
): docvalues.StringValue{},
|
): docvalues.StringValue{},
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.wants-mounts-for",
|
"x-systemd.wants-mounts-for",
|
||||||
`Configures a RequiresMountsFor= or WantsMountsFor= dependency between the created mount unit and other mount units. The argument must be an absolute path. This option may be specified more than once. See RequiresMountsFor= or WantsMountsFor= in systemd.unit(5) for details.
|
`Configures a RequiresMountsFor= or WantsMountsFor= dependency between the created mount unit and other mount units. The argument must be an absolute path. This option may be specified more than once. See RequiresMountsFor= or WantsMountsFor= in systemd.unit(5) for details.
|
||||||
|
|
||||||
Added in version 220.`,
|
Added in version 220.`,
|
||||||
): docvalues.StringValue{},
|
): docvalues.StringValue{},
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.requires-mounts-for",
|
"x-systemd.requires-mounts-for",
|
||||||
`Configures a RequiresMountsFor= or WantsMountsFor= dependency between the created mount unit and other mount units. The argument must be an absolute path. This option may be specified more than once. See RequiresMountsFor= or WantsMountsFor= in systemd.unit(5) for details.
|
`Configures a RequiresMountsFor= or WantsMountsFor= dependency between the created mount unit and other mount units. The argument must be an absolute path. This option may be specified more than once. See RequiresMountsFor= or WantsMountsFor= in systemd.unit(5) for details.
|
||||||
|
|
||||||
Added in version 220.`,
|
Added in version 220.`,
|
||||||
): docvalues.StringValue{},
|
): docvalues.StringValue{},
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.device-bound",
|
"x-systemd.device-bound",
|
||||||
`Takes a boolean argument. If true or no argument, a BindsTo= dependency on the backing device is set. If false, the mount unit is not stopped no matter whether the backing device is still present. This is useful when the file system is backed by volume managers. If not set, and the mount comes from unit fragments, i.e. generated from /etc/fstab by systemd-fstab-generator(8) or loaded from a manually configured mount unit, a combination of Requires= and StopPropagatedFrom= dependencies is set on the backing device. If doesn't, only Requires= is used.
|
`Takes a boolean argument. If true or no argument, a BindsTo= dependency on the backing device is set. If false, the mount unit is not stopped no matter whether the backing device is still present. This is useful when the file system is backed by volume managers. If not set, and the mount comes from unit fragments, i.e. generated from /etc/fstab by systemd-fstab-generator(8) or loaded from a manually configured mount unit, a combination of Requires= and StopPropagatedFrom= dependencies is set on the backing device. If doesn't, only Requires= is used.
|
||||||
|
|
||||||
Added in version 233.`,
|
Added in version 233.`,
|
||||||
): docvalues.EnumValue{
|
): docvalues.EnumValue{
|
||||||
@ -323,13 +307,13 @@ Added in version 233.`,
|
|||||||
},
|
},
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.idle-timeout",
|
"x-systemd.idle-timeout",
|
||||||
`Configures the idle timeout of the automount unit. See TimeoutIdleSec= in systemd.automount(5) for details.
|
`Configures the idle timeout of the automount unit. See TimeoutIdleSec= in systemd.automount(5) for details.
|
||||||
|
|
||||||
Added in version 220.`,
|
Added in version 220.`,
|
||||||
): docvalues.StringValue{},
|
): docvalues.StringValue{},
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.device-timeout",
|
"x-systemd.device-timeout",
|
||||||
`Configure how long systemd should wait for a device to show up before giving up on an entry from /etc/fstab. Specify a time in seconds or explicitly append a unit such as "s", "min", "h", "ms".
|
`Configure how long systemd should wait for a device to show up before giving up on an entry from /etc/fstab. Specify a time in seconds or explicitly append a unit such as "s", "min", "h", "ms".
|
||||||
|
|
||||||
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
||||||
|
|
||||||
@ -337,7 +321,7 @@ Added in version 215.`,
|
|||||||
): docvalues.StringValue{},
|
): docvalues.StringValue{},
|
||||||
docvalues.CreateEnumStringWithDoc(
|
docvalues.CreateEnumStringWithDoc(
|
||||||
"x-systemd.mount-timeout",
|
"x-systemd.mount-timeout",
|
||||||
`Configure how long systemd should wait for the mount command to finish before giving up on an entry from /etc/fstab. Specify a time in seconds or explicitly append a unit such as "s", "min", "h", "ms".
|
`Configure how long systemd should wait for the mount command to finish before giving up on an entry from /etc/fstab. Specify a time in seconds or explicitly append a unit such as "s", "min", "h", "ms".
|
||||||
|
|
||||||
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file.
|
||||||
|
|
||||||
@ -345,130 +329,149 @@ See TimeoutSec= below for details.
|
|||||||
|
|
||||||
Added in version 233.`,
|
Added in version 233.`,
|
||||||
): docvalues.StringValue{},
|
): docvalues.StringValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"fscontext",
|
||||||
|
"The fscontext= option works for all filesystems, regardless of their xattr support. The fscontext option sets the overarching filesystem label to a specific security context. This filesystem label is separate from the individual labels on the files. It represents the entire filesystem for certain kinds of permission checks, such as during mount or file creation. Individual file labels are still obtained from the xattrs on the files themselves. The context option actually sets the aggregate context that fscontext provides, in addition to supplying the same label for individual files.",
|
||||||
|
): docvalues.StringValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"defcontext",
|
||||||
|
"You can set the default security context for unlabeled files using defcontext= option. This overrides the value set for unlabeled files in the policy and requires a filesystem that supports xattr labeling.",
|
||||||
|
): docvalues.StringValue{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMountOptionField(
|
func createMountOptionField(
|
||||||
options []docvalues.EnumString,
|
options []docvalues.EnumString,
|
||||||
assignOption map[docvalues.EnumString]docvalues.DeprecatedValue,
|
assignOption map[docvalues.EnumString]docvalues.DeprecatedValue,
|
||||||
) docvalues.DeprecatedValue {
|
) docvalues.DeprecatedValue {
|
||||||
dynamicOptions := docvalues.MergeKeyEnumAssignmentMaps(defaultAssignOptions, assignOption)
|
// dynamicOptions := docvalues.MergeKeyEnumAssignmentMaps(defaultAssignOptions, assignOption)
|
||||||
|
|
||||||
return docvalues.ArrayValue{
|
return docvalues.ArrayValue{
|
||||||
Separator: ",",
|
Separator: ",",
|
||||||
DuplicatesExtractor: &mountOptionsExtractor,
|
DuplicatesExtractor: &MountOptionsExtractor,
|
||||||
SubValue: docvalues.OrValue{
|
SubValue: docvalues.OrValue{
|
||||||
Values: []docvalues.DeprecatedValue{
|
Values: []docvalues.DeprecatedValue{
|
||||||
docvalues.KeyEnumAssignmentValue{
|
docvalues.KeyEnumAssignmentValue{
|
||||||
Values: dynamicOptions,
|
Values: assignOption,
|
||||||
ValueIsOptional: false,
|
ValueIsOptional: false,
|
||||||
Separator: "=",
|
Separator: "=",
|
||||||
},
|
},
|
||||||
docvalues.EnumValue{
|
docvalues.EnumValue{
|
||||||
EnforceValues: true,
|
EnforceValues: true,
|
||||||
Values: append(defaultOptions, options...),
|
Values: options,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultMountOptionsField = createMountOptionField([]docvalues.EnumString{}, map[docvalues.EnumString]docvalues.DeprecatedValue{})
|
type optionField struct {
|
||||||
|
Assignable map[docvalues.EnumString]docvalues.DeprecatedValue
|
||||||
var MountOptionsMapField = map[string]docvalues.DeprecatedValue{
|
Enums []docvalues.EnumString
|
||||||
"adfs": createMountOptionField(
|
}
|
||||||
commondocumentation.AdfsDocumentationEnums,
|
|
||||||
commondocumentation.AdfsDocumentationAssignable,
|
var DefaultMountOptionsField = createMountOptionField(DefaultOptions, DefaultAssignOptions)
|
||||||
),
|
|
||||||
"affs": createMountOptionField(
|
var MountOptionsMapField = map[string]optionField{
|
||||||
commondocumentation.AffsDocumentationEnums,
|
"adfs": {
|
||||||
commondocumentation.AffsDocumentationAssignable,
|
Enums: commondocumentation.AdfsDocumentationEnums,
|
||||||
),
|
Assignable: commondocumentation.AdfsDocumentationAssignable,
|
||||||
"btrfs": createMountOptionField(
|
},
|
||||||
commondocumentation.BtrfsDocumentationEnums,
|
"affs": {
|
||||||
commondocumentation.BtrfsDocumentationAssignable,
|
Enums: commondocumentation.AffsDocumentationEnums,
|
||||||
),
|
Assignable: commondocumentation.AffsDocumentationAssignable,
|
||||||
"debugfs": createMountOptionField(
|
},
|
||||||
commondocumentation.DebugfsDocumentationEnums,
|
"btrfs": {
|
||||||
commondocumentation.DebugfsDocumentationAssignable,
|
Enums: commondocumentation.BtrfsDocumentationEnums,
|
||||||
),
|
Assignable: commondocumentation.BtrfsDocumentationAssignable,
|
||||||
"ext2": createMountOptionField(
|
},
|
||||||
commondocumentation.Ext2DocumentationEnums,
|
"debugfs": {
|
||||||
commondocumentation.Ext2DocumentationAssignable,
|
Enums: commondocumentation.DebugfsDocumentationEnums,
|
||||||
),
|
Assignable: commondocumentation.DebugfsDocumentationAssignable,
|
||||||
"ext3": createMountOptionField(
|
},
|
||||||
append(commondocumentation.Ext2DocumentationEnums, commondocumentation.Ext3DocumentationEnums...),
|
"ext2": {
|
||||||
docvalues.MergeKeyEnumAssignmentMaps(commondocumentation.Ext2DocumentationAssignable, commondocumentation.Ext3DocumentationAssignable),
|
Enums: commondocumentation.Ext2DocumentationEnums,
|
||||||
),
|
Assignable: commondocumentation.Ext2DocumentationAssignable,
|
||||||
"ext4": createMountOptionField(
|
},
|
||||||
append(append(commondocumentation.Ext2DocumentationEnums, commondocumentation.Ext3DocumentationEnums...), commondocumentation.Ext4DocumentationEnums...),
|
"ext3": {
|
||||||
docvalues.MergeKeyEnumAssignmentMaps(commondocumentation.Ext2DocumentationAssignable, docvalues.MergeKeyEnumAssignmentMaps(commondocumentation.Ext3DocumentationAssignable, commondocumentation.Ext4DocumentationAssignable)),
|
Enums: append(commondocumentation.Ext2DocumentationEnums, commondocumentation.Ext3DocumentationEnums...),
|
||||||
),
|
Assignable: docvalues.MergeKeyEnumAssignmentMaps(commondocumentation.Ext2DocumentationAssignable, commondocumentation.Ext3DocumentationAssignable),
|
||||||
"devpts": createMountOptionField(
|
},
|
||||||
commondocumentation.DevptsDocumentationEnums,
|
"ext4": {
|
||||||
commondocumentation.DevptsDocumentationAssignable,
|
Enums: append(
|
||||||
),
|
append(
|
||||||
"fat": createMountOptionField(
|
commondocumentation.Ext2DocumentationEnums,
|
||||||
commondocumentation.FatDocumentationEnums,
|
commondocumentation.Ext3DocumentationEnums...,
|
||||||
commondocumentation.FatDocumentationAssignable,
|
),
|
||||||
),
|
commondocumentation.Ext4DocumentationEnums...,
|
||||||
"hfs": createMountOptionField(
|
),
|
||||||
commondocumentation.HfsDocumentationEnums,
|
Assignable: docvalues.MergeKeyEnumAssignmentMaps(commondocumentation.Ext2DocumentationAssignable, docvalues.MergeKeyEnumAssignmentMaps(commondocumentation.Ext3DocumentationAssignable, commondocumentation.Ext4DocumentationAssignable)),
|
||||||
commondocumentation.HfsDocumentationAssignable,
|
},
|
||||||
),
|
"devpts": {
|
||||||
"hpfs": createMountOptionField(
|
Enums: commondocumentation.DevptsDocumentationEnums,
|
||||||
commondocumentation.HpfsDocumentationEnums,
|
Assignable: commondocumentation.DevptsDocumentationAssignable,
|
||||||
commondocumentation.HpfsDocumentationAssignable,
|
},
|
||||||
),
|
"fat": {
|
||||||
"iso9660": createMountOptionField(
|
Enums: commondocumentation.FatDocumentationEnums,
|
||||||
commondocumentation.Iso9660DocumentationEnums,
|
Assignable: commondocumentation.FatDocumentationAssignable,
|
||||||
commondocumentation.Iso9660DocumentationAssignable,
|
},
|
||||||
),
|
"hfs": {
|
||||||
"jfs": createMountOptionField(
|
Enums: commondocumentation.HfsDocumentationEnums,
|
||||||
commondocumentation.JfsDocumentationEnums,
|
Assignable: commondocumentation.HfsDocumentationAssignable,
|
||||||
commondocumentation.JfsDocumentationAssignable,
|
},
|
||||||
),
|
"hpfs": {
|
||||||
"msdos": createMountOptionField(
|
Enums: commondocumentation.HpfsDocumentationEnums,
|
||||||
commondocumentation.MsdosDocumentationEnums,
|
Assignable: commondocumentation.HpfsDocumentationAssignable,
|
||||||
commondocumentation.MsdosDocumentationAssignable,
|
},
|
||||||
),
|
"iso9660": {
|
||||||
"ncpfs": createMountOptionField(
|
Enums: commondocumentation.Iso9660DocumentationEnums,
|
||||||
commondocumentation.NcpfsDocumentationEnums,
|
Assignable: commondocumentation.Iso9660DocumentationAssignable,
|
||||||
commondocumentation.NcpfsDocumentationAssignable,
|
},
|
||||||
),
|
"jfs": {
|
||||||
"ntfs": createMountOptionField(
|
Enums: commondocumentation.JfsDocumentationEnums,
|
||||||
commondocumentation.NtfsDocumentationEnums,
|
Assignable: commondocumentation.JfsDocumentationAssignable,
|
||||||
commondocumentation.NtfsDocumentationAssignable,
|
},
|
||||||
),
|
"msdos": {
|
||||||
"overlay": createMountOptionField(
|
Enums: commondocumentation.MsdosDocumentationEnums,
|
||||||
commondocumentation.OverlayDocumentationEnums,
|
Assignable: commondocumentation.MsdosDocumentationAssignable,
|
||||||
commondocumentation.OverlayDocumentationAssignable,
|
},
|
||||||
),
|
"ncpfs": {
|
||||||
"reiserfs": createMountOptionField(
|
Enums: commondocumentation.NcpfsDocumentationEnums,
|
||||||
commondocumentation.ReiserfsDocumentationEnums,
|
Assignable: commondocumentation.NcpfsDocumentationAssignable,
|
||||||
commondocumentation.ReiserfsDocumentationAssignable,
|
},
|
||||||
),
|
"ntfs": {
|
||||||
"usbfs": createMountOptionField(
|
Enums: commondocumentation.NtfsDocumentationEnums,
|
||||||
commondocumentation.UsbfsDocumentationEnums,
|
Assignable: commondocumentation.NtfsDocumentationAssignable,
|
||||||
commondocumentation.UsbfsDocumentationAssignable,
|
},
|
||||||
),
|
"overlay": {
|
||||||
"ubifs": createMountOptionField(
|
Enums: commondocumentation.OverlayDocumentationEnums,
|
||||||
commondocumentation.UbifsDocumentationEnums,
|
Assignable: commondocumentation.OverlayDocumentationAssignable,
|
||||||
commondocumentation.UbifsDocumentationAssignable,
|
},
|
||||||
),
|
"reiserfs": {
|
||||||
"udf": createMountOptionField(
|
Enums: commondocumentation.ReiserfsDocumentationEnums,
|
||||||
commondocumentation.UdfDocumentationEnums,
|
Assignable: commondocumentation.ReiserfsDocumentationAssignable,
|
||||||
commondocumentation.UdfDocumentationAssignable,
|
},
|
||||||
),
|
"usbfs": {
|
||||||
"ufs": createMountOptionField(
|
Enums: commondocumentation.UsbfsDocumentationEnums,
|
||||||
commondocumentation.UfsDocumentationEnums,
|
Assignable: commondocumentation.UsbfsDocumentationAssignable,
|
||||||
commondocumentation.UfsDocumentationAssignable,
|
},
|
||||||
),
|
"ubifs": {
|
||||||
"umsdos": createMountOptionField(
|
Enums: commondocumentation.UbifsDocumentationEnums,
|
||||||
commondocumentation.UmsdosDocumentationEnums,
|
Assignable: commondocumentation.UbifsDocumentationAssignable,
|
||||||
commondocumentation.UmsdosDocumentationAssignable,
|
},
|
||||||
),
|
"udf": {
|
||||||
"vfat": createMountOptionField(
|
Enums: commondocumentation.UdfDocumentationEnums,
|
||||||
commondocumentation.VfatDocumentationEnums,
|
Assignable: commondocumentation.UdfDocumentationAssignable,
|
||||||
commondocumentation.VfatDocumentationAssignable,
|
},
|
||||||
),
|
"ufs": {
|
||||||
|
Enums: commondocumentation.UfsDocumentationEnums,
|
||||||
|
Assignable: commondocumentation.UfsDocumentationAssignable,
|
||||||
|
},
|
||||||
|
"umsdos": {
|
||||||
|
Enums: commondocumentation.UmsdosDocumentationEnums,
|
||||||
|
Assignable: commondocumentation.UmsdosDocumentationAssignable,
|
||||||
|
},
|
||||||
|
"vfat": {
|
||||||
|
Enums: commondocumentation.VfatDocumentationEnums,
|
||||||
|
Assignable: commondocumentation.VfatDocumentationAssignable,
|
||||||
|
},
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package fstabdocumentation
|
package fields
|
||||||
|
|
||||||
import docvalues "config-lsp/doc-values"
|
import docvalues "config-lsp/doc-values"
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package fstabdocumentation
|
package fields
|
||||||
|
|
||||||
import (
|
import (
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
||||||
@ -15,7 +15,7 @@ var LabelField = docvalues.RegexValue{
|
|||||||
var SpecField = docvalues.OrValue{
|
var SpecField = docvalues.OrValue{
|
||||||
Values: []docvalues.DeprecatedValue{
|
Values: []docvalues.DeprecatedValue{
|
||||||
docvalues.PathValue{
|
docvalues.PathValue{
|
||||||
RequiredType: docvalues.PathTypeFile & docvalues.PathTypeExistenceOptional,
|
RequiredType: docvalues.PathTypeExistenceOptional,
|
||||||
},
|
},
|
||||||
docvalues.KeyEnumAssignmentValue{
|
docvalues.KeyEnumAssignmentValue{
|
||||||
Separator: "=",
|
Separator: "=",
|
@ -1,4 +1,4 @@
|
|||||||
package fstabdocumentation
|
package fields
|
||||||
|
|
||||||
import (
|
import (
|
||||||
docvalues "config-lsp/doc-values"
|
docvalues "config-lsp/doc-values"
|
@ -1,9 +1,10 @@
|
|||||||
package fstab
|
package fstab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
fstabdocumentation "config-lsp/handlers/fstab/documentation"
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/fstab/ast"
|
||||||
|
fields "config-lsp/handlers/fstab/fields"
|
||||||
handlers "config-lsp/handlers/fstab/handlers"
|
handlers "config-lsp/handlers/fstab/handlers"
|
||||||
"config-lsp/handlers/fstab/parser"
|
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -12,22 +13,21 @@ func TestValidBasicExample(t *testing.T) {
|
|||||||
input := utils.Dedent(`
|
input := utils.Dedent(`
|
||||||
LABEL=test /mnt/test ext4 defaults 0 0
|
LABEL=test /mnt/test ext4 defaults 0 0
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatal("ParseFromContent failed with error", errors)
|
t.Fatal("Parse failed with error", errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get hover for first field
|
// Get hover for first field
|
||||||
rawEntry, _ := p.Entries.Get(uint32(0))
|
rawEntry, _ := p.Entries.Get(uint32(0))
|
||||||
entry := rawEntry.(parser.FstabEntry)
|
entry := rawEntry.(*ast.FstabEntry)
|
||||||
|
|
||||||
println("Getting hover info")
|
println("Getting hover info")
|
||||||
{
|
{
|
||||||
hover, err := handlers.GetHoverInfo(&entry, uint32(0))
|
hover, err := handlers.GetHoverInfo(uint32(0), common.IndexPosition(0), entry)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("getHoverInfo failed with error", err)
|
t.Fatal("getHoverInfo failed with error", err)
|
||||||
@ -38,7 +38,7 @@ LABEL=test /mnt/test ext4 defaults 0 0
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get hover for second field
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal("getHoverInfo failed with error", err)
|
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)
|
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 {
|
if err != nil {
|
||||||
t.Fatal("getHoverInfo failed with error", err)
|
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")
|
println("Getting completions")
|
||||||
{
|
{
|
||||||
completions, err := handlers.GetCompletion(entry.Line, uint32(0))
|
completions, err := handlers.GetCompletion(entry, common.CursorPosition(0))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("getCompletion failed with error", err)
|
t.Fatal("getCompletion failed with error", err)
|
||||||
@ -79,49 +75,59 @@ 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 {
|
if err != nil {
|
||||||
t.Fatal("getCompletion failed with error", err)
|
t.Fatal("getCompletion failed with error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedLength := len(utils.KeysOfMap(fstabdocumentation.MountOptionsMapField))
|
expectedLength := len(utils.KeysOfMap(fields.MountOptionsMapField))
|
||||||
if len(completions) != expectedLength {
|
if len(completions) != expectedLength {
|
||||||
t.Fatal("getCompletion failed to return correct number of completions. Got:", len(completions), "but expected:", 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) {
|
func TestInvalidOptionsExample(t *testing.T) {
|
||||||
input := utils.Dedent(`
|
input := utils.Dedent(`
|
||||||
LABEL=test /mnt/test btrfs subvol=backup,fat=32 0 0
|
LABEL=test /mnt/test btrfs subvol=backup,fat=32 0 0
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatal("ParseFromContent returned error", errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncompleteExample(t *testing.T) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
LABEL=test /mnt/test defaults 0 0
|
||||||
|
`)
|
||||||
|
p := ast.NewFstabConfig()
|
||||||
|
|
||||||
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatal("ParseFromContent returned error", errors)
|
t.Fatal("ParseFromContent returned error", errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get hover for first field
|
rawFirstEntry, _ := p.Entries.Get(uint32(0))
|
||||||
println("Checking values")
|
firstEntry := rawFirstEntry.(*ast.FstabEntry)
|
||||||
{
|
name := firstEntry.GetFieldAtPosition(common.CursorPosition(0))
|
||||||
diagnostics := p.AnalyzeValues()
|
if !(name == ast.FstabFieldSpec) {
|
||||||
|
t.Errorf("GetFieldAtPosition failed to return correct field name. Got: %v but expected: %v", name, ast.FstabFieldSpec)
|
||||||
|
}
|
||||||
|
|
||||||
if len(diagnostics) == 0 {
|
name = firstEntry.GetFieldAtPosition(common.CursorPosition(9))
|
||||||
t.Fatal("AnalyzeValues should have returned error")
|
if !(name == ast.FstabFieldSpec) {
|
||||||
}
|
t.Errorf("GetFieldAtPosition failed to return correct field name. Got: %v but expected: %v", name, ast.FstabFieldSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
name = firstEntry.GetFieldAtPosition(common.CursorPosition(21))
|
||||||
|
if !(name == ast.FstabFieldFileSystemType) {
|
||||||
|
t.Errorf("GetFieldAtPosition failed to return correct field name. Got: %v but expected: %v", name, ast.FstabFieldFileSystemType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,14 +156,32 @@ UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1
|
|||||||
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0
|
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0
|
||||||
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2
|
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArchExample1WithComments(t *testing.T) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
# Hello there!
|
||||||
|
UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1
|
||||||
|
# How are you?
|
||||||
|
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0 # And I am trailing!
|
||||||
|
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2 # I am tailing too!
|
||||||
|
`)
|
||||||
|
p := ast.NewFstabConfig()
|
||||||
|
|
||||||
|
errors := p.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArchExample2(t *testing.T) {
|
func TestArchExample2(t *testing.T) {
|
||||||
@ -167,10 +191,9 @@ func TestArchExample2(t *testing.T) {
|
|||||||
/dev/sda3 /home ext4 defaults 0 2
|
/dev/sda3 /home ext4 defaults 0 2
|
||||||
/dev/sda4 none swap defaults 0 0
|
/dev/sda4 none swap defaults 0 0
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
@ -184,14 +207,14 @@ LABEL=System / ext4 defaults 0 1
|
|||||||
LABEL=Data /home ext4 defaults 0 2
|
LABEL=Data /home ext4 defaults 0 2
|
||||||
LABEL=Swap none swap defaults 0 0
|
LABEL=Swap none swap defaults 0 0
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArchExample4(t *testing.T) {
|
func TestArchExample4(t *testing.T) {
|
||||||
@ -201,10 +224,9 @@ UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1
|
|||||||
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2
|
UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2
|
||||||
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0
|
UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
@ -218,10 +240,9 @@ PARTLABEL=GNU/Linux / ext4 defaults 0 1
|
|||||||
PARTLABEL=Home /home ext4 defaults 0 2
|
PARTLABEL=Home /home ext4 defaults 0 2
|
||||||
PARTLABEL=Swap none swap defaults 0 0
|
PARTLABEL=Swap none swap defaults 0 0
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
@ -235,13 +256,74 @@ PARTUUID=98a81274-10f7-40db-872a-03df048df366 / ext4 defaults 0 1
|
|||||||
PARTUUID=7280201c-fc5d-40f2-a9b2-466611d3d49e /home ext4 defaults 0 2
|
PARTUUID=7280201c-fc5d-40f2-a9b2-466611d3d49e /home ext4 defaults 0 2
|
||||||
PARTUUID=039b6c1c-7553-4455-9537-1befbc9fbc5b none swap defaults 0 0
|
PARTUUID=039b6c1c-7553-4455-9537-1befbc9fbc5b none swap defaults 0 0
|
||||||
`)
|
`)
|
||||||
p := parser.FstabParser{}
|
p := ast.NewFstabConfig()
|
||||||
p.Clear()
|
|
||||||
|
|
||||||
errors := p.ParseFromContent(input)
|
errors := p.Parse(input)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
t.Fatalf("ParseFromContent failed with error %v", errors)
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLinuxConfigExample(t *testing.T) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
UUID=80b496fa-ce2d-4dcf-9afc-bcaa731a67f1 /mnt/example ext4 defaults 0 2
|
||||||
|
`)
|
||||||
|
p := ast.NewFstabConfig()
|
||||||
|
|
||||||
|
errors := p.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test1(t *testing.T) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
PARTLABEL="rootfs" / ext4 noatime,lazytime,rw 0 0
|
||||||
|
`)
|
||||||
|
p := ast.NewFstabConfig()
|
||||||
|
|
||||||
|
errors := p.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test2(t *testing.T) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
/dev/sda /home1 xfs defaults 1 2
|
||||||
|
/dev/sdb /homeB xfs noauto,nobarrier,rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0
|
||||||
|
/dev/sdc /homeC xfs noauto,defaults 0 0
|
||||||
|
/dev/sdd /homeD xfs noauto,rw,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0
|
||||||
|
/dev/sde /homeE xfs defaults 0 0
|
||||||
|
`)
|
||||||
|
p := ast.NewFstabConfig()
|
||||||
|
|
||||||
|
errors := p.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test3(t *testing.T) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
/dev/disk/by-label/dsp /dsp auto ro
|
||||||
|
/dev/disk/by-partlabel/modem_a /firmware auto ro
|
||||||
|
/dev/disk/by-partlabel/persist /persist auto ro,discard,nosuid,nodev,noexec
|
||||||
|
/dev/disk/by-partlabel/userdata /data auto discard,noatime,nodiratime,nosuid,nodev,nofail 0 0
|
||||||
|
/dev/disk/by-partlabel/cache /cache ext4 relatime,data=ordered,noauto_da_alloc,discard,noexec,nodev,nosuid,x-systemd.makefs 0 0
|
||||||
|
/dev/nvme0n1 /data/media auto discard,nosuid,nodev,nofail,x-systemd.device-timeout=5s 0 0
|
||||||
|
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 := ast.NewFstabConfig()
|
||||||
|
|
||||||
|
errors := p.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("ParseFromContent failed with error %v", errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,70 +1,93 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/doc-values"
|
"config-lsp/common"
|
||||||
"config-lsp/handlers/fstab/documentation"
|
"config-lsp/handlers/fstab/ast"
|
||||||
"config-lsp/handlers/fstab/parser"
|
"config-lsp/handlers/fstab/fields"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/tliron/glsp/protocol_3_16"
|
"github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetCompletion(
|
func GetCompletion(
|
||||||
line parser.FstabLine,
|
entry *ast.FstabEntry,
|
||||||
cursor uint32,
|
cursor common.CursorPosition,
|
||||||
) ([]protocol.CompletionItem, error) {
|
) ([]protocol.CompletionItem, error) {
|
||||||
targetField := line.GetFieldAtPosition(cursor)
|
targetField := entry.GetFieldAtPosition(cursor)
|
||||||
|
|
||||||
switch targetField {
|
switch targetField {
|
||||||
case parser.FstabFieldSpec:
|
case ast.FstabFieldSpec:
|
||||||
value, cursor := GetFieldSafely(line.Fields.Spec, cursor)
|
value, cursor := getFieldSafely(entry.Fields.Spec, cursor)
|
||||||
|
|
||||||
return fstabdocumentation.SpecField.DeprecatedFetchCompletions(
|
return fields.SpecField.DeprecatedFetchCompletions(
|
||||||
value,
|
value,
|
||||||
cursor,
|
cursor,
|
||||||
), nil
|
), nil
|
||||||
case parser.FstabFieldMountPoint:
|
case ast.FstabFieldMountPoint:
|
||||||
value, cursor := GetFieldSafely(line.Fields.MountPoint, cursor)
|
value, cursor := getFieldSafely(entry.Fields.MountPoint, cursor)
|
||||||
|
|
||||||
return fstabdocumentation.MountPointField.DeprecatedFetchCompletions(
|
return fields.MountPointField.DeprecatedFetchCompletions(
|
||||||
value,
|
value,
|
||||||
cursor,
|
cursor,
|
||||||
), nil
|
), nil
|
||||||
case parser.FstabFieldFileSystemType:
|
case ast.FstabFieldFileSystemType:
|
||||||
value, cursor := GetFieldSafely(line.Fields.FilesystemType, cursor)
|
value, cursor := getFieldSafely(entry.Fields.FilesystemType, cursor)
|
||||||
|
|
||||||
return fstabdocumentation.FileSystemTypeField.DeprecatedFetchCompletions(
|
return fields.FileSystemTypeField.DeprecatedFetchCompletions(
|
||||||
value,
|
value,
|
||||||
cursor,
|
cursor,
|
||||||
), nil
|
), nil
|
||||||
case parser.FstabFieldOptions:
|
case ast.FstabFieldOptions:
|
||||||
fileSystemType := line.Fields.FilesystemType.Value
|
line, cursor := getFieldSafely(entry.Fields.Options, cursor)
|
||||||
|
fileSystemType := entry.Fields.FilesystemType.Value.Value
|
||||||
|
completions := make([]protocol.CompletionItem, 0, 50)
|
||||||
|
|
||||||
var optionsField docvalues.DeprecatedValue
|
for _, completion := range fields.DefaultMountOptionsField.DeprecatedFetchCompletions(line, cursor) {
|
||||||
|
var documentation string
|
||||||
|
|
||||||
if foundField, found := fstabdocumentation.MountOptionsMapField[fileSystemType]; found {
|
switch completion.Documentation.(type) {
|
||||||
optionsField = foundField
|
case string:
|
||||||
} else {
|
documentation = completion.Documentation.(string)
|
||||||
optionsField = fstabdocumentation.DefaultMountOptionsField
|
case *string:
|
||||||
|
documentation = *completion.Documentation.(*string)
|
||||||
|
}
|
||||||
|
|
||||||
|
completion.Documentation = protocol.MarkupContent{
|
||||||
|
Kind: protocol.MarkupKindMarkdown,
|
||||||
|
Value: documentation + "\n\n" + "From: _Default Mount Options_",
|
||||||
|
}
|
||||||
|
completions = append(completions, completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
value, cursor := GetFieldSafely(line.Fields.Options, cursor)
|
for _, completion := range entry.FetchMountOptionsField(false).DeprecatedFetchCompletions(line, cursor) {
|
||||||
|
var documentation string
|
||||||
|
|
||||||
completions := optionsField.DeprecatedFetchCompletions(
|
switch completion.Documentation.(type) {
|
||||||
value,
|
case string:
|
||||||
cursor,
|
documentation = completion.Documentation.(string)
|
||||||
)
|
case *string:
|
||||||
|
documentation = *completion.Documentation.(*string)
|
||||||
|
}
|
||||||
|
|
||||||
|
completion.Documentation = protocol.MarkupContent{
|
||||||
|
Kind: protocol.MarkupKindMarkdown,
|
||||||
|
Value: documentation + "\n\n" + fmt.Sprintf("From: _%s_", fileSystemType),
|
||||||
|
}
|
||||||
|
completions = append(completions, completion)
|
||||||
|
}
|
||||||
|
|
||||||
return completions, nil
|
return completions, nil
|
||||||
case parser.FstabFieldFreq:
|
case ast.FstabFieldFreq:
|
||||||
value, cursor := GetFieldSafely(line.Fields.Freq, cursor)
|
value, cursor := getFieldSafely(entry.Fields.Freq, cursor)
|
||||||
|
|
||||||
return fstabdocumentation.FreqField.DeprecatedFetchCompletions(
|
return fields.FreqField.DeprecatedFetchCompletions(
|
||||||
value,
|
value,
|
||||||
cursor,
|
cursor,
|
||||||
), nil
|
), nil
|
||||||
case parser.FstabFieldPass:
|
case ast.FstabFieldPass:
|
||||||
value, cursor := GetFieldSafely(line.Fields.Pass, cursor)
|
value, cursor := getFieldSafely(entry.Fields.Pass, cursor)
|
||||||
|
|
||||||
return fstabdocumentation.PassField.DeprecatedFetchCompletions(
|
return fields.PassField.DeprecatedFetchCompletions(
|
||||||
value,
|
value,
|
||||||
cursor,
|
cursor,
|
||||||
), nil
|
), nil
|
||||||
@ -75,14 +98,18 @@ func GetCompletion(
|
|||||||
|
|
||||||
// Safely get value and new cursor position
|
// Safely get value and new cursor position
|
||||||
// If field is nil, return empty string and 0
|
// If field is nil, return empty string and 0
|
||||||
func GetFieldSafely(field *parser.Field, character uint32) (string, uint32) {
|
func getFieldSafely(field *ast.FstabField, cursor common.CursorPosition) (string, uint32) {
|
||||||
if field == nil {
|
if field == nil {
|
||||||
return "", 0
|
return "", 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if field.Value == "" {
|
if field.Value.Value == "" {
|
||||||
return "", 0
|
return "", 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return field.Value, character - field.Start
|
if uint32(cursor) < field.Start.Character {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.Value.Raw, common.CursorToCharacterIndex(uint32(cursor) - field.Start.Character)
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/doc-values"
|
"config-lsp/common"
|
||||||
"config-lsp/handlers/fstab/documentation"
|
"config-lsp/handlers/fstab/ast"
|
||||||
"config-lsp/handlers/fstab/parser"
|
|
||||||
"github.com/tliron/glsp/protocol_3_16"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetHoverInfo(entry *parser.FstabEntry, cursor uint32) (*protocol.Hover, error) {
|
func GetHoverInfo(
|
||||||
line := entry.Line
|
line uint32,
|
||||||
targetField := line.GetFieldAtPosition(cursor)
|
index common.IndexPosition,
|
||||||
|
entry *ast.FstabEntry,
|
||||||
|
) (*protocol.Hover, error) {
|
||||||
|
targetField := entry.GetFieldAtPosition(index)
|
||||||
|
|
||||||
switch targetField {
|
switch targetField {
|
||||||
case parser.FstabFieldSpec:
|
case ast.FstabFieldSpec:
|
||||||
return &SpecHoverField, nil
|
return &SpecHoverField, nil
|
||||||
case parser.FstabFieldMountPoint:
|
case ast.FstabFieldMountPoint:
|
||||||
return &MountPointHoverField, nil
|
return &MountPointHoverField, nil
|
||||||
case parser.FstabFieldFileSystemType:
|
case ast.FstabFieldFileSystemType:
|
||||||
return &FileSystemTypeField, nil
|
return &FileSystemTypeField, nil
|
||||||
case parser.FstabFieldOptions:
|
case ast.FstabFieldOptions:
|
||||||
fileSystemType := line.Fields.FilesystemType.Value
|
optionsField := entry.FetchMountOptionsField(true)
|
||||||
var optionsField docvalues.DeprecatedValue
|
|
||||||
|
|
||||||
if foundField, found := fstabdocumentation.MountOptionsMapField[fileSystemType]; found {
|
if optionsField == nil {
|
||||||
optionsField = foundField
|
return nil, nil
|
||||||
} else {
|
|
||||||
optionsField = fstabdocumentation.DefaultMountOptionsField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
relativeCursor := cursor - line.Fields.Options.Start
|
relativeCursor := uint32(index) - entry.Fields.Options.Start.Character
|
||||||
fieldInfo := optionsField.DeprecatedFetchHoverInfo(line.Fields.Options.Value, relativeCursor)
|
fieldInfo := optionsField.DeprecatedFetchHoverInfo(entry.Fields.Options.Value.Value, relativeCursor)
|
||||||
|
|
||||||
hover := protocol.Hover{
|
hover := protocol.Hover{
|
||||||
Contents: protocol.MarkupContent{
|
Contents: protocol.MarkupContent{
|
||||||
@ -40,9 +40,9 @@ func GetHoverInfo(entry *parser.FstabEntry, cursor uint32) (*protocol.Hover, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &hover, nil
|
return &hover, nil
|
||||||
case parser.FstabFieldFreq:
|
case ast.FstabFieldFreq:
|
||||||
return &FreqHoverField, nil
|
return &FreqHoverField, nil
|
||||||
case parser.FstabFieldPass:
|
case ast.FstabFieldPass:
|
||||||
return &PassHoverField, nil
|
return &PassHoverField, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package lsp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
fstabdocumentation "config-lsp/handlers/fstab/documentation"
|
"config-lsp/handlers/fstab/ast"
|
||||||
|
fstabdocumentation "config-lsp/handlers/fstab/fields"
|
||||||
"config-lsp/handlers/fstab/handlers"
|
"config-lsp/handlers/fstab/handlers"
|
||||||
"config-lsp/handlers/fstab/parser"
|
|
||||||
"config-lsp/handlers/fstab/shared"
|
"config-lsp/handlers/fstab/shared"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
@ -12,9 +12,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
|
||||||
p := shared.DocumentParserMap[params.TextDocument.URI]
|
d := shared.DocumentParserMap[params.TextDocument.URI]
|
||||||
|
cursor := common.LSPCharacterAsCursorPosition(params.Position.Character)
|
||||||
|
|
||||||
entry, found := p.GetEntry(params.Position.Line)
|
rawEntry, found := d.Config.Entries.Get(params.Position.Line)
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
// Empty line, return spec completions
|
// Empty line, return spec completions
|
||||||
@ -24,12 +25,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
|
|||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.Type == parser.FstabEntryTypeComment {
|
entry := rawEntry.(*ast.FstabEntry)
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor := common.CursorToCharacterIndex(params.Position.Character)
|
return handlers.GetCompletion(entry, cursor)
|
||||||
line := entry.Line
|
|
||||||
|
|
||||||
return handlers.GetCompletion(line, cursor)
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@ package lsp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/fstab/analyzer"
|
||||||
"config-lsp/handlers/fstab/shared"
|
"config-lsp/handlers/fstab/shared"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
@ -15,21 +17,21 @@ func TextDocumentDidChange(
|
|||||||
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
|
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
|
||||||
common.ClearDiagnostics(context, params.TextDocument.URI)
|
common.ClearDiagnostics(context, params.TextDocument.URI)
|
||||||
|
|
||||||
p := shared.DocumentParserMap[params.TextDocument.URI]
|
d := shared.DocumentParserMap[params.TextDocument.URI]
|
||||||
p.Clear()
|
d.Config.Clear()
|
||||||
|
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
diagnostics := make([]protocol.Diagnostic, 0)
|
||||||
errors := p.ParseFromContent(content)
|
errors := d.Config.Parse(content)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
diagnostics = append(diagnostics, utils.Map(
|
diagnostics = append(diagnostics, utils.Map(
|
||||||
errors,
|
errors,
|
||||||
func(err common.ParseError) protocol.Diagnostic {
|
func(err common.LSPError) protocol.Diagnostic {
|
||||||
return err.ToDiagnostic()
|
return err.ToDiagnostic()
|
||||||
},
|
},
|
||||||
)...)
|
)...)
|
||||||
} else {
|
} else {
|
||||||
diagnostics = append(diagnostics, p.AnalyzeValues()...)
|
diagnostics = append(diagnostics, analyzer.Analyze(d)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(diagnostics) > 0 {
|
if len(diagnostics) > 0 {
|
||||||
|
@ -2,9 +2,11 @@ package lsp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
"config-lsp/handlers/fstab/parser"
|
"config-lsp/handlers/fstab/analyzer"
|
||||||
|
"config-lsp/handlers/fstab/ast"
|
||||||
"config-lsp/handlers/fstab/shared"
|
"config-lsp/handlers/fstab/shared"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
@ -15,24 +17,26 @@ func TextDocumentDidOpen(
|
|||||||
) error {
|
) error {
|
||||||
common.ClearDiagnostics(context, params.TextDocument.URI)
|
common.ClearDiagnostics(context, params.TextDocument.URI)
|
||||||
|
|
||||||
p := parser.FstabParser{}
|
config := ast.NewFstabConfig()
|
||||||
p.Clear()
|
d := &shared.FstabDocument{
|
||||||
shared.DocumentParserMap[params.TextDocument.URI] = &p
|
Config: config,
|
||||||
|
}
|
||||||
|
shared.DocumentParserMap[params.TextDocument.URI] = d
|
||||||
|
|
||||||
content := params.TextDocument.Text
|
content := params.TextDocument.Text
|
||||||
|
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
diagnostics := make([]protocol.Diagnostic, 0)
|
||||||
errors := p.ParseFromContent(content)
|
errors := d.Config.Parse(content)
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
diagnostics = append(diagnostics, utils.Map(
|
diagnostics = append(diagnostics, utils.Map(
|
||||||
errors,
|
errors,
|
||||||
func(err common.ParseError) protocol.Diagnostic {
|
func(err common.LSPError) protocol.Diagnostic {
|
||||||
return err.ToDiagnostic()
|
return err.ToDiagnostic()
|
||||||
},
|
},
|
||||||
)...)
|
)...)
|
||||||
} else {
|
} else {
|
||||||
diagnostics = append(diagnostics, p.AnalyzeValues()...)
|
diagnostics = append(diagnostics, analyzer.Analyze(d)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(diagnostics) > 0 {
|
if len(diagnostics) > 0 {
|
||||||
|
@ -1,29 +1,33 @@
|
|||||||
package lsp
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/fstab/ast"
|
||||||
"config-lsp/handlers/fstab/handlers"
|
"config-lsp/handlers/fstab/handlers"
|
||||||
"config-lsp/handlers/fstab/parser"
|
|
||||||
"config-lsp/handlers/fstab/shared"
|
"config-lsp/handlers/fstab/shared"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
|
func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
|
||||||
cursor := params.Position.Character
|
line := params.Position.Line
|
||||||
|
index := common.LSPCharacterAsIndexPosition(params.Position.Character)
|
||||||
|
|
||||||
p := shared.DocumentParserMap[params.TextDocument.URI]
|
d := shared.DocumentParserMap[params.TextDocument.URI]
|
||||||
|
|
||||||
entry, found := p.GetEntry(params.Position.Line)
|
rawEntry, found := d.Config.Entries.Get(params.Position.Line)
|
||||||
|
|
||||||
// Empty line
|
// Empty line
|
||||||
if !found {
|
if !found {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment line
|
entry := rawEntry.(*ast.FstabEntry)
|
||||||
if entry.Type == parser.FstabEntryTypeComment {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return handlers.GetHoverInfo(entry, cursor)
|
return handlers.GetHoverInfo(
|
||||||
|
line,
|
||||||
|
index,
|
||||||
|
entry,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,387 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,8 +1,13 @@
|
|||||||
package shared
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/fstab/parser"
|
"config-lsp/handlers/fstab/ast"
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DocumentParserMap = map[protocol.DocumentUri]*parser.FstabParser{}
|
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,
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package analyzer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
|
docvalues "config-lsp/doc-values"
|
||||||
"config-lsp/handlers/ssh_config/ast"
|
"config-lsp/handlers/ssh_config/ast"
|
||||||
"config-lsp/handlers/ssh_config/fields"
|
"config-lsp/handlers/ssh_config/fields"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
@ -40,7 +41,7 @@ func checkOption(
|
|||||||
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
|
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
|
||||||
checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
|
checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
|
||||||
|
|
||||||
_, found := fields.Options[option.Key.Key]
|
docOption, found := fields.Options[option.Key.Key]
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
// Diagnostics will be handled by `values.go`
|
// Diagnostics will be handled by `values.go`
|
||||||
@ -61,6 +62,19 @@ func checkOption(
|
|||||||
if option.OptionValue != nil {
|
if option.OptionValue != nil {
|
||||||
checkIsUsingDoubleQuotes(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
checkIsUsingDoubleQuotes(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
||||||
checkQuotesAreClosed(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
checkQuotesAreClosed(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
||||||
|
|
||||||
|
invalidValues := docOption.DeprecatedCheckIsValid(option.OptionValue.Value.Value)
|
||||||
|
|
||||||
|
for _, invalidValue := range invalidValues {
|
||||||
|
err := docvalues.LSPErrorFromInvalidValue(option.Start.Line, *invalidValue)
|
||||||
|
err.ShiftCharacter(option.OptionValue.Start.Character)
|
||||||
|
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Range: err.Range.ToLSPRange(),
|
||||||
|
Message: err.Err.Error(),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if option.Separator == nil || option.Separator.Value.Value == "" {
|
if option.Separator == nil || option.Separator.Value.Value == "" {
|
||||||
|
@ -42,7 +42,7 @@ User root
|
|||||||
|
|
||||||
analyzeStructureIsValid(ctx)
|
analyzeStructureIsValid(ctx)
|
||||||
|
|
||||||
if len(ctx.diagnostics) != 0 {
|
if len(ctx.diagnostics) != 1 {
|
||||||
t.Fatalf("Expected no errors, got %v", ctx.diagnostics)
|
t.Fatalf("Expected no errors, got %v", ctx.diagnostics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,8 +85,8 @@ Match
|
|||||||
|
|
||||||
analyzeStructureIsValid(ctx)
|
analyzeStructureIsValid(ctx)
|
||||||
|
|
||||||
if len(ctx.diagnostics) != 1 {
|
if len(ctx.diagnostics) != 2 {
|
||||||
t.Fatalf("Expected 1 error, got %v", ctx.diagnostics)
|
t.Fatalf("Expected 2 errors (separator error and value error), got %v", ctx.diagnostics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ Match
|
|||||||
|
|
||||||
analyzeStructureIsValid(ctx)
|
analyzeStructureIsValid(ctx)
|
||||||
|
|
||||||
if len(ctx.diagnostics) != 0 {
|
if len(ctx.diagnostics) != 1 {
|
||||||
t.Fatalf("Expected no errors, got %v", ctx.diagnostics)
|
t.Fatalf("Expected 1 error, got %v", ctx.diagnostics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,13 @@ func createListenerContext() *sshListenerContext {
|
|||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sshParserListener struct {
|
||||||
|
*parser.BaseConfigListener
|
||||||
|
Config *SSHConfig
|
||||||
|
Errors []common.LSPError
|
||||||
|
sshContext *sshListenerContext
|
||||||
|
}
|
||||||
|
|
||||||
func createListener(
|
func createListener(
|
||||||
config *SSHConfig,
|
config *SSHConfig,
|
||||||
context *sshListenerContext,
|
context *sshListenerContext,
|
||||||
@ -37,13 +44,6 @@ func createListener(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type sshParserListener struct {
|
|
||||||
*parser.BaseConfigListener
|
|
||||||
Config *SSHConfig
|
|
||||||
Errors []common.LSPError
|
|
||||||
sshContext *sshListenerContext
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sshParserListener) EnterEntry(ctx *parser.EntryContext) {
|
func (s *sshParserListener) EnterEntry(ctx *parser.EntryContext) {
|
||||||
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
location.ChangeBothLines(s.sshContext.line)
|
location.ChangeBothLines(s.sshContext.line)
|
||||||
|
@ -5,6 +5,9 @@ ROOT=$(git rev-parse --show-toplevel)/server
|
|||||||
# aliases
|
# aliases
|
||||||
cd $ROOT/handlers/aliases && antlr4 -Dlanguage=Go -o ast/parser Aliases.g4
|
cd $ROOT/handlers/aliases && antlr4 -Dlanguage=Go -o ast/parser Aliases.g4
|
||||||
|
|
||||||
|
# fstab
|
||||||
|
cd $ROOT/hanlders/fstab && antlr4 -Dlanguage=Go -o ast/parser Fstab.g4
|
||||||
|
|
||||||
# sshd_config
|
# sshd_config
|
||||||
cd $ROOT/handlers/sshd_config && antlr4 -Dlanguage=Go -o ast/parser Config.g4
|
cd $ROOT/handlers/sshd_config && antlr4 -Dlanguage=Go -o ast/parser Config.g4
|
||||||
cd $ROOT/handlers/sshd_config/match-parser && antlr4 -Dlanguage=Go -o parser Match.g4
|
cd $ROOT/handlers/sshd_config/match-parser && antlr4 -Dlanguage=Go -o parser Match.g4
|
||||||
|
Loading…
x
Reference in New Issue
Block a user