Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 15 additions & 22 deletions .github/workflows/benchstat-pr.yml
Original file line number Diff line number Diff line change
@@ -1,50 +1,43 @@
name: Benchstat
name: benchstat

on: [pull_request]

jobs:
incoming:
benchstat:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.19'

# Generate benchmark report for main branch
- name: Checkout
uses: actions/checkout@v3
with:
ref: main
- name: Benchmark
run: go test ./... -count=5 -run=Benchmark -bench=. | tee -a bench.txt
- name: Upload Benchmark
uses: actions/upload-artifact@v3
with:
name: bench-incoming
name: bench-current
path: bench.txt
current:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.19'

# Generate benchmark report for the PR
- name: Checkout
uses: actions/checkout@v3
with:
ref: main
- name: Benchmark
run: go test ./... -count=5 -run=Benchmark -bench=. | tee -a bench.txt
- name: Upload Benchmark
uses: actions/upload-artifact@v3
with:
name: bench-current
name: bench-incoming
path: bench.txt
benchstat:
needs: [incoming, current]
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.19'

# Compare the two reports
- name: Checkout
uses: actions/checkout@v3
- name: Install benchstat
run: go install golang.org/x/perf/cmd/benchstat@latest
- name: Download Incoming
Expand Down Expand Up @@ -79,4 +72,4 @@ jobs:
${{ steps.benchstat_content.outputs.content }}
```
comment_includes: 'Benchstat (compared to main):'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
118 changes: 114 additions & 4 deletions pkg/server/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import (
"context"
"strings"

"github.com/google/go-jsonnet/ast"
"github.com/grafana/jsonnet-language-server/pkg/ast/processing"
"github.com/grafana/jsonnet-language-server/pkg/nodestack"
position "github.com/grafana/jsonnet-language-server/pkg/position_conversion"
"github.com/grafana/jsonnet-language-server/pkg/utils"
"github.com/jdbaldry/go-language-server-protocol/lsp/protocol"
log "github.com/sirupsen/logrus"
)

func (s *Server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
Expand All @@ -14,14 +19,98 @@ func (s *Server) Completion(ctx context.Context, params *protocol.CompletionPara
return nil, utils.LogErrorf("Completion: %s: %w", errorRetrievingDocument, err)
}

items := []protocol.CompletionItem{}
line := getCompletionLine(doc.item.Text, params.Position)

// Short-circuit if it's a stdlib completion
if items := s.completionStdLib(line); len(items) > 0 {
return &protocol.CompletionList{IsIncomplete: false, Items: items}, nil
}

// Otherwise, parse the AST and search for completions
if doc.ast == nil {
log.Errorf("Completion: document was never successfully parsed, can't autocomplete")
return nil, nil
}

searchStack, err := processing.FindNodeByPosition(doc.ast, position.ProtocolToAST(params.Position))
if err != nil {
log.Errorf("Completion: error computing node: %v", err)
return nil, nil
}

items := s.completionFromStack(line, searchStack)
return &protocol.CompletionList{IsIncomplete: false, Items: items}, nil
}

line := strings.Split(doc.item.Text, "\n")[params.Position.Line]
charIndex := int(params.Position.Character)
func getCompletionLine(fileContent string, position protocol.Position) string {
line := strings.Split(fileContent, "\n")[position.Line]
charIndex := int(position.Character)
if charIndex > len(line) {
charIndex = len(line)
}
line = line[:charIndex]
return line
}

func (s *Server) completionFromStack(line string, stack *nodestack.NodeStack) []protocol.CompletionItem {
var items []protocol.CompletionItem

lineWords := strings.Split(line, " ")
lastWord := lineWords[len(lineWords)-1]

indexes := strings.Split(lastWord, ".")
firstIndex, indexes := indexes[0], indexes[1:]

if firstIndex == "self" && len(indexes) > 0 {
fieldPrefix := indexes[0]

for !stack.IsEmpty() {
curr := stack.Pop()

switch curr := curr.(type) {
case *ast.Binary:
stack.Push(curr.Left)
stack.Push(curr.Right)
case *ast.DesugaredObject:
for _, field := range curr.Fields {
label := processing.FieldNameToString(field.Name)
// Ignore fields that don't match the prefix
if !strings.HasPrefix(label, fieldPrefix) {
continue
}

// Ignore the current field
if strings.Contains(line, label+":") {
continue
}

items = append(items, createCompletionItem(label, "self."+label, protocol.FieldCompletion, field.Body))
}
}
}
} else if len(indexes) == 0 {
// firstIndex is a variable (local) completion
for !stack.IsEmpty() {
if curr, ok := stack.Pop().(*ast.Local); ok {
for _, bind := range curr.Binds {
label := string(bind.Variable)

if !strings.HasPrefix(label, firstIndex) {
continue
}

items = append(items, createCompletionItem(label, label, protocol.VariableCompletion, bind.Body))
}
}
}
}

return items
}

func (s *Server) completionStdLib(line string) []protocol.CompletionItem {
items := []protocol.CompletionItem{}

stdIndex := strings.LastIndex(line, "std.")
if stdIndex != -1 {
userInput := line[stdIndex+4:]
Expand Down Expand Up @@ -55,5 +144,26 @@ func (s *Server) Completion(ctx context.Context, params *protocol.CompletionPara
items = append(items, funcContains...)
}

return &protocol.CompletionList{IsIncomplete: false, Items: items}, nil
return items
}

func createCompletionItem(label, detail string, kind protocol.CompletionItemKind, body ast.Node) protocol.CompletionItem {
insertText := label
if asFunc, ok := body.(*ast.Function); ok {
kind = protocol.FunctionCompletion
params := []string{}
for _, param := range asFunc.Parameters {
params = append(params, string(param.Name))
}
paramsString := "(" + strings.Join(params, ", ") + ")"
detail += paramsString
insertText += paramsString
}

return protocol.CompletionItem{
Label: label,
Detail: detail,
Kind: kind,
InsertText: insertText,
}
}
Loading