Skip to content

Commit

Permalink
feat: add handler for document linked editing ranges requests
Browse files Browse the repository at this point in the history
  • Loading branch information
fr3shw3b committed Jun 24, 2024
1 parent d4647d1 commit f55ccb7
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions lsp_3_17/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ type Handler struct {
documentOnTypeFormatting DocumentOnTypeFormattingHandlerFunc
documentRename DocumentRenameHandlerFunc
documentPrepareRename DocumentPrepareRenameHandlerFunc
documentLinkedEditingRange DocumentLinkedEditingRangeHandlerFunc

isInitialized bool
// Provides a mapping of method names to the respective handlers
Expand Down
34 changes: 34 additions & 0 deletions lsp_3_17/handler_language_features.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,14 @@ func WithDocumentPrepareRenameHandler(handler DocumentPrepareRenameHandlerFunc)
}
}

// WithDocumentLinkedEditingRangeHandler sets the handler for the
// `textDocument/linkedEditingRange` request.
func WithDocumentLinkedEditingRangeHandler(handler DocumentLinkedEditingRangeHandlerFunc) HandlerOption {
return func(root *Handler) {
root.SetDocumentLinkedEditingRangeHandler(handler)
}
}

// SetGotoDeclarationHandler sets the handler for the `textDocument/declaration` request.
func (h *Handler) SetGotoDeclarationHandler(handler GotoDeclarationHandlerFunc) {
h.mu.Lock()
Expand Down Expand Up @@ -627,6 +635,14 @@ func (h *Handler) SetDocumentPrepareRenameHandler(handler DocumentPrepareRenameH
h.messageHandlers[MethodDocumentPrepareRename] = createDocumentPrepareRenameHandler(h)
}

// SetDocumentLinkedEditingRangeHandler sets the handler for the `textDocument/linkedEditingRange` request.
func (h *Handler) SetDocumentLinkedEditingRangeHandler(handler DocumentLinkedEditingRangeHandlerFunc) {
h.mu.Lock()
defer h.mu.Unlock()
h.documentLinkedEditingRange = handler
h.messageHandlers[MethodDocumentLinkedEditingRange] = createDocumentLinkedEditingRangeHandler(h)
}

func createGotoDeclarationHandler(root *Handler) common.Handler {
return common.HandlerFunc(
func(
Expand Down Expand Up @@ -1364,3 +1380,21 @@ func createDocumentPrepareRenameHandler(root *Handler) common.Handler {
},
)
}

func createDocumentLinkedEditingRangeHandler(root *Handler) common.Handler {
return common.HandlerFunc(
func(
ctx *common.LSPContext,
) (r any, validMethod bool, validParams bool, err error) {
validMethod = true
if root.documentLinkedEditingRange != nil {
var params LinkedEditingRangeParams
if err = json.Unmarshal(ctx.Params, &params); err == nil {
validParams = true
r, err = root.documentLinkedEditingRange(ctx, &params)
}
}
return
},
)
}
62 changes: 62 additions & 0 deletions lsp_3_17/handler_language_features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2606,3 +2606,65 @@ func (s *HandlerTestSuite) Test_calls_document_prepare_rename_request_handler()
s.Require().NoError(err)
s.Require().Equal(rangeWithPlaceholder, returnedRangeWithPlaceholder)
}

func (s *HandlerTestSuite) Test_calls_document_linked_editing_range_request_handler() {
logger, err := zap.NewDevelopment()
s.Require().NoError(err)

ctx, cancel := context.WithTimeout(context.Background(), server.DefaultTimeout)
defer cancel()

wordPattern := "\\d+"
linkedEditingRanges := LinkedEditingRanges{
Ranges: []Range{
{
Start: Position{
Line: 300,
Character: 24,
},
End: Position{
Line: 300,
Character: 115,
},
},
},
WordPattern: &wordPattern,
}
serverHandler := NewHandler(
WithDocumentLinkedEditingRangeHandler(
func(ctx *common.LSPContext, params *LinkedEditingRangeParams) (*LinkedEditingRanges, error) {
return &linkedEditingRanges, nil
},
),
)
// Emulate the LSP initialisation process.
serverHandler.SetInitialized(true)
srv := server.NewServer(serverHandler, true, nil, nil)

container := createTestConnectionsContainer(srv.NewHandler())

go srv.Serve(container.serverConn, logger)

clientLSPContext := server.NewLSPContext(ctx, container.clientConn, nil)

linkedEditingRangeParams := &LinkedEditingRangeParams{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{
URI: "file:///test_doc_linked_editing_range.go",
},
Position: Position{
Line: 311,
Character: 24,
},
},
}

returnedLinkedEditingRanges := LinkedEditingRanges{}
err = clientLSPContext.Call(
MethodDocumentLinkedEditingRange,
linkedEditingRangeParams,
&returnedLinkedEditingRanges,
)
s.Require().NoError(err)
s.Require().Equal(linkedEditingRanges, returnedLinkedEditingRanges)
}
30 changes: 30 additions & 0 deletions lsp_3_17/language_features.go
Original file line number Diff line number Diff line change
Expand Up @@ -2979,3 +2979,33 @@ type RangeWithPlaceholder struct {
type PrepareRenameDefaultBehavior struct {
DefaultBehavior bool `json:"defaultBehavior"`
}

// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_linkedEditingRange

const MethodDocumentLinkedEditingRange = Method("textDocument/linkedEditingRange")

// DocumentLinkedEditingRangeHandlerFunc is the function signature for the textDocument/linkedEditingRange
// request handler that can be registered for a language server.
type DocumentLinkedEditingRangeHandlerFunc func(
ctx *common.LSPContext,
params *LinkedEditingRangeParams,
) (*LinkedEditingRanges, error)

// LinkedEditingRangeParams contains parameters for the textDocument/linkedEditingRange requests.
type LinkedEditingRangeParams struct {
TextDocumentPositionParams
WorkDoneProgressParams
}

// LinkedEditingRanges contains the response data type for a textDocument/linkedEditingRange request.
type LinkedEditingRanges struct {
// A list of ranges that can be renamed together. The ranges must have
// identical length and contain identical text content. The ranges cannot
// overlap.
Ranges []Range `json:"ranges"`

// An optional word pattern (regular expression) that describes valid
// contents for the given ranges. If no pattern is provided, the client
// configuration's word pattern will be used.
WordPattern *string `json:"wordPattern,omitempty"`
}

0 comments on commit f55ccb7

Please sign in to comment.