From df38ff03871004c731b59e92c86035dffab906f8 Mon Sep 17 00:00:00 2001 From: Andre Sutherland Date: Tue, 25 Jun 2024 13:52:16 +0100 Subject: [PATCH] feat: add handler for workspace will create files request --- lsp_3_17/handler.go | 1 + lsp_3_17/handler_workspace_features.go | 33 +++++++++++ lsp_3_17/handler_workspace_features_test.go | 61 +++++++++++++++++++++ lsp_3_17/workspaces.go | 28 ++++++++++ 4 files changed, 123 insertions(+) diff --git a/lsp_3_17/handler.go b/lsp_3_17/handler.go index 3d09856..2f20a7c 100644 --- a/lsp_3_17/handler.go +++ b/lsp_3_17/handler.go @@ -91,6 +91,7 @@ type Handler struct { workspaceSymbolResolve WorkspaceSymbolResolveHandlerFunc workspaceDidChangeConfiguration WorkspaceDidChangeConfigurationHandlerFunc workspaceDidChangeFolders WorkspaceDidChangeFoldersHandlerFunc + workspaceWillCreateFiles WorkspaceWillCreateFilesHandlerFunc isInitialized bool // Provides a mapping of method names to the respective handlers diff --git a/lsp_3_17/handler_workspace_features.go b/lsp_3_17/handler_workspace_features.go index 6641fff..532a4b0 100644 --- a/lsp_3_17/handler_workspace_features.go +++ b/lsp_3_17/handler_workspace_features.go @@ -34,6 +34,13 @@ func WithWorkspaceDidChangeFoldersHandler(handler WorkspaceDidChangeFoldersHandl } } +// WithWorkspaceWillCreateFilesHandler sets the handler for the `workspace/willCreateFiles` request. +func WithWorkspaceWillCreateFilesHandler(handler WorkspaceWillCreateFilesHandlerFunc) HandlerOption { + return func(root *Handler) { + root.SetWorkspaceWillCreateFilesHandler(handler) + } +} + // SetWorkspaceSymbolHandler sets the handler for the `workspace/symbol` request. func (h *Handler) SetWorkspaceSymbolHandler(handler WorkspaceSymbolHandlerFunc) { h.mu.Lock() @@ -66,6 +73,14 @@ func (h *Handler) SetWorkspaceDidChangeFoldersHandler(handler WorkspaceDidChange h.messageHandlers[MethodWorkspaceDidChangeFolders] = createWorkspaceDidChangeFoldersHandler(h) } +// SetWorkspaceWillCreateFilesHandler sets the handler for the `workspace/willCreateFiles` request. +func (h *Handler) SetWorkspaceWillCreateFilesHandler(handler WorkspaceWillCreateFilesHandlerFunc) { + h.mu.Lock() + defer h.mu.Unlock() + h.workspaceWillCreateFiles = handler + h.messageHandlers[MethodWorkspaceWillCreateFiles] = createWorkspaceWillCreateFilesHandler(h) +} + func createWorkspaceSymbolHandler(root *Handler) common.Handler { return common.HandlerFunc( func( @@ -137,3 +152,21 @@ func createWorkspaceDidChangeFoldersHandler(root *Handler) common.Handler { }, ) } + +func createWorkspaceWillCreateFilesHandler(root *Handler) common.Handler { + return common.HandlerFunc( + func( + ctx *common.LSPContext, + ) (r any, validMethod bool, validParams bool, err error) { + validMethod = true + if root.workspaceWillCreateFiles != nil { + var params CreateFilesParams + if err = json.Unmarshal(ctx.Params, ¶ms); err == nil { + validParams = true + r, err = root.workspaceWillCreateFiles(ctx, ¶ms) + } + } + return + }, + ) +} diff --git a/lsp_3_17/handler_workspace_features_test.go b/lsp_3_17/handler_workspace_features_test.go index bd1c417..298b491 100644 --- a/lsp_3_17/handler_workspace_features_test.go +++ b/lsp_3_17/handler_workspace_features_test.go @@ -229,3 +229,64 @@ func (s *HandlerTestSuite) Test_calls_workspace_did_change_folders_notification_ s.Require().Equal(didChangeFoldersParams, *receivedParams) } } + +func (s *HandlerTestSuite) Test_calls_workspace_will_create_files_request_handler() { + logger, err := zap.NewDevelopment() + s.Require().NoError(err) + + ctx, cancel := context.WithTimeout(context.Background(), server.DefaultTimeout) + defer cancel() + + workspaceEdit := WorkspaceEdit{ + Changes: map[string][]TextEdit{ + "edit1": []TextEdit{ + { + Range: &Range{ + Start: Position{ + Line: 1, + Character: 1, + }, + End: Position{ + Line: 1, + Character: 5, + }, + }, + NewText: "help", + }, + }, + }, + } + serverHandler := NewHandler( + WithWorkspaceWillCreateFilesHandler( + func(ctx *common.LSPContext, params *CreateFilesParams) (*WorkspaceEdit, error) { + return &workspaceEdit, 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) + + createFilesParams := &CreateFilesParams{ + Files: []FileCreate{ + { + URI: "file:///test_doc.go", + }, + }, + } + + returnedEdit := WorkspaceEdit{} + err = clientLSPContext.Call( + MethodWorkspaceWillCreateFiles, + createFilesParams, + &returnedEdit, + ) + s.Require().NoError(err) + s.Require().Equal(workspaceEdit, returnedEdit) +} diff --git a/lsp_3_17/workspaces.go b/lsp_3_17/workspaces.go index 0d1c3ef..c4bfd42 100644 --- a/lsp_3_17/workspaces.go +++ b/lsp_3_17/workspaces.go @@ -542,3 +542,31 @@ type WorkspaceFoldersChangeEvent struct { // The array of removed workspace folders. Removed []WorkspaceFolder `json:"removed"` } + +// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_willCreateFiles + +const MethodWorkspaceWillCreateFiles = Method("workspace/willCreateFiles") + +// WorkspaceWillCreateFilesHandlerFunc is the function signature for the +// `workspace/willCreateFiles` method. +type WorkspaceWillCreateFilesHandlerFunc func( + context *common.LSPContext, + params *CreateFilesParams, +) (*WorkspaceEdit, error) + +// CreateFilesParams contains the parameters sent in notifications/requests +// for user-initiated creation of files. +// +// @since 3.16.0 +type CreateFilesParams struct { + // An array of all files/folders created in this operation. + Files []FileCreate `json:"files"` +} + +// FileCreate represents information on a file/folder creation. +// +// @since 3.16.0 +type FileCreate struct { + // A file:// URI for the location of the file/folder being created. + URI string `json:"uri"` +}