Skip to content
Draft
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
71 changes: 71 additions & 0 deletions server/call_hierarchy_contributor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2026 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.

package server

import (
"context"

core "typefox.dev/fastbelt"
"typefox.dev/lsp"
)

// CallHierarchyContributor provides language-specific logic for call hierarchy navigation.
//
// Usage:
//
// type MyCallHierarchyContributor struct{ sc *service.Container }
//
// func (c *MyCallHierarchyContributor) PrepareItem(ctx context.Context, token *core.Token, node core.AstNode) *lsp.CallHierarchyItem {
// if funcDecl, ok := node.(*ast.FunctionDeclaration); ok {
// return &lsp.CallHierarchyItem{
// Name: funcDecl.Name,
// Kind: lsp.Function,
// URI: node.Document().URI.DocumentURI(),
// Range: node.Segment().Range.LspRange(),
// }
// }
// return nil
// }
//
// func (c *MyCallHierarchyContributor) FindIncomingCalls(ctx context.Context, item *lsp.CallHierarchyItem) []lsp.CallHierarchyIncomingCall {
// // Use references to find call sites
// return calls
// }
type CallHierarchyContributor interface {
// PrepareItem creates a call hierarchy item for a symbol at the given position.
// Return nil if the symbol at the position is not callable (not a function/method).
// The returned item will be passed to incoming/outgoing call requests.
PrepareItem(ctx context.Context, token *core.Token, node core.AstNode) *lsp.CallHierarchyItem

// FindIncomingCalls finds all locations that call the given symbol.
// Each incoming call should include the call site location and the item
// representing the caller.
FindIncomingCalls(ctx context.Context, item *lsp.CallHierarchyItem) []lsp.CallHierarchyIncomingCall

// FindOutgoingCalls finds all locations called by the given symbol.
// Each outgoing call should include the call site location and the item
// representing the callee.
FindOutgoingCalls(ctx context.Context, item *lsp.CallHierarchyItem) []lsp.CallHierarchyOutgoingCall
}

// DefaultCallHierarchyContributor is the default implementation of [CallHierarchyContributor].
// It returns nil for all operations, effectively disabling call hierarchy.
type DefaultCallHierarchyContributor struct{}

func NewDefaultCallHierarchyContributor() CallHierarchyContributor {
return &DefaultCallHierarchyContributor{}
}

func (c *DefaultCallHierarchyContributor) PrepareItem(ctx context.Context, token *core.Token, node core.AstNode) *lsp.CallHierarchyItem {
return nil
}

func (c *DefaultCallHierarchyContributor) FindIncomingCalls(ctx context.Context, item *lsp.CallHierarchyItem) []lsp.CallHierarchyIncomingCall {
return nil
}

func (c *DefaultCallHierarchyContributor) FindOutgoingCalls(ctx context.Context, item *lsp.CallHierarchyItem) []lsp.CallHierarchyOutgoingCall {
return nil
}
87 changes: 87 additions & 0 deletions server/call_hierarchy_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2026 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.

package server

import (
"context"

core "typefox.dev/fastbelt"
"typefox.dev/fastbelt/util/service"
"typefox.dev/fastbelt/workspace"
"typefox.dev/lsp"
)

// CallHierarchyProvider is a service for handling LSP call hierarchy requests, delegating
// the language-specific call detection to [CallHierarchyContributor].
type CallHierarchyProvider interface {
HandlePrepareCallHierarchyRequest(ctx context.Context, params *lsp.CallHierarchyPrepareParams) ([]lsp.CallHierarchyItem, error)
HandleIncomingCallsRequest(ctx context.Context, params *lsp.CallHierarchyIncomingCallsParams) ([]lsp.CallHierarchyIncomingCall, error)
HandleOutgoingCallsRequest(ctx context.Context, params *lsp.CallHierarchyOutgoingCallsParams) ([]lsp.CallHierarchyOutgoingCall, error)
}

// DefaultCallHierarchyProvider is the default implementation of [CallHierarchyProvider].
// It delegates call detection to [CallHierarchyContributor].
type DefaultCallHierarchyProvider struct {
sc *service.Container
}

func NewDefaultCallHierarchyProvider(sc *service.Container) CallHierarchyProvider {
return &DefaultCallHierarchyProvider{sc: sc}
}

func (p *DefaultCallHierarchyProvider) HandlePrepareCallHierarchyRequest(ctx context.Context, params *lsp.CallHierarchyPrepareParams) ([]lsp.CallHierarchyItem, error) {
documentManager := service.MustGet[workspace.DocumentManager](p.sc)
uri := core.ParseURI(string(params.TextDocument.URI))
doc := documentManager.Get(uri)
if doc == nil {
return nil, nil
}

contributor, err := service.Get[CallHierarchyContributor](p.sc)
if err != nil {
// No contributor available
return nil, nil
}

// Find token at cursor position
offset := doc.TextDoc.OffsetAt(params.Position)
first, _ := doc.Tokens.SearchOffset2(offset)
if first == nil {
return nil, nil
}

// Get the AST node for the token and delegate to the contributor
node := first.Element
item := contributor.PrepareItem(ctx, first, node)
if item == nil {
return nil, nil
}

return []lsp.CallHierarchyItem{*item}, nil
}

func (p *DefaultCallHierarchyProvider) HandleIncomingCallsRequest(ctx context.Context, params *lsp.CallHierarchyIncomingCallsParams) ([]lsp.CallHierarchyIncomingCall, error) {
contributor, err := service.Get[CallHierarchyContributor](p.sc)
if err != nil {
// No contributor available
return nil, nil
}

incomingCalls := contributor.FindIncomingCalls(ctx, &params.Item)

return incomingCalls, nil
}

func (p *DefaultCallHierarchyProvider) HandleOutgoingCallsRequest(ctx context.Context, params *lsp.CallHierarchyOutgoingCallsParams) ([]lsp.CallHierarchyOutgoingCall, error) {
contributor, err := service.Get[CallHierarchyContributor](p.sc)
if err != nil {
// No contributor available
return nil, nil
}

outgoingCalls := contributor.FindOutgoingCalls(ctx, &params.Item)

return outgoingCalls, nil
}
45 changes: 45 additions & 0 deletions server/code_action_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2026 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.

package server

import (
"context"

"typefox.dev/fastbelt/util/service"
"typefox.dev/lsp"
)

// CodeActionProvider is a service for handling LSP code action requests.
//
// Usage:
//
// type MyCodeActionProvider struct{ sc *service.Container }
//
// func (p *MyCodeActionProvider) HandleCodeActionRequest(ctx context.Context, params *lsp.CodeActionParams) ([]lsp.CodeAction, error) {
// // Analyze diagnostics and context, return quick fixes
// return []lsp.CodeAction{
// {
// Title: "Add missing import",
// Kind: lsp.QuickFix,
// Edit: &lsp.WorkspaceEdit{...},
// },
// }, nil
// }
type CodeActionProvider interface {
HandleCodeActionRequest(ctx context.Context, params *lsp.CodeActionParams) ([]lsp.CodeAction, error)
}

// DefaultCodeActionProvider returns no code actions.
type DefaultCodeActionProvider struct {
sc *service.Container
}

func NewDefaultCodeActionProvider(sc *service.Container) CodeActionProvider {
return &DefaultCodeActionProvider{sc: sc}
}

func (p *DefaultCodeActionProvider) HandleCodeActionRequest(ctx context.Context, params *lsp.CodeActionParams) ([]lsp.CodeAction, error) {
return []lsp.CodeAction{}, nil
}
47 changes: 47 additions & 0 deletions server/code_lens_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2026 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.

package server

import (
"context"

"typefox.dev/fastbelt/util/service"
"typefox.dev/lsp"
)

// CodeLensProvider is a service for handling LSP code lens requests.
//
// Usage:
//
// type MyCodeLensProvider struct{ sc *service.Container }
//
// func (p *MyCodeLensProvider) HandleCodeLensRequest(ctx context.Context, params *lsp.CodeLensParams) ([]lsp.CodeLens, error) {
// // Analyze document and return inline commands/info
// return []lsp.CodeLens{
// {
// Range: lsp.Range{...},
// Command: &lsp.Command{
// Title: "3 references",
// Command: "showReferences",
// },
// },
// }, nil
// }
type CodeLensProvider interface {
HandleCodeLensRequest(ctx context.Context, params *lsp.CodeLensParams) ([]lsp.CodeLens, error)
}

// DefaultCodeLensProvider returns no code lenses.
type DefaultCodeLensProvider struct {
sc *service.Container
}

func NewDefaultCodeLensProvider(sc *service.Container) CodeLensProvider {
return &DefaultCodeLensProvider{sc: sc}
}

func (p *DefaultCodeLensProvider) HandleCodeLensRequest(ctx context.Context, params *lsp.CodeLensParams) ([]lsp.CodeLens, error) {
return []lsp.CodeLens{}, nil
}
Loading