mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat: Add textsync parser; Improvements
This commit is contained in:
parent
c129b6507a
commit
da4577ac22
2
.gitignore
vendored
2
.gitignore
vendored
@ -23,3 +23,5 @@ go.work.sum
|
|||||||
|
|
||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
test.lua
|
||||||
|
154
common/parser.go
Normal file
154
common/parser.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimpleConfigPosition struct {
|
||||||
|
Line int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleConfigLine struct {
|
||||||
|
Value string
|
||||||
|
Position SimpleConfigPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SimpleConfigLine) IsCursorAtRootOption(cursor int) bool {
|
||||||
|
if cursor <= len(l.Value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleConfigOptions struct {
|
||||||
|
Separator string
|
||||||
|
IgnorePattern regexp.Regexp
|
||||||
|
AvailableOptions *map[string]Option
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleConfigParser struct {
|
||||||
|
Lines map[string]SimpleConfigLine
|
||||||
|
Options SimpleConfigOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
type OptionAlreadyExistsError struct {
|
||||||
|
Option string
|
||||||
|
}
|
||||||
|
func (e OptionAlreadyExistsError) Error() string {
|
||||||
|
return fmt.Sprintf("Option %s already exists", e.Option)
|
||||||
|
}
|
||||||
|
type OptionUnknownError struct {
|
||||||
|
Option string
|
||||||
|
}
|
||||||
|
func (e OptionUnknownError) Error() string {
|
||||||
|
return fmt.Sprintf("Option '%s' does not exist", e.Option)
|
||||||
|
}
|
||||||
|
type MalformedLineError struct {
|
||||||
|
Line string
|
||||||
|
}
|
||||||
|
func (e MalformedLineError) Error() string {
|
||||||
|
return fmt.Sprintf("Malformed line: %s", e.Line)
|
||||||
|
}
|
||||||
|
type LineNotFoundError struct {}
|
||||||
|
func (e LineNotFoundError) Error() string {
|
||||||
|
return "Line not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimpleConfigParser) AddLine(line string, lineNumber int) error {
|
||||||
|
parts := strings.SplitN(line, p.Options.Separator, 2)
|
||||||
|
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return MalformedLineError{
|
||||||
|
Line: line,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
option := parts[0]
|
||||||
|
|
||||||
|
if _, exists := (*p.Options.AvailableOptions)[option]; !exists {
|
||||||
|
return OptionUnknownError{
|
||||||
|
Option: option,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value := ""
|
||||||
|
|
||||||
|
if len(parts) > 1 {
|
||||||
|
value = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := p.Lines[option]; exists {
|
||||||
|
return OptionAlreadyExistsError{
|
||||||
|
Option: option,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Lines[option] = SimpleConfigLine{
|
||||||
|
Value: value,
|
||||||
|
Position: SimpleConfigPosition{
|
||||||
|
Line: lineNumber,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimpleConfigParser) ReplaceOption(option string, value string) {
|
||||||
|
p.Lines[option] = SimpleConfigLine{
|
||||||
|
Value: value,
|
||||||
|
Position: SimpleConfigPosition{
|
||||||
|
Line: p.Lines[option].Position.Line,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimpleConfigParser) RemoveOption(option string) {
|
||||||
|
delete(p.Lines, option)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimpleConfigParser) UpsertOption(option string, value string) {
|
||||||
|
if _, exists := p.Lines[option]; exists {
|
||||||
|
p.ReplaceOption(option, value)
|
||||||
|
} else {
|
||||||
|
p.AddLine(option + p.Options.Separator + value, len(p.Lines))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimpleConfigParser) ParseFromFile(content string) []error {
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
errors := make([]error, 0)
|
||||||
|
|
||||||
|
for index, line := range lines {
|
||||||
|
if p.Options.IgnorePattern.MatchString(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.AddLine(line, index)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimpleConfigParser) Clear() {
|
||||||
|
clear(p.Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use better approach: Store an extra array of lines in order; with references to the SimpleConfigLine
|
||||||
|
func (p SimpleConfigParser) FindByLineNumber(lineNumber int) (string, SimpleConfigLine, error) {
|
||||||
|
for option, line := range p.Lines {
|
||||||
|
if line.Position.Line == lineNumber {
|
||||||
|
return option, line, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", SimpleConfigLine{Value: "", Position: SimpleConfigPosition{Line: 0}}, LineNotFoundError{}
|
||||||
|
}
|
||||||
|
|
26
handlers/openssh/shared.go
Normal file
26
handlers/openssh/shared.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createOpenSSHConfigParser() common.SimpleConfigParser {
|
||||||
|
pattern, err := regexp.Compile(`^(?:#|\s*$)`)
|
||||||
|
|
||||||
|
if (err != nil) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.SimpleConfigParser{
|
||||||
|
Lines: make(map[string]common.SimpleConfigLine),
|
||||||
|
Options: common.SimpleConfigOptions{
|
||||||
|
Separator: " ",
|
||||||
|
IgnorePattern: *pattern,
|
||||||
|
AvailableOptions: &Options,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var Parser = createOpenSSHConfigParser()
|
||||||
|
|
@ -2,7 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
"strings"
|
"errors"
|
||||||
|
|
||||||
"github.com/tliron/glsp"
|
"github.com/tliron/glsp"
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
@ -12,20 +12,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (interface{}, error) {
|
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (interface{}, error) {
|
||||||
line, err := common.GetLine(params.TextDocument.URI, int(params.Position.Line))
|
option, line, err := Parser.FindByLineNumber(int(params.Position.Line))
|
||||||
|
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return [...]protocol.CompletionItem{}, err
|
if line.IsCursorAtRootOption(int(params.Position.Character)) {
|
||||||
}
|
return getRootCompletions(), nil
|
||||||
|
} else {
|
||||||
rootOption := getCurrentOption(line, int(params.Position.Character))
|
return getOptionCompletions(option), nil
|
||||||
|
}
|
||||||
if (rootOption == "") {
|
} else if errors.Is(err, common.LineNotFoundError{}) {
|
||||||
return getRootCompletions(), nil
|
return getRootCompletions(), nil
|
||||||
} else {
|
|
||||||
return getOptionCompletions(rootOption), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRootCompletions() []protocol.CompletionItem {
|
func getRootCompletions() []protocol.CompletionItem {
|
||||||
@ -73,17 +72,3 @@ func getOptionCompletions(optionName string) []protocol.CompletionItem {
|
|||||||
return []protocol.CompletionItem{}
|
return []protocol.CompletionItem{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentOption(line string, position int) string {
|
|
||||||
words := strings.Split(line, " ")
|
|
||||||
|
|
||||||
if len(words) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position <= len(words[0])) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return words[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
16
handlers/openssh/text-document-did-change.go
Normal file
16
handlers/openssh/text-document-did-change.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tliron/glsp"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Todo: Implement incremental parsing
|
||||||
|
func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error {
|
||||||
|
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
|
||||||
|
|
||||||
|
Parser.Clear()
|
||||||
|
Parser.ParseFromFile(content)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
26
handlers/openssh/text-document-did-open.go
Normal file
26
handlers/openssh/text-document-did-open.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/tliron/glsp"
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TextDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocumentParams) error {
|
||||||
|
readBytes, err := os.ReadFile(params.TextDocument.URI[len("file://"):])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
errors := Parser.ParseFromFile(string(readBytes))
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return errors[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
11
main.go
11
main.go
@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
openssh "config-lsp/handlers/openssh"
|
openssh "config-lsp/handlers/openssh"
|
||||||
|
|
||||||
"github.com/tliron/commonlog"
|
"github.com/tliron/commonlog"
|
||||||
@ -27,10 +25,12 @@ func main() {
|
|||||||
commonlog.Configure(1, nil)
|
commonlog.Configure(1, nil)
|
||||||
|
|
||||||
handler = protocol.Handler{
|
handler = protocol.Handler{
|
||||||
Initialize: initialize,
|
Initialize: initialize,
|
||||||
Initialized: initialized,
|
Initialized: initialized,
|
||||||
Shutdown: shutdown,
|
Shutdown: shutdown,
|
||||||
SetTrace: setTrace,
|
SetTrace: setTrace,
|
||||||
|
TextDocumentDidOpen: openssh.TextDocumentDidOpen,
|
||||||
|
TextDocumentDidChange: openssh.TextDocumentDidChange,
|
||||||
TextDocumentCompletion: openssh.TextDocumentCompletion,
|
TextDocumentCompletion: openssh.TextDocumentCompletion,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ func main() {
|
|||||||
|
|
||||||
func initialize(context *glsp.Context, params *protocol.InitializeParams) (any, error) {
|
func initialize(context *glsp.Context, params *protocol.InitializeParams) (any, error) {
|
||||||
capabilities := handler.CreateServerCapabilities()
|
capabilities := handler.CreateServerCapabilities()
|
||||||
|
capabilities.TextDocumentSync = protocol.TextDocumentSyncKindFull
|
||||||
|
|
||||||
return protocol.InitializeResult{
|
return protocol.InitializeResult{
|
||||||
Capabilities: capabilities,
|
Capabilities: capabilities,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user