Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
5nord committed Aug 1, 2024
2 parents 7391787 + 56d60ca commit 90d2d70
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 5 deletions.
19 changes: 19 additions & 0 deletions internal/lsp/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
const DIAGNOSTICS_CONFIG_KEY = "ttcn3.experimental.diagnostics.enabled"
const FORMATTER_CONFIG_KEY = "ttcn3.experimental.format.enabled"
const SEMANTIC_TOKENS_CONFIG_KEY = "ttcn3.experimental.semanticTokens.enabled"
const INLAY_HINT_CONFIG_KEY = "ttcn3.experimental.inlayHint.enabled"

func (s *Server) Config(section string) interface{} {
v, err := s.client.Configuration(context.TODO(), &protocol.ParamConfiguration{
Expand Down Expand Up @@ -79,6 +80,24 @@ func (s *Server) didChangeConfiguration(ctx context.Context, _ *protocol.DidChan
// NOTE: dynamic registration of diagnostics is only available from lsp 3.17 on
}

confRes, ok = s.Config(INLAY_HINT_CONFIG_KEY).(bool)
if !ok {
confRes = false
}
if s.clientCapability.HasDynRegForInlayHint && s.serverConfig.InlayHintEnabled != confRes {
s.serverConfig.InlayHintEnabled = confRes
if confRes {
regList = append(regList, protocol.Registration{
ID: "TEXTDOCUMENT_INLAYHINT",
Method: "textDocument/inlayHint",
RegisterOptions: newInlayHintRegistrationOptions()})
} else {
unregList = append(unregList, protocol.Unregistration{
ID: "TEXTDOCUMENT_INLAYHINT",
Method: "textDocument/inlayHint"})
}
}

if len(regList) > 0 {
s.client.RegisterCapability(ctx, &protocol.RegistrationParams{Registrations: regList})
}
Expand Down
28 changes: 28 additions & 0 deletions internal/lsp/general.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ func (s *Server) registerFormatterIfNoDynReg() bool {
return !s.clientCapability.HasDynRegForFormatter
}

func (s *Server) registerInlayHintIfNoDynReg() *protocol.InlayHintRegistrationOptions {
if s.clientCapability.HasDynRegForInlayHint {
return nil
}
return newInlayHintRegistrationOptions()
}

func newInlayHintRegistrationOptions() *protocol.InlayHintRegistrationOptions {
return &protocol.InlayHintRegistrationOptions{
InlayHintOptions: protocol.InlayHintOptions{
ResolveProvider: false,
WorkDoneProgressOptions: protocol.WorkDoneProgressOptions{
WorkDoneProgress: false,
},
},
TextDocumentRegistrationOptions: protocol.TextDocumentRegistrationOptions{
DocumentSelector: protocol.DocumentSelector{
protocol.DocumentFilter{Language: "ttcn3", Scheme: "file", Pattern: "**/*.ttcn3"},
},
},
StaticRegistrationOptions: protocol.StaticRegistrationOptions{
ID: "TEXTDOCUMENT_INLAYHINT",
},
}
}

func newSemanticTokens() *protocol.SemanticTokensRegistrationOptions {
return &protocol.SemanticTokensRegistrationOptions{

Expand Down Expand Up @@ -74,6 +100,7 @@ func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitializ

return &protocol.InitializeResult{
Capabilities: protocol.ServerCapabilities{
InlayHintProvider: s.registerInlayHintIfNoDynReg(),
CodeActionProvider: false,
CompletionProvider: protocol.CompletionOptions{TriggerCharacters: []string{"."}},
DefinitionProvider: true,
Expand Down Expand Up @@ -123,6 +150,7 @@ func (s *Server) evaluateClientCapabilities(params *protocol.ParamInitialize) {
s.clientCapability.HasDynRegForDiagnostics = false // NOTE: available only from LSP 3.17 on
s.clientCapability.HasDynRegForFormatter = params.Capabilities.TextDocument.Formatting.DynamicRegistration
s.clientCapability.HasDynRegForSemTok = params.Capabilities.TextDocument.SemanticTokens.DynamicRegistration
s.clientCapability.HasDynRegForInlayHint = params.Capabilities.TextDocument.InlayHint.DynamicRegistration
}

func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
Expand Down
81 changes: 81 additions & 0 deletions internal/lsp/inlay_hint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package lsp

import (
"context"

"github.com/nokia/ntt/internal/lsp/protocol"
"github.com/nokia/ntt/ttcn3"
"github.com/nokia/ntt/ttcn3/syntax"
)

func (s *Server) inlayHint(ctx context.Context, params *protocol.InlayHintParams) ([]protocol.InlayHint, error) {
if !s.serverConfig.InlayHintEnabled {
return nil, nil
}

file := string(params.TextDocument.URI)
tree := ttcn3.ParseFile(file)
begin := tree.PosFor(int(params.Range.Start.Line)+1, int(params.Range.Start.Character+1))
end := tree.PosFor(int(params.Range.End.Line+1), int(params.Range.End.Character+1))
return ProcessInlayHint(tree, &s.db, begin, end), nil
}

func ProcessInlayHint(tree *ttcn3.Tree, db *ttcn3.DB, begin int, end int) []protocol.InlayHint {
var hints []protocol.InlayHint

tree.Inspect(func(n syntax.Node) bool {
if n == nil || n.End() < begin || end < n.Pos() {
return false
}

if callExpr, ok := n.(*syntax.CallExpr); ok {

for _, decl := range tree.LookupWithDB(callExpr.Fun, db) {

if params := getDeclarationParams(decl.Node); params != nil {

for idx, arg := range callExpr.Args.List {

// Stop processing further arguments after the first assignment notation.
// Value arguments are not allowed: ES 201 873-1, 5.4.2, Restrictions, point o).
if binaryExpr, ok := arg.(*syntax.BinaryExpr); ok {
if binaryExpr.Op.String() == ":=" {
break
}
}

name := params.List[idx].Name.Tok.String()
lbl := protocol.InlayHintLabelPart{Value: name + " :="}
pos := syntax.Begin(arg)
pos.Line -= 1
pos.Column -= 1
ppos := protocol.Position{Line: uint32(pos.Line), Character: uint32(pos.Column)}
hint := protocol.InlayHint{
Position: ppos,
Label: []protocol.InlayHintLabelPart{lbl},
Kind: protocol.Parameter,
PaddingRight: true,
}
hints = append(hints, hint)

}

// Stop after the first declaration is processed.
break
}
}
}
return true
})
return hints
}

func getDeclarationParams(node syntax.Node) *syntax.FormalPars {
if decl, ok := node.(*syntax.FuncDecl); ok {
return decl.Params
}
if decl, ok := node.(*syntax.TemplateDecl); ok {
return decl.Params
}
return nil
}
115 changes: 115 additions & 0 deletions internal/lsp/inlay_hint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package lsp_test

import (
"fmt"
"testing"

"github.com/nokia/ntt/internal/fs"
"github.com/nokia/ntt/internal/lsp"
"github.com/nokia/ntt/internal/lsp/protocol"
"github.com/nokia/ntt/ttcn3"
"github.com/stretchr/testify/assert"
)

type Hint struct {
Line uint32
Char uint32
Label string
}

func TestInlayHintForFunction(t *testing.T) {
actual := testInlayHint(t, nil, `
module Test {
function func(integer a := 0, integer b := 0, integer c := 0) {}
function test() {
func(1, 2, 3)
func(1,
2,
3)
func(a := 1, b := 2, c := 3)
func(1 + 2)
func(1, 2, c := 3)
}
}`)

assert.Equal(t, []Hint{
// All parameters in the same line.
{Line: 4, Char: 13, Label: "a :="},
{Line: 4, Char: 16, Label: "b :="},
{Line: 4, Char: 19, Label: "c :="},
// Parameters spanning multiple lines.
{Line: 5, Char: 13, Label: "a :="},
{Line: 6, Char: 13, Label: "b :="},
{Line: 7, Char: 13, Label: "c :="},
// Binary expression parameter.
{Line: 8, Char: 13, Label: "a :="},
// Mixed assignment / value list notation.
{Line: 9, Char: 13, Label: "a :="},
{Line: 9, Char: 14, Label: "b :="},
}[0], actual[0])
}

func TestInlayHintForTemplate(t *testing.T) {
actual := testInlayHint(t, nil, `
module Test {
template integer templ(integer x, integer y) := (x .. y)
function test() {
var template integer t := templ(2, 3)
}
}`)

assert.Equal(t, []Hint{
{Line: 4, Char: 40, Label: "x :="},
{Line: 6, Char: 43, Label: "y :="},
}[0], actual[0])
}

func TestInlayHintNestedCalls(t *testing.T) {
actual := testInlayHint(t, nil, `
module Test {
function foo(integer a) return integer { return 1; }
function bar(integer b) return integer { return 1; }
function baz(integer c) return integer { return 1; }
function test() {
foo(bar(baz(1)))
}
}`)

assert.Equal(t, []Hint{
{Line: 6, Char: 12, Label: "a :="},
{Line: 6, Char: 16, Label: "b :="},
{Line: 6, Char: 20, Label: "c :="},
}[0], actual[0])
}

func testInlayHint(t *testing.T, rng *protocol.Range, text string) []Hint {
t.Helper()

file := fmt.Sprintf("%s.ttcn3", t.Name())
fs.SetContent(file, []byte(text))
tree := ttcn3.ParseFile(file)
if tree.Err != nil {
t.Fatal(tree.Err)
}

// Build index to for tree.Lookup to resolve imported symbols.
db := &ttcn3.DB{}
db.Index(file)

begin := tree.Pos()
end := tree.End()
if rng != nil {
begin = tree.PosFor(int(rng.Start.Line), int(rng.Start.Character))
end = tree.PosFor(int(rng.End.Line), int(rng.End.Character))
}

var hints []Hint
for _, h := range lsp.ProcessInlayHint(tree, db, begin, end) {
hints = append(hints, Hint{
Line: h.Position.Line,
Char: h.Position.Character,
Label: h.Label[0].Value,
})
}
return hints
}
Loading

0 comments on commit 90d2d70

Please sign in to comment.