{"id":46156437,"url":"https://github.com/leon-bckl/lsp-framework","last_synced_at":"2026-03-02T10:02:35.812Z","repository":{"id":207021352,"uuid":"704507770","full_name":"leon-bckl/lsp-framework","owner":"leon-bckl","description":"Language Server Protocol implementation in C++","archived":false,"fork":false,"pushed_at":"2025-11-07T19:10:35.000Z","size":382,"stargazers_count":54,"open_issues_count":0,"forks_count":12,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-11-07T21:10:10.080Z","etag":null,"topics":["cpp","language-server-protocol","lsp","lsp-client","lsp-server"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leon-bckl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-10-13T12:08:15.000Z","updated_at":"2025-11-07T19:10:39.000Z","dependencies_parsed_at":"2024-03-16T01:40:44.756Z","dependency_job_id":"747e25de-f42d-4d09-901f-60068e02fb74","html_url":"https://github.com/leon-bckl/lsp-framework","commit_stats":null,"previous_names":["leon-bckl/lsp-framework"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/leon-bckl/lsp-framework","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leon-bckl%2Flsp-framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leon-bckl%2Flsp-framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leon-bckl%2Flsp-framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leon-bckl%2Flsp-framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leon-bckl","download_url":"https://codeload.github.com/leon-bckl/lsp-framework/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leon-bckl%2Flsp-framework/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29998082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T09:59:02.300Z","status":"ssl_error","status_checked_at":"2026-03-02T09:59:02.001Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","language-server-protocol","lsp","lsp-client","lsp-server"],"created_at":"2026-03-02T10:02:28.459Z","updated_at":"2026-03-02T10:02:35.770Z","avatar_url":"https://github.com/leon-bckl.png","language":"C++","readme":"# lsp-framework\n\nThis is an implementation of the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/overviews/lsp/overview/) in C++. It can be used to implement both servers and clients that communicate using the LSP.\n\n## Overview\n\nThe goal of this library is to make implementing LSP servers and clients easy and type safe.\nAll LSP types and messages are proper C++ structs. There's no need to manually read or write JSON which is annyoing and error-prone. The framework handles serialization and deserialization automatically.\n\nAll messages can be found in the generated `\u003clsp/messages.h\u003e` header with requests inside the `lsp::requests` and notifications inside the `lsp::notifications` namespace respectively. All types like the message parameters or results can be found in `\u003clsp/types.h\u003e`.\n\n## Building And Linking\n\nThere aren't any external dependencies except for `cmake` and a compiler that supports C++20.\n\nThe project is built as a static library. LSP type definitions, messages and serialization boilerplate are generated from the official [meta model](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/lsp/3.17/metaModel/metaModel.json) during the build.\n\n`cmake -S . -B build \u0026\u0026 cmake --build build --parallel`\n\nIf you use `lsp` as an external dependency, make sure the cmake config option `LSP_INSTALL` is enabled. Then install the `lsp` target:\n\n`cmake --build build --target install`\n\nIn your project, you can link it by using `find_package`, like:\n\n```cmake\nfind_package(lsp 1.3.0 EXACT REQUIRED)\ntarget_link_library(${CURRENT_TARGET} PUBLIC lsp::lsp)\n```\n\n## Examples\n\nAn example server and client implementation can be found in [lsp-framework/examples](./examples/).\n\nThey aren't built by default unless the cmake option `LSP_BUILD_EXAMPLES` is enabled.\n\n### Client\n\nLaunch with `LspClientExample --exe=\u003cserver_executable\u003e \u003cargs\u003e` to start the given server executable with optional arguments and use it via stdio.\n\nAlternatively `LspClientExample --port=\u003cportnum\u003e` to connect to an already launched server instance that is listening on the given port.\n\n### Server\n\nThe server example can be launched with `LspServerExample --port=\u003cportnum\u003e` to start a server instance that listens on the given port for incoming client connections.\n\nWithout arguments it will wait for input on stdin.\n\n## Basic Usage\n\nFirst you need to establish a connection to the client or server you want to communicate with. The library provides communication via stdio and sockets. If you need another way of communicating with the other process (e.g. named pipes) you can extend `lsp::io::Stream` and implement the `read` and `write` methods.\n\nCreate an `lsp::Connection` using a stream and then an `lsp::MessageHandler` with the connection:\n\n```cpp\n#include \u003clsp/messages.h\u003e // Generated message definitions\n#include \u003clsp/connection.h\u003e\n#include \u003clsp/io/standardio.h\u003e\n#include \u003clsp/messagehandler.h\u003e\n\nauto connection     = lsp::Connection(lsp::io::standardIO());\nauto messageHandler = lsp::MessageHandler(connection);\n```\n\nThe message handler is the core of the framework. It is used to register callbacks for incoming requests and notifications as well as send outgoing requests and notifications.\n\nOnce the callbacks are registered it is necessary to enter the message processing loop which needs to call `lsp::MessageHandler::processIncomingMessages`. This call will block until input becomes available.\n\n```cpp\nwhile(running)\n    messageHandler.processIncomingMessages();\n```\n\n### Request Callbacks\n\nRequest handlers are registered using the `lsp::MessageHandler::add` method. The following example registers a callback for the `initialize` request which is the first request sent to a server by a client in the LSP:\n\n```cpp\nmessageHandler.add\u003clsp::requests::Initialize\u003e(\n      [](lsp::requests::Initialize::Params\u0026\u0026 params)\n      {\n         return lsp::requests::Initialize::Result{\n            .serverInfo = lsp::InitializeResultServerInfo{\n                .name    = \"Language Server\",\n                .version = \"1.0.0\"\n            },\n            .capabilities = {\n                .positionEncoding = lsp::PositionEncodingKind::UTF16\n            }\n         };\n      });\n```\n\nThe template parameter is the type of the message the callback is for and determines its signature. The callback must have a parameter of type `MessageType::Params` but only if the message has parameters. It is passed as an rvalue reference to avoid potentially expensive copies like the entire text of a document from the `textDocument/didOpen` notification.\n\nThe callback returns the result of the request (`MessageType::Result`) if it has one.\n\nThe id of the current request can be obtained using `lsp::MessageHandler::currentRequestId`. However, this function can only be called from inside of a request callback. Otherwise a `std::logic_error` is thrown.\n\nIt is also possible to send messages that do not have a generated c++ type. This option is not type-safe but allows for sending custom notifications and requests that are not part of the specification or are not yet contained in the meta model used for generating the code.\n\nIn order to send these types of generic messages you need to call the non-template overload of `MessageHandler::add` which takes the message method string and a callback function that is invoked with the request payload and returns the response. Both are of type `lsp::json::Any`. There is an async overload as well that expects the result of the callback to be `std::future\u003clsp::json::Any\u003e`.\n\n`MessageHandler::add` returns a reference to the handler itself in order to easily chain multiple callback registrations without repeating the handler instance over and over:\n\n```cpp\nmessageHandler.add\u003c\u003e().add\u003c\u003e().add\u003c\u003e();\n```\n\n### Notification Callbacks\n\nNotfication callbacks are registered in exactly the same way as request callbacks. The only difference is that they never return a result:\n\n```cpp\nmessageHandler.add\u003clsp::notifications::Exit\u003e([](){ ... });\n\n```\n\n### Asynchronous Request Callbacks\n\nSome requests might take a longer time to process than others. In order to not stall the handling of other incoming messages, it is possible to do the processing asynchronously.\n\nAsynchronous callbacks work exactly the same as regular request callbacks with the only difference being that they return a `std::future\u003cMessageType::Result\u003e`. Processing happens in a worker thread inside of the message handler. Worker threads are only created if there are asynchronous request handlers. Otherwise the handler will not create any extra threads. \n\n```cpp\nmessageHandler.add\u003clsp::requests::TextDocument_Hover\u003e(\n    [](lsp::requests::TextDocument_Hover::Params\u0026\u0026 params)\n    {\n        return std::async(std::launch::deferred,\n            [](lsp::requests::TextDocument_Hover::Params\u0026\u0026 params)\n            {\n                auto result = lsp::TextDocument_HoverResult{};\n                // init hover result here\n                return result;\n            }, std::move(params));\n    }\n```\n\nNotification callbacks can also be executed asynchronously. They must return a `std::future\u003cvoid\u003e`.\n\n### Returning Error Responses\n\nIf an error occurs while processing the request and no proper result can be provided an error response should be sent back. In order to do that simply throw an `lsp::RequestError` from inside of the callback (`#include \u003clsp/error.h\u003e`):\n\n```cpp\nthrow lsp::RequestError(lsp::MessageError::InvalidParams, \"Invalid parameters received\");\n```\n\n### Sending Requests\n\nRequests are sent using the `lsp::MessageHandler::sendRequest` method. Just like with registering the callbacks, it takes a template parameter for the message type. An `lsp::MessageId` identifying the sent request is returned.\n\nThere are two versions of this method:\n\nOne returns a `std::future\u003cMessageType::Result\u003e` in addition to the message id. The future will become ready once a response was received. Don't call `std::future::wait` on the same thread that calls `processIncomingMessages` since it would block. If an error response was returned, the future will rethrow it so make sure to handle that case.\n\n```cpp\nauto params = lsp::requests::TextDocument_Diagnostic::Params{...}\nauto [id, result] = messageHandler.sendRequest\n    \u003clsp::requests::TextDocument_Diagnostic\u003e(std::move(params));\n```\n\nThe second version allows specifying callbacks for the success and error cases. The success callback has a `MessageType::Result` parameter and the error callback an `lsp::ResponseError` containing the error code and message from the response.\n\n```cpp\nauto params = lsp::requests::TextDocument_Diagnostic::Params{...}\nauto messageId = messageHandler.sendRequest\u003clsp::requests::TextDocument_Diagnostic\u003e(\n    std::move(params),\n    [](lsp::requests::TextDocument_Diagnostic::Result\u0026\u0026 result){\n        // Called on success with the payload of the response\n    },\n    [](const lsp::ResponseError\u0026 error){\n        // Called in the error case\n    });\n```\n\n`lsp::MessageHandler::currentRequestId` can be called from inside such a callback to obtain the id of the request.\n\nJust like with handling requests it is also possible to send generic json messages using the non-template overloads of `MessageHandler::sendRequest`.\n\n### Sending Notifications\n\nNotifications are sent using `lsp::MessageHandler::sendNotification`. They don't have a message id and don't receive a response which means all you need are the parameters if the notification has any:\n\n```cpp\n// With params\nauto params = lsp::notifications::TextDocument_PublishDiagnostics::Params{...};\nmessageHandler.sendNotification\n    \u003clsp::notifications::TextDocument_PublishDiagnostics\u003e(std::move(params));\n\n// Without params\nmessageHandler.sendNotification\u003clsp::notifications::Exit\u003e();\n\n```\n\n## Starting a Server Process\n\nWhen implementing an LSP client it usually is responsible for creating the server process. This can be done with the `lsp::Process` class. It has a member `stdIO` which can be used to initialize a connection via the standard input and output of the process.\n\n```cpp\n#include \u003clsp/process.h\u003e\n#include \u003clsp/connection.h\u003e\n\nauto process    = lsp::Process(\"/usr/bin/clangd\", {/*args*/});\nauto connection = lsp::Connection(process.stdIO());\n```\n\n## Using Sockets\n\nSockets are a typical method of communication between language servers and clients. The framework supports connecting to an existing address and port as well as creating a server and listening for incoming connections. `lsp/io/socket.h` needs to be included in order to be able to use the socket functions.\n\nClients can use `lsp::io::Socket::connect` to create a new socket for a given address/port combination and use it to initialize a connection:\n\n```cpp\nauto port       = 12345;\nauto socket     = lsp::io::Socket::connect(lsp::io::Socket::Localhost, port);\nauto connection = lsp::Connection(socket);\n```\n\nServers need to listen for incoming socket connections. This is done by creating an `lsp::io::SocketListener` and calling its `listen` method in a loop. It waits until a new socket connection is made and returns an `lsp::io::Socket`. Since multiple connections can be accepted at once, it is possible for a single server executable to communicate with multiple clients. The following example creates a socket server which is listening for incoming connections. If one is made, a new thread is spawned which uses the socket to create and run a new server instance for that connection:\n\n```cpp\nauto port           = 12345;\nauto socketListener = lsp::io::SocketListener(port);\n\nwhile(socketListener.isReady())\n{\n    auto socket = socketListener.listen();\n\n    if(!socket.isOpen())\n        break;\n\n    std::thread([socket = std::move(socket)]() mutable\n    {\n        auto connection     = lsp::Connection(socket);\n        auto messageHandler = lsp::MessageHandler(connection);\n        // ...\n    }).detach();\n}\n```\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleon-bckl%2Flsp-framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleon-bckl%2Flsp-framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleon-bckl%2Flsp-framework/lists"}