Skip to content

Commit

Permalink
Experimental LSP server (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
mickael-menu committed Apr 4, 2021
1 parent be78def commit ba41fe5
Show file tree
Hide file tree
Showing 21 changed files with 1,553 additions and 56 deletions.
8 changes: 5 additions & 3 deletions cmd/container.go → adapter/container.go
@@ -1,4 +1,4 @@
package cmd
package adapter

import (
"io"
Expand All @@ -22,6 +22,7 @@ import (
)

type Container struct {
Version string
Config zk.Config
Date date.Provider
Logger util.Logger
Expand All @@ -32,7 +33,7 @@ type Container struct {
zkErr error
}

func NewContainer() (*Container, error) {
func NewContainer(version string) (*Container, error) {
wrap := errors.Wrapper("initialization")

config := zk.NewDefaultConfig()
Expand All @@ -52,7 +53,8 @@ func NewContainer() (*Container, error) {
date := date.NewFrozenNow()

return &Container{
Config: config,
Version: version,
Config: config,
// zk is short-lived, so we freeze the current date to use the same
// date for any template rendering during the execution.
Date: &date,
Expand Down
152 changes: 152 additions & 0 deletions adapter/lsp/document.go
@@ -0,0 +1,152 @@
package lsp

import (
"regexp"
"strings"

protocol "github.com/tliron/glsp/protocol_3_16"
"github.com/tliron/kutil/logging"
)

// document represents an opened file.
type document struct {
URI protocol.DocumentUri
Content string
Log logging.Logger
lines []string
}

// ApplyChanges updates the content of the document from LSP textDocument/didChange events.
func (d *document) ApplyChanges(changes []interface{}) {
for _, change := range changes {
switch c := change.(type) {
case protocol.TextDocumentContentChangeEvent:
startIndex, endIndex := c.Range.IndexesIn(d.Content)
d.Content = d.Content[:startIndex] + c.Text + d.Content[endIndex:]
case protocol.TextDocumentContentChangeEventWhole:
d.Content = c.Text
}
}

d.lines = nil
}

var nonEmptyString = regexp.MustCompile(`\S+`)

// WordAt returns the word found at the given location.
// Credit https://github.com/aca/neuron-language-server/blob/450a7cff71c14e291ee85ff8a0614fa9d4dd5145/utils.go#L13
func (d *document) WordAt(pos protocol.Position) string {
line, ok := d.GetLine(int(pos.Line))
if !ok {
return ""
}

charIdx := int(pos.Character)
wordIdxs := nonEmptyString.FindAllStringIndex(line, -1)
for _, wordIdx := range wordIdxs {
if wordIdx[0] <= charIdx && charIdx <= wordIdx[1] {
return line[wordIdx[0]:wordIdx[1]]
}
}

return ""
}

// GetLine returns the line at the given index.
func (d *document) GetLine(index int) (string, bool) {
lines := d.GetLines()
if index < 0 || index > len(lines) {
return "", false
}
return lines[index], true
}

// GetLines returns all the lines in the document.
func (d *document) GetLines() []string {
if d.lines == nil {
// We keep \r on purpose, to avoid messing up position conversions.
d.lines = strings.Split(d.Content, "\n")
}
return d.lines
}

// LookBehind returns the n characters before the given position, on the same line.
func (d *document) LookBehind(pos protocol.Position, length int) string {
line, ok := d.GetLine(int(pos.Line))
if !ok {
return ""
}

charIdx := int(pos.Character)
if length > charIdx {
return line[0:charIdx]
}
return line[(charIdx - length):charIdx]
}

var wikiLinkRegex = regexp.MustCompile(`\[?\[\[(.+?)(?:\|(.+?))?\]\]`)
var markdownLinkRegex = regexp.MustCompile(`\[([^\]]+?[^\\])\]\((.+?[^\\])\)`)

// DocumentLinkAt returns the internal or external link found in the document
// at the given position.
func (d *document) DocumentLinkAt(pos protocol.Position) (*documentLink, error) {
links, err := d.DocumentLinks()
if err != nil {
return nil, err
}

for _, link := range links {
if positionInRange(d.Content, link.Range, pos) {
return &link, nil
}
}

return nil, nil
}

// DocumentLinks returns all the internal and external links found in the
// document.
func (d *document) DocumentLinks() ([]documentLink, error) {
links := []documentLink{}

lines := d.GetLines()
for lineIndex, line := range lines {

appendLink := func(href string, start, end int) {
if href == "" {
return
}

links = append(links, documentLink{
Href: href,
Range: protocol.Range{
Start: protocol.Position{
Line: protocol.UInteger(lineIndex),
Character: protocol.UInteger(start),
},
End: protocol.Position{
Line: protocol.UInteger(lineIndex),
Character: protocol.UInteger(end),
},
},
})
}

for _, match := range markdownLinkRegex.FindAllStringSubmatchIndex(line, -1) {
href := line[match[4]:match[5]]
appendLink(href, match[0], match[1])
}

for _, match := range wikiLinkRegex.FindAllStringSubmatchIndex(line, -1) {
href := line[match[2]:match[3]]
appendLink(href, match[0], match[1])
}
}

return links, nil
}

type documentLink struct {
Href string
Range protocol.Range
}

0 comments on commit ba41fe5

Please sign in to comment.