fix(fstab): Several improvements and bugfixes

This commit is contained in:
Myzel394 2024-09-08 22:55:25 +02:00
parent a7dc7fc6e8
commit e0c9c44f38
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
21 changed files with 214 additions and 161 deletions

View File

@ -126,7 +126,7 @@ func (v ArrayValue) getCurrentValue(line string, cursor uint32) (string, uint32)
line,
v.Separator,
// defaults
min(len(line)-1, int(cursor)),
min(len(line)-1, int(cursor)-1),
)
if found {

View File

@ -63,7 +63,7 @@ func (v OrValue) FetchCompletions(line string, cursor uint32) []protocol.Complet
_, found := utils.FindPreviousCharacter(
line,
keyEnumValue.Separator,
max(0, int(cursor-1)),
int(cursor-1),
)
if found {

View File

@ -2,6 +2,8 @@ package fstab
import (
fstabdocumentation "config-lsp/handlers/fstab/documentation"
handlers "config-lsp/handlers/fstab/handlers"
"config-lsp/handlers/fstab/parser"
"config-lsp/utils"
"testing"
)
@ -17,53 +19,55 @@ LABEL=test /mnt/test btrfs subvol=backup,fat=32 0 0
// to be wrong. Use a treemap instead of a map.
func TestValidBasicExample(t *testing.T) {
// Arrange
parser := FstabParser{}
p := parser.FstabParser{}
p.Clear()
errors := parser.ParseFromContent(sampleValidBasicExample)
errors := p.ParseFromContent(sampleValidBasicExample)
if len(errors) > 0 {
t.Fatal("ParseFromContent failed with error", errors)
}
// Get hover for first field
entry := parser.entries[0]
rawEntry, _ := p.Entries.Get(uint32(0))
entry := rawEntry.(parser.FstabEntry)
println("Getting hover info")
{
hover, err := getHoverInfo(&entry, uint32(0))
hover, err := handlers.GetHoverInfo(&entry, uint32(0))
if err != nil {
t.Fatal("getHoverInfo failed with error", err)
}
if hover.Contents != SpecHoverField.Contents {
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", SpecHoverField.Contents)
if hover.Contents != handlers.SpecHoverField.Contents {
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", handlers.SpecHoverField.Contents)
}
// Get hover for second field
hover, err = getHoverInfo(&entry, uint32(11))
hover, err = handlers.GetHoverInfo(&entry, uint32(11))
if err != nil {
t.Fatal("getHoverInfo failed with error", err)
}
if hover.Contents != MountPointHoverField.Contents {
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", MountPointHoverField.Contents)
if hover.Contents != handlers.MountPointHoverField.Contents {
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", handlers.MountPointHoverField.Contents)
}
hover, err = getHoverInfo(&entry, uint32(20))
hover, err = handlers.GetHoverInfo(&entry, uint32(20))
if err != nil {
t.Fatal("getHoverInfo failed with error", err)
}
if hover.Contents != MountPointHoverField.Contents {
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", MountPointHoverField.Contents)
if hover.Contents != handlers.MountPointHoverField.Contents {
t.Fatal("getHoverInfo failed to return correct hover content. Got:", hover.Contents, "but expected:", handlers.MountPointHoverField.Contents)
}
}
println("Getting completions")
{
completions, err := getCompletion(entry.Line, uint32(0))
completions, err := handlers.GetCompletion(entry.Line, uint32(0))
if err != nil {
t.Fatal("getCompletion failed with error", err)
@ -79,7 +83,7 @@ func TestValidBasicExample(t *testing.T) {
}
{
completions, err := getCompletion(entry.Line, uint32(21))
completions, err := handlers.GetCompletion(entry.Line, uint32(21))
if err != nil {
t.Fatal("getCompletion failed with error", err)
@ -93,7 +97,7 @@ func TestValidBasicExample(t *testing.T) {
println("Checking values")
{
diagnostics := parser.AnalyzeValues()
diagnostics := p.AnalyzeValues()
if len(diagnostics) > 0 {
t.Fatal("AnalyzeValues failed with error", diagnostics)
@ -103,9 +107,10 @@ func TestValidBasicExample(t *testing.T) {
func TestInvalidOptionsExample(t *testing.T) {
// Arrange
parser := FstabParser{}
p := parser.FstabParser{}
p.Clear()
errors := parser.ParseFromContent(sampleInvalidOptionsExample)
errors := p.ParseFromContent(sampleInvalidOptionsExample)
if len(errors) > 0 {
t.Fatal("ParseFromContent returned error", errors)
@ -114,7 +119,7 @@ func TestInvalidOptionsExample(t *testing.T) {
// Get hover for first field
println("Checking values")
{
diagnostics := parser.AnalyzeValues()
diagnostics := p.AnalyzeValues()
if len(diagnostics) == 0 {
t.Fatal("AnalyzeValues should have returned error")

View File

@ -1,65 +1,41 @@
package fstab
package handlers
import (
docvalues "config-lsp/doc-values"
fstabdocumentation "config-lsp/handlers/fstab/documentation"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
"config-lsp/doc-values"
"config-lsp/handlers/fstab/documentation"
"config-lsp/handlers/fstab/parser"
"github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
parser := documentParserMap[params.TextDocument.URI]
entry, found := parser.GetEntry(params.Position.Line)
if !found {
// Empty line, return spec completions
return fstabdocumentation.SpecField.FetchCompletions(
"",
params.Position.Character,
), nil
}
if entry.Type == FstabEntryTypeComment {
return nil, nil
}
cursor := params.Position.Character
line := entry.Line
return getCompletion(line, cursor)
}
func getCompletion(
line FstabLine,
func GetCompletion(
line parser.FstabLine,
cursor uint32,
) ([]protocol.CompletionItem, error) {
targetField := line.GetFieldAtPosition(cursor)
switch targetField {
case FstabFieldSpec:
case parser.FstabFieldSpec:
value, cursor := GetFieldSafely(line.Fields.Spec, cursor)
return fstabdocumentation.SpecField.FetchCompletions(
value,
cursor,
), nil
case FstabFieldMountPoint:
case parser.FstabFieldMountPoint:
value, cursor := GetFieldSafely(line.Fields.MountPoint, cursor)
return fstabdocumentation.MountPointField.FetchCompletions(
value,
cursor,
), nil
case FstabFieldFileSystemType:
case parser.FstabFieldFileSystemType:
value, cursor := GetFieldSafely(line.Fields.FilesystemType, cursor)
return fstabdocumentation.FileSystemTypeField.FetchCompletions(
value,
cursor,
), nil
case FstabFieldOptions:
case parser.FstabFieldOptions:
fileSystemType := line.Fields.FilesystemType.Value
var optionsField docvalues.Value
@ -78,14 +54,14 @@ func getCompletion(
)
return completions, nil
case FstabFieldFreq:
case parser.FstabFieldFreq:
value, cursor := GetFieldSafely(line.Fields.Freq, cursor)
return fstabdocumentation.FreqField.FetchCompletions(
value,
cursor,
), nil
case FstabFieldPass:
case parser.FstabFieldPass:
value, cursor := GetFieldSafely(line.Fields.Pass, cursor)
return fstabdocumentation.PassField.FetchCompletions(
@ -99,7 +75,7 @@ func getCompletion(
// Safely get value and new cursor position
// If field is nil, return empty string and 0
func GetFieldSafely(field *Field, character uint32) (string, uint32) {
func GetFieldSafely(field *parser.Field, character uint32) (string, uint32) {
if field == nil {
return "", 0
}

View File

@ -1,4 +1,4 @@
package fstab
package handlers
import protocol "github.com/tliron/glsp/protocol_3_16"

View File

@ -1,46 +1,25 @@
package fstab
package handlers
import (
docvalues "config-lsp/doc-values"
fstabdocumentation "config-lsp/handlers/fstab/documentation"
"config-lsp/doc-values"
"config-lsp/handlers/fstab/documentation"
"config-lsp/handlers/fstab/parser"
"github.com/tliron/glsp/protocol_3_16"
"strings"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
cursor := params.Position.Character
parser := documentParserMap[params.TextDocument.URI]
entry, found := parser.GetEntry(params.Position.Line)
// Empty line
if !found {
return nil, nil
}
// Comment line
if entry.Type == FstabEntryTypeComment {
return nil, nil
}
return getHoverInfo(entry, cursor)
}
func getHoverInfo(entry *FstabEntry, cursor uint32) (*protocol.Hover, error) {
func GetHoverInfo(entry *parser.FstabEntry, cursor uint32) (*protocol.Hover, error) {
line := entry.Line
targetField := line.GetFieldAtPosition(cursor)
switch targetField {
case FstabFieldSpec:
case parser.FstabFieldSpec:
return &SpecHoverField, nil
case FstabFieldMountPoint:
case parser.FstabFieldMountPoint:
return &MountPointHoverField, nil
case FstabFieldFileSystemType:
case parser.FstabFieldFileSystemType:
return &FileSystemTypeField, nil
case FstabFieldOptions:
case parser.FstabFieldOptions:
fileSystemType := line.Fields.FilesystemType.Value
var optionsField docvalues.Value
@ -61,9 +40,9 @@ func getHoverInfo(entry *FstabEntry, cursor uint32) (*protocol.Hover, error) {
}
return &hover, nil
case FstabFieldFreq:
case parser.FstabFieldFreq:
return &FreqHoverField, nil
case FstabFieldPass:
case parser.FstabFieldPass:
return &PassHoverField, nil
}

View File

@ -0,0 +1,34 @@
package lsp
import (
fstabdocumentation "config-lsp/handlers/fstab/documentation"
"config-lsp/handlers/fstab/handlers"
"config-lsp/handlers/fstab/parser"
"config-lsp/handlers/fstab/shared"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
p := shared.DocumentParserMap[params.TextDocument.URI]
entry, found := p.GetEntry(params.Position.Line)
if !found {
// Empty line, return spec completions
return fstabdocumentation.SpecField.FetchCompletions(
"",
params.Position.Character,
), nil
}
if entry.Type == parser.FstabEntryTypeComment {
return nil, nil
}
cursor := params.Position.Character
line := entry.Line
return handlers.GetCompletion(line, cursor)
}

View File

@ -1,7 +1,8 @@
package fstab
package lsp
import (
"config-lsp/common"
"config-lsp/handlers/fstab/shared"
"config-lsp/utils"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
@ -14,11 +15,11 @@ func TextDocumentDidChange(
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
common.ClearDiagnostics(context, params.TextDocument.URI)
parser := documentParserMap[params.TextDocument.URI]
parser.Clear()
p := shared.DocumentParserMap[params.TextDocument.URI]
p.Clear()
diagnostics := make([]protocol.Diagnostic, 0)
errors := parser.ParseFromContent(content)
errors := p.ParseFromContent(content)
if len(errors) > 0 {
diagnostics = append(diagnostics, utils.Map(
@ -27,10 +28,10 @@ func TextDocumentDidChange(
return err.ToDiagnostic()
},
)...)
} else {
diagnostics = append(diagnostics, p.AnalyzeValues()...)
}
diagnostics = append(diagnostics, parser.AnalyzeValues()...)
if len(diagnostics) > 0 {
common.SendDiagnostics(context, params.TextDocument.URI, diagnostics)
}

View File

@ -0,0 +1,13 @@
package lsp
import (
shared "config-lsp/handlers/fstab/shared"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error {
delete(shared.DocumentParserMap, params.TextDocument.URI)
return nil
}

View File

@ -0,0 +1,43 @@
package lsp
import (
"config-lsp/common"
"config-lsp/handlers/fstab/parser"
"config-lsp/handlers/fstab/shared"
"config-lsp/utils"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentDidOpen(
context *glsp.Context,
params *protocol.DidOpenTextDocumentParams,
) error {
common.ClearDiagnostics(context, params.TextDocument.URI)
p := parser.FstabParser{}
p.Clear()
shared.DocumentParserMap[params.TextDocument.URI] = &p
content := params.TextDocument.Text
diagnostics := make([]protocol.Diagnostic, 0)
errors := p.ParseFromContent(content)
if len(errors) > 0 {
diagnostics = append(diagnostics, utils.Map(
errors,
func(err common.ParseError) protocol.Diagnostic {
return err.ToDiagnostic()
},
)...)
} else {
diagnostics = append(diagnostics, p.AnalyzeValues()...)
}
if len(diagnostics) > 0 {
common.SendDiagnostics(context, params.TextDocument.URI, diagnostics)
}
return nil
}

View File

@ -0,0 +1,29 @@
package lsp
import (
"config-lsp/handlers/fstab/handlers"
"config-lsp/handlers/fstab/parser"
"config-lsp/handlers/fstab/shared"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
cursor := params.Position.Character
p := shared.DocumentParserMap[params.TextDocument.URI]
entry, found := p.GetEntry(params.Position.Line)
// Empty line
if !found {
return nil, nil
}
// Comment line
if entry.Type == parser.FstabEntryTypeComment {
return nil, nil
}
return handlers.GetHoverInfo(entry, cursor)
}

View File

@ -1,4 +1,4 @@
package fstab
package parser
import (
"config-lsp/common"
@ -6,10 +6,12 @@ import (
fstabdocumentation "config-lsp/handlers/fstab/documentation"
"fmt"
"regexp"
"slices"
"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*#`)
@ -203,7 +205,8 @@ type FstabEntry struct {
}
type FstabParser struct {
entries []FstabEntry
// [uint32]FstabEntry - line number to entry mapping
Entries *treemap.Map
}
func (p *FstabParser) AddLine(line string, lineNumber int) error {
@ -302,7 +305,7 @@ func (p *FstabParser) AddLine(line string, lineNumber int) error {
},
},
}
p.entries = append(p.entries, entry)
p.Entries.Put(entry.Line.Line, entry)
return nil
}
@ -311,7 +314,7 @@ func (p *FstabParser) AddCommentLine(line string, lineNumber int) {
entry := FstabLine{
Line: uint32(lineNumber),
}
p.entries = append(p.entries, FstabEntry{
p.Entries.Put(entry.Line, FstabEntry{
Type: FstabEntryTypeComment,
Line: entry,
})
@ -345,33 +348,29 @@ func (p *FstabParser) ParseFromContent(content string) []common.ParseError {
}
func (p *FstabParser) GetEntry(line uint32) (*FstabEntry, bool) {
index, found := slices.BinarySearchFunc(p.entries, line, func(entry FstabEntry, line uint32) int {
if entry.Line.Line < line {
return -1
}
if entry.Line.Line > line {
return 1
}
return 0
})
rawEntry, found := p.Entries.Get(line)
if !found {
return nil, false
}
return &p.entries[index], true
entry := rawEntry.(FstabEntry)
return &entry, true
}
func (p *FstabParser) Clear() {
p.entries = []FstabEntry{}
p.Entries = treemap.NewWith(gods.UInt32Comparator)
}
func (p *FstabParser) AnalyzeValues() []protocol.Diagnostic {
diagnostics := []protocol.Diagnostic{}
for _, entry := range p.entries {
it := p.Entries.Iterator()
for it.Next() {
entry := it.Value().(FstabEntry)
switch entry.Type {
case FstabEntryTypeLine:
newDiagnostics := entry.Line.CheckIsValid()

View File

@ -1,7 +0,0 @@
package fstab
import (
protocol "github.com/tliron/glsp/protocol_3_16"
)
var documentParserMap = map[protocol.DocumentUri]*FstabParser{}

View File

@ -0,0 +1,8 @@
package shared
import (
"config-lsp/handlers/fstab/parser"
protocol "github.com/tliron/glsp/protocol_3_16"
)
var DocumentParserMap = map[protocol.DocumentUri]*parser.FstabParser{}

View File

@ -1,30 +0,0 @@
package fstab
import (
"config-lsp/common"
"config-lsp/utils"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func TextDocumentDidOpen(
context *glsp.Context,
params *protocol.DidOpenTextDocumentParams,
) error {
common.ClearDiagnostics(context, params.TextDocument.URI)
parser := FstabParser{}
documentParserMap[params.TextDocument.URI] = &parser
errors := parser.ParseFromContent(params.TextDocument.Text)
diagnostics := utils.Map(
errors,
func(err common.ParseError) protocol.Diagnostic {
return err.ToDiagnostic()
},
)
common.SendDiagnostics(context, params.TextDocument.URI, diagnostics)
return nil
}

View File

@ -24,7 +24,7 @@ func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionPa
switch *language {
case LanguageFstab:
fallthrough
return nil, nil
case LanguageHosts:
return hosts.TextDocumentCodeAction(context, params)
case LanguageSSHDConfig:

View File

@ -2,7 +2,7 @@ package roothandler
import (
aliases "config-lsp/handlers/aliases/lsp"
"config-lsp/handlers/fstab"
fstab "config-lsp/handlers/fstab/lsp"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"

View File

@ -2,7 +2,7 @@ package roothandler
import (
aliases "config-lsp/handlers/aliases/lsp"
"config-lsp/handlers/fstab"
fstab "config-lsp/handlers/fstab/lsp"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"

View File

@ -2,6 +2,7 @@ package roothandler
import (
aliases "config-lsp/handlers/aliases/lsp"
fstab "config-lsp/handlers/fstab/lsp"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"
@ -26,8 +27,9 @@ func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDo
rootHandler.RemoveDocument(params.TextDocument.URI)
switch *language {
case LanguageFstab:
case LanguageSSHDConfig:
case LanguageFstab:
return fstab.TextDocumentDidClose(context, params)
case LanguageWireguard:
return wireguard.TextDocumentDidClose(context, params)
case LanguageHosts:

View File

@ -2,11 +2,12 @@ package roothandler
import (
"config-lsp/common"
"fmt"
aliases "config-lsp/handlers/aliases/lsp"
fstab "config-lsp/handlers/fstab"
fstab "config-lsp/handlers/fstab/lsp"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"
"fmt"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"

View File

@ -2,7 +2,7 @@ package roothandler
import (
aliases "config-lsp/handlers/aliases/lsp"
"config-lsp/handlers/fstab"
fstab "config-lsp/handlers/fstab/lsp"
hosts "config-lsp/handlers/hosts/lsp"
wireguard "config-lsp/handlers/wireguard/lsp"