Skip to content

Commit

Permalink
Auto-completion for nolol
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaumgarten committed Dec 23, 2020
1 parent 9069d70 commit 3a88b0c
Show file tree
Hide file tree
Showing 13 changed files with 571 additions and 99 deletions.
3 changes: 2 additions & 1 deletion examples/nolol/array.nolol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Set :d to the value you want to storem :a to the address to store it into and :m to 1 in order to store a value
// Set :m to 0 and :a to the index to retrieve a value into :d

// is good
// is best
define data = :d
define addr = :a
define write = :m
Expand All @@ -20,4 +22,3 @@ $ _if write then mem3 = data else data = mem3 end; addr = 0; goto start $
$ _if write then mem4 = data else data = mem4 end; addr = 0; goto start $
$ _if write then mem5 = data else data = mem5 end; addr = 0; goto start $
$ _if write then mem6 = data else data = mem6 end; addr = 0; goto start $

4 changes: 2 additions & 2 deletions examples/nolol/array_user.nolol
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ define write = :m
// store value at idx in array
macro set(idx, value)
write = 1
data = value
data = value
addr = idx
// this will block until addr is 0.
// the array will set addr to 0 once it stored the value
wait addr
wait addr
end

// retrieve value from idx
Expand Down
7 changes: 3 additions & 4 deletions pkg/langserver/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"sync"

"github.com/dbaumgarten/yodk/pkg/lsp"
"github.com/dbaumgarten/yodk/pkg/nolol/nast"
"github.com/dbaumgarten/yodk/pkg/nolol"
)

var NotFoundError = fmt.Errorf("File not found in cache")
Expand All @@ -17,9 +17,8 @@ type Cache struct {
}

type DiagnosticResults struct {
Macros map[string]*nast.MacroDefinition
Definitions map[string]*nast.Definition
Variables []string
Variables []string
AnalysisReport *nolol.AnalysisReport
}

func NewCache() *Cache {
Expand Down
140 changes: 88 additions & 52 deletions pkg/langserver/completions.go
Original file line number Diff line number Diff line change
@@ -1,74 +1,98 @@
package langserver

import (
"strconv"
"strings"

"github.com/dbaumgarten/yodk/pkg/lsp"
"github.com/dbaumgarten/yodk/pkg/parser/ast"
)

var completionItemGroups = map[string][]string{
"unaryOps": {"abs", "sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "not"},
"keywords": {"if", "then", "else", "end", "goto"},
"binaryOps": {"and", "or"},
}
func (s *LangServer) GetCompletions(params *lsp.CompletionParams) (*lsp.CompletionList, error) {
var items []lsp.CompletionItem

if strings.HasSuffix(string(params.TextDocument.URI), ".yolol") {
items = make([]lsp.CompletionItem, 0, len(DefaultYololCompletions)+15)
items = append(items, DefaultYololCompletions...)
items = append(items, s.getVariableCompletions(params)...)
} else if strings.HasSuffix(string(params.TextDocument.URI), ".nolol") {
items = make([]lsp.CompletionItem, 0, len(DefaultNololCompletions)+30)
items = append(items, DefaultNololCompletions...)
items = append(items, s.getNololCompletions(params)...)
} else {
return nil, nil
}

// see here: https://microsoft.github.io/language-server-protocol/specifications/specification-current/
var completionItemTypes = map[string]float64{
"unaryOps": 24,
"keywords": 14,
"binaryOps": 24,
return &lsp.CompletionList{
IsIncomplete: false,
Items: items,
}, nil
}

var completionItemDocs = map[string]string{
"abs": "abs X: Returns the absolute value of X",
"sqrt": "sqrt X: Returns the square-root of X",
"sin": "sin X: Return the sine (degree) of X",
"cos": "cos X: Return the cosine (degree) of X",
"tan": "sin X: Return the tangent (degree) of X",
"asin": "asin X: Return the inverse sine (degree) of X",
"acos": "asin X: Return the inverse cosine (degree) of X",
"atan": "asin X: Return the inverse tanget (degree) of X",
"not": "not X: Returns 1 if X is 0, otherwise it returns 0",
"and": "X and Y: Returns true if X and Y are true",
"or": "X or Y: Returns true if X or Y are true",
}
func (s *LangServer) getNololCompletions(params *lsp.CompletionParams) []lsp.CompletionItem {
diags, err := s.cache.GetDiagnostics(params.TextDocument.URI)
if err != nil {
return []lsp.CompletionItem{}
}

func buildDefaultCompletionItems() []lsp.CompletionItem {
items := make([]lsp.CompletionItem, 0, 50)
for k, v := range completionItemGroups {
kind := completionItemTypes[k]
for _, str := range v {
docs, hasDocs := completionItemDocs[str]
item := lsp.CompletionItem{
Label: str,
Kind: kind,
}
if hasDocs {
item.Detail = docs
}
items = append(items, item)
}
analysis := diags.AnalysisReport
if analysis == nil {
return []lsp.CompletionItem{}
}
return items
}

var defaultCompletionItems []lsp.CompletionItem
items := make([]lsp.CompletionItem, len(analysis.Variables)+len(analysis.Definitions)+len(analysis.Macros))

func init() {
defaultCompletionItems = buildDefaultCompletionItems()
}
for _, v := range analysis.GetVarsAtLine(int(params.Position.Line) + 1) {
items = append(items, lsp.CompletionItem{
Label: v,
Kind: 6,
})
}

func (s *LangServer) GetCompletions(params *lsp.CompletionParams) (*lsp.CompletionList, error) {
items := make([]lsp.CompletionItem, 0, len(defaultCompletionItems)+15)
for _, m := range analysis.Macros {
item := lsp.CompletionItem{
Detail: m.Name + "(" + strings.Join(m.Arguments, ",") + ")",
Label: m.Name,
Kind: 15,
InsertText: m.Name + argsToSnippet(m.Arguments),
InsertTextFormat: 2,
}
if doc, exists := analysis.Docstrings[m.Name]; exists {
item.Documentation = doc
}
items = append(items, item)
}

items = append(items, defaultCompletionItems...)
items = append(items, s.getVariableCompletions(params)...)
for _, d := range analysis.Definitions {
kind := 21.0
insert := ""
detail := ""
if len(d.Placeholders) > 0 {
kind = 3.0
detail += d.Name + "(" + strings.Join(d.Placeholders, ",") + ")"
insert = d.Name + argsToSnippet(d.Placeholders)
}
item := lsp.CompletionItem{
Label: d.Name,
Kind: kind,
InsertText: insert,
Detail: detail,
InsertTextFormat: 2,
}
if doc, exists := analysis.Docstrings[d.Name]; exists {
item.Documentation = doc
}
items = append(items, item)
}

return &lsp.CompletionList{
IsIncomplete: true,
Items: items,
}, nil
for _, l := range analysis.Labels {
items = append(items, lsp.CompletionItem{
Label: l,
Kind: 13,
})
}

return items
}

func (s *LangServer) getVariableCompletions(params *lsp.CompletionParams) []lsp.CompletionItem {
Expand Down Expand Up @@ -115,3 +139,15 @@ func findUsedVariables(prog *ast.Program) []string {

return vars
}

func argsToSnippet(args []string) string {
snip := "("
for i, arg := range args {
snip += "${" + strconv.Itoa(i+1) + ":" + arg + "}"
if i != len(args)-1 {
snip += ","
}
}
snip += ")$0"
return snip
}
Loading

0 comments on commit 3a88b0c

Please sign in to comment.