From ff95d84f2fbc744673adce2c8465143423c130c6 Mon Sep 17 00:00:00 2001 From: root <2057479591@qq.com> Date: Mon, 15 Jul 2024 00:58:54 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E9=87=8D=E5=BB=BA=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ CMakeLists.txt | 15 +++++++-------- lsp/CMakeLists.txt | 23 +++++++++++++++++++++++ {include => lsp/include/lsp}/client.h | 0 {include => lsp/include/lsp}/json.hpp | 0 {include => lsp/include/lsp}/protocol.h | 0 {include => lsp/include/lsp}/transport.h | 0 {include => lsp/include/lsp}/uri.h | 0 lsp/src/empty.cpp | 1 + test/CMakeLists.txt | 14 ++++++++++++++ {src => test/src}/main.cpp | 5 +++-- 11 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 lsp/CMakeLists.txt rename {include => lsp/include/lsp}/client.h (100%) rename {include => lsp/include/lsp}/json.hpp (100%) rename {include => lsp/include/lsp}/protocol.h (100%) rename {include => lsp/include/lsp}/transport.h (100%) rename {include => lsp/include/lsp}/uri.h (100%) create mode 100644 lsp/src/empty.cpp create mode 100644 test/CMakeLists.txt rename {src => test/src}/main.cpp (94%) diff --git a/.gitignore b/.gitignore index 910a5f1..3795072 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,7 @@ *.app .idea/ +.vs/ +out/ +CMakeSettings.json cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 3295c1d..c15f383 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,9 @@ -cmake_minimum_required(VERSION 3.14) -project(Lsp) +cmake_minimum_required(VERSION 3.5) +project(LspCPP VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) + +add_subdirectory(lsp) +add_subdirectory(test) -set(CMAKE_CXX_STANDARD 14) -include_directories(include) -include_directories(src) -aux_source_directory(src/ sources) -set(lsp_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" ) -add_executable(LspClientTest ${sources}) diff --git a/lsp/CMakeLists.txt b/lsp/CMakeLists.txt new file mode 100644 index 0000000..9bb44d6 --- /dev/null +++ b/lsp/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.5) + +project(LspCore VERSION 0.1 LANGUAGES CXX) + +file(GLOB_RECURSE CODE_FILES + *.cpp + *.h + *.hpp +) + +message( ${CODE_FILES}) + +add_library( ${PROJECT_NAME} + ${CODE_FILES} +) + +target_include_directories(${PROJECT_NAME} PUBLIC include) + +set_target_properties(${PROJECT_NAME} PROPERTIES + ENABLE_EXPORTS TRUE +) + + diff --git a/include/client.h b/lsp/include/lsp/client.h similarity index 100% rename from include/client.h rename to lsp/include/lsp/client.h diff --git a/include/json.hpp b/lsp/include/lsp/json.hpp similarity index 100% rename from include/json.hpp rename to lsp/include/lsp/json.hpp diff --git a/include/protocol.h b/lsp/include/lsp/protocol.h similarity index 100% rename from include/protocol.h rename to lsp/include/lsp/protocol.h diff --git a/include/transport.h b/lsp/include/lsp/transport.h similarity index 100% rename from include/transport.h rename to lsp/include/lsp/transport.h diff --git a/include/uri.h b/lsp/include/lsp/uri.h similarity index 100% rename from include/uri.h rename to lsp/include/lsp/uri.h diff --git a/lsp/src/empty.cpp b/lsp/src/empty.cpp new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/lsp/src/empty.cpp @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..b437fdf --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.5) +project(LspTest VERSION 0.1 LANGUAGES CXX) + +file(GLOB_RECURSE CODE_FILES + *.cpp + *.h + *.hpp +) + +add_executable( ${PROJECT_NAME} + ${CODE_FILES} +) + +target_link_libraries(${PROJECT_NAME} PUBLIC LspCore) \ No newline at end of file diff --git a/src/main.cpp b/test/src/main.cpp similarity index 94% rename from src/main.cpp rename to test/src/main.cpp index c0804fb..e0a0102 100644 --- a/src/main.cpp +++ b/test/src/main.cpp @@ -1,6 +1,7 @@ -#include +#include #include -#include "client.h" +#include + int main() { URI uri; uri.parse("https://www.baidu.com/test/asdf"); From b369b0d94c8e28952b4ebbef6f752e163d3a36ab Mon Sep 17 00:00:00 2001 From: root <2057479591@qq.com> Date: Mon, 15 Jul 2024 16:39:20 +0800 Subject: [PATCH 2/9] =?UTF-8?q?1.=20=E8=B0=83=E6=95=B4protocol.h=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E7=BB=93=E6=9E=84=E4=BD=93=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=EF=BC=8C=E4=BD=BF=E4=B9=8B=E5=AE=B9=E6=98=93?= =?UTF-8?q?=E9=98=85=E8=AF=BB=EF=BC=9B=202.=20=E6=B7=BB=E5=8A=A0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E8=AF=8D=E8=AF=AD=E5=88=97=E8=A1=A8=E7=9A=84api?= =?UTF-8?q?=E5=92=8Cjson=E5=AE=9A=E4=B9=89=EF=BC=9B=203.=20=E5=AF=B9?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=92=8Ccmake=E8=BF=9B=E8=A1=8C=E8=B0=83?= =?UTF-8?q?=E6=95=B4=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + CMakeLists.txt | 1 + lsp/include/lsp/client.h | 55 ++-- lsp/include/lsp/json.hpp | 3 +- lsp/include/lsp/protocol.h | 506 +++++++++++++++++++++--------------- lsp/include/lsp/transport.h | 4 +- lsp/include/lsp/uri.h | 2 +- test/src/main.cpp | 89 +++++-- 8 files changed, 413 insertions(+), 249 deletions(-) diff --git a/.gitignore b/.gitignore index 3795072..c7da0c7 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,8 @@ .idea/ .vs/ +.cache/ out/ +compile_commands.json CMakeSettings.json cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index c15f383..34fd428 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ project(LspCPP VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_subdirectory(lsp) add_subdirectory(test) diff --git a/lsp/include/lsp/client.h b/lsp/include/lsp/client.h index 62a8f4b..8868e9c 100644 --- a/lsp/include/lsp/client.h +++ b/lsp/include/lsp/client.h @@ -1,4 +1,4 @@ -// +// // Created by Alex on 2020/1/28. // @@ -32,6 +32,7 @@ class LanguageClient : public JsonTransport { RequestID RegisterCapability() { return SendRequest("client/registerCapability"); } + //=============文档同步相关API========================================= void DidOpen(DocumentUri uri, string_ref text, string_ref languageId = "cpp") { DidOpenTextDocumentParams params; params.textDocument.uri = std::move(uri); @@ -52,6 +53,33 @@ class LanguageClient : public JsonTransport { params.wantDiagnostics = wantDiagnostics; SendNotify("textDocument/didChange", params); } + //=============语言特性相关API========================================= + // 跳转 + RequestID GoToDefinition(DocumentUri uri, Position position) { + TextDocumentPositionParams params; + params.textDocument.uri = std::move(uri); + params.position = position; + return SendRequest("textDocument/definition", std::move(params)); + } + // 跳转 + RequestID GoToDeclaration(DocumentUri uri, Position position) { + TextDocumentPositionParams params; + params.textDocument.uri = std::move(uri); + params.position = position; + return SendRequest("textDocument/declaration", std::move(params)); + } + RequestID References(DocumentUri uri, Position position) { + ReferenceParams params; + params.textDocument.uri = std::move(uri); + params.position = position; + return SendRequest("textDocument/references", std::move(params)); + } + // 获取文档的词语列表 + RequestID SemanticTokensALL(DocumentUri uri, ProgressToken token = "") { + SemanticTokensParams params; + params.textDocument.uri = uri; + return SendRequest(METHOD_SemanticTokensFull, std::move(params)); + } RequestID RangeFomatting(DocumentUri uri, Range range) { DocumentRangeFormattingParams params; params.textDocument.uri = std::move(uri); @@ -101,24 +129,6 @@ class LanguageClient : public JsonTransport { params.position = position; return SendRequest("textDocument/signatureHelp", std::move(params)); } - RequestID GoToDefinition(DocumentUri uri, Position position) { - TextDocumentPositionParams params; - params.textDocument.uri = std::move(uri); - params.position = position; - return SendRequest("textDocument/definition", std::move(params)); - } - RequestID GoToDeclaration(DocumentUri uri, Position position) { - TextDocumentPositionParams params; - params.textDocument.uri = std::move(uri); - params.position = position; - return SendRequest("textDocument/declaration", std::move(params)); - } - RequestID References(DocumentUri uri, Position position) { - ReferenceParams params; - params.textDocument.uri = std::move(uri); - params.position = position; - return SendRequest("textDocument/references", std::move(params)); - } RequestID SwitchSourceHeader(DocumentUri uri) { TextDocumentIdentifier params; params.uri = std::move(uri); @@ -167,6 +177,9 @@ class LanguageClient : public JsonTransport { params.resolve = resolve; return SendRequest("textDocument/typeHierarchy", std::move(params)); } + + + //=============工作区相关API========================================= RequestID WorkspaceSymbol(string_ref query) { WorkspaceSymbolParams params; params.query = query; @@ -303,13 +316,11 @@ class ProcessLanguageClient : public LanguageClient { } catch (std::exception &e) { //printf("read error -> %s\nread -> %s\n ", e.what(), read.c_str()); } - //printf("message %d:\n%s\n", length, read.c_str()); return true; } bool writeJson(json &json) override { - std::string content = json.dump(); + std::string content = json.dump(-1, ' ', false, nlohmann::detail::error_handler_t::ignore); std::string header = "Content-Length: " + std::to_string(content.length()) + "\r\n\r\n" + content; - //printf("send:\n%s\n", content.c_str()); return Write(header); } }; diff --git a/lsp/include/lsp/json.hpp b/lsp/include/lsp/json.hpp index 77bd173..6e09483 100644 --- a/lsp/include/lsp/json.hpp +++ b/lsp/include/lsp/json.hpp @@ -1,4 +1,4 @@ -/* +/* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ | | |__ | | | | | | version 3.7.3 @@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/lsp/include/lsp/protocol.h b/lsp/include/lsp/protocol.h index f57e415..65b2552 100644 --- a/lsp/include/lsp/protocol.h +++ b/lsp/include/lsp/protocol.h @@ -1,4 +1,4 @@ -// +// // Created by Alex on 2020/1/28. // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. @@ -6,6 +6,16 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // Most of the code comes from clangd(Protocol.h) +//version LSP 3.17 https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification +//this file contents have 4 parts, you can search it to find what you want. +//or add something in correct place. +// +//1. basic JSON struct +//2. Lifecycle Messages struct +//3. Document Synchronization struct (include part of clanged's json param) +//4. Language Feartures struct (may content others) +//5. workspace..(None) + #ifndef LSP_PROTOCOL_H #define LSP_PROTOCOL_H #include @@ -14,6 +24,7 @@ #include #include #include "uri.h" + #define MAP_JSON(...) {j = {__VA_ARGS__};} #define MAP_KEY(KEY) {#KEY, value.KEY} #define MAP_TO(KEY, TO) {KEY, value.TO} @@ -26,7 +37,21 @@ static void from_json(const json& j, Type& value) FROM \ }; \ } + +static std::string METHOD_DidOpen = "textDocument/didOpen"; +static std::string METHOD_DidClose = "textDocument/didClose"; +static std::string METHOD_DidChange = "textDocument/didChange"; +static std::string METHOD_Definition = "textDocument/definition"; +static std::string METHOD_Declaration = "textDocument/declaration"; +static std::string METHOD_References = "textDocument/references"; +static std::string METHOD_SemanticTokensFull = "textDocument/semanticTokens/full"; +static std::string METHOD_PublishDiagnostics = "textDocument/publishDiagnostics"; + + using TextType = string_ref; +//TODO: 支持多个类型, string和int +using ProgressToken = string_ref; + enum class ErrorCode { // Defined by JSON RPC. ParseError = -32700, @@ -47,109 +72,11 @@ class LSPError { LSPError(std::string Message, ErrorCode Code) : Message(std::move(Message)), Code(Code) {} }; -JSON_SERIALIZE(URIForFile, {j = value.file;}, {value.file = j.get();}); -struct TextDocumentIdentifier { - /// The text document's URI. - DocumentUri uri; -}; -JSON_SERIALIZE(TextDocumentIdentifier, MAP_JSON(MAP_KEY(uri)), {}); - -struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier { - int version = 0; -}; -JSON_SERIALIZE(VersionedTextDocumentIdentifier, MAP_JSON(MAP_KEY(uri), MAP_KEY(version)), {}); - -struct Position { - /// Line position in a document (zero-based). - int line = 0; - /// Character offset on a line in a document (zero-based). - /// WARNING: this is in UTF-16 codepoints, not bytes or characters! - /// Use the functions in SourceCode.h to construct/interpret Positions. - int character = 0; - friend bool operator==(const Position &LHS, const Position &RHS) { - return std::tie(LHS.line, LHS.character) == - std::tie(RHS.line, RHS.character); - } - friend bool operator!=(const Position &LHS, const Position &RHS) { - return !(LHS == RHS); - } - friend bool operator<(const Position &LHS, const Position &RHS) { - return std::tie(LHS.line, LHS.character) < - std::tie(RHS.line, RHS.character); - } - friend bool operator<=(const Position &LHS, const Position &RHS) { - return std::tie(LHS.line, LHS.character) <= - std::tie(RHS.line, RHS.character); - } -}; -JSON_SERIALIZE(Position, MAP_JSON(MAP_KEY(line), MAP_KEY(character)), {FROM_KEY(line);FROM_KEY(character)}); - -struct Range { - /// The range's start position. - Position start; - - /// The range's end position. - Position end; - - friend bool operator==(const Range &LHS, const Range &RHS) { - return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end); - } - friend bool operator!=(const Range &LHS, const Range &RHS) { - return !(LHS == RHS); - } - friend bool operator<(const Range &LHS, const Range &RHS) { - return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end); - } - bool contains(Position Pos) const { return start <= Pos && Pos < end; } - bool contains(Range Rng) const { - return start <= Rng.start && Rng.end <= end; - } -}; -JSON_SERIALIZE(Range, MAP_JSON(MAP_KEY(start), MAP_KEY(end)), {FROM_KEY(start);FROM_KEY(end)}); - -struct Location { - /// The text document's URI. - std::string uri; - Range range; - - friend bool operator==(const Location &LHS, const Location &RHS) { - return LHS.uri == RHS.uri && LHS.range == RHS.range; - } - friend bool operator!=(const Location &LHS, const Location &RHS) { - return !(LHS == RHS); - } - friend bool operator<(const Location &LHS, const Location &RHS) { - return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range); - } -}; -JSON_SERIALIZE(Location, MAP_JSON(MAP_KEY(uri), MAP_KEY(range)), {FROM_KEY(uri);FROM_KEY(range)}); - -struct TextEdit { - /// The range of the text document to be manipulated. To insert - /// text into a document create a range where start === end. - Range range; - - /// The string to be inserted. For delete operations use an - /// empty string. - std::string newText; -}; -JSON_SERIALIZE(TextEdit, MAP_JSON(MAP_KEY(range), MAP_KEY(newText)), {FROM_KEY(range);FROM_KEY(newText);}); - -struct TextDocumentItem { - /// The text document's URI. - DocumentUri uri; - - /// The text document's language identifier. - string_ref languageId; - - /// The version number of this document (it will strictly increase after each - int version = 0; +JSON_SERIALIZE(URIForFile, {j = value.file;}, {value.file = j.get();});\ - /// The content of the opened text document. - string_ref text; -}; -JSON_SERIALIZE(TextDocumentItem, MAP_JSON( - MAP_KEY(uri), MAP_KEY(languageId), MAP_KEY(version), MAP_KEY(text)), {}); +//======================================================================================= +//=================================== 1.basic JSON struct================================ +//======================================================================================= enum class TraceLevel { Off = 0, @@ -251,22 +178,207 @@ NLOHMANN_JSON_SERIALIZE_ENUM(OffsetEncoding, { {OffsetEncoding::UTF8, "utf-8"}, {OffsetEncoding::UTF16, "utf-16"}, {OffsetEncoding::UTF32, "utf-32"}, -}) -NLOHMANN_JSON_SERIALIZE_ENUM(MarkupKind, { - {MarkupKind::PlainText, "plaintext"}, - {MarkupKind::Markdown, "markdown"}, -}) -NLOHMANN_JSON_SERIALIZE_ENUM(ResourceOperationKind, { - {ResourceOperationKind::Create, "create"}, - {ResourceOperationKind::Rename, "rename"}, - {ResourceOperationKind::Delete, "dename"} -}) -NLOHMANN_JSON_SERIALIZE_ENUM(FailureHandlingKind, { - {FailureHandlingKind::Abort, "abort"}, - {FailureHandlingKind::Transactional, "transactional"}, - {FailureHandlingKind::Undo, "undo"}, - {FailureHandlingKind::TextOnlyTransactional, "textOnlyTransactional"} -}) + }) + NLOHMANN_JSON_SERIALIZE_ENUM(MarkupKind, { + {MarkupKind::PlainText, "plaintext"}, + {MarkupKind::Markdown, "markdown"}, + }) + NLOHMANN_JSON_SERIALIZE_ENUM(ResourceOperationKind, { + {ResourceOperationKind::Create, "create"}, + {ResourceOperationKind::Rename, "rename"}, + {ResourceOperationKind::Delete, "dename"} + }) + NLOHMANN_JSON_SERIALIZE_ENUM(FailureHandlingKind, { + {FailureHandlingKind::Abort, "abort"}, + {FailureHandlingKind::Transactional, "transactional"}, + {FailureHandlingKind::Undo, "undo"}, + {FailureHandlingKind::TextOnlyTransactional, "textOnlyTransactional"} + }) + +struct Position { + /// Line position in a document (zero-based). + int line = 0; + /// Character offset on a line in a document (zero-based). + /// WARNING: this is in UTF-16 codepoints, not bytes or characters! + /// Use the functions in SourceCode.h to construct/interpret Positions. + int character = 0; + friend bool operator==(const Position &LHS, const Position &RHS) { + return std::tie(LHS.line, LHS.character) == + std::tie(RHS.line, RHS.character); + } + friend bool operator!=(const Position &LHS, const Position &RHS) { + return !(LHS == RHS); + } + friend bool operator<(const Position &LHS, const Position &RHS) { + return std::tie(LHS.line, LHS.character) < + std::tie(RHS.line, RHS.character); + } + friend bool operator<=(const Position &LHS, const Position &RHS) { + return std::tie(LHS.line, LHS.character) <= + std::tie(RHS.line, RHS.character); + } +}; +JSON_SERIALIZE(Position, MAP_JSON(MAP_KEY(line), MAP_KEY(character)), {FROM_KEY(line);FROM_KEY(character)}); + +struct Range { + /// The range's start position. + Position start; + + /// The range's end position. + Position end; + + friend bool operator==(const Range &LHS, const Range &RHS) { + return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end); + } + friend bool operator!=(const Range &LHS, const Range &RHS) { + return !(LHS == RHS); + } + friend bool operator<(const Range &LHS, const Range &RHS) { + return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end); + } + bool contains(Position Pos) const { return start <= Pos && Pos < end; } + bool contains(Range Rng) const { + return start <= Rng.start && Rng.end <= end; + } +}; +JSON_SERIALIZE(Range, MAP_JSON(MAP_KEY(start), MAP_KEY(end)), {FROM_KEY(start);FROM_KEY(end)}); + +struct TextDocumentItem { + /// The text document's URI. + DocumentUri uri; + + /// The text document's language identifier. + string_ref languageId; + + /// The version number of this document (it will strictly increase after each + int version = 0; + + /// The content of the opened text document. + string_ref text; +}; +JSON_SERIALIZE(TextDocumentItem, MAP_JSON( + MAP_KEY(uri), MAP_KEY(languageId), MAP_KEY(version), MAP_KEY(text)), {}); + +struct TextDocumentIdentifier { + /// The text document's URI. + DocumentUri uri; +}; +JSON_SERIALIZE(TextDocumentIdentifier, MAP_JSON(MAP_KEY(uri)), {}); + +struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier { + int version = 0; +}; +JSON_SERIALIZE(VersionedTextDocumentIdentifier, MAP_JSON(MAP_KEY(uri), MAP_KEY(version)), {}); + +struct TextDocumentPositionParams { + /// The text document. + TextDocumentIdentifier textDocument; + + /// The position inside the text document. + Position position; +}; +JSON_SERIALIZE(TextDocumentPositionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position)), {}); + +struct TextEdit { + /// The range of the text document to be manipulated. To insert + /// text into a document create a range where start === end. + Range range; + + /// The string to be inserted. For delete operations use an + /// empty string. + std::string newText; +}; +JSON_SERIALIZE(TextEdit, MAP_JSON(MAP_KEY(range), MAP_KEY(newText)), { FROM_KEY(range); FROM_KEY(newText); }); + +struct Location { + /// The text document's URI. + std::string uri; + Range range; + + friend bool operator==(const Location& LHS, const Location& RHS) { + return LHS.uri == RHS.uri && LHS.range == RHS.range; + } + friend bool operator!=(const Location& LHS, const Location& RHS) { + return !(LHS == RHS); + } + friend bool operator<(const Location& LHS, const Location& RHS) { + return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range); + } +}; +JSON_SERIALIZE(Location, MAP_JSON(MAP_KEY(uri), MAP_KEY(range)), { FROM_KEY(uri); FROM_KEY(range); }); + +struct DiagnosticRelatedInformation { + /// The location of this related diagnostic information. + Location location; + /// The message of this related diagnostic information. + std::string message; +}; +JSON_SERIALIZE(DiagnosticRelatedInformation, MAP_JSON(MAP_KEY(location), MAP_KEY(message)), { FROM_KEY(location); FROM_KEY(message); }); +struct CodeAction; + +struct Diagnostic { + /// The range at which the message applies. + Range range; + + /// The diagnostic's severity. Can be omitted. If omitted it is up to the + /// client to interpret diagnostics as error, warning, info or hint. + int severity = 0; + + /// The diagnostic's code. Can be omitted. + std::string code; + + /// A human-readable string describing the source of this + /// diagnostic, e.g. 'typescript' or 'super lint'. + std::string source; + + /// The diagnostic's message. + std::string message; + + /// An array of related diagnostic information, e.g. when symbol-names within + /// a scope collide all definitions can be marked via this property. + option> relatedInformation; + + /// The diagnostic's category. Can be omitted. + /// An LSP extension that's used to send the name of the category over to the + /// client. The category typically describes the compilation stage during + /// which the issue was produced, e.g. "Semantic Issue" or "Parse Issue". + option category; + + /// Clangd extension: code actions related to this diagnostic. + /// Only with capability textDocument.publishDiagnostics.codeActionsInline. + /// (These actions can also be obtained using textDocument/codeAction). + option> codeActions; +}; +JSON_SERIALIZE(Diagnostic, {/*NOT REQUIRED*/ }, { FROM_KEY(range); FROM_KEY(code); FROM_KEY(source); FROM_KEY(message); + FROM_KEY(relatedInformation); FROM_KEY(category); FROM_KEY(codeActions); }); + +struct MarkupContent { + MarkupKind kind = MarkupKind::PlainText; + std::string value; +}; +JSON_SERIALIZE(MarkupContent, {}, { FROM_KEY(kind); FROM_KEY(value) }); + +struct WorkDoneProgressParams { + /** + * An optional token that a server can use to report work done progress. + */ + option workDoneToken; +}; +JSON_SERIALIZE(WorkDoneProgressParams, MAP_JSON(MAP_KEY(workDoneToken)), {}); + +struct PartialResultParams { + /** + * An optional token that a server can use to report partial results (e.g. + * streaming) to the client. + */ + option partialResultToken; +}; +JSON_SERIALIZE(PartialResultParams, MAP_JSON(MAP_KEY(partialResultToken)), {}); + + +//======================================================================================= +//===================================2.Lifecycle Messages struct========================== +//======================================================================================= struct ClientCapabilities { /// The supported set of SymbolKinds for workspace/symbol. @@ -472,6 +584,13 @@ struct UnregistrationParams { }; JSON_SERIALIZE(UnregistrationParams, MAP_JSON(MAP_KEY(unregisterations)), {}); + + +//======================================================================================= +//===================================3.Document Synchronization struct=================== +//======================================================================================= + + struct DidOpenTextDocumentParams { /// The document that was opened. TextDocumentItem textDocument; @@ -560,6 +679,44 @@ struct DocumentOnTypeFormattingParams { }; JSON_SERIALIZE(DocumentOnTypeFormattingParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(ch)), {}); + +//======================================================================================= +//===================================4.Language Feartures struct========================= +//======================================================================================= + +struct DeclarationParams : public TextDocumentPositionParams, + WorkDoneProgressParams, PartialResultParams { + +}; +JSON_SERIALIZE(DeclarationParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +struct DefinitionParams : public TextDocumentPositionParams, + WorkDoneProgressParams, PartialResultParams { +}; +JSON_SERIALIZE(DefinitionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +struct TypeDefinitionParams : public TextDocumentPositionParams, + WorkDoneProgressParams, PartialResultParams { +}; +JSON_SERIALIZE(TypeDefinitionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +struct ImplementationParams : public TextDocumentPositionParams, + WorkDoneProgressParams, PartialResultParams { +}; +JSON_SERIALIZE(ImplementationParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +struct ReferenceParams : public TextDocumentPositionParams, +WorkDoneProgressParams, PartialResultParams { + // For now, no options like context.includeDeclaration are supported. +}; +JSON_SERIALIZE(ReferenceParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)),{}); + +struct Hover { + /// The hover's content + MarkupContent contents; + + /// An optional range is a range inside a text document + /// that is used to visualize a hover, e.g. by changing the background color. + option range; +}; +JSON_SERIALIZE(Hover, {}, { FROM_KEY(contents); FROM_KEY(range) }); + struct FoldingRangeParams { /// The document to format. TextDocumentIdentifier textDocument; @@ -626,6 +783,14 @@ JSON_SERIALIZE(SelectionRange, {}, { } }); +struct SemanticTokensParams : public WorkDoneProgressParams, PartialResultParams { + /** + * The text document. + */ + TextDocumentIdentifier textDocument; +}; +JSON_SERIALIZE(SemanticTokensParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); + struct DocumentFormattingParams { /// The document to format. TextDocumentIdentifier textDocument; @@ -638,51 +803,6 @@ struct DocumentSymbolParams { }; JSON_SERIALIZE(DocumentSymbolParams, MAP_JSON(MAP_KEY(textDocument)), {}); -struct DiagnosticRelatedInformation { - /// The location of this related diagnostic information. - Location location; - /// The message of this related diagnostic information. - std::string message; -}; -JSON_SERIALIZE(DiagnosticRelatedInformation, MAP_JSON(MAP_KEY(location), MAP_KEY(message)), {FROM_KEY(location);FROM_KEY(message);}); -struct CodeAction; - -struct Diagnostic { - /// The range at which the message applies. - Range range; - - /// The diagnostic's severity. Can be omitted. If omitted it is up to the - /// client to interpret diagnostics as error, warning, info or hint. - int severity = 0; - - /// The diagnostic's code. Can be omitted. - std::string code; - - /// A human-readable string describing the source of this - /// diagnostic, e.g. 'typescript' or 'super lint'. - std::string source; - - /// The diagnostic's message. - std::string message; - - /// An array of related diagnostic information, e.g. when symbol-names within - /// a scope collide all definitions can be marked via this property. - option> relatedInformation; - - /// The diagnostic's category. Can be omitted. - /// An LSP extension that's used to send the name of the category over to the - /// client. The category typically describes the compilation stage during - /// which the issue was produced, e.g. "Semantic Issue" or "Parse Issue". - option category; - - /// Clangd extension: code actions related to this diagnostic. - /// Only with capability textDocument.publishDiagnostics.codeActionsInline. - /// (These actions can also be obtained using textDocument/codeAction). - option> codeActions; -}; -JSON_SERIALIZE(Diagnostic, {/*NOT REQUIRED*/},{FROM_KEY(range);FROM_KEY(code);FROM_KEY(source);FROM_KEY(message); - FROM_KEY(relatedInformation);FROM_KEY(category);FROM_KEY(codeActions);}); - struct PublishDiagnosticsParams { /** * The URI for which diagnostic information is reported. @@ -713,6 +833,7 @@ struct CodeActionParams { }; JSON_SERIALIZE(CodeActionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(range), MAP_KEY(context)), {}); +// 过时了,不建议使用 struct WorkspaceEdit { /// Holds changes to existing resources. option>> changes; @@ -721,7 +842,7 @@ struct WorkspaceEdit { /// no support for versioned edits. }; JSON_SERIALIZE(WorkspaceEdit, MAP_JSON(MAP_KEY(changes)), {FROM_KEY(changes);}); - +// 过时了,不建议使用 struct TweakArgs { /// A file provided by the client on a textDocument/codeAction request. std::string file; @@ -731,7 +852,7 @@ struct TweakArgs { std::string tweakID; }; JSON_SERIALIZE(TweakArgs, MAP_JSON(MAP_KEY(file), MAP_KEY(selection), MAP_KEY(tweakID)), {FROM_KEY(file);FROM_KEY(selection);FROM_KEY(tweakID);}); - +// 过时了,不建议使用 struct ExecuteCommandParams { std::string command; // Arguments @@ -739,7 +860,7 @@ struct ExecuteCommandParams { option tweakArgs; }; JSON_SERIALIZE(ExecuteCommandParams, MAP_JSON(MAP_KEY(command), MAP_KEY(workspaceEdit), MAP_KEY(tweakArgs)), {}); - +// 过时了,不建议使用 struct LspCommand : public ExecuteCommandParams { std::string title; }; @@ -801,15 +922,6 @@ struct ApplyWorkspaceEditParams { }; JSON_SERIALIZE(ApplyWorkspaceEditParams, MAP_JSON(MAP_KEY(edit)), {}); -struct TextDocumentPositionParams { - /// The text document. - TextDocumentIdentifier textDocument; - - /// The position inside the text document. - Position position; -}; -JSON_SERIALIZE(TextDocumentPositionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position)), {}); - enum class CompletionTriggerKind { /// Completion was triggered by typing an identifier (24x7 code /// complete), manual invocation (e.g Ctrl+Space) or via API. @@ -834,22 +946,6 @@ struct CompletionParams : TextDocumentPositionParams { }; JSON_SERIALIZE(CompletionParams, MAP_JSON(MAP_KEY(context), MAP_KEY(textDocument), MAP_KEY(position)), {}); -struct MarkupContent { - MarkupKind kind = MarkupKind::PlainText; - std::string value; -}; -JSON_SERIALIZE(MarkupContent, {}, {FROM_KEY(kind);FROM_KEY(value)}); - -struct Hover { - /// The hover's content - MarkupContent contents; - - /// An optional range is a range inside a text document - /// that is used to visualize a hover, e.g. by changing the background color. - option range; -}; -JSON_SERIALIZE(Hover, {}, {FROM_KEY(contents);FROM_KEY(range)}); - enum class InsertTextFormat { Missing = 0, /// The primary text to be inserted is treated as a plain string. @@ -1084,17 +1180,19 @@ struct TypeHierarchyItem { /// so don't declare it. }; -struct ReferenceParams : public TextDocumentPositionParams { - // For now, no options like context.includeDeclaration are supported. -}; -JSON_SERIALIZE(ReferenceParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position)), {}); -struct FileStatus { - /// The text document's URI. - DocumentUri uri; - /// The human-readable string presents the current state of the file, can be - /// shown in the UI (e.g. status bar). - TextType state; - // FIXME: add detail messages. -}; + + + + +//struct FileStatus { +// /// The text document's URI. +// DocumentUri uri; +// /// The human-readable string presents the current state of the file, can be +// /// shown in the UI (e.g. status bar). +// TextType state; +// // FIXME: add detail messages. +//}; + + #endif //LSP_PROTOCOL_H diff --git a/lsp/include/lsp/transport.h b/lsp/include/lsp/transport.h index 52f20a2..05c6943 100644 --- a/lsp/include/lsp/transport.h +++ b/lsp/include/lsp/transport.h @@ -1,4 +1,4 @@ -// +// // Created by Alex on 2020/1/28. // @@ -106,7 +106,7 @@ class JsonTransport : public Transport { } } catch (std::exception &e) { - //printf("error -> %s\n", e.what()); + printf("error -> %s\n", e.what()); } } return 0; diff --git a/lsp/include/lsp/uri.h b/lsp/include/lsp/uri.h index 9b96212..dce3e99 100644 --- a/lsp/include/lsp/uri.h +++ b/lsp/include/lsp/uri.h @@ -1,4 +1,4 @@ -// +// // Created by Alex on 2020/1/28. // diff --git a/test/src/main.cpp b/test/src/main.cpp index e0a0102..c7006e6 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -1,39 +1,90 @@ #include #include +#include #include +using namespace nlohmann; + +bool readFile(std::string& path, std::string* data) +{ + std::ifstream ifStream(path, std::ios::binary | std::ios::in); + if (ifStream.is_open()) { + ifStream.seekg(0, std::ios::end); + int len = ifStream.tellg(); + ifStream.seekg(0, std::ios::beg); + char* buff = new char[len]; + ifStream.read(buff, len); + data->append(buff); + delete[]buff; + } + else { + std::cout << "file open fail!!!" << std::endl; + return false; + } + return true; +} int main() { - URI uri; - uri.parse("https://www.baidu.com/test/asdf"); - printf("%s", uri.host()); - printf("%s", uri.path()); + std::string file1_uri = "file:///D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp/test/src/main.cpp"; + std::string file1_path = "D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp/test/src/main.cpp"; + std::string root_uri = "file:///D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp"; + //std::string file1_text; + //std::string file2_text; + ProcessLanguageClient client(R"(D:\c_workstation\soft_tool\clangd_17.0.3\bin\clangd.exe)"); + MapMessageHandler msgHandler; + auto debugFunc = [](value& j) { + //value method = j.at("method"); + std::cout << "========================================\n"; + std::cout << j << std::endl; + }; + msgHandler.bindNotify(METHOD_PublishDiagnostics.c_str(), debugFunc); + msgHandler.bindNotify(METHOD_DidOpen.c_str(), debugFunc); + msgHandler.bindNotify(METHOD_DidClose.c_str(), debugFunc); - return 0; - ProcessLanguageClient client(R"(F:\LLVM\bin\clangd.exe)"); - MapMessageHandler my; std::thread thread([&] { - client.loop(my); + client.loop(msgHandler); }); - string_ref file = "file:///C:/Users/Administrator/Desktop/test.c"; - - std::string text = "int main() { return 0; }\n"; int res; while (scanf("%d", &res)) { - if (res == 1) { + switch (res) + { + case 0: // 退出 + { client.Exit(); thread.detach(); return 0; + break; + } + case 1: // 初始化 + { + msgHandler.bindResponse(client.Initialize(string_ref(root_uri)), debugFunc); + break; + } + case 2: // 打开文件 + { + std::string text; + readFile(file1_path, &text); + client.DidOpen(file1_uri, text); + break; + } + case 3: // 关闭文件 + { + client.DidClose(string_ref(file1_uri)); + break; } - if (res == 2) { - client.Initialize(); + case 4: // 获取词语列表 + { + msgHandler.bindResponse(client.SemanticTokensALL(string_ref(file1_uri)), debugFunc); + break; } - if (res == 3) { - client.DidOpen(file, text); - client.Sync(); + case 5: // 悬停信息 + { + Position pos{8, 13}; + msgHandler.bindResponse(client.Hover(string_ref(file1_uri), pos), debugFunc); + break; } - if (res == 4) { - client.Formatting(file); + default: + break; } } return 0; From 356492cd6d59f00735bbbd202bc643fd64b698bb Mon Sep 17 00:00:00 2001 From: root <2057479591@qq.com> Date: Fri, 19 Jul 2024 14:56:04 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=9A=84=E7=BC=96=E8=AF=91=E9=80=89=E9=A1=B9?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c15f383..db9521b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,12 @@ project(LspCPP VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) +option(LspBuildTest "build test" ON) + add_subdirectory(lsp) -add_subdirectory(test) +if(LspBuildTest) + add_subdirectory(test) +endif() + From e42edf2e70a646f7cdbef23a96f7235bb67fd145 Mon Sep 17 00:00:00 2001 From: firemeatman <2057479591@qq.com> Date: Sat, 20 Jul 2024 20:04:11 +0800 Subject: [PATCH 4/9] =?UTF-8?q?-=20=E6=B7=BB=E5=8A=A0=E6=82=AC=E6=B5=AE?= =?UTF-8?q?=E6=8F=90=E7=A4=BAHover=E8=AF=B7=E6=B1=82=E7=9A=84=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lsp/include/lsp/protocol.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lsp/include/lsp/protocol.h b/lsp/include/lsp/protocol.h index 65b2552..5f8d846 100644 --- a/lsp/include/lsp/protocol.h +++ b/lsp/include/lsp/protocol.h @@ -38,14 +38,15 @@ }; \ } -static std::string METHOD_DidOpen = "textDocument/didOpen"; -static std::string METHOD_DidClose = "textDocument/didClose"; -static std::string METHOD_DidChange = "textDocument/didChange"; -static std::string METHOD_Definition = "textDocument/definition"; -static std::string METHOD_Declaration = "textDocument/declaration"; -static std::string METHOD_References = "textDocument/references"; -static std::string METHOD_SemanticTokensFull = "textDocument/semanticTokens/full"; -static std::string METHOD_PublishDiagnostics = "textDocument/publishDiagnostics"; +const static std::string METHOD_DidOpen = "textDocument/didOpen"; +const static std::string METHOD_DidClose = "textDocument/didClose"; +const static std::string METHOD_DidChange = "textDocument/didChange"; +const static std::string METHOD_Definition = "textDocument/definition"; +const static std::string METHOD_Declaration = "textDocument/declaration"; +const static std::string METHOD_References = "textDocument/references"; +const static std::string METHOD_SemanticTokensFull = "textDocument/semanticTokens/full"; +const static std::string METHOD_PublishDiagnostics = "textDocument/publishDiagnostics"; +const static std::string METHOD_Hover = "textDocument/hover"; using TextType = string_ref; @@ -707,6 +708,10 @@ WorkDoneProgressParams, PartialResultParams { }; JSON_SERIALIZE(ReferenceParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)),{}); +struct HoverParam: public TextDocumentPositionParams, WorkDoneProgressParams { + +}; +JSON_SERIALIZE(HoverParam, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken)), {}); struct Hover { /// The hover's content MarkupContent contents; From e925ac03743875d0f9bbffd3ab6495bfb68ce4ec Mon Sep 17 00:00:00 2001 From: firemeatman <2057479591@qq.com> Date: Thu, 25 Jul 2024 00:19:44 +0800 Subject: [PATCH 5/9] =?UTF-8?q?-=E6=9B=B4=E6=94=B9transpot=E7=AD=89?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=9A=E4=B9=89=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?JsonIOLayer=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=B0=86IO=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E5=88=86=E7=A6=BB=EF=BC=8C=E4=BF=AE=E6=94=B9=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E4=B8=BA=E7=BB=84=E5=90=88=EF=BC=9B=20-=E6=9B=B4?= =?UTF-8?q?=E6=94=B9MapHandel=E7=B1=BB=EF=BC=8C=E4=BD=BF=E4=B9=8B=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=BA=BF=E7=A8=8B=E5=AE=89=E5=85=A8=EF=BC=8C=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E8=AE=BF=E9=97=AE=E8=80=85=E6=A8=A1=E5=BC=8F=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=EF=BC=9B=20-=E4=BF=AE=E5=A4=8D=E7=AE=A1=E9=81=93?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=85=B3=E9=97=AD=E5=AF=BC=E8=87=B4=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=B8=8D=E8=83=BD=E6=AD=A3=E5=B8=B8=E9=80=80=E5=87=BA?= =?UTF-8?q?=E7=9A=84Bug=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lsp/include/lsp/client.h | 53 +++++++++++--- lsp/include/lsp/protocol.h | 3 +- lsp/include/lsp/transport.h | 139 +++++++++++++++++++++++++++++++----- 3 files changed, 165 insertions(+), 30 deletions(-) diff --git a/lsp/include/lsp/client.h b/lsp/include/lsp/client.h index 8868e9c..a9434d7 100644 --- a/lsp/include/lsp/client.h +++ b/lsp/include/lsp/client.h @@ -7,8 +7,10 @@ #include "transport.h" #include "protocol.h" #include "windows.h" + class LanguageClient : public JsonTransport { public: + LanguageClient(MapMessageHandler& msgHandler, JsonIOLayer& jsonIO) : JsonTransport(msgHandler, jsonIO){} virtual ~LanguageClient() = default; public: RequestID Initialize(option rootUri = {}) { @@ -212,12 +214,18 @@ class LanguageClient : public JsonTransport { notify(method, params); } }; -class ProcessLanguageClient : public LanguageClient { -public: +class PipJsonIO : public JsonIOLayer{ +private: HANDLE fReadIn = nullptr, fWriteIn = nullptr; HANDLE fReadOut = nullptr, fWriteOut = nullptr; PROCESS_INFORMATION fProcess = {nullptr}; - explicit ProcessLanguageClient(const char *program, const char *arguments = "") { + + bool m_isClosed{false}; + std::mutex m_closeLock; +public: + explicit PipJsonIO(const char *program, const char *arguments = "") + :JsonIOLayer() + { SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = true; @@ -227,6 +235,14 @@ class ProcessLanguageClient : public LanguageClient { if (!CreatePipe(&fReadOut, &fWriteOut, &sa, 1024 * 1024)) { printf("Create Out Pipe error\n"); } + + // 设置非阻塞 + DWORD mode = PIPE_NOWAIT; + if (!SetNamedPipeHandleState(fReadOut, &mode, NULL, NULL)) { + + std::cerr << "Failed to set non-blocking mode for readout pipe." << std::endl; + } + STARTUPINFO si = {0}; si.cb = sizeof(si); si.hStdInput = fReadIn; @@ -240,13 +256,17 @@ class ProcessLanguageClient : public LanguageClient { //m_exec.start(program, arguments); //m_exec.set_wait_timeout(exec_stream_t::s_child, INFINITE); } - ~ProcessLanguageClient() override { - /* - DisconnectNamedPipe(fReadIn); - DisconnectNamedPipe(fWriteIn); - DisconnectNamedPipe(fReadOut); - DisconnectNamedPipe(fWriteOut); - */ + virtual ~PipJsonIO(){ + this->PipJsonIO::close(); + } + + void close() override + { + std::lock_guard _guard(m_closeLock); + + if(m_isClosed){ + return; + } CloseHandle(fReadIn); CloseHandle(fWriteIn); CloseHandle(fReadOut); @@ -259,7 +279,16 @@ class ProcessLanguageClient : public LanguageClient { } CloseHandle(fProcess.hThread); CloseHandle(fProcess.hProcess); + + m_isClosed = true; } + + bool isClosed() override + { + std::lock_guard _guard(m_closeLock); + return m_isClosed; + } + void SkipLine() { char read; DWORD hasRead; @@ -311,9 +340,13 @@ class ProcessLanguageClient : public LanguageClient { SkipLine(); std::string read; Read(length, read); + if(read.empty()){ + return false; + } try { json = json::parse(read); } catch (std::exception &e) { + return false; //printf("read error -> %s\nread -> %s\n ", e.what(), read.c_str()); } return true; diff --git a/lsp/include/lsp/protocol.h b/lsp/include/lsp/protocol.h index 5f8d846..86b129a 100644 --- a/lsp/include/lsp/protocol.h +++ b/lsp/include/lsp/protocol.h @@ -47,7 +47,8 @@ const static std::string METHOD_References = "textDocument/references"; const static std::string METHOD_SemanticTokensFull = "textDocument/semanticTokens/full"; const static std::string METHOD_PublishDiagnostics = "textDocument/publishDiagnostics"; const static std::string METHOD_Hover = "textDocument/hover"; - +const static std::string METHOD_SignatureHelp = "textDocument/signatureHelp"; +const static std::string METHOD_Completion = "textDocument/completion"; using TextType = string_ref; //TODO: 支持多个类型, string和int diff --git a/lsp/include/lsp/transport.h b/lsp/include/lsp/transport.h index 05c6943..40411e5 100644 --- a/lsp/include/lsp/transport.h +++ b/lsp/include/lsp/transport.h @@ -8,24 +8,82 @@ #include "uri.h" #include #include +#include +#include + using value = json; using RequestID = std::string; class MessageHandler { +public: + enum class MsgType{ + Request, + Response, + Error, + Notify + }; public: MessageHandler() = default; - virtual void onNotify(string_ref method, value ¶ms) {} - virtual void onResponse(value &ID, value &result) {} - virtual void onError(value &ID, value &error) {} - virtual void onRequest(string_ref method, value ¶ms, value &ID) {} + + virtual void onAnyJsonRPC(MsgType type, value& dataPart, string_ref method = nullptr) = 0; + virtual void onNotify(string_ref method, value ¶ms) = 0; + virtual void onResponse(value &ID, value &result) = 0; + virtual void onError(value &ID, value &error) = 0; + virtual void onRequest(string_ref method, value ¶ms, value &ID) = 0; }; + + class MapMessageHandler : public MessageHandler { public: std::map> m_calls; std::map> m_notify; std::vector>> m_requests; + std::function m_anyJsonCallback = nullptr; + std::mutex m_lock; + + // 用于线程安全的访问类 + class Accessor{ + MapMessageHandler& m_other; + std::unique_lock m_guard; + public: + explicit Accessor(MapMessageHandler& other) : m_other(other), m_guard(other.m_lock){} + + template + void bindRequest(const char *method, std::function func) { + m_other.bindRequest(method, func); + } + void bindRequest(const char *method, std::function func) { + m_other.bindRequest(method, func); + } + template + void bindNotify(const char *method, std::function func) { + m_other.bindNotify(method, func); + } + void bindNotify(const char *method, std::function func) { + m_other.bindNotify(method, func); + } + void bindResponse(RequestID id, std::functionfunc) { + m_other.bindResponse(id, func); + } + void bindAnyJsonRPC(std::function func){ + m_other.bindAnyJsonRPC(func); + } + + void onAnyJsonRPC(MsgType type, value& dataPart, string_ref method = nullptr){m_other.onAnyJsonRPC(type, dataPart, method);} + void onNotify(string_ref method, value ¶ms){m_other.onNotify(method, params);} + void onResponse(value &ID, value &result){m_other.onResponse(ID, result);} + void onError(value &ID, value &error){m_other.onError(ID, error);} + void onRequest(string_ref method, value ¶ms, value &ID){m_other.onRequest(method,params,ID);} + }; + +public: MapMessageHandler() = default; + + Accessor access(){ + return Accessor(*this); + } + template void bindRequest(const char *method, std::function func) { m_calls[method] = [=](json ¶ms, json &id) { @@ -49,6 +107,10 @@ class MapMessageHandler : public MessageHandler { void bindResponse(RequestID id, std::functionfunc) { m_requests.emplace_back(id, std::move(func)); } + void bindAnyJsonRPC(std::function func){ + m_anyJsonCallback = func; + } + void onNotify(string_ref method, value ¶ms) override { std::string str = method.str(); if (m_notify.count(str)) { @@ -73,59 +135,98 @@ class MapMessageHandler : public MessageHandler { m_calls[string](params, ID); } } + void onAnyJsonRPC(MsgType type, value& dataPart, string_ref method = nullptr) override{ + if(m_anyJsonCallback){ + m_anyJsonCallback(type, dataPart, method); + } + } + }; class Transport { public: + virtual ~Transport(){}; + virtual void notify(string_ref method, value ¶ms) = 0; virtual void request(string_ref method, value ¶ms, RequestID &id) = 0; - virtual int loop(MessageHandler &) = 0; + virtual int loop(MessageHandler &){return 0;} + virtual int safeLoop() = 0; + virtual void requestStopLoop() = 0; }; -class JsonTransport : public Transport { +class JsonIOLayer{ public: + virtual ~JsonIOLayer(){}; + + virtual bool readJson(value &) = 0; + virtual bool writeJson(value &) = 0; + virtual void close() = 0; + virtual bool isClosed() = 0; +}; + +class JsonTransport : public Transport { +private: + MapMessageHandler& m_msgHandler; + JsonIOLayer& m_jsonIO; + const char *jsonrpc = "2.0"; - int loop(MessageHandler &handler) override { - while (true) { + std::atomic havingStopRequest = false; + std::chrono::milliseconds runningFrequency = std::chrono::milliseconds(20); +public: + explicit JsonTransport(MapMessageHandler& msgHandler, JsonIOLayer& jsonIO) : m_msgHandler(msgHandler), m_jsonIO(jsonIO){} + virtual ~JsonTransport(); + + int safeLoop() override{ + while (!havingStopRequest){ try { value value; - if (readJson(value)) { + if(m_jsonIO.isClosed()){throw "closed";} + if (m_jsonIO.readJson(value)){ + MapMessageHandler::Accessor accessor = m_msgHandler.access(); if (value.count("id")) { if (value.contains("method")) { - handler.onRequest(value["method"].get(), value["params"], value["id"]); + accessor.onAnyJsonRPC(MessageHandler::MsgType::Request, value["params"], value["method"].get()); + accessor.onRequest(value["method"].get(), value["params"], value["id"]); } else if (value.contains("result")) { - handler.onResponse(value["id"], value["result"]); + accessor.onAnyJsonRPC(MessageHandler::MsgType::Response, value["result"], value["id"].get()); + accessor.onResponse(value["id"], value["result"]); } else if (value.contains("error")) { - handler.onError(value["id"], value["error"]); + accessor.onAnyJsonRPC(MessageHandler::MsgType::Error, value["error"]); + accessor.onError(value["id"], value["error"]); } } else if (value.contains("method")) { if (value.contains("params")) { - handler.onNotify(value["method"].get(), value["params"]); + accessor.onAnyJsonRPC(MessageHandler::MsgType::Response, value["params"], value["method"].get()); + accessor.onNotify(value["method"].get(), value["params"]); } } } } catch (std::exception &e) { - - printf("error -> %s\n", e.what()); + //std::cout< Date: Thu, 25 Jul 2024 16:12:27 +0800 Subject: [PATCH 6/9] =?UTF-8?q?-=20=E6=B7=BB=E5=8A=A0LspCore=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4=EF=BC=8C=E6=A8=A1=E5=9D=97=E5=8C=96?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E5=A4=B4=E6=96=87=E4=BB=B6=EF=BC=9B=20-=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9protocol=E6=96=87=E4=BB=B6=E4=B8=AD=E7=9A=84J?= =?UTF-8?q?son=E5=BA=8F=E5=88=97=E5=8C=96=EF=BC=8C=E5=B0=86=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E5=92=8C=E5=AE=9A=E4=B9=89=E5=88=86=E7=A6=BB=EF=BC=8C?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=AC=AC=E4=B8=89=E6=96=B9=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E6=97=B6=E5=8F=AF=E8=83=BD=E9=80=A0=E6=88=90=E7=9A=84=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E9=97=AE=E9=A2=98=EF=BC=8C=E5=9C=A8Qt=E4=B8=8A?= =?UTF-8?q?=E7=9A=84=E7=BC=96=E8=AF=91=E5=B7=B2=E9=80=9A=E8=BF=87=EF=BC=9B?= =?UTF-8?q?=20-=20=E6=9B=B4=E6=94=B9=E4=B8=80=E4=BA=9B=E7=B1=BB=E7=9A=84?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lsp/include/lsp/client.h | 150 +------------- lsp/include/lsp/iolayer.h | 46 +++++ lsp/include/lsp/protocol.h | 383 +++++++++++++++--------------------- lsp/include/lsp/transport.h | 28 ++- lsp/include/lsp/uri.h | 93 +++++---- lsp/src/iolayer.cpp | 148 ++++++++++++++ lsp/src/protocol.cpp | 156 +++++++++++++++ lsp/src/transport.cpp | 2 + 8 files changed, 593 insertions(+), 413 deletions(-) create mode 100644 lsp/include/lsp/iolayer.h create mode 100644 lsp/src/iolayer.cpp create mode 100644 lsp/src/protocol.cpp create mode 100644 lsp/src/transport.cpp diff --git a/lsp/include/lsp/client.h b/lsp/include/lsp/client.h index a9434d7..38f1d0a 100644 --- a/lsp/include/lsp/client.h +++ b/lsp/include/lsp/client.h @@ -4,14 +4,16 @@ #ifndef LSP_CLIENT_H #define LSP_CLIENT_H -#include "transport.h" -#include "protocol.h" -#include "windows.h" + +#include +#include + +namespace LspCore { class LanguageClient : public JsonTransport { public: LanguageClient(MapMessageHandler& msgHandler, JsonIOLayer& jsonIO) : JsonTransport(msgHandler, jsonIO){} - virtual ~LanguageClient() = default; + virtual ~LanguageClient(){} public: RequestID Initialize(option rootUri = {}) { InitializeParams params; @@ -214,148 +216,10 @@ class LanguageClient : public JsonTransport { notify(method, params); } }; -class PipJsonIO : public JsonIOLayer{ -private: - HANDLE fReadIn = nullptr, fWriteIn = nullptr; - HANDLE fReadOut = nullptr, fWriteOut = nullptr; - PROCESS_INFORMATION fProcess = {nullptr}; - - bool m_isClosed{false}; - std::mutex m_closeLock; -public: - explicit PipJsonIO(const char *program, const char *arguments = "") - :JsonIOLayer() - { - SECURITY_ATTRIBUTES sa = {0}; - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = true; - if (!CreatePipe(&fReadIn, &fWriteIn, &sa, 1024 * 1024)) { - printf("Create In Pipe error\n"); - } - if (!CreatePipe(&fReadOut, &fWriteOut, &sa, 1024 * 1024)) { - printf("Create Out Pipe error\n"); - } - // 设置非阻塞 - DWORD mode = PIPE_NOWAIT; - if (!SetNamedPipeHandleState(fReadOut, &mode, NULL, NULL)) { - std::cerr << "Failed to set non-blocking mode for readout pipe." << std::endl; - } - - STARTUPINFO si = {0}; - si.cb = sizeof(si); - si.hStdInput = fReadIn; - si.hStdOutput = fWriteOut; - si.dwFlags = STARTF_USESTDHANDLES; - if (!CreateProcessA(program, (char *) arguments, 0, 0, TRUE, - CREATE_NO_WINDOW, 0, 0, (LPSTARTUPINFOA) &si, &fProcess)) { - printf("Create Process error\n"); - } - - //m_exec.start(program, arguments); - //m_exec.set_wait_timeout(exec_stream_t::s_child, INFINITE); - } - virtual ~PipJsonIO(){ - this->PipJsonIO::close(); - } +} - void close() override - { - std::lock_guard _guard(m_closeLock); - if(m_isClosed){ - return; - } - CloseHandle(fReadIn); - CloseHandle(fWriteIn); - CloseHandle(fReadOut); - CloseHandle(fWriteOut); - if (!TerminateProcess(fProcess.hProcess, 0)) { - printf("teminate process error!\n"); - } - if (!TerminateThread(fProcess.hThread, 0)) { - printf("teminate thread error!\n"); - } - CloseHandle(fProcess.hThread); - CloseHandle(fProcess.hProcess); - - m_isClosed = true; - } - - bool isClosed() override - { - std::lock_guard _guard(m_closeLock); - return m_isClosed; - } - - void SkipLine() { - char read; - DWORD hasRead; - while (ReadFile(fReadOut, &read, 1, &hasRead, NULL)) { - if (read == '\n') { - break; - } - } - } - int ReadLength() { - // "Content-Length: " - char szReadBuffer[255]; - DWORD hasRead; - int length = 0; - while (ReadFile(fReadOut, &szReadBuffer[length], 1, &hasRead, NULL)) { - if (szReadBuffer[length] == '\n') { - break; - } - length++; - } - return atoi(szReadBuffer + 16); - } - void Read(int length, std::string &out) { - int readSize = 0; - DWORD hasRead; - out.resize(length); - while (ReadFile(fReadOut, &out[readSize], length, &hasRead, NULL)) { - readSize += hasRead; - if (readSize >= length) { - break; - } - } - } - bool Write(std::string &in) { - DWORD hasWritten; - int writeSize = 0; - int totalSize = in.length(); - while (WriteFile(fWriteIn, &in[writeSize], totalSize, &hasWritten, 0)) { - writeSize += hasWritten; - if (writeSize >= totalSize) { - break; - } - } - return true; - } - bool readJson(json &json) override { - json.clear(); - int length = ReadLength(); - SkipLine(); - std::string read; - Read(length, read); - if(read.empty()){ - return false; - } - try { - json = json::parse(read); - } catch (std::exception &e) { - return false; - //printf("read error -> %s\nread -> %s\n ", e.what(), read.c_str()); - } - return true; - } - bool writeJson(json &json) override { - std::string content = json.dump(-1, ' ', false, nlohmann::detail::error_handler_t::ignore); - std::string header = "Content-Length: " + std::to_string(content.length()) + "\r\n\r\n" + content; - return Write(header); - } -}; #endif //LSP_CLIENT_H diff --git a/lsp/include/lsp/iolayer.h b/lsp/include/lsp/iolayer.h new file mode 100644 index 0000000..3a1ed1a --- /dev/null +++ b/lsp/include/lsp/iolayer.h @@ -0,0 +1,46 @@ +#ifndef IOLAYER_H +#define IOLAYER_H + +#include +#include +#include +#include "windows.h" + +namespace LspCore { +using value = json; + +class JsonIOLayer{ +public: + virtual ~JsonIOLayer(){} + + virtual bool readJson(value &) = 0; + virtual bool writeJson(value &) = 0; + virtual void close() = 0; + virtual bool isClosed() = 0; +}; + +class PipJsonIO : public JsonIOLayer{ +private: + HANDLE fReadIn = nullptr, fWriteIn = nullptr; + HANDLE fReadOut = nullptr, fWriteOut = nullptr; + PROCESS_INFORMATION fProcess = {nullptr}; + + bool m_isClosed{false}; + std::mutex m_closeLock; +public: + explicit PipJsonIO(const char *program, const char *arguments = ""); + virtual ~PipJsonIO(); +private: + void SkipLine(); + int ReadLength(); + void Read(int length, std::string &out); + bool Write(std::string &in); +public: + void close() override; + bool isClosed() override; + bool readJson(json &json) override; + bool writeJson(json &json) override; +}; + +} +#endif // IOLAYER_H diff --git a/lsp/include/lsp/protocol.h b/lsp/include/lsp/protocol.h index 86b129a..3e8c665 100644 --- a/lsp/include/lsp/protocol.h +++ b/lsp/include/lsp/protocol.h @@ -16,27 +16,44 @@ //4. Language Feartures struct (may content others) //5. workspace..(None) + #ifndef LSP_PROTOCOL_H #define LSP_PROTOCOL_H + #include #include #include #include #include -#include "uri.h" +#include + +namespace LspCore { + +using json = nlohmann::json; #define MAP_JSON(...) {j = {__VA_ARGS__};} #define MAP_KEY(KEY) {#KEY, value.KEY} #define MAP_TO(KEY, TO) {KEY, value.TO} #define MAP_KV(K, ...) {K, {__VA_ARGS__}} #define FROM_KEY(KEY) if (j.contains(#KEY)) j.at(#KEY).get_to(value.KEY); -#define JSON_SERIALIZE(Type, TO, FROM) \ - namespace nlohmann { \ - template <> struct adl_serializer { \ - static void to_json(json& j, const Type& value) TO \ - static void from_json(const json& j, Type& value) FROM \ - }; \ - } +// #define JSON_SERIALIZE(Type, TO, FROM) \ +// namespace nlohmann{\ +// template<>\ +// struct adl_serializer { \ +// static void to_json(json& j, const Type& value) TO \ +// static void from_json(const json& j, Type& value) FROM \ +// };\ +// } + +#define JSON_Convert_Declaration(Type) \ +void to_json(json& j, const Type& value);\ +void from_json(const json& j, Type& value);\ + +#define JSON_Convert_Definition(Type, TO, FROM) \ +void to_json(json& j, const Type& value) TO \ +void from_json(const json& j, Type& value) FROM \ + + const static std::string METHOD_DidOpen = "textDocument/didOpen"; const static std::string METHOD_DidClose = "textDocument/didClose"; @@ -51,6 +68,8 @@ const static std::string METHOD_SignatureHelp = "textDocument/signatureHelp"; const static std::string METHOD_Completion = "textDocument/completion"; using TextType = string_ref; +using DocumentUri = string_ref; + //TODO: 支持多个类型, string和int using ProgressToken = string_ref; @@ -72,19 +91,18 @@ class LSPError { ErrorCode Code; static char ID; LSPError(std::string Message, ErrorCode Code) - : Message(std::move(Message)), Code(Code) {} -}; -JSON_SERIALIZE(URIForFile, {j = value.file;}, {value.file = j.get();});\ - -//======================================================================================= -//=================================== 1.basic JSON struct================================ -//======================================================================================= - -enum class TraceLevel { - Off = 0, - Messages = 1, - Verbose = 2, -}; + : Message(std::move(Message)), Code(Code) {} +}; +JSON_Convert_Declaration(URIForFile) + //======================================================================================= + //=================================== 1.basic JSON struct================================ + //======================================================================================= + + enum class TraceLevel { + Off = 0, + Messages = 1, + Verbose = 2, + }; enum class TextDocumentSyncKind { /// Documents should not be synced at all. None = 0, @@ -176,26 +194,26 @@ enum class FailureHandlingKind { TextOnlyTransactional }; NLOHMANN_JSON_SERIALIZE_ENUM(OffsetEncoding, { - {OffsetEncoding::UnsupportedEncoding, "unspported"}, - {OffsetEncoding::UTF8, "utf-8"}, - {OffsetEncoding::UTF16, "utf-16"}, - {OffsetEncoding::UTF32, "utf-32"}, - }) - NLOHMANN_JSON_SERIALIZE_ENUM(MarkupKind, { - {MarkupKind::PlainText, "plaintext"}, - {MarkupKind::Markdown, "markdown"}, - }) - NLOHMANN_JSON_SERIALIZE_ENUM(ResourceOperationKind, { - {ResourceOperationKind::Create, "create"}, - {ResourceOperationKind::Rename, "rename"}, - {ResourceOperationKind::Delete, "dename"} - }) - NLOHMANN_JSON_SERIALIZE_ENUM(FailureHandlingKind, { - {FailureHandlingKind::Abort, "abort"}, - {FailureHandlingKind::Transactional, "transactional"}, - {FailureHandlingKind::Undo, "undo"}, - {FailureHandlingKind::TextOnlyTransactional, "textOnlyTransactional"} - }) + {OffsetEncoding::UnsupportedEncoding, "unspported"}, + {OffsetEncoding::UTF8, "utf-8"}, + {OffsetEncoding::UTF16, "utf-16"}, + {OffsetEncoding::UTF32, "utf-32"}, + }) +NLOHMANN_JSON_SERIALIZE_ENUM(MarkupKind, { + {MarkupKind::PlainText, "plaintext"}, + {MarkupKind::Markdown, "markdown"}, + }) +NLOHMANN_JSON_SERIALIZE_ENUM(ResourceOperationKind, { + {ResourceOperationKind::Create, "create"}, + {ResourceOperationKind::Rename, "rename"}, + {ResourceOperationKind::Delete, "dename"} + }) +NLOHMANN_JSON_SERIALIZE_ENUM(FailureHandlingKind, { + {FailureHandlingKind::Abort, "abort"}, + {FailureHandlingKind::Transactional, "transactional"}, + {FailureHandlingKind::Undo, "undo"}, + {FailureHandlingKind::TextOnlyTransactional, "textOnlyTransactional"} + }) struct Position { /// Line position in a document (zero-based). @@ -220,7 +238,7 @@ struct Position { std::tie(RHS.line, RHS.character); } }; -JSON_SERIALIZE(Position, MAP_JSON(MAP_KEY(line), MAP_KEY(character)), {FROM_KEY(line);FROM_KEY(character)}); +JSON_Convert_Declaration(Position) struct Range { /// The range's start position. @@ -243,7 +261,7 @@ struct Range { return start <= Rng.start && Rng.end <= end; } }; -JSON_SERIALIZE(Range, MAP_JSON(MAP_KEY(start), MAP_KEY(end)), {FROM_KEY(start);FROM_KEY(end)}); +JSON_Convert_Declaration(Range) struct TextDocumentItem { /// The text document's URI. @@ -258,19 +276,18 @@ struct TextDocumentItem { /// The content of the opened text document. string_ref text; }; -JSON_SERIALIZE(TextDocumentItem, MAP_JSON( - MAP_KEY(uri), MAP_KEY(languageId), MAP_KEY(version), MAP_KEY(text)), {}); +JSON_Convert_Declaration(TextDocumentItem) struct TextDocumentIdentifier { /// The text document's URI. DocumentUri uri; }; -JSON_SERIALIZE(TextDocumentIdentifier, MAP_JSON(MAP_KEY(uri)), {}); +JSON_Convert_Declaration(TextDocumentIdentifier) struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier { int version = 0; }; -JSON_SERIALIZE(VersionedTextDocumentIdentifier, MAP_JSON(MAP_KEY(uri), MAP_KEY(version)), {}); +JSON_Convert_Declaration(VersionedTextDocumentIdentifier) struct TextDocumentPositionParams { /// The text document. @@ -279,7 +296,7 @@ struct TextDocumentPositionParams { /// The position inside the text document. Position position; }; -JSON_SERIALIZE(TextDocumentPositionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position)), {}); +JSON_Convert_Declaration(TextDocumentPositionParams) struct TextEdit { /// The range of the text document to be manipulated. To insert @@ -290,7 +307,7 @@ struct TextEdit { /// empty string. std::string newText; }; -JSON_SERIALIZE(TextEdit, MAP_JSON(MAP_KEY(range), MAP_KEY(newText)), { FROM_KEY(range); FROM_KEY(newText); }); +JSON_Convert_Declaration(TextEdit) struct Location { /// The text document's URI. @@ -307,7 +324,7 @@ struct Location { return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range); } }; -JSON_SERIALIZE(Location, MAP_JSON(MAP_KEY(uri), MAP_KEY(range)), { FROM_KEY(uri); FROM_KEY(range); }); +JSON_Convert_Declaration(Location) struct DiagnosticRelatedInformation { /// The location of this related diagnostic information. @@ -315,7 +332,8 @@ struct DiagnosticRelatedInformation { /// The message of this related diagnostic information. std::string message; }; -JSON_SERIALIZE(DiagnosticRelatedInformation, MAP_JSON(MAP_KEY(location), MAP_KEY(message)), { FROM_KEY(location); FROM_KEY(message); }); +JSON_Convert_Declaration(DiagnosticRelatedInformation) + struct CodeAction; struct Diagnostic { @@ -351,14 +369,13 @@ struct Diagnostic { /// (These actions can also be obtained using textDocument/codeAction). option> codeActions; }; -JSON_SERIALIZE(Diagnostic, {/*NOT REQUIRED*/ }, { FROM_KEY(range); FROM_KEY(code); FROM_KEY(source); FROM_KEY(message); - FROM_KEY(relatedInformation); FROM_KEY(category); FROM_KEY(codeActions); }); +JSON_Convert_Declaration(Diagnostic) struct MarkupContent { MarkupKind kind = MarkupKind::PlainText; std::string value; }; -JSON_SERIALIZE(MarkupContent, {}, { FROM_KEY(kind); FROM_KEY(value) }); +JSON_Convert_Declaration(MarkupContent) struct WorkDoneProgressParams { /** @@ -366,7 +383,7 @@ struct WorkDoneProgressParams { */ option workDoneToken; }; -JSON_SERIALIZE(WorkDoneProgressParams, MAP_JSON(MAP_KEY(workDoneToken)), {}); +JSON_Convert_Declaration(WorkDoneProgressParams) struct PartialResultParams { /** @@ -375,7 +392,7 @@ struct PartialResultParams { */ option partialResultToken; }; -JSON_SERIALIZE(PartialResultParams, MAP_JSON(MAP_KEY(partialResultToken)), {}); +JSON_Convert_Declaration(PartialResultParams) //======================================================================================= @@ -439,33 +456,7 @@ struct ClientCapabilities { } } }; -JSON_SERIALIZE(ClientCapabilities,MAP_JSON( - MAP_KV("textDocument", - MAP_KV("publishDiagnostics", // PublishDiagnosticsClientCapabilities - MAP_TO("categorySupport", DiagnosticCategory), - MAP_TO("codeActionsInline", DiagnosticFixes), - MAP_TO("relatedInformation", DiagnosticRelatedInformation), - ), - MAP_KV("completion", // CompletionClientCapabilities - MAP_KV("completionItem", - MAP_TO("snippetSupport", CompletionSnippets), - MAP_TO("deprecatedSupport", CompletionDeprecated)), - MAP_KV("completionItemKind", MAP_TO("valueSet", CompletionItemKinds)), - MAP_TO("editsNearCursor", CompletionFixes) - ), - MAP_KV("codeAction", MAP_TO("codeActionLiteralSupport", CodeActionStructure)), - MAP_KV("documentSymbol", MAP_TO("hierarchicalDocumentSymbolSupport", HierarchicalDocumentSymbol)), - MAP_KV("hover", //HoverClientCapabilities - MAP_TO("contentFormat", HoverContentFormat)), - MAP_KV("signatureHelp", MAP_KV("signatureInformation", MAP_KV("parameterInformation", MAP_TO("labelOffsetSupport", OffsetsInSignatureHelp))))), - MAP_KV("workspace", // WorkspaceEditClientCapabilities - MAP_KV("symbol", // WorkspaceSymbolClientCapabilities - MAP_KV("symbolKind", - MAP_TO("valueSet", WorkspaceSymbolKinds))), - MAP_TO("applyEdit", ApplyEdit), - MAP_KV("workspaceEdit", // WorkspaceEditClientCapabilities - MAP_TO("documentChanges", DocumentChanges))), - MAP_TO("offsetEncoding", offsetEncoding)), {}); +JSON_Convert_Declaration(ClientCapabilities) struct ServerCapabilities { json capabilities; @@ -488,28 +479,20 @@ struct ServerCapabilities { return false; } }; -JSON_SERIALIZE(ServerCapabilities, {}, { - value.capabilities = j; - FROM_KEY(textDocumentSync); - j["documentOnTypeFormattingProvider"]["firstTriggerCharacter"].get_to(value.formattingTrigger); - j["completionProvider"]["resolveProvider"].get_to(value.resolveProvider); - j["completionProvider"]["triggerCharacters"].get_to(value.completionTrigger); - j["executeCommandProvider"]["commands"].get_to(value.executeCommands); -}); +JSON_Convert_Declaration(ServerCapabilities) struct ClangdCompileCommand { TextType workingDirectory; std::vector compilationCommand; }; -JSON_SERIALIZE(ClangdCompileCommand,MAP_JSON( - MAP_KEY(workingDirectory), MAP_KEY(compilationCommand)), {}); +JSON_Convert_Declaration(ClangdCompileCommand) struct ConfigurationSettings { // Changes to the in-memory compilation database. // The key of the map is a file name. std::map compilationDatabaseChanges; }; -JSON_SERIALIZE(ConfigurationSettings, MAP_JSON(MAP_KEY(compilationDatabaseChanges)), {}); +JSON_Convert_Declaration(ConfigurationSettings) struct InitializationOptions { // What we can change throught the didChangeConfiguration request, we can @@ -525,11 +508,7 @@ struct InitializationOptions { /// Clients supports show file status for textDocument/clangd.fileStatus. bool clangdFileStatus = false; }; -JSON_SERIALIZE(InitializationOptions, MAP_JSON( - MAP_KEY(configSettings), - MAP_KEY(compilationDatabasePath), - MAP_KEY(fallbackFlags), - MAP_KEY(clangdFileStatus)), {}); +JSON_Convert_Declaration(InitializationOptions) struct InitializeParams { unsigned processId = 0; @@ -538,22 +517,17 @@ struct InitializeParams { option rootPath; InitializationOptions initializationOptions; }; -JSON_SERIALIZE(InitializeParams, MAP_JSON( - MAP_KEY(processId), - MAP_KEY(capabilities), - MAP_KEY(rootUri), - MAP_KEY(initializationOptions), - MAP_KEY(rootPath)), {}); +JSON_Convert_Declaration(InitializeParams) enum class MessageType { /// An error message. - Error = 1, + Error = 1, /// A warning message. - Warning = 2, + Warning = 2, /// An information message. - Info = 3, + Info = 3, /// A log message. - Log = 4, + Log = 4, }; struct ShowMessageParams { /// The message type. @@ -561,7 +535,7 @@ struct ShowMessageParams { /// The actual message. std::string message; }; -JSON_SERIALIZE(ShowMessageParams, {}, {FROM_KEY(type); FROM_KEY(message)}); +JSON_Convert_Declaration(ShowMessageParams) struct Registration { /** @@ -574,17 +548,17 @@ struct Registration { */ TextType method; }; -JSON_SERIALIZE(Registration, MAP_JSON(MAP_KEY(id), MAP_KEY(method)), {}); +JSON_Convert_Declaration(Registration) struct RegistrationParams { std::vector registrations; }; -JSON_SERIALIZE(RegistrationParams, MAP_JSON(MAP_KEY(registrations)), {}); +JSON_Convert_Declaration(RegistrationParams) struct UnregistrationParams { std::vector unregisterations; }; -JSON_SERIALIZE(UnregistrationParams, MAP_JSON(MAP_KEY(unregisterations)), {}); +JSON_Convert_Declaration(UnregistrationParams) @@ -594,16 +568,16 @@ JSON_SERIALIZE(UnregistrationParams, MAP_JSON(MAP_KEY(unregisterations)), {}); struct DidOpenTextDocumentParams { -/// The document that was opened. + /// The document that was opened. TextDocumentItem textDocument; }; -JSON_SERIALIZE(DidOpenTextDocumentParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Declaration(DidOpenTextDocumentParams) struct DidCloseTextDocumentParams { /// The document that was closed. TextDocumentIdentifier textDocument; }; -JSON_SERIALIZE(DidCloseTextDocumentParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Declaration(DidCloseTextDocumentParams) struct TextDocumentContentChangeEvent { /// The range of the document that changed. @@ -614,7 +588,7 @@ struct TextDocumentContentChangeEvent { /// The new text of the range/document. std::string text; }; -JSON_SERIALIZE(TextDocumentContentChangeEvent, MAP_JSON(MAP_KEY(range), MAP_KEY(rangeLength), MAP_KEY(text)), {}); +JSON_Convert_Declaration(TextDocumentContentChangeEvent) struct DidChangeTextDocumentParams { /// The document that did change. The version number points @@ -631,15 +605,15 @@ struct DidChangeTextDocumentParams { /// This is a clangd extension. option wantDiagnostics; }; -JSON_SERIALIZE(DidChangeTextDocumentParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(contentChanges), MAP_KEY(wantDiagnostics)), {}); +JSON_Convert_Declaration(DidChangeTextDocumentParams) enum class FileChangeType { /// The file got created. - Created = 1, + Created = 1, /// The file got changed. - Changed = 2, + Changed = 2, /// The file got deleted. - Deleted = 3 + Deleted = 3 }; struct FileEvent { /// The file's URI. @@ -647,18 +621,18 @@ struct FileEvent { /// The change type. FileChangeType type = FileChangeType::Created; }; -JSON_SERIALIZE(FileEvent, MAP_JSON(MAP_KEY(uri), MAP_KEY(type)), {}); +JSON_Convert_Declaration(FileEvent) struct DidChangeWatchedFilesParams { /// The actual file events. std::vector changes; }; -JSON_SERIALIZE(DidChangeWatchedFilesParams, MAP_JSON(MAP_KEY(changes)), {}); +JSON_Convert_Declaration(DidChangeWatchedFilesParams) struct DidChangeConfigurationParams { ConfigurationSettings settings; }; -JSON_SERIALIZE(DidChangeConfigurationParams, MAP_JSON(MAP_KEY(settings)), {}); +JSON_Convert_Declaration(DidChangeConfigurationParams) struct DocumentRangeFormattingParams { /// The document to format. @@ -667,7 +641,7 @@ struct DocumentRangeFormattingParams { /// The range to format Range range; }; -JSON_SERIALIZE(DocumentRangeFormattingParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(range)), {}); +JSON_Convert_Declaration(DocumentRangeFormattingParams) struct DocumentOnTypeFormattingParams { /// The document to format. @@ -679,7 +653,7 @@ struct DocumentOnTypeFormattingParams { /// The character that has been typed. TextType ch; }; -JSON_SERIALIZE(DocumentOnTypeFormattingParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(ch)), {}); +JSON_Convert_Declaration(DocumentOnTypeFormattingParams) //======================================================================================= @@ -687,32 +661,37 @@ JSON_SERIALIZE(DocumentOnTypeFormattingParams, MAP_JSON(MAP_KEY(textDocument), M //======================================================================================= struct DeclarationParams : public TextDocumentPositionParams, - WorkDoneProgressParams, PartialResultParams { + WorkDoneProgressParams, PartialResultParams { }; -JSON_SERIALIZE(DeclarationParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Declaration(DeclarationParams) + struct DefinitionParams : public TextDocumentPositionParams, - WorkDoneProgressParams, PartialResultParams { + WorkDoneProgressParams, PartialResultParams { }; -JSON_SERIALIZE(DefinitionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Declaration(DefinitionParams) + struct TypeDefinitionParams : public TextDocumentPositionParams, - WorkDoneProgressParams, PartialResultParams { + WorkDoneProgressParams, PartialResultParams { }; -JSON_SERIALIZE(TypeDefinitionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Declaration(TypeDefinitionParams) + struct ImplementationParams : public TextDocumentPositionParams, - WorkDoneProgressParams, PartialResultParams { + WorkDoneProgressParams, PartialResultParams { }; -JSON_SERIALIZE(ImplementationParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Declaration(ImplementationParams) + struct ReferenceParams : public TextDocumentPositionParams, -WorkDoneProgressParams, PartialResultParams { + WorkDoneProgressParams, PartialResultParams { // For now, no options like context.includeDeclaration are supported. }; -JSON_SERIALIZE(ReferenceParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)),{}); +JSON_Convert_Declaration(ReferenceParams) struct HoverParam: public TextDocumentPositionParams, WorkDoneProgressParams { }; -JSON_SERIALIZE(HoverParam, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken)), {}); +JSON_Convert_Declaration(HoverParam) + struct Hover { /// The hover's content MarkupContent contents; @@ -721,13 +700,13 @@ struct Hover { /// that is used to visualize a hover, e.g. by changing the background color. option range; }; -JSON_SERIALIZE(Hover, {}, { FROM_KEY(contents); FROM_KEY(range) }); +JSON_Convert_Declaration(Hover) struct FoldingRangeParams { /// The document to format. TextDocumentIdentifier textDocument; }; -JSON_SERIALIZE(FoldingRangeParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Declaration(FoldingRangeParams) enum class FoldingRangeKind { Comment, @@ -735,10 +714,10 @@ enum class FoldingRangeKind { Region, }; NLOHMANN_JSON_SERIALIZE_ENUM(FoldingRangeKind, { - {FoldingRangeKind::Comment, "comment"}, - {FoldingRangeKind::Imports, "imports"}, - {FoldingRangeKind::Region, "region"} -}) + {FoldingRangeKind::Comment, "comment"}, + {FoldingRangeKind::Imports, "imports"}, + {FoldingRangeKind::Region, "region"} + }) struct FoldingRange { /** @@ -762,32 +741,20 @@ struct FoldingRange { FoldingRangeKind kind; }; -JSON_SERIALIZE(FoldingRange, {}, { - FROM_KEY(startLine); - FROM_KEY(startCharacter); - FROM_KEY(endLine); - FROM_KEY(endCharacter); - FROM_KEY(kind); -}); +JSON_Convert_Declaration(FoldingRange) struct SelectionRangeParams { /// The document to format. TextDocumentIdentifier textDocument; std::vector positions; }; -JSON_SERIALIZE(SelectionRangeParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(positions)), {}); +JSON_Convert_Declaration(SelectionRangeParams) struct SelectionRange { Range range; std::unique_ptr parent; }; -JSON_SERIALIZE(SelectionRange, {}, { - FROM_KEY(range); - if (j.contains("parent")) { - value.parent = std::make_unique(); - j.at("parent").get_to(*value.parent); - } -}); +JSON_Convert_Declaration(SelectionRange) struct SemanticTokensParams : public WorkDoneProgressParams, PartialResultParams { /** @@ -795,19 +762,19 @@ struct SemanticTokensParams : public WorkDoneProgressParams, PartialResultParams */ TextDocumentIdentifier textDocument; }; -JSON_SERIALIZE(SemanticTokensParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Declaration(SemanticTokensParams) struct DocumentFormattingParams { /// The document to format. TextDocumentIdentifier textDocument; }; -JSON_SERIALIZE(DocumentFormattingParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Declaration(DocumentFormattingParams) struct DocumentSymbolParams { // The text document to find symbols in. TextDocumentIdentifier textDocument; }; -JSON_SERIALIZE(DocumentSymbolParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Declaration(DocumentSymbolParams) struct PublishDiagnosticsParams { /** @@ -815,17 +782,17 @@ struct PublishDiagnosticsParams { */ std::string uri; /** - * An array of diagnostic information items. - */ + * An array of diagnostic information items. + */ std::vector diagnostics; }; -JSON_SERIALIZE(PublishDiagnosticsParams, {}, {FROM_KEY(uri);FROM_KEY(diagnostics);}); +JSON_Convert_Declaration(PublishDiagnosticsParams) struct CodeActionContext { /// An array of diagnostics. std::vector diagnostics; }; -JSON_SERIALIZE(CodeActionContext, MAP_JSON(MAP_KEY(diagnostics)), {}); +JSON_Convert_Declaration(CodeActionContext) struct CodeActionParams { /// The document in which the command was invoked. @@ -837,7 +804,7 @@ struct CodeActionParams { /// Context carrying additional information. CodeActionContext context; }; -JSON_SERIALIZE(CodeActionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(range), MAP_KEY(context)), {}); +JSON_Convert_Declaration(CodeActionParams) // 过时了,不建议使用 struct WorkspaceEdit { @@ -847,7 +814,8 @@ struct WorkspaceEdit { /// Note: "documentChanges" is not currently used because currently there is /// no support for versioned edits. }; -JSON_SERIALIZE(WorkspaceEdit, MAP_JSON(MAP_KEY(changes)), {FROM_KEY(changes);}); +JSON_Convert_Declaration(WorkspaceEdit) + // 过时了,不建议使用 struct TweakArgs { /// A file provided by the client on a textDocument/codeAction request. @@ -857,7 +825,8 @@ struct TweakArgs { /// ID of the tweak that should be executed. Corresponds to Tweak::id(). std::string tweakID; }; -JSON_SERIALIZE(TweakArgs, MAP_JSON(MAP_KEY(file), MAP_KEY(selection), MAP_KEY(tweakID)), {FROM_KEY(file);FROM_KEY(selection);FROM_KEY(tweakID);}); +JSON_Convert_Declaration(TweakArgs) + // 过时了,不建议使用 struct ExecuteCommandParams { std::string command; @@ -865,13 +834,13 @@ struct ExecuteCommandParams { option workspaceEdit; option tweakArgs; }; -JSON_SERIALIZE(ExecuteCommandParams, MAP_JSON(MAP_KEY(command), MAP_KEY(workspaceEdit), MAP_KEY(tweakArgs)), {}); +JSON_Convert_Declaration(ExecuteCommandParams) + // 过时了,不建议使用 struct LspCommand : public ExecuteCommandParams { std::string title; }; -JSON_SERIALIZE(LspCommand, MAP_JSON(MAP_KEY(command), MAP_KEY(workspaceEdit), MAP_KEY(tweakArgs), MAP_KEY(title)), - {FROM_KEY(command);FROM_KEY(workspaceEdit);FROM_KEY(tweakArgs);FROM_KEY(title);}); + struct CodeAction { /// A short, human-readable, title for this code action. @@ -890,8 +859,7 @@ struct CodeAction { /// and a command, first the edit is executed and then the command. option command; }; -JSON_SERIALIZE(CodeAction, MAP_JSON(MAP_KEY(title), MAP_KEY(kind), MAP_KEY(diagnostics), MAP_KEY(edit), MAP_KEY(command)), - {FROM_KEY(title);FROM_KEY(kind);FROM_KEY(diagnostics);FROM_KEY(edit);FROM_KEY(command)}); +JSON_Convert_Declaration(CodeAction) struct SymbolInformation { /// The name of this symbol. @@ -903,7 +871,7 @@ struct SymbolInformation { /// The name of the symbol containing this symbol. std::string containerName; }; -JSON_SERIALIZE(SymbolInformation, MAP_JSON(MAP_KEY(name), MAP_KEY(kind), MAP_KEY(location), MAP_KEY(containerName)), {FROM_KEY(name);FROM_KEY(kind);FROM_KEY(location);FROM_KEY(containerName)}); +JSON_Convert_Declaration(SymbolInformation) struct SymbolDetails { TextType name; @@ -921,22 +889,22 @@ struct WorkspaceSymbolParams { /// A non-empty query string TextType query; }; -JSON_SERIALIZE(WorkspaceSymbolParams, MAP_JSON(MAP_KEY(query)), {}); +JSON_Convert_Declaration(WorkspaceSymbolParams) struct ApplyWorkspaceEditParams { WorkspaceEdit edit; }; -JSON_SERIALIZE(ApplyWorkspaceEditParams, MAP_JSON(MAP_KEY(edit)), {}); +JSON_Convert_Declaration(ApplyWorkspaceEditParams) enum class CompletionTriggerKind { /// Completion was triggered by typing an identifier (24x7 code /// complete), manual invocation (e.g Ctrl+Space) or via API. - Invoked = 1, + Invoked = 1, /// Completion was triggered by a trigger character specified by /// the `triggerCharacters` properties of the `CompletionRegistrationOptions`. - TriggerCharacter = 2, + TriggerCharacter = 2, /// Completion was re-triggered as the current completion list is incomplete. - TriggerTriggerForIncompleteCompletions = 3 + TriggerTriggerForIncompleteCompletions = 3 }; struct CompletionContext { /// How the completion was triggered. @@ -945,17 +913,17 @@ struct CompletionContext { /// Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` option triggerCharacter; }; -JSON_SERIALIZE(CompletionContext, MAP_JSON(MAP_KEY(triggerKind), MAP_KEY(triggerCharacter)), {}); +JSON_Convert_Declaration(CompletionContext) struct CompletionParams : TextDocumentPositionParams { option context; }; -JSON_SERIALIZE(CompletionParams, MAP_JSON(MAP_KEY(context), MAP_KEY(textDocument), MAP_KEY(position)), {}); +JSON_Convert_Declaration(CompletionContext) enum class InsertTextFormat { Missing = 0, /// The primary text to be inserted is treated as a plain string. - PlainText = 1, + PlainText = 1, /// The primary text to be inserted is treated as a snippet. /// /// A snippet can define tab stops and placeholders with `$1`, `$2` @@ -965,8 +933,8 @@ enum class InsertTextFormat { /// /// See also: /// https//github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md - Snippet = 2, -}; + Snippet = 2, + }; struct CompletionItem { /// The label of this completion item. By default also the text that is /// inserted when selecting this completion. @@ -1020,18 +988,7 @@ struct CompletionItem { // data?: any - A data entry field that is preserved on a completion item // between a completion and a completion resolve request. }; -JSON_SERIALIZE(CompletionItem, {}, { - FROM_KEY(label); - FROM_KEY(kind); - FROM_KEY(detail); - FROM_KEY(documentation); - FROM_KEY(sortText); - FROM_KEY(filterText); - FROM_KEY(insertText); - FROM_KEY(insertTextFormat); - FROM_KEY(textEdit); - FROM_KEY(additionalTextEdits); -}); +JSON_Convert_Declaration(CompletionItem) struct CompletionList { /// The list is not complete. Further typing should result in recomputing the @@ -1041,10 +998,7 @@ struct CompletionList { /// The completion items. std::vector items; }; -JSON_SERIALIZE(CompletionList, {}, { - FROM_KEY(isIncomplete); - FROM_KEY(items); -}); +JSON_Convert_Declaration(CompletionList) struct ParameterInformation { @@ -1060,11 +1014,8 @@ struct ParameterInformation { /// The documentation of this parameter. Optional. std::string documentation; }; -JSON_SERIALIZE(ParameterInformation, {}, { - FROM_KEY(labelString); - FROM_KEY(labelOffsets); - FROM_KEY(documentation); -}); +JSON_Convert_Declaration(ParameterInformation) + struct SignatureInformation { /// The label of this signature. Mandatory. @@ -1076,11 +1027,8 @@ struct SignatureInformation { /// The parameters of this signature. std::vector parameters; }; -JSON_SERIALIZE(SignatureInformation, {}, { - FROM_KEY(label); - FROM_KEY(documentation); - FROM_KEY(parameters); -}); +JSON_Convert_Declaration(SignatureInformation) + struct SignatureHelp { /// The resulting signatures. std::vector signatures; @@ -1095,11 +1043,7 @@ struct SignatureHelp { /// not currently serialized for the LSP. Position argListStart; }; -JSON_SERIALIZE(SignatureHelp, {}, { - FROM_KEY(signatures); - FROM_KEY(activeParameter); - FROM_KEY(argListStart); -}); +JSON_Convert_Declaration(SignatureHelp) struct RenameParams { /// The document that was opened. @@ -1111,7 +1055,7 @@ struct RenameParams { /// The new name of the symbol. std::string newName; }; -JSON_SERIALIZE(RenameParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(newName)), {}); +JSON_Convert_Declaration(RenameParams) enum class DocumentHighlightKind { Text = 1, Read = 2, Write = 3 }; @@ -1140,7 +1084,7 @@ struct TypeHierarchyParams : public TextDocumentPositionParams { /// The direction of the hierarchy levels to resolve. TypeHierarchyDirection direction = TypeHierarchyDirection::Parents; }; -JSON_SERIALIZE(TypeHierarchyParams, MAP_JSON(MAP_KEY(resolve), MAP_KEY(direction), MAP_KEY(textDocument), MAP_KEY(position)), {}); +JSON_Convert_Declaration(TypeHierarchyParams) struct TypeHierarchyItem { /// The human readable name of the hierarchy item. @@ -1186,10 +1130,6 @@ struct TypeHierarchyItem { /// so don't declare it. }; - - - - //struct FileStatus { // /// The text document's URI. // DocumentUri uri; @@ -1199,6 +1139,7 @@ struct TypeHierarchyItem { // // FIXME: add detail messages. //}; +} #endif //LSP_PROTOCOL_H diff --git a/lsp/include/lsp/transport.h b/lsp/include/lsp/transport.h index 40411e5..96dbec8 100644 --- a/lsp/include/lsp/transport.h +++ b/lsp/include/lsp/transport.h @@ -5,12 +5,15 @@ #ifndef LSP_TRANSPORT_H #define LSP_TRANSPORT_H -#include "uri.h" +#include +#include +#include #include #include -#include #include +namespace LspCore { + using value = json; using RequestID = std::string; @@ -145,7 +148,7 @@ class MapMessageHandler : public MessageHandler { class Transport { public: - virtual ~Transport(){}; + virtual ~Transport(){} virtual void notify(string_ref method, value ¶ms) = 0; virtual void request(string_ref method, value ¶ms, RequestID &id) = 0; @@ -154,16 +157,6 @@ class Transport { virtual void requestStopLoop() = 0; }; -class JsonIOLayer{ -public: - virtual ~JsonIOLayer(){}; - - virtual bool readJson(value &) = 0; - virtual bool writeJson(value &) = 0; - virtual void close() = 0; - virtual bool isClosed() = 0; -}; - class JsonTransport : public Transport { private: MapMessageHandler& m_msgHandler; @@ -174,7 +167,7 @@ class JsonTransport : public Transport { std::chrono::milliseconds runningFrequency = std::chrono::milliseconds(20); public: explicit JsonTransport(MapMessageHandler& msgHandler, JsonIOLayer& jsonIO) : m_msgHandler(msgHandler), m_jsonIO(jsonIO){} - virtual ~JsonTransport(); + virtual ~JsonTransport(){} int safeLoop() override{ while (!havingStopRequest){ @@ -201,8 +194,8 @@ class JsonTransport : public Transport { } } } - } catch (std::exception &e) { - //std::cout< #include #include -#include "json.hpp" +#include + +namespace LspCore { + using json = nlohmann::json; + class string_ref { public: static const size_t npos = ~size_t(0); @@ -77,26 +81,25 @@ class option { T &operator*() { return value(); } explicit operator bool() const { return fHas; } }; -namespace nlohmann { - template - struct adl_serializer> { - static void to_json(json& j, const option& opt) { - if (opt.has()) { - j = opt.value(); - } else { - j = nullptr; - } - } - static void from_json(const json& j, option& opt) { - if (j.is_null()) { - opt = option(); - } else { - opt = option(j.get()); - } - } - }; + +template +void to_json(json& j, const LspCore::option& opt) { + if (opt.has()) { + j = opt.value(); + } else { + j = nullptr; + } +} +template +void from_json(const json& j, LspCore::option& opt) { + if (j.is_null()) { + opt = LspCore::option(); + } else { + opt = LspCore::option(j.get()); + } } + inline uint8_t FromHex(const char digit) { if (digit >= '0' && digit <= '9') return digit - '0'; @@ -152,7 +155,6 @@ struct URIForFile { URIForFile() = default; inline std::string &str() { return file; } }; -using DocumentUri = string_ref; class URI { public: @@ -193,29 +195,29 @@ class URI { public: void parse(sview uri) { static const std::regex pattern{ - "^([a-zA-Z]+[\\w\\+\\-\\.]+)?(\\://)?" //< scheme - "(([^:@]+)(\\:([^@]+))?@)?" //< username && password - "([^/:?#]+)?(\\:(\\d+))?" //< hostname && port - "([^?#]+)" //< path - "(\\?([^#]*))?" //< query - "(#(.*))?$" //< fragment + "^([a-zA-Z]+[\\w\\+\\-\\.]+)?(\\://)?" //< scheme + "(([^:@]+)(\\:([^@]+))?@)?" //< username && password + "([^/:?#]+)?(\\:(\\d+))?" //< hostname && port + "([^?#]+)" //< path + "(\\?([^#]*))?" //< query + "(#(.*))?$" //< fragment }; static std::cmatch parts; m_uri = Decode(uri); if (std::regex_match(m_uri.c_str(), parts, pattern)) { m_path = sview(m_uri.c_str() + parts.position(10), parts.length(10)); m_scheme = parts.length(1) ? - sview(m_uri.c_str() + parts.position(1), parts.length(1)) : sview{}; + sview(m_uri.c_str() + parts.position(1), parts.length(1)) : sview{}; m_userinfo = parts.length(3) ? - sview(m_uri.data() + parts.position(3), parts.length(3)) : sview{}; + sview(m_uri.data() + parts.position(3), parts.length(3)) : sview{}; m_host = parts.length(7) ? - sview(m_uri.data() + parts.position(7), parts.length(7)) : sview{}; + sview(m_uri.data() + parts.position(7), parts.length(7)) : sview{}; m_port = parts.length(9) ? - sview(m_uri.data() + parts.position(9), parts.length(9)) : sview{}; + sview(m_uri.data() + parts.position(9), parts.length(9)) : sview{}; m_query = parts.length(11) ? - sview(m_uri.data() + parts.position(11), parts.length(11)) : sview{}; + sview(m_uri.data() + parts.position(11), parts.length(11)) : sview{}; m_fragment = parts.length(13) ? - sview(m_uri.data() + parts.position(13), parts.length(13)) : sview{}; + sview(m_uri.data() + parts.position(13), parts.length(13)) : sview{}; } } sview path() { return m_path; } @@ -226,7 +228,7 @@ class URI { sview query() { return m_query; } sview fragment() { return m_fragment; } sview query(sview key) { -/* + /* static const std::regex query_token_pattern {"[^?=&]+"}; auto query = query_.to_string(); auto it = std::sregex_iterator(query.cbegin(), query.cend(), query_token_pattern); @@ -265,4 +267,29 @@ class URI { }; +} + +// namespace nlohmann { + +// template +// struct adl_serializer> { +// static void to_json(json& j, const LspCore::option& opt) { +// if (opt.has()) { +// j = opt.value(); +// } else { +// j = nullptr; +// } +// } +// static void from_json(const json& j, LspCore::option& opt) { +// if (j.is_null()) { +// opt = LspCore::option(); +// } else { +// opt = LspCore::option(j.get()); +// } +// } +// }; + +// } + + #endif //LSP_URI_H diff --git a/lsp/src/iolayer.cpp b/lsp/src/iolayer.cpp new file mode 100644 index 0000000..3b1678f --- /dev/null +++ b/lsp/src/iolayer.cpp @@ -0,0 +1,148 @@ +#include +#include + +namespace LspCore { + +PipJsonIO::PipJsonIO(const char *program, const char *arguments) + :JsonIOLayer() +{ + SECURITY_ATTRIBUTES sa = {0}; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; + if (!CreatePipe(&fReadIn, &fWriteIn, &sa, 1024 * 1024)) { + printf("Create In Pipe error\n"); + } + if (!CreatePipe(&fReadOut, &fWriteOut, &sa, 1024 * 1024)) { + printf("Create Out Pipe error\n"); + } + + // 设置非阻塞 + DWORD mode = PIPE_NOWAIT; + if (!SetNamedPipeHandleState(fReadOut, &mode, NULL, NULL)) { + + std::cerr << "Failed to set non-blocking mode for readout pipe." << std::endl; + } + + STARTUPINFO si = {0}; + si.cb = sizeof(si); + si.hStdInput = fReadIn; + si.hStdOutput = fWriteOut; + si.dwFlags = STARTF_USESTDHANDLES; + if (!CreateProcessA(program, (char *) arguments, 0, 0, TRUE, + CREATE_NO_WINDOW, 0, 0, (LPSTARTUPINFOA) &si, &fProcess)) { + printf("Create Process error\n"); + } + + //m_exec.start(program, arguments); + //m_exec.set_wait_timeout(exec_stream_t::s_child, INFINITE); +} + +PipJsonIO::~PipJsonIO(){ + this->PipJsonIO::close(); +} + + +void PipJsonIO::SkipLine() { + char read; + DWORD hasRead; + while (ReadFile(fReadOut, &read, 1, &hasRead, NULL)) { + if (read == '\n') { + break; + } + } +} + +int PipJsonIO::ReadLength() { + // "Content-Length: " + char szReadBuffer[255]; + DWORD hasRead; + int length = 0; + while (ReadFile(fReadOut, &szReadBuffer[length], 1, &hasRead, NULL)) { + if (szReadBuffer[length] == '\n') { + break; + } + length++; + } + return atoi(szReadBuffer + 16); +} + +void PipJsonIO::Read(int length, std::string &out) +{ + int readSize = 0; + DWORD hasRead; + out.resize(length); + while (ReadFile(fReadOut, &out[readSize], length, &hasRead, NULL)) { + readSize += hasRead; + if (readSize >= length) { + break; + } + } +} +bool PipJsonIO::Write(std::string &in) { + DWORD hasWritten; + int writeSize = 0; + size_t totalSize = in.length(); + while (WriteFile(fWriteIn, &in[writeSize], totalSize, &hasWritten, 0)) { + writeSize += hasWritten; + if (writeSize >= totalSize) { + break; + } + } + return true; +} + +void PipJsonIO::close() +{ + std::lock_guard _guard(m_closeLock); + + if(m_isClosed){ + return; + } + CloseHandle(fReadIn); + CloseHandle(fWriteIn); + CloseHandle(fReadOut); + CloseHandle(fWriteOut); + if (!TerminateProcess(fProcess.hProcess, 0)) { + printf("teminate process error!\n"); + } + if (!TerminateThread(fProcess.hThread, 0)) { + printf("teminate thread error!\n"); + } + CloseHandle(fProcess.hThread); + CloseHandle(fProcess.hProcess); + + m_isClosed = true; +} + +bool PipJsonIO::isClosed() +{ + std::lock_guard _guard(m_closeLock); + return m_isClosed; +} + +bool PipJsonIO::readJson(json &json) { + json.clear(); + int length = ReadLength(); + SkipLine(); + std::string read; + Read(length, read); + if(read.empty()){ + return false; + } + try { + json = json::parse(read); + } catch (nlohmann::detail::exception& e) { + return false; + printf("read error -> %s\nread -> %s\n ", e.what(), read.c_str()); + } + return true; +} +bool PipJsonIO::writeJson(json &json) +{ + std::string content = json.dump(-1, ' ', false, nlohmann::detail::error_handler_t::ignore); + std::string header = "Content-Length: " + std::to_string(content.length()) + "\r\n\r\n" + content; + return Write(header); +} + +} + diff --git a/lsp/src/protocol.cpp b/lsp/src/protocol.cpp new file mode 100644 index 0000000..a136fcb --- /dev/null +++ b/lsp/src/protocol.cpp @@ -0,0 +1,156 @@ +#include + +namespace LspCore { + +JSON_Convert_Definition(URIForFile, {j = value.file;}, {value.file = j.get();}) +JSON_Convert_Definition(Position, MAP_JSON(MAP_KEY(line), MAP_KEY(character)), {FROM_KEY(line);FROM_KEY(character)}) +JSON_Convert_Definition(Range, MAP_JSON(MAP_KEY(start), MAP_KEY(end)), {FROM_KEY(start);FROM_KEY(end)}) +JSON_Convert_Definition(TextDocumentItem, MAP_JSON(MAP_KEY(uri), MAP_KEY(languageId), MAP_KEY(version), MAP_KEY(text)), {}) +JSON_Convert_Definition(TextDocumentIdentifier, MAP_JSON(MAP_KEY(uri)), {}) +JSON_Convert_Definition(VersionedTextDocumentIdentifier, MAP_JSON(MAP_KEY(uri), MAP_KEY(version)), {}) +JSON_Convert_Definition(TextDocumentPositionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position)), {}) +JSON_Convert_Definition(TextEdit, MAP_JSON(MAP_KEY(range), MAP_KEY(newText)), { FROM_KEY(range); FROM_KEY(newText); }); +JSON_Convert_Definition(Location, MAP_JSON(MAP_KEY(uri), MAP_KEY(range)), { FROM_KEY(uri); FROM_KEY(range); }); +JSON_Convert_Definition(DiagnosticRelatedInformation, MAP_JSON(MAP_KEY(location), MAP_KEY(message)), { FROM_KEY(location); FROM_KEY(message); }); +JSON_Convert_Definition(Diagnostic, {/*NOT REQUIRED*/ }, { FROM_KEY(range); FROM_KEY(code); FROM_KEY(source); FROM_KEY(message); + FROM_KEY(relatedInformation); FROM_KEY(category); FROM_KEY(codeActions); }); +JSON_Convert_Definition(MarkupContent, {}, { FROM_KEY(kind); FROM_KEY(value) }); +JSON_Convert_Definition(WorkDoneProgressParams, MAP_JSON(MAP_KEY(workDoneToken)), {}); +JSON_Convert_Definition(PartialResultParams, MAP_JSON(MAP_KEY(partialResultToken)), {}); +JSON_Convert_Definition(ClientCapabilities,MAP_JSON( + MAP_KV("textDocument", + MAP_KV("publishDiagnostics", // PublishDiagnosticsClientCapabilities + MAP_TO("categorySupport", DiagnosticCategory), + MAP_TO("codeActionsInline", DiagnosticFixes), + MAP_TO("relatedInformation", DiagnosticRelatedInformation), + ), + MAP_KV("completion", // CompletionClientCapabilities + MAP_KV("completionItem", + MAP_TO("snippetSupport", CompletionSnippets), + MAP_TO("deprecatedSupport", CompletionDeprecated)), + MAP_KV("completionItemKind", MAP_TO("valueSet", CompletionItemKinds)), + MAP_TO("editsNearCursor", CompletionFixes) + ), + MAP_KV("codeAction", MAP_TO("codeActionLiteralSupport", CodeActionStructure)), + MAP_KV("documentSymbol", MAP_TO("hierarchicalDocumentSymbolSupport", HierarchicalDocumentSymbol)), + MAP_KV("hover", //HoverClientCapabilities + MAP_TO("contentFormat", HoverContentFormat)), + MAP_KV("signatureHelp", MAP_KV("signatureInformation", MAP_KV("parameterInformation", MAP_TO("labelOffsetSupport", OffsetsInSignatureHelp))))), + MAP_KV("workspace", // WorkspaceEditClientCapabilities + MAP_KV("symbol", // WorkspaceSymbolClientCapabilities + MAP_KV("symbolKind", + MAP_TO("valueSet", WorkspaceSymbolKinds))), + MAP_TO("applyEdit", ApplyEdit), + MAP_KV("workspaceEdit", // WorkspaceEditClientCapabilities + MAP_TO("documentChanges", DocumentChanges))), + MAP_TO("offsetEncoding", offsetEncoding)), {}); +JSON_Convert_Definition(ServerCapabilities, {}, { + value.capabilities = j; + FROM_KEY(textDocumentSync); + j["documentOnTypeFormattingProvider"]["firstTriggerCharacter"].get_to(value.formattingTrigger); + j["completionProvider"]["resolveProvider"].get_to(value.resolveProvider); + j["completionProvider"]["triggerCharacters"].get_to(value.completionTrigger); + j["executeCommandProvider"]["commands"].get_to(value.executeCommands); +}); +JSON_Convert_Definition(ClangdCompileCommand,MAP_JSON( + MAP_KEY(workingDirectory), MAP_KEY(compilationCommand)), {}); +JSON_Convert_Definition(ConfigurationSettings, MAP_JSON(MAP_KEY(compilationDatabaseChanges)), {}); +JSON_Convert_Definition(InitializationOptions, MAP_JSON( + MAP_KEY(configSettings), + MAP_KEY(compilationDatabasePath), + MAP_KEY(fallbackFlags), + MAP_KEY(clangdFileStatus)), {}); +JSON_Convert_Definition(InitializeParams, MAP_JSON( + MAP_KEY(processId), + MAP_KEY(capabilities), + MAP_KEY(rootUri), + MAP_KEY(initializationOptions), + MAP_KEY(rootPath)), {}); +JSON_Convert_Definition(ShowMessageParams, {}, {FROM_KEY(type); FROM_KEY(message)}); +JSON_Convert_Definition(Registration, MAP_JSON(MAP_KEY(id), MAP_KEY(method)), {}); +JSON_Convert_Definition(RegistrationParams, MAP_JSON(MAP_KEY(registrations)), {}); +JSON_Convert_Definition(UnregistrationParams, MAP_JSON(MAP_KEY(unregisterations)), {}); +JSON_Convert_Definition(DidOpenTextDocumentParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Definition(DidCloseTextDocumentParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Definition(TextDocumentContentChangeEvent, MAP_JSON(MAP_KEY(range), MAP_KEY(rangeLength), MAP_KEY(text)), {}); +JSON_Convert_Definition(DidChangeTextDocumentParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(contentChanges), MAP_KEY(wantDiagnostics)), {}); +JSON_Convert_Definition(FileEvent, MAP_JSON(MAP_KEY(uri), MAP_KEY(type)), {}); +JSON_Convert_Definition(DidChangeWatchedFilesParams, MAP_JSON(MAP_KEY(changes)), {}); +JSON_Convert_Definition(DidChangeConfigurationParams, MAP_JSON(MAP_KEY(settings)), {}); +JSON_Convert_Definition(DocumentRangeFormattingParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(range)), {}); +JSON_Convert_Definition(DocumentOnTypeFormattingParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(ch)), {}); +JSON_Convert_Definition(DeclarationParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Definition(DefinitionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Definition(TypeDefinitionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Definition(ImplementationParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Definition(ReferenceParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)),{}); +JSON_Convert_Definition(HoverParam, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(workDoneToken)), {}); +JSON_Convert_Definition(Hover, {}, { FROM_KEY(contents); FROM_KEY(range) }); +JSON_Convert_Definition(FoldingRangeParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Definition(FoldingRange, {}, { + FROM_KEY(startLine); + FROM_KEY(startCharacter); + FROM_KEY(endLine); + FROM_KEY(endCharacter); + FROM_KEY(kind); +}); +JSON_Convert_Definition(SelectionRangeParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(positions)), {}); +JSON_Convert_Definition(SelectionRange, {}, { + FROM_KEY(range); + if (j.contains("parent")) { + value.parent = std::make_unique(); + j.at("parent").get_to(*value.parent); + } +}); +JSON_Convert_Definition(SemanticTokensParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(workDoneToken), MAP_KEY(partialResultToken)), {}); +JSON_Convert_Definition(DocumentFormattingParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Definition(DocumentSymbolParams, MAP_JSON(MAP_KEY(textDocument)), {}); +JSON_Convert_Definition(PublishDiagnosticsParams, {}, {FROM_KEY(uri);FROM_KEY(diagnostics);}); +JSON_Convert_Definition(CodeActionContext, MAP_JSON(MAP_KEY(diagnostics)), {}); +JSON_Convert_Definition(WorkspaceEdit, MAP_JSON(MAP_KEY(changes)), {}); +JSON_Convert_Definition(TweakArgs, MAP_JSON(MAP_KEY(file), MAP_KEY(selection), MAP_KEY(tweakID)),{}) +JSON_Convert_Definition(ExecuteCommandParams, MAP_JSON(MAP_KEY(command), MAP_KEY(workspaceEdit), MAP_KEY(tweakArgs)),{}) +JSON_Convert_Definition(CodeActionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(range), MAP_KEY(context)), {}); +JSON_Convert_Definition(CodeAction, MAP_JSON(MAP_KEY(title), MAP_KEY(kind), MAP_KEY(diagnostics), MAP_KEY(edit), MAP_KEY(command)), + {FROM_KEY(title);FROM_KEY(kind);FROM_KEY(diagnostics);FROM_KEY(edit);FROM_KEY(command)}); +JSON_Convert_Definition(SymbolInformation, MAP_JSON(MAP_KEY(name), MAP_KEY(kind), MAP_KEY(location), MAP_KEY(containerName)), {FROM_KEY(name);FROM_KEY(kind);FROM_KEY(location);FROM_KEY(containerName)}); +JSON_Convert_Definition(WorkspaceSymbolParams, MAP_JSON(MAP_KEY(query)), {}); +JSON_Convert_Definition(ApplyWorkspaceEditParams, MAP_JSON(MAP_KEY(edit)), {}); +JSON_Convert_Definition(CompletionContext, MAP_JSON(MAP_KEY(triggerKind), MAP_KEY(triggerCharacter)), {}); +JSON_Convert_Definition(CompletionParams, MAP_JSON(MAP_KEY(context), MAP_KEY(textDocument), MAP_KEY(position)), {}); +JSON_Convert_Definition(CompletionItem, {}, { + FROM_KEY(label); + FROM_KEY(kind); + FROM_KEY(detail); + FROM_KEY(documentation); + FROM_KEY(sortText); + FROM_KEY(filterText); + FROM_KEY(insertText); + FROM_KEY(insertTextFormat); + FROM_KEY(textEdit); + FROM_KEY(additionalTextEdits); +}); +JSON_Convert_Definition(CompletionList, {}, { + FROM_KEY(isIncomplete); + FROM_KEY(items); +}); +JSON_Convert_Definition(ParameterInformation, {}, { + FROM_KEY(labelString); + FROM_KEY(labelOffsets); + FROM_KEY(documentation); +}); +JSON_Convert_Definition(SignatureInformation, {}, { + FROM_KEY(label); + FROM_KEY(documentation); + FROM_KEY(parameters); +}); +JSON_Convert_Definition(SignatureHelp, {}, { + FROM_KEY(signatures); + FROM_KEY(activeParameter); + FROM_KEY(argListStart); +}); +JSON_Convert_Definition(RenameParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(newName)), {}); +JSON_Convert_Definition(TypeHierarchyParams, MAP_JSON(MAP_KEY(resolve), MAP_KEY(direction), MAP_KEY(textDocument), MAP_KEY(position)), {}); + +} + diff --git a/lsp/src/transport.cpp b/lsp/src/transport.cpp new file mode 100644 index 0000000..a4c5258 --- /dev/null +++ b/lsp/src/transport.cpp @@ -0,0 +1,2 @@ +#include + From 6109f5ba3eedb4049a8e55e27aa2640bc58952c9 Mon Sep 17 00:00:00 2001 From: firemeatman <2057479591@qq.com> Date: Thu, 25 Jul 2024 16:36:51 +0800 Subject: [PATCH 7/9] =?UTF-8?q?-=20=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E9=83=A8=E5=88=86=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++++------------ test/src/main.cpp | 53 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 371826f..495b409 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,6 @@ -# lsp-cpp -An easy [language-server-protocol](https://github.com/microsoft/language-server-protocol) client +# lsp-cpp -* 一个简单的lsp客户端 -* c++上目前似乎并没有一个简单好用lsp的客户端 -* 所以就想给我的代码编辑框写一个lsp client +该项目从 "lsp-cpp项目(An easy language-server-protocol client)"Fork而来。 +源项目是一个十分简单的Lsp客户端,通俗易懂。官方的LspCpp有点庞大不太用的来,而此项目足够简单,适合简单使用。 +本项目的目的是基于lsp-client进行完善和扩展,包括跨平台支持、模块化改造、协议扩充等。 -只有头文件(header-only) 直接引用就好了 - -cmake: - -` -include_directories(${lsp_include_dirs}) -` - -> 很多代码参考clangd 有的直接Ctrl+C了 (●'◡'●) diff --git a/test/src/main.cpp b/test/src/main.cpp index c7006e6..8ad187b 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -3,6 +3,7 @@ #include #include using namespace nlohmann; +using namespace LspCore; bool readFile(std::string& path, std::string* data) { @@ -23,41 +24,59 @@ bool readFile(std::string& path, std::string* data) return true; } +void copyCompileCommandsFiles() +{ + std::string source = "D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp/out/build/x64-Debug/compile_commands.json"; + std::string target = "D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp/compile_commands.json"; + std::ifstream src(source, std::ios::binary); + std::ofstream dst(target, std::ios::binary); + + dst << src.rdbuf(); +} +auto debugFunc = [](value& j) { + //value method = j.at("method"); + std::cout << "========================================\n"; + std::cout << j << std::endl; +}; + + int main() { std::string file1_uri = "file:///D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp/test/src/main.cpp"; std::string file1_path = "D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp/test/src/main.cpp"; std::string root_uri = "file:///D:/c_workstation/projects/Third_Fork_Projects/lsp-cpp"; - //std::string file1_text; - //std::string file2_text; - ProcessLanguageClient client(R"(D:\c_workstation\soft_tool\clangd_17.0.3\bin\clangd.exe)"); + PipJsonIO jsonIO(R"(D:\c_workstation\soft_tool\clangd_17.0.3\bin\clangd.exe)"); MapMessageHandler msgHandler; - auto debugFunc = [](value& j) { - //value method = j.at("method"); - std::cout << "========================================\n"; - std::cout << j << std::endl; - }; - msgHandler.bindNotify(METHOD_PublishDiagnostics.c_str(), debugFunc); - msgHandler.bindNotify(METHOD_DidOpen.c_str(), debugFunc); - msgHandler.bindNotify(METHOD_DidClose.c_str(), debugFunc); + LanguageClient client(msgHandler, jsonIO); + + copyCompileCommandsFiles(); + { + MapMessageHandler::Accessor accessor = msgHandler.access(); + accessor.bindNotify(METHOD_PublishDiagnostics.c_str(), debugFunc); + accessor.bindNotify(METHOD_DidOpen.c_str(), debugFunc); + accessor.bindNotify(METHOD_DidClose.c_str(), debugFunc); + } std::thread thread([&] { - client.loop(msgHandler); + client.safeLoop(); }); int res; while (scanf("%d", &res)) { + switch (res) { case 0: // 退出 { client.Exit(); - thread.detach(); + client.requestStopLoop(); + thread.join(); return 0; break; } case 1: // 初始化 { - msgHandler.bindResponse(client.Initialize(string_ref(root_uri)), debugFunc); + MapMessageHandler::Accessor accessor = msgHandler.access(); + accessor.bindResponse(client.Initialize(string_ref(root_uri)), debugFunc); break; } case 2: // 打开文件 @@ -74,13 +93,15 @@ int main() { } case 4: // 获取词语列表 { - msgHandler.bindResponse(client.SemanticTokensALL(string_ref(file1_uri)), debugFunc); + MapMessageHandler::Accessor accessor = msgHandler.access(); + accessor.bindResponse(client.SemanticTokensALL(string_ref(file1_uri)), debugFunc); break; } case 5: // 悬停信息 { + MapMessageHandler::Accessor accessor = msgHandler.access(); Position pos{8, 13}; - msgHandler.bindResponse(client.Hover(string_ref(file1_uri), pos), debugFunc); + accessor.bindResponse(client.Hover(string_ref(file1_uri), pos), debugFunc); break; } default: From 1629c0b7bf27bad4a20eb2d7c5f22fe6215eafca Mon Sep 17 00:00:00 2001 From: firemeatman <97668763+firemeatman@users.noreply.github.com> Date: Thu, 25 Jul 2024 17:03:15 +0800 Subject: [PATCH 8/9] Update README.md --- README.md | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 495b409..0092f63 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,122 @@ # lsp-cpp -该项目从 "lsp-cpp项目(An easy language-server-protocol client)"Fork而来。 -源项目是一个十分简单的Lsp客户端,通俗易懂。官方的LspCpp有点庞大不太用的来,而此项目足够简单,适合简单使用。 +该项目从 "lsp-cpp项目(An easy language-server-protocol client)"Fork而来。源项目:https://github.com/alextsao1999/lsp-cpp + +源项目是一个十分简单的Lsp语言客户端,通俗易懂。官方的LspCpp有点庞大不太用的来,而此项目足够简单,适合简单使用。 + 本项目的目的是基于lsp-client进行完善和扩展,包括跨平台支持、模块化改造、协议扩充等。 + +# 1.平台支持 + +> 可以运行的平台 +- windows +- linux(未完成) +> 与其他框架的集成使用 +- Qt6.x,编译通过,正常使用。示例项目: 代码编辑器[https://github.com/firemeatman/CodeEditor] + +# 2. 如何引入第三方项目 +cmake + +# 3. 代码示例 + +```c++ +#include +#include +#include +#include +using namespace nlohmann; +using namespace LspCore; + +bool readFile(std::string& path, std::string* data) +{ + // 这里就不实现了 + return true; +} + +void copyCompileCommandsFiles() +{ + // 这里就不实现了 +} +auto debugFunc = [](value& j) { + //value method = j.at("method"); + std::cout << "========================================\n"; + std::cout << j << std::endl; +}; + +int main() { + std::string file1_uri = "file:///yourTestCodeFilePath"; + std::string file1_path = "yourTestCodeFilePath"; + std::string root_uri = "file:///yourTestCodeFileRootPath"; + // 1. 创建Lsp客户端 + PipJsonIO jsonIO(R"(clangd.exe)"); // Lsp服务器路径, 这里我用的是Clangd. + MapMessageHandler msgHandler; + LanguageClient client(msgHandler, jsonIO); + // 2. 复制compile_commands.json到工作区, clangd需要这个文件;通过命令应该也能指定该文件路径和clangd工作文件夹 + copyCompileCommandsFiles(); + + // 3. 绑定一些通知消息的回调函数 + { + MapMessageHandler::Accessor accessor = msgHandler.access(); // 访问者模式访问MapMessageHandler的接口,线程安全 + accessor.bindNotify(METHOD_PublishDiagnostics.c_str(), debugFunc); + accessor.bindNotify(METHOD_DidOpen.c_str(), debugFunc); + accessor.bindNotify(METHOD_DidClose.c_str(), debugFunc); + } + // 4. 线程中运行Lsp客户端 + std::thread thread([&] { + client.safeLoop(); + }); + + // 5. 测试与本地Lsp服务器的通信 + int res; + while (scanf("%d", &res)) { + + switch (res) + { + case 0: // 退出 + { + client.Exit(); + client.requestStopLoop(); + thread.join(); + return 0; + break; + } + case 1: // 初始化 + { + MapMessageHandler::Accessor accessor = msgHandler.access();// 访问者模式访问MapMessageHandler的接口,线程安全 + accessor.bindResponse(client.Initialize(string_ref(root_uri)), debugFunc);// 发出初始化请求消息,并绑定回调函数 + break; + } + case 2: // 打开文件 + { + std::string text; + readFile(file1_path, &text); + client.DidOpen(file1_uri, text); + break; + } + case 3: // 关闭文件 + { + client.DidClose(string_ref(file1_uri)); + break; + } + case 4: // 获取词语列表 + { + MapMessageHandler::Accessor accessor = msgHandler.access(); + accessor.bindResponse(client.SemanticTokensALL(string_ref(file1_uri)), debugFunc); + break; + } + case 5: // 悬停信息 + { + MapMessageHandler::Accessor accessor = msgHandler.access(); + Position pos{8, 13}; + accessor.bindResponse(client.Hover(string_ref(file1_uri), pos), debugFunc); + break; + } + default: + break; + } + } + return 0; +} + +``` From 546cdfef1eef27b10d36b8ab3896c468a2bedfe8 Mon Sep 17 00:00:00 2001 From: firemeatman <97668763+firemeatman@users.noreply.github.com> Date: Thu, 25 Jul 2024 17:52:45 +0800 Subject: [PATCH 9/9] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0092f63..5acbbf9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # lsp-cpp -该项目从 "lsp-cpp项目(An easy language-server-protocol client)"Fork而来。源项目:https://github.com/alextsao1999/lsp-cpp +该项目从 "lsp-cpp项目(An easy language-server-protocol client)"Fork而来。源项目:[https://github.com/alextsao1999/lsp-cpp] +由于源项目已经不维护了,所以本fork项目独立了出来,新的项目地址:[https://github.com/firemeatman/LspClient] 源项目是一个十分简单的Lsp语言客户端,通俗易懂。官方的LspCpp有点庞大不太用的来,而此项目足够简单,适合简单使用。