├── CMakeLists.txt ├── README.md ├── .gitignore ├── src └── main.cpp ├── include ├── transport.h ├── uri.h ├── client.h └── protocol.h └── LICENSE /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(Lsp) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | include_directories(include) 7 | include_directories(src) 8 | aux_source_directory(src/ sources) 9 | set(lsp_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" ) 10 | add_executable(LspClientTest ${sources}) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lsp-cpp 2 | An easy [language-server-protocol](https://github.com/microsoft/language-server-protocol) client 3 | 4 | * 一个简单的lsp客户端 5 | * c++上目前似乎并没有一个简单好用lsp的客户端 6 | * 所以就想给我的代码编辑框写一个lsp client 7 | 8 | 只有头文件(header-only) 直接引用就好了 9 | 10 | cmake: 11 | 12 | ` 13 | include_directories(${lsp_include_dirs}) 14 | ` 15 | 16 | > 很多代码参考clangd 有的直接Ctrl+C了 (●'◡'●) 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .idea/ 35 | cmake-build-debug/ 36 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "client.h" 4 | int main() { 5 | URI uri; 6 | uri.parse("https://www.baidu.com/test/asdf"); 7 | printf("%s", uri.host()); 8 | printf("%s", uri.path()); 9 | 10 | return 0; 11 | ProcessLanguageClient client(R"(F:\LLVM\bin\clangd.exe)"); 12 | MapMessageHandler my; 13 | std::thread thread([&] { 14 | client.loop(my); 15 | }); 16 | 17 | string_ref file = "file:///C:/Users/Administrator/Desktop/test.c"; 18 | 19 | std::string text = "int main() { return 0; }\n"; 20 | int res; 21 | while (scanf("%d", &res)) { 22 | if (res == 1) { 23 | client.Exit(); 24 | thread.detach(); 25 | return 0; 26 | } 27 | if (res == 2) { 28 | client.Initialize(); 29 | } 30 | if (res == 3) { 31 | client.DidOpen(file, text); 32 | client.Sync(); 33 | } 34 | if (res == 4) { 35 | client.Formatting(file); 36 | } 37 | } 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /include/transport.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex on 2020/1/28. 3 | // 4 | 5 | #ifndef LSP_TRANSPORT_H 6 | #define LSP_TRANSPORT_H 7 | 8 | #include "uri.h" 9 | #include 10 | #include 11 | using value = json; 12 | using RequestID = std::string; 13 | 14 | class MessageHandler { 15 | public: 16 | MessageHandler() = default; 17 | virtual void onNotify(string_ref method, value ¶ms) {} 18 | virtual void onResponse(value &ID, value &result) {} 19 | virtual void onError(value &ID, value &error) {} 20 | virtual void onRequest(string_ref method, value ¶ms, value &ID) {} 21 | 22 | }; 23 | class MapMessageHandler : public MessageHandler { 24 | public: 25 | std::map> m_calls; 26 | std::map> m_notify; 27 | std::vector>> m_requests; 28 | MapMessageHandler() = default; 29 | template 30 | void bindRequest(const char *method, std::function func) { 31 | m_calls[method] = [=](json ¶ms, json &id) { 32 | Param param = params.get(); 33 | func(param, id.get()); 34 | }; 35 | } 36 | void bindRequest(const char *method, std::function func) { 37 | m_calls[method] = std::move(func); 38 | } 39 | template 40 | void bindNotify(const char *method, std::function func) { 41 | m_notify[method] = [=](json ¶ms) { 42 | Param param = params.get(); 43 | func(param); 44 | }; 45 | } 46 | void bindNotify(const char *method, std::function func) { 47 | m_notify[method] = std::move(func); 48 | } 49 | void bindResponse(RequestID id, std::functionfunc) { 50 | m_requests.emplace_back(id, std::move(func)); 51 | } 52 | void onNotify(string_ref method, value ¶ms) override { 53 | std::string str = method.str(); 54 | if (m_notify.count(str)) { 55 | m_notify[str](params); 56 | } 57 | } 58 | void onResponse(value &ID, value &result) override { 59 | for (int i = 0; i < m_requests.size(); ++i) { 60 | if (ID == m_requests[i].first) { 61 | m_requests[i].second(result); 62 | m_requests.erase(m_requests.begin() + i); 63 | return; 64 | } 65 | } 66 | } 67 | void onError(value &ID, value &error) override { 68 | 69 | } 70 | void onRequest(string_ref method, value ¶ms, value &ID) override { 71 | std::string string = method.str(); 72 | if (m_calls.count(string)) { 73 | m_calls[string](params, ID); 74 | } 75 | } 76 | }; 77 | 78 | class Transport { 79 | public: 80 | virtual void notify(string_ref method, value ¶ms) = 0; 81 | virtual void request(string_ref method, value ¶ms, RequestID &id) = 0; 82 | virtual int loop(MessageHandler &) = 0; 83 | }; 84 | 85 | class JsonTransport : public Transport { 86 | public: 87 | const char *jsonrpc = "2.0"; 88 | int loop(MessageHandler &handler) override { 89 | while (true) { 90 | try { 91 | value value; 92 | if (readJson(value)) { 93 | if (value.count("id")) { 94 | if (value.contains("method")) { 95 | handler.onRequest(value["method"].get(), value["params"], value["id"]); 96 | } else if (value.contains("result")) { 97 | handler.onResponse(value["id"], value["result"]); 98 | } else if (value.contains("error")) { 99 | handler.onError(value["id"], value["error"]); 100 | } 101 | } else if (value.contains("method")) { 102 | if (value.contains("params")) { 103 | handler.onNotify(value["method"].get(), value["params"]); 104 | } 105 | } 106 | } 107 | } catch (std::exception &e) { 108 | 109 | //printf("error -> %s\n", e.what()); 110 | } 111 | } 112 | return 0; 113 | } 114 | void notify(string_ref method, value ¶ms) override { 115 | json value = {{"jsonrpc", jsonrpc}, 116 | {"method", method}, 117 | {"params", params}}; 118 | writeJson(value); 119 | } 120 | void request(string_ref method, value ¶ms, RequestID &id) override { 121 | json rpc = {{"jsonrpc", jsonrpc}, 122 | {"id", id}, 123 | {"method", method}, 124 | {"params", params}}; 125 | writeJson(rpc); 126 | } 127 | virtual bool readJson(value &) = 0; 128 | virtual bool writeJson(value &) = 0; 129 | }; 130 | 131 | #endif //LSP_TRANSPORT_H 132 | -------------------------------------------------------------------------------- /include/uri.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex on 2020/1/28. 3 | // 4 | 5 | #ifndef LSP_URI_H 6 | #define LSP_URI_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "json.hpp" 13 | using json = nlohmann::json; 14 | class string_ref { 15 | public: 16 | static const size_t npos = ~size_t(0); 17 | using iterator = const char *; 18 | using const_iterator = const char *; 19 | using size_type = size_t; 20 | private: 21 | const char *m_ref = nullptr; 22 | size_t m_length = 0; 23 | public: 24 | constexpr string_ref() : m_ref(nullptr), m_length(0) {} 25 | constexpr string_ref(std::nullptr_t) : m_ref(nullptr), m_length(0) {} 26 | constexpr string_ref(const char *ref, size_t length) : m_ref(ref), m_length(length) {} 27 | string_ref(const char *ref) : m_ref(ref), m_length(std::strlen(ref)) {} 28 | string_ref(const std::string &string) : m_ref(string.c_str()), m_length(string.length()) {} 29 | inline operator const char*() const { return m_ref; } 30 | inline std::string str() const { return std::string(m_ref, m_length); } 31 | inline bool operator==(const string_ref &ref) const { 32 | return m_length == ref.m_length && strcmp(m_ref, ref.m_ref) == 0; 33 | } 34 | inline bool operator==(const char *ref) const { 35 | return strcmp(m_ref, ref) == 0; 36 | } 37 | inline bool operator>(const string_ref &ref) const { return m_length > ref.m_length; } 38 | inline bool operator<(const string_ref &ref) const { return m_length < ref.m_length; } 39 | inline const char *c_str() const { return m_ref; } 40 | inline bool empty() const { return m_length == 0; } 41 | iterator begin() const { return m_ref; } 42 | iterator end() const { return m_ref + m_length; } 43 | inline const char *data() const { return m_ref; } 44 | inline size_t size() const { return m_length; } 45 | inline size_t length() const { return m_length; } 46 | char front() const { return m_ref[0]; } 47 | char back() const { return m_ref[m_length - 1]; } 48 | char operator[](size_t index) const { return m_ref[index]; } 49 | 50 | }; 51 | template 52 | class option { 53 | public: 54 | T fStorage = T(); 55 | bool fHas = false; 56 | constexpr option() = default; 57 | option(const T &y) : fStorage(y), fHas(true) {} 58 | option(T &&y) : fStorage(std::move(y)), fHas(true) {} 59 | option &operator=(T &&v) { 60 | fStorage = std::move(v); 61 | fHas = true; 62 | return *this; 63 | } 64 | option &operator=(const T &v) { 65 | fStorage = v; 66 | fHas = true; 67 | return *this; 68 | } 69 | const T *ptr() const { return &fStorage; } 70 | T *ptr() { return &fStorage; } 71 | const T &value() const { return fStorage; } 72 | T &value() { return fStorage; } 73 | bool has() const { return fHas; } 74 | const T *operator->() const { return ptr(); } 75 | T *operator->() { return ptr(); } 76 | const T &operator*() const { return value(); } 77 | T &operator*() { return value(); } 78 | explicit operator bool() const { return fHas; } 79 | }; 80 | namespace nlohmann { 81 | template 82 | struct adl_serializer> { 83 | static void to_json(json& j, const option& opt) { 84 | if (opt.has()) { 85 | j = opt.value(); 86 | } else { 87 | j = nullptr; 88 | } 89 | } 90 | static void from_json(const json& j, option& opt) { 91 | if (j.is_null()) { 92 | opt = option(); 93 | } else { 94 | opt = option(j.get()); 95 | } 96 | } 97 | }; 98 | } 99 | 100 | inline uint8_t FromHex(const char digit) { 101 | if (digit >= '0' && digit <= '9') 102 | return digit - '0'; 103 | if (digit >= 'a' && digit <= 'f') 104 | return digit - 'a' + 10; 105 | if (digit >= 'A' && digit <= 'F') 106 | return digit - 'A' + 10; 107 | return 0; 108 | } 109 | inline uint8_t FromHex(const char n1, const char n2) { 110 | return (FromHex(n1) << 4) + FromHex(n2); 111 | } 112 | 113 | struct URIForFile { 114 | std::string file; 115 | static std::string UriEncode(string_ref ref) { 116 | static const char *hexs = "0123456789ABCDEF"; 117 | static const char *symbol = "._-*/:"; 118 | std::string result; 119 | for (uint8_t ch : ref) { 120 | if (ch == '\\') { 121 | ch = '/'; 122 | } 123 | if (std::isalnum(ch) || strchr(symbol, ch)) { 124 | if (ch == '/' && result.back() == '/') { 125 | continue; 126 | } 127 | result += ch; 128 | } else if (ch == ' ') { 129 | result += '+'; 130 | } else { 131 | result += '%'; 132 | result += hexs[ch >> 4]; 133 | result += hexs[ch & 0xF]; 134 | } 135 | } 136 | return std::move(result); 137 | } 138 | explicit operator bool() const { return !file.empty(); } 139 | friend bool operator==(const URIForFile &LHS, const URIForFile &RHS) { 140 | return LHS.file == RHS.file; 141 | } 142 | friend bool operator!=(const URIForFile &LHS, const URIForFile &RHS) { 143 | return !(LHS == RHS); 144 | } 145 | friend bool operator<(const URIForFile &LHS, const URIForFile &RHS) { 146 | return LHS.file < RHS.file; 147 | } 148 | void from(string_ref path) { 149 | file = "file:///" + UriEncode(path); 150 | } 151 | explicit URIForFile(const char *str) : file(str) {} 152 | URIForFile() = default; 153 | inline std::string &str() { return file; } 154 | }; 155 | using DocumentUri = string_ref; 156 | 157 | class URI { 158 | public: 159 | using sview = string_ref; 160 | static std::string Encode(sview input) { 161 | static const char *hexs = "0123456789ABCDEF"; 162 | static const char *unreserved = "-._~"; 163 | std::string res; 164 | res.reserve(input.size()); 165 | for (auto chr : input) { 166 | if (std::isalnum(chr) || std::strchr(unreserved, chr)) { 167 | res += chr; 168 | } else { 169 | res += '%'; 170 | res += hexs[chr >> 4]; 171 | res += hexs[chr & 0xF]; 172 | } 173 | } 174 | return res; 175 | } 176 | static std::string Decode(sview input) { 177 | static const char *reserved = ":/?#[]@!$&'()*+,;="; 178 | std::string res; 179 | res.reserve(input.size()); 180 | for (auto iter = input.begin(), end = input.end(); iter != end; ++iter) { 181 | if (*iter == '%') { 182 | const uint8_t n1 = (*(++iter)); 183 | const uint8_t n2 = (*(++iter)); 184 | res += static_cast(FromHex(n1, n2)); 185 | } else if (*iter == '+') { 186 | res += ' '; 187 | } else { 188 | res += *iter; 189 | } 190 | } 191 | return res; 192 | } 193 | public: 194 | void parse(sview uri) { 195 | static const std::regex pattern{ 196 | "^([a-zA-Z]+[\\w\\+\\-\\.]+)?(\\://)?" //< scheme 197 | "(([^:@]+)(\\:([^@]+))?@)?" //< username && password 198 | "([^/:?#]+)?(\\:(\\d+))?" //< hostname && port 199 | "([^?#]+)" //< path 200 | "(\\?([^#]*))?" //< query 201 | "(#(.*))?$" //< fragment 202 | }; 203 | static std::cmatch parts; 204 | m_uri = Decode(uri); 205 | if (std::regex_match(m_uri.c_str(), parts, pattern)) { 206 | m_path = sview(m_uri.c_str() + parts.position(10), parts.length(10)); 207 | m_scheme = parts.length(1) ? 208 | sview(m_uri.c_str() + parts.position(1), parts.length(1)) : sview{}; 209 | m_userinfo = parts.length(3) ? 210 | sview(m_uri.data() + parts.position(3), parts.length(3)) : sview{}; 211 | m_host = parts.length(7) ? 212 | sview(m_uri.data() + parts.position(7), parts.length(7)) : sview{}; 213 | m_port = parts.length(9) ? 214 | sview(m_uri.data() + parts.position(9), parts.length(9)) : sview{}; 215 | m_query = parts.length(11) ? 216 | sview(m_uri.data() + parts.position(11), parts.length(11)) : sview{}; 217 | m_fragment = parts.length(13) ? 218 | sview(m_uri.data() + parts.position(13), parts.length(13)) : sview{}; 219 | } 220 | } 221 | sview path() { return m_path; } 222 | sview scheme() { return m_scheme; } 223 | sview userinfo() { return m_userinfo; } 224 | sview host() { return m_host; } 225 | sview port() { return m_port; } 226 | sview query() { return m_query; } 227 | sview fragment() { return m_fragment; } 228 | sview query(sview key) { 229 | /* 230 | static const std::regex query_token_pattern {"[^?=&]+"}; 231 | auto query = query_.to_string(); 232 | auto it = std::sregex_iterator(query.cbegin(), query.cend(), query_token_pattern); 233 | auto end = std::sregex_iterator(); 234 | while (it not_eq end) { 235 | auto key = it->str(); 236 | if (++it not_eq end) { 237 | queries_[key] = it->str(); 238 | } else { 239 | queries_[key]; 240 | } 241 | } 242 | */ 243 | 244 | std::regex regex("([a-zA-Z_]+)="); 245 | std::cmatch part; 246 | if (std::regex_match(m_uri.c_str(), part, regex)) { 247 | 248 | } 249 | 250 | return {}; 251 | } 252 | std::string uri() { 253 | return Encode(m_uri); 254 | } 255 | 256 | private: 257 | std::string m_uri; 258 | sview m_path; 259 | sview m_scheme; 260 | sview m_userinfo; 261 | sview m_host; 262 | sview m_port; 263 | sview m_query; 264 | sview m_fragment; 265 | 266 | }; 267 | 268 | #endif //LSP_URI_H 269 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /include/client.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex on 2020/1/28. 3 | // 4 | 5 | #ifndef LSP_CLIENT_H 6 | #define LSP_CLIENT_H 7 | #include "transport.h" 8 | #include "protocol.h" 9 | #include "windows.h" 10 | class LanguageClient : public JsonTransport { 11 | public: 12 | virtual ~LanguageClient() = default; 13 | public: 14 | RequestID Initialize(option rootUri = {}) { 15 | InitializeParams params; 16 | params.processId = GetCurrentProcessId(); 17 | params.rootUri = rootUri; 18 | return SendRequest("initialize", params); 19 | } 20 | RequestID Shutdown() { 21 | return SendRequest("shutdown"); 22 | } 23 | RequestID Sync() { 24 | return SendRequest("sync"); 25 | } 26 | void Exit() { 27 | SendNotify("exit"); 28 | } 29 | void Initialized() { 30 | SendNotify("initialized"); 31 | } 32 | RequestID RegisterCapability() { 33 | return SendRequest("client/registerCapability"); 34 | } 35 | void DidOpen(DocumentUri uri, string_ref text, string_ref languageId = "cpp") { 36 | DidOpenTextDocumentParams params; 37 | params.textDocument.uri = std::move(uri); 38 | params.textDocument.text = text; 39 | params.textDocument.languageId = languageId; 40 | SendNotify("textDocument/didOpen", params); 41 | } 42 | void DidClose(DocumentUri uri) { 43 | DidCloseTextDocumentParams params; 44 | params.textDocument.uri = std::move(uri); 45 | SendNotify("textDocument/didClose", params); 46 | } 47 | void DidChange(DocumentUri uri, std::vector &changes, 48 | option wantDiagnostics = {}) { 49 | DidChangeTextDocumentParams params; 50 | params.textDocument.uri = std::move(uri); 51 | params.contentChanges = std::move(changes); 52 | params.wantDiagnostics = wantDiagnostics; 53 | SendNotify("textDocument/didChange", params); 54 | } 55 | RequestID RangeFomatting(DocumentUri uri, Range range) { 56 | DocumentRangeFormattingParams params; 57 | params.textDocument.uri = std::move(uri); 58 | params.range = range; 59 | return SendRequest("textDocument/rangeFormatting", params); 60 | } 61 | RequestID FoldingRange(DocumentUri uri) { 62 | FoldingRangeParams params; 63 | params.textDocument.uri = std::move(uri); 64 | return SendRequest("textDocument/foldingRange", params); 65 | } 66 | RequestID SelectionRange(DocumentUri uri, std::vector &positions) { 67 | SelectionRangeParams params; 68 | params.textDocument.uri = std::move(uri); 69 | params.positions = std::move(positions); 70 | return SendRequest("textDocument/selectionRange", params); 71 | } 72 | RequestID OnTypeFormatting(DocumentUri uri, Position position, string_ref ch) { 73 | DocumentOnTypeFormattingParams params; 74 | params.textDocument.uri = std::move(uri); 75 | params.position = position; 76 | params.ch = std::move(ch); 77 | return SendRequest("textDocument/onTypeFormatting", std::move(params)); 78 | } 79 | RequestID Formatting(DocumentUri uri) { 80 | DocumentFormattingParams params; 81 | params.textDocument.uri = std::move(uri); 82 | return SendRequest("textDocument/formatting", std::move(params)); 83 | } 84 | RequestID CodeAction(DocumentUri uri, Range range, CodeActionContext context) { 85 | CodeActionParams params; 86 | params.textDocument.uri = std::move(uri); 87 | params.range = range; 88 | params.context = std::move(context); 89 | return SendRequest("textDocument/codeAction", std::move(params)); 90 | } 91 | RequestID Completion(DocumentUri uri, Position position, option context = {}) { 92 | CompletionParams params; 93 | params.textDocument.uri = std::move(uri); 94 | params.position = position; 95 | params.context = context; 96 | return SendRequest("textDocument/completion", params); 97 | } 98 | RequestID SignatureHelp(DocumentUri uri, Position position) { 99 | TextDocumentPositionParams params; 100 | params.textDocument.uri = std::move(uri); 101 | params.position = position; 102 | return SendRequest("textDocument/signatureHelp", std::move(params)); 103 | } 104 | RequestID GoToDefinition(DocumentUri uri, Position position) { 105 | TextDocumentPositionParams params; 106 | params.textDocument.uri = std::move(uri); 107 | params.position = position; 108 | return SendRequest("textDocument/definition", std::move(params)); 109 | } 110 | RequestID GoToDeclaration(DocumentUri uri, Position position) { 111 | TextDocumentPositionParams params; 112 | params.textDocument.uri = std::move(uri); 113 | params.position = position; 114 | return SendRequest("textDocument/declaration", std::move(params)); 115 | } 116 | RequestID References(DocumentUri uri, Position position) { 117 | ReferenceParams params; 118 | params.textDocument.uri = std::move(uri); 119 | params.position = position; 120 | return SendRequest("textDocument/references", std::move(params)); 121 | } 122 | RequestID SwitchSourceHeader(DocumentUri uri) { 123 | TextDocumentIdentifier params; 124 | params.uri = std::move(uri); 125 | return SendRequest("textDocument/references", std::move(params)); 126 | } 127 | RequestID Rename(DocumentUri uri, Position position, string_ref newName) { 128 | RenameParams params; 129 | params.textDocument.uri = std::move(uri); 130 | params.position = position; 131 | params.newName = newName; 132 | return SendRequest("textDocument/rename", std::move(params)); 133 | } 134 | RequestID Hover(DocumentUri uri, Position position) { 135 | TextDocumentPositionParams params; 136 | params.textDocument.uri = std::move(uri); 137 | params.position = position; 138 | return SendRequest("textDocument/hover", std::move(params)); 139 | } 140 | RequestID DocumentSymbol(DocumentUri uri) { 141 | DocumentSymbolParams params; 142 | params.textDocument.uri = std::move(uri); 143 | return SendRequest("textDocument/documentSymbol", std::move(params)); 144 | } 145 | RequestID DocumentColor(DocumentUri uri) { 146 | DocumentSymbolParams params; 147 | params.textDocument.uri = std::move(uri); 148 | return SendRequest("textDocument/documentColor", std::move(params)); 149 | } 150 | RequestID DocumentHighlight(DocumentUri uri, Position position) { 151 | TextDocumentPositionParams params; 152 | params.textDocument.uri = std::move(uri); 153 | params.position = position; 154 | return SendRequest("textDocument/documentHighlight", std::move(params)); 155 | } 156 | RequestID SymbolInfo(DocumentUri uri, Position position) { 157 | TextDocumentPositionParams params; 158 | params.textDocument.uri = std::move(uri); 159 | params.position = position; 160 | return SendRequest("textDocument/symbolInfo", std::move(params)); 161 | } 162 | RequestID TypeHierarchy(DocumentUri uri, Position position, TypeHierarchyDirection direction, int resolve) { 163 | TypeHierarchyParams params; 164 | params.textDocument.uri = std::move(uri); 165 | params.position = position; 166 | params.direction = direction; 167 | params.resolve = resolve; 168 | return SendRequest("textDocument/typeHierarchy", std::move(params)); 169 | } 170 | RequestID WorkspaceSymbol(string_ref query) { 171 | WorkspaceSymbolParams params; 172 | params.query = query; 173 | return SendRequest("workspace/symbol", std::move(params)); 174 | } 175 | RequestID ExecuteCommand(string_ref cmd, option tweakArgs = {}, option workspaceEdit = {}) { 176 | ExecuteCommandParams params; 177 | params.tweakArgs = tweakArgs; 178 | params.workspaceEdit = workspaceEdit; 179 | params.command = cmd; 180 | return SendRequest("workspace/executeCommand", std::move(params)); 181 | } 182 | RequestID DidChangeWatchedFiles(std::vector &changes) { 183 | DidChangeWatchedFilesParams params; 184 | params.changes = std::move(changes); 185 | return SendRequest("workspace/didChangeWatchedFiles", std::move(params)); 186 | } 187 | RequestID DidChangeConfiguration(ConfigurationSettings &settings) { 188 | DidChangeConfigurationParams params; 189 | params.settings = std::move(settings); 190 | return SendRequest("workspace/didChangeConfiguration", std::move(params)); 191 | } 192 | public: 193 | RequestID SendRequest(string_ref method, value params = json()) { 194 | RequestID id = method.str(); 195 | request(method, params, id); 196 | return id; 197 | } 198 | void SendNotify(string_ref method, value params = json()) { 199 | notify(method, params); 200 | } 201 | }; 202 | class ProcessLanguageClient : public LanguageClient { 203 | public: 204 | HANDLE fReadIn = nullptr, fWriteIn = nullptr; 205 | HANDLE fReadOut = nullptr, fWriteOut = nullptr; 206 | PROCESS_INFORMATION fProcess = {nullptr}; 207 | explicit ProcessLanguageClient(const char *program, const char *arguments = "") { 208 | SECURITY_ATTRIBUTES sa = {0}; 209 | sa.nLength = sizeof(SECURITY_ATTRIBUTES); 210 | sa.bInheritHandle = true; 211 | if (!CreatePipe(&fReadIn, &fWriteIn, &sa, 1024 * 1024)) { 212 | printf("Create In Pipe error\n"); 213 | } 214 | if (!CreatePipe(&fReadOut, &fWriteOut, &sa, 1024 * 1024)) { 215 | printf("Create Out Pipe error\n"); 216 | } 217 | STARTUPINFO si = {0}; 218 | si.cb = sizeof(si); 219 | si.hStdInput = fReadIn; 220 | si.hStdOutput = fWriteOut; 221 | si.dwFlags = STARTF_USESTDHANDLES; 222 | if (!CreateProcessA(program, (char *) arguments, 0, 0, TRUE, 223 | CREATE_NO_WINDOW, 0, 0, (LPSTARTUPINFOA) &si, &fProcess)) { 224 | printf("Create Process error\n"); 225 | } 226 | 227 | //m_exec.start(program, arguments); 228 | //m_exec.set_wait_timeout(exec_stream_t::s_child, INFINITE); 229 | } 230 | ~ProcessLanguageClient() override { 231 | /* 232 | DisconnectNamedPipe(fReadIn); 233 | DisconnectNamedPipe(fWriteIn); 234 | DisconnectNamedPipe(fReadOut); 235 | DisconnectNamedPipe(fWriteOut); 236 | */ 237 | CloseHandle(fReadIn); 238 | CloseHandle(fWriteIn); 239 | CloseHandle(fReadOut); 240 | CloseHandle(fWriteOut); 241 | if (!TerminateProcess(fProcess.hProcess, 0)) { 242 | printf("teminate process error!\n"); 243 | } 244 | if (!TerminateThread(fProcess.hThread, 0)) { 245 | printf("teminate thread error!\n"); 246 | } 247 | CloseHandle(fProcess.hThread); 248 | CloseHandle(fProcess.hProcess); 249 | } 250 | void SkipLine() { 251 | char read; 252 | DWORD hasRead; 253 | while (ReadFile(fReadOut, &read, 1, &hasRead, NULL)) { 254 | if (read == '\n') { 255 | break; 256 | } 257 | } 258 | } 259 | int ReadLength() { 260 | // "Content-Length: " 261 | char szReadBuffer[255]; 262 | DWORD hasRead; 263 | int length = 0; 264 | while (ReadFile(fReadOut, &szReadBuffer[length], 1, &hasRead, NULL)) { 265 | if (szReadBuffer[length] == '\n') { 266 | break; 267 | } 268 | length++; 269 | } 270 | return atoi(szReadBuffer + 16); 271 | } 272 | void Read(int length, std::string &out) { 273 | int readSize = 0; 274 | DWORD hasRead; 275 | out.resize(length); 276 | while (ReadFile(fReadOut, &out[readSize], length, &hasRead, NULL)) { 277 | readSize += hasRead; 278 | if (readSize >= length) { 279 | break; 280 | } 281 | } 282 | } 283 | bool Write(std::string &in) { 284 | DWORD hasWritten; 285 | int writeSize = 0; 286 | int totalSize = in.length(); 287 | while (WriteFile(fWriteIn, &in[writeSize], totalSize, &hasWritten, 0)) { 288 | writeSize += hasWritten; 289 | if (writeSize >= totalSize) { 290 | break; 291 | } 292 | } 293 | return true; 294 | } 295 | bool readJson(json &json) override { 296 | json.clear(); 297 | int length = ReadLength(); 298 | SkipLine(); 299 | std::string read; 300 | Read(length, read); 301 | try { 302 | json = json::parse(read); 303 | } catch (std::exception &e) { 304 | //printf("read error -> %s\nread -> %s\n ", e.what(), read.c_str()); 305 | } 306 | //printf("message %d:\n%s\n", length, read.c_str()); 307 | return true; 308 | } 309 | bool writeJson(json &json) override { 310 | std::string content = json.dump(); 311 | std::string header = "Content-Length: " + std::to_string(content.length()) + "\r\n\r\n" + content; 312 | //printf("send:\n%s\n", content.c_str()); 313 | return Write(header); 314 | } 315 | }; 316 | 317 | #endif //LSP_CLIENT_H 318 | -------------------------------------------------------------------------------- /include/protocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex on 2020/1/28. 3 | // 4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 | // See https://llvm.org/LICENSE.txt for license information. 6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 | // Most of the code comes from clangd(Protocol.h) 8 | 9 | #ifndef LSP_PROTOCOL_H 10 | #define LSP_PROTOCOL_H 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "uri.h" 17 | #define MAP_JSON(...) {j = {__VA_ARGS__};} 18 | #define MAP_KEY(KEY) {#KEY, value.KEY} 19 | #define MAP_TO(KEY, TO) {KEY, value.TO} 20 | #define MAP_KV(K, ...) {K, {__VA_ARGS__}} 21 | #define FROM_KEY(KEY) if (j.contains(#KEY)) j.at(#KEY).get_to(value.KEY); 22 | #define JSON_SERIALIZE(Type, TO, FROM) \ 23 | namespace nlohmann { \ 24 | template <> struct adl_serializer { \ 25 | static void to_json(json& j, const Type& value) TO \ 26 | static void from_json(const json& j, Type& value) FROM \ 27 | }; \ 28 | } 29 | using TextType = string_ref; 30 | enum class ErrorCode { 31 | // Defined by JSON RPC. 32 | ParseError = -32700, 33 | InvalidRequest = -32600, 34 | MethodNotFound = -32601, 35 | InvalidParams = -32602, 36 | InternalError = -32603, 37 | ServerNotInitialized = -32002, 38 | UnknownErrorCode = -32001, 39 | // Defined by the protocol. 40 | RequestCancelled = -32800, 41 | }; 42 | class LSPError { 43 | public: 44 | std::string Message; 45 | ErrorCode Code; 46 | static char ID; 47 | LSPError(std::string Message, ErrorCode Code) 48 | : Message(std::move(Message)), Code(Code) {} 49 | }; 50 | JSON_SERIALIZE(URIForFile, {j = value.file;}, {value.file = j.get();}); 51 | struct TextDocumentIdentifier { 52 | /// The text document's URI. 53 | DocumentUri uri; 54 | }; 55 | JSON_SERIALIZE(TextDocumentIdentifier, MAP_JSON(MAP_KEY(uri)), {}); 56 | 57 | struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier { 58 | int version = 0; 59 | }; 60 | JSON_SERIALIZE(VersionedTextDocumentIdentifier, MAP_JSON(MAP_KEY(uri), MAP_KEY(version)), {}); 61 | 62 | struct Position { 63 | /// Line position in a document (zero-based). 64 | int line = 0; 65 | /// Character offset on a line in a document (zero-based). 66 | /// WARNING: this is in UTF-16 codepoints, not bytes or characters! 67 | /// Use the functions in SourceCode.h to construct/interpret Positions. 68 | int character = 0; 69 | friend bool operator==(const Position &LHS, const Position &RHS) { 70 | return std::tie(LHS.line, LHS.character) == 71 | std::tie(RHS.line, RHS.character); 72 | } 73 | friend bool operator!=(const Position &LHS, const Position &RHS) { 74 | return !(LHS == RHS); 75 | } 76 | friend bool operator<(const Position &LHS, const Position &RHS) { 77 | return std::tie(LHS.line, LHS.character) < 78 | std::tie(RHS.line, RHS.character); 79 | } 80 | friend bool operator<=(const Position &LHS, const Position &RHS) { 81 | return std::tie(LHS.line, LHS.character) <= 82 | std::tie(RHS.line, RHS.character); 83 | } 84 | }; 85 | JSON_SERIALIZE(Position, MAP_JSON(MAP_KEY(line), MAP_KEY(character)), {FROM_KEY(line);FROM_KEY(character)}); 86 | 87 | struct Range { 88 | /// The range's start position. 89 | Position start; 90 | 91 | /// The range's end position. 92 | Position end; 93 | 94 | friend bool operator==(const Range &LHS, const Range &RHS) { 95 | return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end); 96 | } 97 | friend bool operator!=(const Range &LHS, const Range &RHS) { 98 | return !(LHS == RHS); 99 | } 100 | friend bool operator<(const Range &LHS, const Range &RHS) { 101 | return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end); 102 | } 103 | bool contains(Position Pos) const { return start <= Pos && Pos < end; } 104 | bool contains(Range Rng) const { 105 | return start <= Rng.start && Rng.end <= end; 106 | } 107 | }; 108 | JSON_SERIALIZE(Range, MAP_JSON(MAP_KEY(start), MAP_KEY(end)), {FROM_KEY(start);FROM_KEY(end)}); 109 | 110 | struct Location { 111 | /// The text document's URI. 112 | std::string uri; 113 | Range range; 114 | 115 | friend bool operator==(const Location &LHS, const Location &RHS) { 116 | return LHS.uri == RHS.uri && LHS.range == RHS.range; 117 | } 118 | friend bool operator!=(const Location &LHS, const Location &RHS) { 119 | return !(LHS == RHS); 120 | } 121 | friend bool operator<(const Location &LHS, const Location &RHS) { 122 | return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range); 123 | } 124 | }; 125 | JSON_SERIALIZE(Location, MAP_JSON(MAP_KEY(uri), MAP_KEY(range)), {FROM_KEY(uri);FROM_KEY(range)}); 126 | 127 | struct TextEdit { 128 | /// The range of the text document to be manipulated. To insert 129 | /// text into a document create a range where start === end. 130 | Range range; 131 | 132 | /// The string to be inserted. For delete operations use an 133 | /// empty string. 134 | std::string newText; 135 | }; 136 | JSON_SERIALIZE(TextEdit, MAP_JSON(MAP_KEY(range), MAP_KEY(newText)), {FROM_KEY(range);FROM_KEY(newText);}); 137 | 138 | struct TextDocumentItem { 139 | /// The text document's URI. 140 | DocumentUri uri; 141 | 142 | /// The text document's language identifier. 143 | string_ref languageId; 144 | 145 | /// The version number of this document (it will strictly increase after each 146 | int version = 0; 147 | 148 | /// The content of the opened text document. 149 | string_ref text; 150 | }; 151 | JSON_SERIALIZE(TextDocumentItem, MAP_JSON( 152 | MAP_KEY(uri), MAP_KEY(languageId), MAP_KEY(version), MAP_KEY(text)), {}); 153 | 154 | enum class TraceLevel { 155 | Off = 0, 156 | Messages = 1, 157 | Verbose = 2, 158 | }; 159 | enum class TextDocumentSyncKind { 160 | /// Documents should not be synced at all. 161 | None = 0, 162 | /// Documents are synced by always sending the full content of the document. 163 | Full = 1, 164 | /// Documents are synced by sending the full content on open. After that 165 | /// only incremental updates to the document are send. 166 | Incremental = 2, 167 | }; 168 | enum class CompletionItemKind { 169 | Missing = 0, 170 | Text = 1, 171 | Method = 2, 172 | Function = 3, 173 | Constructor = 4, 174 | Field = 5, 175 | Variable = 6, 176 | Class = 7, 177 | Interface = 8, 178 | Module = 9, 179 | Property = 10, 180 | Unit = 11, 181 | Value = 12, 182 | Enum = 13, 183 | Keyword = 14, 184 | Snippet = 15, 185 | Color = 16, 186 | File = 17, 187 | Reference = 18, 188 | Folder = 19, 189 | EnumMember = 20, 190 | Constant = 21, 191 | Struct = 22, 192 | Event = 23, 193 | Operator = 24, 194 | TypeParameter = 25, 195 | }; 196 | enum class SymbolKind { 197 | File = 1, 198 | Module = 2, 199 | Namespace = 3, 200 | Package = 4, 201 | Class = 5, 202 | Method = 6, 203 | Property = 7, 204 | Field = 8, 205 | Constructor = 9, 206 | Enum = 10, 207 | Interface = 11, 208 | Function = 12, 209 | Variable = 13, 210 | Constant = 14, 211 | String = 15, 212 | Number = 16, 213 | Boolean = 17, 214 | Array = 18, 215 | Object = 19, 216 | Key = 20, 217 | Null = 21, 218 | EnumMember = 22, 219 | Struct = 23, 220 | Event = 24, 221 | Operator = 25, 222 | TypeParameter = 26 223 | }; 224 | enum class OffsetEncoding { 225 | // Any string is legal on the wire. Unrecognized encodings parse as this. 226 | UnsupportedEncoding, 227 | // Length counts code units of UTF-16 encoded text. (Standard LSP behavior). 228 | UTF16, 229 | // Length counts bytes of UTF-8 encoded text. (Clangd extension). 230 | UTF8, 231 | // Length counts codepoints in unicode text. (Clangd extension). 232 | UTF32, 233 | }; 234 | enum class MarkupKind { 235 | PlainText, 236 | Markdown, 237 | }; 238 | enum class ResourceOperationKind { 239 | Create, 240 | Rename, 241 | Delete 242 | }; 243 | enum class FailureHandlingKind { 244 | Abort, 245 | Transactional, 246 | Undo, 247 | TextOnlyTransactional 248 | }; 249 | NLOHMANN_JSON_SERIALIZE_ENUM(OffsetEncoding, { 250 | {OffsetEncoding::UnsupportedEncoding, "unspported"}, 251 | {OffsetEncoding::UTF8, "utf-8"}, 252 | {OffsetEncoding::UTF16, "utf-16"}, 253 | {OffsetEncoding::UTF32, "utf-32"}, 254 | }) 255 | NLOHMANN_JSON_SERIALIZE_ENUM(MarkupKind, { 256 | {MarkupKind::PlainText, "plaintext"}, 257 | {MarkupKind::Markdown, "markdown"}, 258 | }) 259 | NLOHMANN_JSON_SERIALIZE_ENUM(ResourceOperationKind, { 260 | {ResourceOperationKind::Create, "create"}, 261 | {ResourceOperationKind::Rename, "rename"}, 262 | {ResourceOperationKind::Delete, "dename"} 263 | }) 264 | NLOHMANN_JSON_SERIALIZE_ENUM(FailureHandlingKind, { 265 | {FailureHandlingKind::Abort, "abort"}, 266 | {FailureHandlingKind::Transactional, "transactional"}, 267 | {FailureHandlingKind::Undo, "undo"}, 268 | {FailureHandlingKind::TextOnlyTransactional, "textOnlyTransactional"} 269 | }) 270 | 271 | struct ClientCapabilities { 272 | /// The supported set of SymbolKinds for workspace/symbol. 273 | /// workspace.symbol.symbolKind.valueSet 274 | std::vector WorkspaceSymbolKinds; 275 | /// Whether the client accepts diagnostics with codeActions attached inline. 276 | /// textDocument.publishDiagnostics.codeActionsInline. 277 | bool DiagnosticFixes = true; 278 | 279 | /// Whether the client accepts diagnostics with related locations. 280 | /// textDocument.publishDiagnostics.relatedInformation. 281 | bool DiagnosticRelatedInformation = true; 282 | 283 | /// Whether the client accepts diagnostics with category attached to it 284 | /// using the "category" extension. 285 | /// textDocument.publishDiagnostics.categorySupport 286 | bool DiagnosticCategory = true; 287 | 288 | /// Client supports snippets as insert text. 289 | /// textDocument.completion.completionItem.snippetSupport 290 | bool CompletionSnippets = true; 291 | 292 | bool CompletionDeprecated = true; 293 | 294 | /// Client supports completions with additionalTextEdit near the cursor. 295 | /// This is a clangd extension. (LSP says this is for unrelated text only). 296 | /// textDocument.completion.editsNearCursor 297 | bool CompletionFixes = true; 298 | 299 | /// Client supports hierarchical document symbols. 300 | bool HierarchicalDocumentSymbol = true; 301 | 302 | /// Client supports processing label offsets instead of a simple label string. 303 | bool OffsetsInSignatureHelp = true; 304 | 305 | /// The supported set of CompletionItemKinds for textDocument/completion. 306 | /// textDocument.completion.completionItemKind.valueSet 307 | std::vector CompletionItemKinds; 308 | 309 | /// Client supports CodeAction return value for textDocument/codeAction. 310 | /// textDocument.codeAction.codeActionLiteralSupport. 311 | bool CodeActionStructure = true; 312 | /// Supported encodings for LSP character offsets. (clangd extension). 313 | std::vector offsetEncoding = {OffsetEncoding::UTF8}; 314 | /// The content format that should be used for Hover requests. 315 | std::vector HoverContentFormat = {MarkupKind::PlainText}; 316 | 317 | bool ApplyEdit = false; 318 | bool DocumentChanges = false; 319 | ClientCapabilities() { 320 | for (int i = 1; i <= 26; ++i) { 321 | WorkspaceSymbolKinds.push_back((SymbolKind) i); 322 | } 323 | for (int i = 0; i <= 25; ++i) { 324 | CompletionItemKinds.push_back((CompletionItemKind) i); 325 | } 326 | } 327 | }; 328 | JSON_SERIALIZE(ClientCapabilities,MAP_JSON( 329 | MAP_KV("textDocument", 330 | MAP_KV("publishDiagnostics", // PublishDiagnosticsClientCapabilities 331 | MAP_TO("categorySupport", DiagnosticCategory), 332 | MAP_TO("codeActionsInline", DiagnosticFixes), 333 | MAP_TO("relatedInformation", DiagnosticRelatedInformation), 334 | ), 335 | MAP_KV("completion", // CompletionClientCapabilities 336 | MAP_KV("completionItem", 337 | MAP_TO("snippetSupport", CompletionSnippets), 338 | MAP_TO("deprecatedSupport", CompletionDeprecated)), 339 | MAP_KV("completionItemKind", MAP_TO("valueSet", CompletionItemKinds)), 340 | MAP_TO("editsNearCursor", CompletionFixes) 341 | ), 342 | MAP_KV("codeAction", MAP_TO("codeActionLiteralSupport", CodeActionStructure)), 343 | MAP_KV("documentSymbol", MAP_TO("hierarchicalDocumentSymbolSupport", HierarchicalDocumentSymbol)), 344 | MAP_KV("hover", //HoverClientCapabilities 345 | MAP_TO("contentFormat", HoverContentFormat)), 346 | MAP_KV("signatureHelp", MAP_KV("signatureInformation", MAP_KV("parameterInformation", MAP_TO("labelOffsetSupport", OffsetsInSignatureHelp))))), 347 | MAP_KV("workspace", // WorkspaceEditClientCapabilities 348 | MAP_KV("symbol", // WorkspaceSymbolClientCapabilities 349 | MAP_KV("symbolKind", 350 | MAP_TO("valueSet", WorkspaceSymbolKinds))), 351 | MAP_TO("applyEdit", ApplyEdit), 352 | MAP_KV("workspaceEdit", // WorkspaceEditClientCapabilities 353 | MAP_TO("documentChanges", DocumentChanges))), 354 | MAP_TO("offsetEncoding", offsetEncoding)), {}); 355 | 356 | struct ServerCapabilities { 357 | json capabilities; 358 | /** 359 | * Defines how text documents are synced. Is either a detailed structure defining each notification or 360 | * for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`. 361 | */ 362 | TextDocumentSyncKind textDocumentSync = TextDocumentSyncKind::None; 363 | bool resolveProvider = false; 364 | std::vector executeCommands; 365 | std::vector signatureHelpTrigger; 366 | std::vector formattingTrigger; 367 | std::vector completionTrigger; 368 | bool hasProvider(std::string &name) { 369 | if (capabilities.contains(name)) { 370 | if (capabilities[name].type() == json::value_t::boolean) { 371 | return capabilities["name"]; 372 | } 373 | } 374 | return false; 375 | } 376 | }; 377 | JSON_SERIALIZE(ServerCapabilities, {}, { 378 | value.capabilities = j; 379 | FROM_KEY(textDocumentSync); 380 | j["documentOnTypeFormattingProvider"]["firstTriggerCharacter"].get_to(value.formattingTrigger); 381 | j["completionProvider"]["resolveProvider"].get_to(value.resolveProvider); 382 | j["completionProvider"]["triggerCharacters"].get_to(value.completionTrigger); 383 | j["executeCommandProvider"]["commands"].get_to(value.executeCommands); 384 | }); 385 | 386 | struct ClangdCompileCommand { 387 | TextType workingDirectory; 388 | std::vector compilationCommand; 389 | }; 390 | JSON_SERIALIZE(ClangdCompileCommand,MAP_JSON( 391 | MAP_KEY(workingDirectory), MAP_KEY(compilationCommand)), {}); 392 | 393 | struct ConfigurationSettings { 394 | // Changes to the in-memory compilation database. 395 | // The key of the map is a file name. 396 | std::map compilationDatabaseChanges; 397 | }; 398 | JSON_SERIALIZE(ConfigurationSettings, MAP_JSON(MAP_KEY(compilationDatabaseChanges)), {}); 399 | 400 | struct InitializationOptions { 401 | // What we can change throught the didChangeConfiguration request, we can 402 | // also set through the initialize request (initializationOptions field). 403 | ConfigurationSettings configSettings; 404 | 405 | option compilationDatabasePath; 406 | // Additional flags to be included in the "fallback command" used when 407 | // the compilation database doesn't describe an opened file. 408 | // The command used will be approximately `clang $FILE $fallbackFlags`. 409 | std::vector fallbackFlags; 410 | 411 | /// Clients supports show file status for textDocument/clangd.fileStatus. 412 | bool clangdFileStatus = false; 413 | }; 414 | JSON_SERIALIZE(InitializationOptions, MAP_JSON( 415 | MAP_KEY(configSettings), 416 | MAP_KEY(compilationDatabasePath), 417 | MAP_KEY(fallbackFlags), 418 | MAP_KEY(clangdFileStatus)), {}); 419 | 420 | struct InitializeParams { 421 | unsigned processId = 0; 422 | ClientCapabilities capabilities; 423 | option rootUri; 424 | option rootPath; 425 | InitializationOptions initializationOptions; 426 | }; 427 | JSON_SERIALIZE(InitializeParams, MAP_JSON( 428 | MAP_KEY(processId), 429 | MAP_KEY(capabilities), 430 | MAP_KEY(rootUri), 431 | MAP_KEY(initializationOptions), 432 | MAP_KEY(rootPath)), {}); 433 | 434 | enum class MessageType { 435 | /// An error message. 436 | Error = 1, 437 | /// A warning message. 438 | Warning = 2, 439 | /// An information message. 440 | Info = 3, 441 | /// A log message. 442 | Log = 4, 443 | }; 444 | struct ShowMessageParams { 445 | /// The message type. 446 | MessageType type = MessageType::Info; 447 | /// The actual message. 448 | std::string message; 449 | }; 450 | JSON_SERIALIZE(ShowMessageParams, {}, {FROM_KEY(type); FROM_KEY(message)}); 451 | 452 | struct Registration { 453 | /** 454 | * The id used to register the request. The id can be used to deregister 455 | * the request again. 456 | */ 457 | TextType id; 458 | /** 459 | * The method / capability to register for. 460 | */ 461 | TextType method; 462 | }; 463 | JSON_SERIALIZE(Registration, MAP_JSON(MAP_KEY(id), MAP_KEY(method)), {}); 464 | 465 | struct RegistrationParams { 466 | std::vector registrations; 467 | }; 468 | JSON_SERIALIZE(RegistrationParams, MAP_JSON(MAP_KEY(registrations)), {}); 469 | 470 | struct UnregistrationParams { 471 | std::vector unregisterations; 472 | }; 473 | JSON_SERIALIZE(UnregistrationParams, MAP_JSON(MAP_KEY(unregisterations)), {}); 474 | 475 | struct DidOpenTextDocumentParams { 476 | /// The document that was opened. 477 | TextDocumentItem textDocument; 478 | }; 479 | JSON_SERIALIZE(DidOpenTextDocumentParams, MAP_JSON(MAP_KEY(textDocument)), {}); 480 | 481 | struct DidCloseTextDocumentParams { 482 | /// The document that was closed. 483 | TextDocumentIdentifier textDocument; 484 | }; 485 | JSON_SERIALIZE(DidCloseTextDocumentParams, MAP_JSON(MAP_KEY(textDocument)), {}); 486 | 487 | struct TextDocumentContentChangeEvent { 488 | /// The range of the document that changed. 489 | option range; 490 | 491 | /// The length of the range that got replaced. 492 | option rangeLength; 493 | /// The new text of the range/document. 494 | std::string text; 495 | }; 496 | JSON_SERIALIZE(TextDocumentContentChangeEvent, MAP_JSON(MAP_KEY(range), MAP_KEY(rangeLength), MAP_KEY(text)), {}); 497 | 498 | struct DidChangeTextDocumentParams { 499 | /// The document that did change. The version number points 500 | /// to the version after all provided content changes have 501 | /// been applied. 502 | TextDocumentIdentifier textDocument; 503 | 504 | /// The actual content changes. 505 | std::vector contentChanges; 506 | 507 | /// Forces diagnostics to be generated, or to not be generated, for this 508 | /// version of the file. If not set, diagnostics are eventually consistent: 509 | /// either they will be provided for this version or some subsequent one. 510 | /// This is a clangd extension. 511 | option wantDiagnostics; 512 | }; 513 | JSON_SERIALIZE(DidChangeTextDocumentParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(contentChanges), MAP_KEY(wantDiagnostics)), {}); 514 | 515 | enum class FileChangeType { 516 | /// The file got created. 517 | Created = 1, 518 | /// The file got changed. 519 | Changed = 2, 520 | /// The file got deleted. 521 | Deleted = 3 522 | }; 523 | struct FileEvent { 524 | /// The file's URI. 525 | URIForFile uri; 526 | /// The change type. 527 | FileChangeType type = FileChangeType::Created; 528 | }; 529 | JSON_SERIALIZE(FileEvent, MAP_JSON(MAP_KEY(uri), MAP_KEY(type)), {}); 530 | 531 | struct DidChangeWatchedFilesParams { 532 | /// The actual file events. 533 | std::vector changes; 534 | }; 535 | JSON_SERIALIZE(DidChangeWatchedFilesParams, MAP_JSON(MAP_KEY(changes)), {}); 536 | 537 | struct DidChangeConfigurationParams { 538 | ConfigurationSettings settings; 539 | }; 540 | JSON_SERIALIZE(DidChangeConfigurationParams, MAP_JSON(MAP_KEY(settings)), {}); 541 | 542 | struct DocumentRangeFormattingParams { 543 | /// The document to format. 544 | TextDocumentIdentifier textDocument; 545 | 546 | /// The range to format 547 | Range range; 548 | }; 549 | JSON_SERIALIZE(DocumentRangeFormattingParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(range)), {}); 550 | 551 | struct DocumentOnTypeFormattingParams { 552 | /// The document to format. 553 | TextDocumentIdentifier textDocument; 554 | 555 | /// The position at which this request was sent. 556 | Position position; 557 | 558 | /// The character that has been typed. 559 | TextType ch; 560 | }; 561 | JSON_SERIALIZE(DocumentOnTypeFormattingParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(ch)), {}); 562 | 563 | struct FoldingRangeParams { 564 | /// The document to format. 565 | TextDocumentIdentifier textDocument; 566 | }; 567 | JSON_SERIALIZE(FoldingRangeParams, MAP_JSON(MAP_KEY(textDocument)), {}); 568 | 569 | enum class FoldingRangeKind { 570 | Comment, 571 | Imports, 572 | Region, 573 | }; 574 | NLOHMANN_JSON_SERIALIZE_ENUM(FoldingRangeKind, { 575 | {FoldingRangeKind::Comment, "comment"}, 576 | {FoldingRangeKind::Imports, "imports"}, 577 | {FoldingRangeKind::Region, "region"} 578 | }) 579 | 580 | struct FoldingRange { 581 | /** 582 | * The zero-based line number from where the folded range starts. 583 | */ 584 | int startLine; 585 | /** 586 | * The zero-based character offset from where the folded range starts. 587 | * If not defined, defaults to the length of the start line. 588 | */ 589 | int startCharacter; 590 | /** 591 | * The zero-based line number where the folded range ends. 592 | */ 593 | int endLine; 594 | /** 595 | * The zero-based character offset before the folded range ends. 596 | * If not defined, defaults to the length of the end line. 597 | */ 598 | int endCharacter; 599 | 600 | FoldingRangeKind kind; 601 | }; 602 | JSON_SERIALIZE(FoldingRange, {}, { 603 | FROM_KEY(startLine); 604 | FROM_KEY(startCharacter); 605 | FROM_KEY(endLine); 606 | FROM_KEY(endCharacter); 607 | FROM_KEY(kind); 608 | }); 609 | 610 | struct SelectionRangeParams { 611 | /// The document to format. 612 | TextDocumentIdentifier textDocument; 613 | std::vector positions; 614 | }; 615 | JSON_SERIALIZE(SelectionRangeParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(positions)), {}); 616 | 617 | struct SelectionRange { 618 | Range range; 619 | std::unique_ptr parent; 620 | }; 621 | JSON_SERIALIZE(SelectionRange, {}, { 622 | FROM_KEY(range); 623 | if (j.contains("parent")) { 624 | value.parent = std::make_unique(); 625 | j.at("parent").get_to(*value.parent); 626 | } 627 | }); 628 | 629 | struct DocumentFormattingParams { 630 | /// The document to format. 631 | TextDocumentIdentifier textDocument; 632 | }; 633 | JSON_SERIALIZE(DocumentFormattingParams, MAP_JSON(MAP_KEY(textDocument)), {}); 634 | 635 | struct DocumentSymbolParams { 636 | // The text document to find symbols in. 637 | TextDocumentIdentifier textDocument; 638 | }; 639 | JSON_SERIALIZE(DocumentSymbolParams, MAP_JSON(MAP_KEY(textDocument)), {}); 640 | 641 | struct DiagnosticRelatedInformation { 642 | /// The location of this related diagnostic information. 643 | Location location; 644 | /// The message of this related diagnostic information. 645 | std::string message; 646 | }; 647 | JSON_SERIALIZE(DiagnosticRelatedInformation, MAP_JSON(MAP_KEY(location), MAP_KEY(message)), {FROM_KEY(location);FROM_KEY(message);}); 648 | struct CodeAction; 649 | 650 | struct Diagnostic { 651 | /// The range at which the message applies. 652 | Range range; 653 | 654 | /// The diagnostic's severity. Can be omitted. If omitted it is up to the 655 | /// client to interpret diagnostics as error, warning, info or hint. 656 | int severity = 0; 657 | 658 | /// The diagnostic's code. Can be omitted. 659 | std::string code; 660 | 661 | /// A human-readable string describing the source of this 662 | /// diagnostic, e.g. 'typescript' or 'super lint'. 663 | std::string source; 664 | 665 | /// The diagnostic's message. 666 | std::string message; 667 | 668 | /// An array of related diagnostic information, e.g. when symbol-names within 669 | /// a scope collide all definitions can be marked via this property. 670 | option> relatedInformation; 671 | 672 | /// The diagnostic's category. Can be omitted. 673 | /// An LSP extension that's used to send the name of the category over to the 674 | /// client. The category typically describes the compilation stage during 675 | /// which the issue was produced, e.g. "Semantic Issue" or "Parse Issue". 676 | option category; 677 | 678 | /// Clangd extension: code actions related to this diagnostic. 679 | /// Only with capability textDocument.publishDiagnostics.codeActionsInline. 680 | /// (These actions can also be obtained using textDocument/codeAction). 681 | option> codeActions; 682 | }; 683 | JSON_SERIALIZE(Diagnostic, {/*NOT REQUIRED*/},{FROM_KEY(range);FROM_KEY(code);FROM_KEY(source);FROM_KEY(message); 684 | FROM_KEY(relatedInformation);FROM_KEY(category);FROM_KEY(codeActions);}); 685 | 686 | struct PublishDiagnosticsParams { 687 | /** 688 | * The URI for which diagnostic information is reported. 689 | */ 690 | std::string uri; 691 | /** 692 | * An array of diagnostic information items. 693 | */ 694 | std::vector diagnostics; 695 | }; 696 | JSON_SERIALIZE(PublishDiagnosticsParams, {}, {FROM_KEY(uri);FROM_KEY(diagnostics);}); 697 | 698 | struct CodeActionContext { 699 | /// An array of diagnostics. 700 | std::vector diagnostics; 701 | }; 702 | JSON_SERIALIZE(CodeActionContext, MAP_JSON(MAP_KEY(diagnostics)), {}); 703 | 704 | struct CodeActionParams { 705 | /// The document in which the command was invoked. 706 | TextDocumentIdentifier textDocument; 707 | 708 | /// The range for which the command was invoked. 709 | Range range; 710 | 711 | /// Context carrying additional information. 712 | CodeActionContext context; 713 | }; 714 | JSON_SERIALIZE(CodeActionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(range), MAP_KEY(context)), {}); 715 | 716 | struct WorkspaceEdit { 717 | /// Holds changes to existing resources. 718 | option>> changes; 719 | 720 | /// Note: "documentChanges" is not currently used because currently there is 721 | /// no support for versioned edits. 722 | }; 723 | JSON_SERIALIZE(WorkspaceEdit, MAP_JSON(MAP_KEY(changes)), {FROM_KEY(changes);}); 724 | 725 | struct TweakArgs { 726 | /// A file provided by the client on a textDocument/codeAction request. 727 | std::string file; 728 | /// A selection provided by the client on a textDocument/codeAction request. 729 | Range selection; 730 | /// ID of the tweak that should be executed. Corresponds to Tweak::id(). 731 | std::string tweakID; 732 | }; 733 | JSON_SERIALIZE(TweakArgs, MAP_JSON(MAP_KEY(file), MAP_KEY(selection), MAP_KEY(tweakID)), {FROM_KEY(file);FROM_KEY(selection);FROM_KEY(tweakID);}); 734 | 735 | struct ExecuteCommandParams { 736 | std::string command; 737 | // Arguments 738 | option workspaceEdit; 739 | option tweakArgs; 740 | }; 741 | JSON_SERIALIZE(ExecuteCommandParams, MAP_JSON(MAP_KEY(command), MAP_KEY(workspaceEdit), MAP_KEY(tweakArgs)), {}); 742 | 743 | struct LspCommand : public ExecuteCommandParams { 744 | std::string title; 745 | }; 746 | JSON_SERIALIZE(LspCommand, MAP_JSON(MAP_KEY(command), MAP_KEY(workspaceEdit), MAP_KEY(tweakArgs), MAP_KEY(title)), 747 | {FROM_KEY(command);FROM_KEY(workspaceEdit);FROM_KEY(tweakArgs);FROM_KEY(title);}); 748 | 749 | struct CodeAction { 750 | /// A short, human-readable, title for this code action. 751 | std::string title; 752 | 753 | /// The kind of the code action. 754 | /// Used to filter code actions. 755 | option kind; 756 | /// The diagnostics that this code action resolves. 757 | option> diagnostics; 758 | 759 | /// The workspace edit this code action performs. 760 | option edit; 761 | 762 | /// A command this code action executes. If a code action provides an edit 763 | /// and a command, first the edit is executed and then the command. 764 | option command; 765 | }; 766 | JSON_SERIALIZE(CodeAction, MAP_JSON(MAP_KEY(title), MAP_KEY(kind), MAP_KEY(diagnostics), MAP_KEY(edit), MAP_KEY(command)), 767 | {FROM_KEY(title);FROM_KEY(kind);FROM_KEY(diagnostics);FROM_KEY(edit);FROM_KEY(command)}); 768 | 769 | struct SymbolInformation { 770 | /// The name of this symbol. 771 | std::string name; 772 | /// The kind of this symbol. 773 | SymbolKind kind = SymbolKind::Class; 774 | /// The location of this symbol. 775 | Location location; 776 | /// The name of the symbol containing this symbol. 777 | std::string containerName; 778 | }; 779 | 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)}); 780 | 781 | struct SymbolDetails { 782 | TextType name; 783 | TextType containerName; 784 | /// Unified Symbol Resolution identifier 785 | /// This is an opaque string uniquely identifying a symbol. 786 | /// Unlike SymbolID, it is variable-length and somewhat human-readable. 787 | /// It is a common representation across several clang tools. 788 | /// (See USRGeneration.h) 789 | TextType USR; 790 | option ID; 791 | }; 792 | 793 | struct WorkspaceSymbolParams { 794 | /// A non-empty query string 795 | TextType query; 796 | }; 797 | JSON_SERIALIZE(WorkspaceSymbolParams, MAP_JSON(MAP_KEY(query)), {}); 798 | 799 | struct ApplyWorkspaceEditParams { 800 | WorkspaceEdit edit; 801 | }; 802 | JSON_SERIALIZE(ApplyWorkspaceEditParams, MAP_JSON(MAP_KEY(edit)), {}); 803 | 804 | struct TextDocumentPositionParams { 805 | /// The text document. 806 | TextDocumentIdentifier textDocument; 807 | 808 | /// The position inside the text document. 809 | Position position; 810 | }; 811 | JSON_SERIALIZE(TextDocumentPositionParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position)), {}); 812 | 813 | enum class CompletionTriggerKind { 814 | /// Completion was triggered by typing an identifier (24x7 code 815 | /// complete), manual invocation (e.g Ctrl+Space) or via API. 816 | Invoked = 1, 817 | /// Completion was triggered by a trigger character specified by 818 | /// the `triggerCharacters` properties of the `CompletionRegistrationOptions`. 819 | TriggerCharacter = 2, 820 | /// Completion was re-triggered as the current completion list is incomplete. 821 | TriggerTriggerForIncompleteCompletions = 3 822 | }; 823 | struct CompletionContext { 824 | /// How the completion was triggered. 825 | CompletionTriggerKind triggerKind = CompletionTriggerKind::Invoked; 826 | /// The trigger character (a single character) that has trigger code complete. 827 | /// Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` 828 | option triggerCharacter; 829 | }; 830 | JSON_SERIALIZE(CompletionContext, MAP_JSON(MAP_KEY(triggerKind), MAP_KEY(triggerCharacter)), {}); 831 | 832 | struct CompletionParams : TextDocumentPositionParams { 833 | option context; 834 | }; 835 | JSON_SERIALIZE(CompletionParams, MAP_JSON(MAP_KEY(context), MAP_KEY(textDocument), MAP_KEY(position)), {}); 836 | 837 | struct MarkupContent { 838 | MarkupKind kind = MarkupKind::PlainText; 839 | std::string value; 840 | }; 841 | JSON_SERIALIZE(MarkupContent, {}, {FROM_KEY(kind);FROM_KEY(value)}); 842 | 843 | struct Hover { 844 | /// The hover's content 845 | MarkupContent contents; 846 | 847 | /// An optional range is a range inside a text document 848 | /// that is used to visualize a hover, e.g. by changing the background color. 849 | option range; 850 | }; 851 | JSON_SERIALIZE(Hover, {}, {FROM_KEY(contents);FROM_KEY(range)}); 852 | 853 | enum class InsertTextFormat { 854 | Missing = 0, 855 | /// The primary text to be inserted is treated as a plain string. 856 | PlainText = 1, 857 | /// The primary text to be inserted is treated as a snippet. 858 | /// 859 | /// A snippet can define tab stops and placeholders with `$1`, `$2` 860 | /// and `${3:foo}`. `$0` defines the final tab stop, it defaults to the end 861 | /// of the snippet. Placeholders with equal identifiers are linked, that is 862 | /// typing in one will update others too. 863 | /// 864 | /// See also: 865 | /// https//github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md 866 | Snippet = 2, 867 | }; 868 | struct CompletionItem { 869 | /// The label of this completion item. By default also the text that is 870 | /// inserted when selecting this completion. 871 | std::string label; 872 | 873 | /// The kind of this completion item. Based of the kind an icon is chosen by 874 | /// the editor. 875 | CompletionItemKind kind = CompletionItemKind::Missing; 876 | 877 | /// A human-readable string with additional information about this item, like 878 | /// type or symbol information. 879 | std::string detail; 880 | 881 | /// A human-readable string that represents a doc-comment. 882 | std::string documentation; 883 | 884 | /// A string that should be used when comparing this item with other items. 885 | /// When `falsy` the label is used. 886 | std::string sortText; 887 | 888 | /// A string that should be used when filtering a set of completion items. 889 | /// When `falsy` the label is used. 890 | std::string filterText; 891 | 892 | /// A string that should be inserted to a document when selecting this 893 | /// completion. When `falsy` the label is used. 894 | std::string insertText; 895 | 896 | /// The format of the insert text. The format applies to both the `insertText` 897 | /// property and the `newText` property of a provided `textEdit`. 898 | InsertTextFormat insertTextFormat = InsertTextFormat::Missing; 899 | 900 | /// An edit which is applied to a document when selecting this completion. 901 | /// When an edit is provided `insertText` is ignored. 902 | /// 903 | /// Note: The range of the edit must be a single line range and it must 904 | /// contain the position at which completion has been requested. 905 | TextEdit textEdit; 906 | 907 | /// An optional array of additional text edits that are applied when selecting 908 | /// this completion. Edits must not overlap with the main edit nor with 909 | /// themselves. 910 | std::vector additionalTextEdits; 911 | 912 | /// Indicates if this item is deprecated. 913 | bool deprecated = false; 914 | 915 | // TODO(krasimir): The following optional fields defined by the language 916 | // server protocol are unsupported: 917 | // 918 | // data?: any - A data entry field that is preserved on a completion item 919 | // between a completion and a completion resolve request. 920 | }; 921 | JSON_SERIALIZE(CompletionItem, {}, { 922 | FROM_KEY(label); 923 | FROM_KEY(kind); 924 | FROM_KEY(detail); 925 | FROM_KEY(documentation); 926 | FROM_KEY(sortText); 927 | FROM_KEY(filterText); 928 | FROM_KEY(insertText); 929 | FROM_KEY(insertTextFormat); 930 | FROM_KEY(textEdit); 931 | FROM_KEY(additionalTextEdits); 932 | }); 933 | 934 | struct CompletionList { 935 | /// The list is not complete. Further typing should result in recomputing the 936 | /// list. 937 | bool isIncomplete = false; 938 | 939 | /// The completion items. 940 | std::vector items; 941 | }; 942 | JSON_SERIALIZE(CompletionList, {}, { 943 | FROM_KEY(isIncomplete); 944 | FROM_KEY(items); 945 | }); 946 | 947 | struct ParameterInformation { 948 | 949 | /// The label of this parameter. Ignored when labelOffsets is set. 950 | std::string labelString; 951 | 952 | /// Inclusive start and exclusive end offsets withing the containing signature 953 | /// label. 954 | /// Offsets are computed by lspLength(), which counts UTF-16 code units by 955 | /// default but that can be overriden, see its documentation for details. 956 | option> labelOffsets; 957 | 958 | /// The documentation of this parameter. Optional. 959 | std::string documentation; 960 | }; 961 | JSON_SERIALIZE(ParameterInformation, {}, { 962 | FROM_KEY(labelString); 963 | FROM_KEY(labelOffsets); 964 | FROM_KEY(documentation); 965 | }); 966 | struct SignatureInformation { 967 | 968 | /// The label of this signature. Mandatory. 969 | std::string label; 970 | 971 | /// The documentation of this signature. Optional. 972 | std::string documentation; 973 | 974 | /// The parameters of this signature. 975 | std::vector parameters; 976 | }; 977 | JSON_SERIALIZE(SignatureInformation, {}, { 978 | FROM_KEY(label); 979 | FROM_KEY(documentation); 980 | FROM_KEY(parameters); 981 | }); 982 | struct SignatureHelp { 983 | /// The resulting signatures. 984 | std::vector signatures; 985 | /// The active signature. 986 | int activeSignature = 0; 987 | /// The active parameter of the active signature. 988 | int activeParameter = 0; 989 | /// Position of the start of the argument list, including opening paren. e.g. 990 | /// foo("first arg", "second arg", 991 | /// ^-argListStart ^-cursor 992 | /// This is a clangd-specific extension, it is only available via C++ API and 993 | /// not currently serialized for the LSP. 994 | Position argListStart; 995 | }; 996 | JSON_SERIALIZE(SignatureHelp, {}, { 997 | FROM_KEY(signatures); 998 | FROM_KEY(activeParameter); 999 | FROM_KEY(argListStart); 1000 | }); 1001 | 1002 | struct RenameParams { 1003 | /// The document that was opened. 1004 | TextDocumentIdentifier textDocument; 1005 | 1006 | /// The position at which this request was sent. 1007 | Position position; 1008 | 1009 | /// The new name of the symbol. 1010 | std::string newName; 1011 | }; 1012 | JSON_SERIALIZE(RenameParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position), MAP_KEY(newName)), {}); 1013 | 1014 | enum class DocumentHighlightKind { Text = 1, Read = 2, Write = 3 }; 1015 | 1016 | struct DocumentHighlight { 1017 | /// The range this highlight applies to. 1018 | Range range; 1019 | /// The highlight kind, default is DocumentHighlightKind.Text. 1020 | DocumentHighlightKind kind = DocumentHighlightKind::Text; 1021 | friend bool operator<(const DocumentHighlight &LHS, 1022 | const DocumentHighlight &RHS) { 1023 | int LHSKind = static_cast(LHS.kind); 1024 | int RHSKind = static_cast(RHS.kind); 1025 | return std::tie(LHS.range, LHSKind) < std::tie(RHS.range, RHSKind); 1026 | } 1027 | friend bool operator==(const DocumentHighlight &LHS, 1028 | const DocumentHighlight &RHS) { 1029 | return LHS.kind == RHS.kind && LHS.range == RHS.range; 1030 | } 1031 | }; 1032 | enum class TypeHierarchyDirection { Children = 0, Parents = 1, Both = 2 }; 1033 | 1034 | struct TypeHierarchyParams : public TextDocumentPositionParams { 1035 | /// The hierarchy levels to resolve. `0` indicates no level. 1036 | int resolve = 0; 1037 | 1038 | /// The direction of the hierarchy levels to resolve. 1039 | TypeHierarchyDirection direction = TypeHierarchyDirection::Parents; 1040 | }; 1041 | JSON_SERIALIZE(TypeHierarchyParams, MAP_JSON(MAP_KEY(resolve), MAP_KEY(direction), MAP_KEY(textDocument), MAP_KEY(position)), {}); 1042 | 1043 | struct TypeHierarchyItem { 1044 | /// The human readable name of the hierarchy item. 1045 | std::string name; 1046 | 1047 | /// Optional detail for the hierarchy item. It can be, for instance, the 1048 | /// signature of a function or method. 1049 | option detail; 1050 | 1051 | /// The kind of the hierarchy item. For instance, class or interface. 1052 | SymbolKind kind; 1053 | 1054 | /// `true` if the hierarchy item is deprecated. Otherwise, `false`. 1055 | bool deprecated; 1056 | 1057 | /// The URI of the text document where this type hierarchy item belongs to. 1058 | DocumentUri uri; 1059 | 1060 | /// The range enclosing this type hierarchy item not including 1061 | /// leading/trailing whitespace but everything else like comments. This 1062 | /// information is typically used to determine if the client's cursor is 1063 | /// inside the type hierarch item to reveal in the symbol in the UI. 1064 | Range range; 1065 | 1066 | /// The range that should be selected and revealed when this type hierarchy 1067 | /// item is being picked, e.g. the name of a function. Must be contained by 1068 | /// the `range`. 1069 | Range selectionRange; 1070 | 1071 | /// If this type hierarchy item is resolved, it contains the direct parents. 1072 | /// Could be empty if the item does not have direct parents. If not defined, 1073 | /// the parents have not been resolved yet. 1074 | option> parents; 1075 | 1076 | /// If this type hierarchy item is resolved, it contains the direct children 1077 | /// of the current item. Could be empty if the item does not have any 1078 | /// descendants. If not defined, the children have not been resolved. 1079 | option> children; 1080 | 1081 | /// The protocol has a slot here for an optional 'data' filed, which can 1082 | /// be used to identify a type hierarchy item in a resolve request. We don't 1083 | /// need this (the item itself is sufficient to identify what to resolve) 1084 | /// so don't declare it. 1085 | }; 1086 | 1087 | struct ReferenceParams : public TextDocumentPositionParams { 1088 | // For now, no options like context.includeDeclaration are supported. 1089 | }; 1090 | JSON_SERIALIZE(ReferenceParams, MAP_JSON(MAP_KEY(textDocument), MAP_KEY(position)), {}); 1091 | struct FileStatus { 1092 | /// The text document's URI. 1093 | DocumentUri uri; 1094 | /// The human-readable string presents the current state of the file, can be 1095 | /// shown in the UI (e.g. status bar). 1096 | TextType state; 1097 | // FIXME: add detail messages. 1098 | }; 1099 | 1100 | #endif //LSP_PROTOCOL_H 1101 | --------------------------------------------------------------------------------