diff --git a/404.html b/404.html index 2ed19920..ccbb6837 100644 --- a/404.html +++ b/404.html @@ -16,7 +16,7 @@
- + \ No newline at end of file diff --git a/assets/dev_contribution.md.DlEIMhd5.js b/assets/dev_contribution.md.C0y1pn0v.js similarity index 50% rename from assets/dev_contribution.md.DlEIMhd5.js rename to assets/dev_contribution.md.C0y1pn0v.js index f30eeaa2..fb2dc4f5 100644 --- a/assets/dev_contribution.md.DlEIMhd5.js +++ b/assets/dev_contribution.md.C0y1pn0v.js @@ -1 +1 @@ -import{_ as t,c as a,o,ae as n}from"./chunks/framework.U1Gow_7P.js";const _=JSON.parse('{"title":"Contribution","description":"","frontmatter":{},"headers":[],"relativePath":"dev/contribution.md","filePath":"en/dev/contribution.md"}'),i={name:"dev/contribution.md"};function r(l,e,s,c,d,u){return o(),a("div",null,e[0]||(e[0]=[n('

Contribution

We welcome any contributions!

Please refer to build to build clice, refer to test and debug to test and debug clice.

Code Style

Naming:

',6)]))}const m=t(i,[["render",r]]);export{_ as __pageData,m as default}; +import{_ as t,c as a,o,ae as n}from"./chunks/framework.U1Gow_7P.js";const _=JSON.parse('{"title":"Contribution","description":"","frontmatter":{},"headers":[],"relativePath":"dev/contribution.md","filePath":"en/dev/contribution.md"}'),i={name:"dev/contribution.md"};function r(l,e,s,c,d,u){return o(),a("div",null,e[0]||(e[0]=[n('

Contribution

We welcome any contributions!

Please refer to build to build clice, refer to test and debug to test and debug clice.

Code Style

Naming:

',6)]))}const m=t(i,[["render",r]]);export{_ as __pageData,m as default}; diff --git a/assets/dev_contribution.md.DlEIMhd5.lean.js b/assets/dev_contribution.md.C0y1pn0v.lean.js similarity index 100% rename from assets/dev_contribution.md.DlEIMhd5.lean.js rename to assets/dev_contribution.md.C0y1pn0v.lean.js diff --git a/assets/guide_quick-start.md.B3b6j6Mp.js b/assets/guide_quick-start.md.B3b6j6Mp.js deleted file mode 100644 index 7f463a2c..00000000 --- a/assets/guide_quick-start.md.B3b6j6Mp.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as a,c as o,o as t,ae as i}from"./chunks/framework.U1Gow_7P.js";const p=JSON.parse('{"title":"Quick Start","description":"","frontmatter":{},"headers":[],"relativePath":"guide/quick-start.md","filePath":"en/guide/quick-start.md"}'),r={name:"guide/quick-start.md"};function s(l,e,n,c,d,h){return t(),o("div",null,e[0]||(e[0]=[i('

Quick Start

Editor Plugins

clice implements the Language Server Protocol. Any editor that supports this protocol can theoretically work with clice to provide features like code completion, diagnostics, go-to-definition, and more.

However, beyond the standard protocol, clice also supports some protocol extensions. For better handling of these protocol extensions and better integration with editors, using clice plugins in specific editors is often a better choice. Most of them work out of the box and support clice's protocol extensions.

Visual Studio Code

Vim/Neovim

Others

Other editors don't have available clice plugins yet (contributions welcome!). To use clice in them, please install clice yourself and refer to the specific editor's documentation on how to use a language server.

Installation

If your editor plugin handles clice's download, you can skip this step.

Download Prebuilt Binary

Download clice binary version through the Release page.

Build from Source

Build clice from source yourself. For specific steps, refer to build.

Project Setup

For clice to correctly understand your code (e.g., find header file locations), you need to provide clice with a compile_commands.json file, also known as a compilation database. The compilation database provides compilation options for each source file.

CMake

For build systems using cmake, add the -DCMAKE_EXPORT_COMPILE_COMMANDS=ON option during build, for example:

cmake
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

This will generate a compile_commands.json file in the build directory.

WARNING

Note: This option only works when cmake's generator is set to makefile and ninja. For other generators, this option will be ignored, meaning the compilation database cannot be generated.

Bazel

Bazel has no native support to generate a compilation database. The recommended solution is to use bazel-compile-commands-extractor. After setting it up, you can generate compile_commands.json with:

bash
bazel run @hedron_compile_commands//:refresh_all

Visual Studio

TODO:

Makefile

TODO:

Xmake

Others

For any other build system, you can try using bear or scan-build to intercept compilation commands and obtain the compilation database (no guarantee of success). We plan to write a new tool in the future that captures compilation commands through a fake compiler approach.

',31)]))}const m=a(r,[["render",s]]);export{p as __pageData,m as default}; diff --git a/assets/guide_quick-start.md.DrPBamDw.js b/assets/guide_quick-start.md.DrPBamDw.js new file mode 100644 index 00000000..8274ff95 --- /dev/null +++ b/assets/guide_quick-start.md.DrPBamDw.js @@ -0,0 +1 @@ +import{_ as a,c as o,o as t,ae as i}from"./chunks/framework.U1Gow_7P.js";const p=JSON.parse('{"title":"Quick Start","description":"","frontmatter":{},"headers":[],"relativePath":"guide/quick-start.md","filePath":"en/guide/quick-start.md"}'),r={name:"guide/quick-start.md"};function s(l,e,n,c,d,h){return t(),o("div",null,e[0]||(e[0]=[i('

Quick Start

Editor Plugins

clice implements the Language Server Protocol. Any editor that supports this protocol can theoretically work with clice to provide features like code completion, diagnostics, go-to-definition, and more.

However, beyond the standard protocol, clice also supports some protocol extensions. For better handling of these protocol extensions and better integration with editors, using clice plugins in specific editors is often a better choice. Most of them work out of the box and support clice's protocol extensions.

Visual Studio Code

Vim/Neovim

Others

Other editors don't have available clice plugins yet (contributions welcome!). To use clice in them, please install clice yourself and refer to the specific editor's documentation on how to use a language server.

Installation

If your editor plugin handles clice's download, you can skip this step.

Download Prebuilt Binary

Download clice binary version through the Release page.

Build from Source

Build clice from source yourself. For specific steps, refer to build.

Project Setup

For clice to correctly understand your code (e.g., find header file locations), you need to provide clice with a compile_commands.json file, also known as a compilation database. The compilation database provides compilation options for each source file.

CMake

For build systems using cmake, add the -DCMAKE_EXPORT_COMPILE_COMMANDS=ON option during build, for example:

cmake
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

This will generate a compile_commands.json file in the build directory.

WARNING

Note: This option only works when cmake's generator is set to makefile and ninja. For other generators, this option will be ignored, meaning the compilation database cannot be generated.

Bazel

Bazel has no native support to generate a compilation database. The recommended solution is to use bazel-compile-commands-extractor. After setting it up, you can generate compile_commands.json with:

bash
bazel run @hedron_compile_commands//:refresh_all

Visual Studio

TODO:

Makefile

TODO:

Xmake

Others

For any other build system, you can try using bear or scan-build to intercept compilation commands and obtain the compilation database (no guarantee of success). We plan to write a new tool in the future that captures compilation commands through a fake compiler approach.

',31)]))}const m=a(r,[["render",s]]);export{p as __pageData,m as default}; diff --git a/assets/guide_quick-start.md.B3b6j6Mp.lean.js b/assets/guide_quick-start.md.DrPBamDw.lean.js similarity index 100% rename from assets/guide_quick-start.md.B3b6j6Mp.lean.js rename to assets/guide_quick-start.md.DrPBamDw.lean.js diff --git a/assets/zh_dev_contribution.md.DTCatoP3.js b/assets/zh_dev_contribution.md.DTCatoP3.js deleted file mode 100644 index d5249ee7..00000000 --- a/assets/zh_dev_contribution.md.DTCatoP3.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,c as a,o,ae as i}from"./chunks/framework.U1Gow_7P.js";const h=JSON.parse('{"title":"Contribution","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/contribution.md","filePath":"zh/dev/contribution.md"}'),n={name:"zh/dev/contribution.md"};function r(l,t,d,c,s,u){return o(),a("div",null,t[0]||(t[0]=[i('

Contribution

我们欢迎任何贡献!

请参考 build 来构建 clice,参考 test and debug 来测试和调试 clice。

Code Style

命名

',6)]))}const b=e(n,[["render",r]]);export{h as __pageData,b as default}; diff --git a/assets/zh_dev_contribution.md.Dh-sErND.js b/assets/zh_dev_contribution.md.Dh-sErND.js new file mode 100644 index 00000000..8802863d --- /dev/null +++ b/assets/zh_dev_contribution.md.Dh-sErND.js @@ -0,0 +1 @@ +import{_ as e,c as a,o,ae as i}from"./chunks/framework.U1Gow_7P.js";const b=JSON.parse('{"title":"Contribution","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/contribution.md","filePath":"zh/dev/contribution.md"}'),n={name:"zh/dev/contribution.md"};function r(l,t,d,c,s,u){return o(),a("div",null,t[0]||(t[0]=[i('

Contribution

我们欢迎任何贡献!

请参考 build 来构建 clice,参考 test and debug 来测试和调试 clice。

Code Style

命名

',6)]))}const h=e(n,[["render",r]]);export{b as __pageData,h as default}; diff --git a/assets/zh_dev_contribution.md.DTCatoP3.lean.js b/assets/zh_dev_contribution.md.Dh-sErND.lean.js similarity index 62% rename from assets/zh_dev_contribution.md.DTCatoP3.lean.js rename to assets/zh_dev_contribution.md.Dh-sErND.lean.js index 5995dd43..b374166e 100644 --- a/assets/zh_dev_contribution.md.DTCatoP3.lean.js +++ b/assets/zh_dev_contribution.md.Dh-sErND.lean.js @@ -1 +1 @@ -import{_ as e,c as a,o,ae as i}from"./chunks/framework.U1Gow_7P.js";const h=JSON.parse('{"title":"Contribution","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/contribution.md","filePath":"zh/dev/contribution.md"}'),n={name:"zh/dev/contribution.md"};function r(l,t,d,c,s,u){return o(),a("div",null,t[0]||(t[0]=[i("",6)]))}const b=e(n,[["render",r]]);export{h as __pageData,b as default}; +import{_ as e,c as a,o,ae as i}from"./chunks/framework.U1Gow_7P.js";const b=JSON.parse('{"title":"Contribution","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/contribution.md","filePath":"zh/dev/contribution.md"}'),n={name:"zh/dev/contribution.md"};function r(l,t,d,c,s,u){return o(),a("div",null,t[0]||(t[0]=[i("",6)]))}const h=e(n,[["render",r]]);export{b as __pageData,h as default}; diff --git a/assets/zh_guide_quick-start.md.BZnA1cly.js b/assets/zh_guide_quick-start.md.BZnA1cly.js deleted file mode 100644 index c0dc3b20..00000000 --- a/assets/zh_guide_quick-start.md.BZnA1cly.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,c as i,o as t,ae as o}from"./chunks/framework.U1Gow_7P.js";const p=JSON.parse('{"title":"Quick Start","description":"","frontmatter":{},"headers":[],"relativePath":"zh/guide/quick-start.md","filePath":"zh/guide/quick-start.md"}'),r={name:"zh/guide/quick-start.md"};function l(s,a,c,n,d,h){return t(),i("div",null,a[0]||(a[0]=[o('

Quick Start

Editor Plugins

clice 实现了 Language Server Protocol,任何支持该协议的编辑器原则上均可以与 clice 一起使用,提供像 code completion, diagnostics, go-to-definition, 等等。

但是除了标准协议之外,clice 还支持一些协议扩展,为了更好的处理这些协议扩展以及更好的与编辑器集成。使用特定编辑器中的 clice 插件往往是更好的选择,它们大多数都是开箱即用的,并且支持 clice 的协议扩展。

Visual Studio Code

Vim/Neovim

Others

其它的编辑器还没有可用的 clice 插件(欢迎贡献!),为了在它们中使用 clice,请自行安装 clice 并参考特定编辑器的文档关于如何使用一个语言服务器。

Installation

如果你的编辑器插件负责了 clice 的下载,可以跳过这一步。

Download Prebuilt Binary

通过 Release 界面下载 clice 二进制版本。

Build from Source

自己从源码编译 clice,具体的步骤参考 build

Project Setup

为了让 clice 能正确理解你的代码(例如找到头文件的位置),需要为 clice 提供一份 compile_commands.json 文件,也就说所谓的 编译数据库。编译数据库中提供了每个源文件的编译选项。

CMake

对于使用 cmake 的构建系统来说,在构建的时候添加 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 选项即可,例如

cmake
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

这会在 build 目录下生成一份 compile_commands.json 文件。

WARNING

注意:只有当 cmake 的生成器选择为 makefile 和 ninja 的时候,这个选项才有作用。对于其它生成器会忽略这个选项,也就是说无法生成编译数据库。

Bazel

Bazel 不支持直接生成编译数据库,推荐使用 bazel-compile-commands-extractor。在安装好之后,你可以这样生成 compile_commands.json:

bash
bazel run @hedron_compile_commands//:refresh_all

Visual Studio

TODO:

Makefile

TODO:

Xmake

Others

对于任意其它的构建系统,可以尝试使用 bear 或者 scan-build 来拦截编译命令并获取到编译数据库(不保证成功)。我们计划在未来编写一个新的工具,通过假编译器的方式来实现编译命令的捕获。

',31)]))}const m=e(r,[["render",l]]);export{p as __pageData,m as default}; diff --git a/assets/zh_guide_quick-start.md.DXlUe3jK.js b/assets/zh_guide_quick-start.md.DXlUe3jK.js new file mode 100644 index 00000000..f5fa25bb --- /dev/null +++ b/assets/zh_guide_quick-start.md.DXlUe3jK.js @@ -0,0 +1 @@ +import{_ as e,c as i,o as t,ae as o}from"./chunks/framework.U1Gow_7P.js";const p=JSON.parse('{"title":"Quick Start","description":"","frontmatter":{},"headers":[],"relativePath":"zh/guide/quick-start.md","filePath":"zh/guide/quick-start.md"}'),r={name:"zh/guide/quick-start.md"};function l(s,a,c,n,d,h){return t(),i("div",null,a[0]||(a[0]=[o('

Quick Start

Editor Plugins

clice 实现了 Language Server Protocol,任何支持该协议的编辑器原则上均可以与 clice 一起使用,提供像 code completion, diagnostics, go-to-definition, 等等。

但是除了标准协议之外,clice 还支持一些协议扩展,为了更好的处理这些协议扩展以及更好的与编辑器集成。使用特定编辑器中的 clice 插件往往是更好的选择,它们大多数都是开箱即用的,并且支持 clice 的协议扩展。

Visual Studio Code

Vim/Neovim

Others

其它的编辑器还没有可用的 clice 插件(欢迎贡献!),为了在它们中使用 clice,请自行安装 clice 并参考特定编辑器的文档关于如何使用一个语言服务器。

Installation

如果你的编辑器插件负责了 clice 的下载,可以跳过这一步。

Download Prebuilt Binary

通过 Release 界面下载 clice 二进制版本。

Build from Source

自己从源码编译 clice,具体的步骤参考 build

Project Setup

为了让 clice 能正确理解你的代码(例如找到头文件的位置),需要为 clice 提供一份 compile_commands.json 文件,也就说所谓的 编译数据库。编译数据库中提供了每个源文件的编译选项。

CMake

对于使用 cmake 的构建系统来说,在构建的时候添加 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 选项即可,例如

cmake
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

这会在 build 目录下生成一份 compile_commands.json 文件。

WARNING

注意:只有当 cmake 的生成器选择为 makefile 和 ninja 的时候,这个选项才有作用。对于其它生成器会忽略这个选项,也就是说无法生成编译数据库。

Bazel

Bazel 不支持直接生成编译数据库,推荐使用 bazel-compile-commands-extractor。在安装好之后,你可以这样生成 compile_commands.json:

bash
bazel run @hedron_compile_commands//:refresh_all

Visual Studio

TODO:

Makefile

TODO:

Xmake

Others

对于任意其它的构建系统,可以尝试使用 bear 或者 scan-build 来拦截编译命令并获取到编译数据库(不保证成功)。我们计划在未来编写一个新的工具,通过假编译器的方式来实现编译命令的捕获。

',31)]))}const m=e(r,[["render",l]]);export{p as __pageData,m as default}; diff --git a/assets/zh_guide_quick-start.md.BZnA1cly.lean.js b/assets/zh_guide_quick-start.md.DXlUe3jK.lean.js similarity index 100% rename from assets/zh_guide_quick-start.md.BZnA1cly.lean.js rename to assets/zh_guide_quick-start.md.DXlUe3jK.lean.js diff --git a/design/architecture.html b/design/architecture.html index b0add6ad..f0818f2a 100644 --- a/design/architecture.html +++ b/design/architecture.html @@ -18,8 +18,8 @@ -
Skip to content

Architecture

Protocol

Use C++ to describe type definitions in the Language Server Protocol.

AST

Some convenient wrappers for clang AST interfaces.

Async

Wrapper for libuv coroutines using C++20 coroutines.

Compiler

Wrapper for clang compilation interfaces, responsible for actual compilation processes and obtaining various compilation information.

Feature

Specific implementations of various LSP features.

Server

clice is a language server, first and foremost a server. It uses libuv as the event library, adopting a common event-driven compilation model. The main thread is responsible for handling requests and dispatching tasks, while the thread pool is responsible for executing time-consuming tasks, such as compilation tasks. Related code is located in the Server directory.

Support

Some other utility libraries.

- +
Skip to content

Architecture

Protocol

Use C++ to describe type definitions in the Language Server Protocol.

AST

Some convenient wrappers for clang AST interfaces.

Async

Wrapper for libuv coroutines using C++20 coroutines.

Compiler

Wrapper for clang compilation interfaces, responsible for actual compilation processes and obtaining various compilation information.

Feature

Specific implementations of various LSP features.

Server

clice is a language server, first and foremost a server. It uses libuv as the event library, adopting a common event-driven compilation model. The main thread is responsible for handling requests and dispatching tasks, while the thread pool is responsible for executing time-consuming tasks, such as compilation tasks. Related code is located in the Server directory.

Support

Some other utility libraries.

+ \ No newline at end of file diff --git a/design/compilation.html b/design/compilation.html index dfb77f67..90c0ddc6 100644 --- a/design/compilation.html +++ b/design/compilation.html @@ -18,12 +18,12 @@ -
Skip to content

Compilation

Incremental Parsing

Every time you modify code, clice must re-parse the file. clice uses a mechanism called preamble to implement incremental compilation to speed up re-parsing. Preamble can be considered a special case of Precompiled Header (embedded in source files). When opening a file, it builds the first few preprocessor directives at the beginning of the file (called preamble) into a PCH cache on disk. Later, when parsing, it can directly load the PCH file, thus skipping the first few preprocessor directives, which can greatly reduce the amount of code that needs to be re-parsed.

For example, for the following code:

cpp
#include <iostream>
+    
Skip to content

Compilation

Incremental Parsing

Every time you modify code, clice must re-parse the file. clice uses a mechanism called preamble to implement incremental compilation to speed up re-parsing. Preamble can be considered a special case of Precompiled Header (embedded in source files). When opening a file, it builds the first few preprocessor directives at the beginning of the file (called preamble) into a PCH cache on disk. Later, when parsing, it can directly load the PCH file, thus skipping the first few preprocessor directives, which can greatly reduce the amount of code that needs to be re-parsed.

For example, for the following code:

cpp
#include <iostream>
 
 int main () {
     std::cout << "Hello world!" << std::endl;
-}

The iostream header file has about 20,000 lines of code. clice will first build the line #include <iostream> into a PCH file, and after completion, use this PCH file to parse the subsequent code. This way, the amount of code that needs to be re-parsed later is reduced to just 5 lines instead of the original 20,000 lines, making it very fast. Unless you modify the code in the preamble section, which requires building a new preamble.

Cancel Compilation

- +}

The iostream header file has about 20,000 lines of code. clice will first build the line #include <iostream> into a PCH file, and after completion, use this PCH file to parse the subsequent code. This way, the amount of code that needs to be re-parsed later is reduced to just 5 lines instead of the original 20,000 lines, making it very fast. Unless you modify the code in the preamble section, which requires building a new preamble.

Cancel Compilation

+ \ No newline at end of file diff --git a/design/header-context.html b/design/header-context.html index 6f47f9b3..c5e66856 100644 --- a/design/header-context.html +++ b/design/header-context.html @@ -18,7 +18,7 @@ -
Skip to content

Header Context

For clangd to work properly, users often need to provide a compile_commands.json file (hereinafter referred to as CDB file). The basic compilation unit of C++'s traditional compilation model is a source file (e.g., .c and .cpp files), where #include simply pastes and copies the content of header files to the corresponding position in the source file. The aforementioned CDB file stores the compilation commands corresponding to each source file. When you open a source file, clangd will use its corresponding compilation command in the CDB to compile this file.

Naturally, there's a question: if the CDB file only contains compilation commands for source files and not header files, how does clangd handle header files? clangd treats header files as source files, and then, according to some rules, such as using the compilation command of the source file in the corresponding directory as the compilation command for that header file. This model is simple and effective, but it ignores some situations.

Since header files are part of source files, there will be cases where their content differs depending on the content that precedes them in the source file. For example:

cpp
// a.h
+    
Skip to content

Header Context

For clangd to work properly, users often need to provide a compile_commands.json file (hereinafter referred to as CDB file). The basic compilation unit of C++'s traditional compilation model is a source file (e.g., .c and .cpp files), where #include simply pastes and copies the content of header files to the corresponding position in the source file. The aforementioned CDB file stores the compilation commands corresponding to each source file. When you open a source file, clangd will use its corresponding compilation command in the CDB to compile this file.

Naturally, there's a question: if the CDB file only contains compilation commands for source files and not header files, how does clangd handle header files? clangd treats header files as source files, and then, according to some rules, such as using the compilation command of the source file in the corresponding directory as the compilation command for that header file. This model is simple and effective, but it ignores some situations.

Since header files are part of source files, there will be cases where their content differs depending on the content that precedes them in the source file. For example:

cpp
// a.h
 #ifdef TEST
 struct X { ... };
 #else
@@ -37,8 +37,8 @@
 
 // b.cpp
 struct X {};
-#include "a.h"

a.h itself cannot be compiled, but when embedded in b.cpp, it compiles normally. In this case, clangd will report an error in a.h, unable to find the definition of X. Obviously, this is because it treats a.h as an independent source file. There are many such header files in libstdc++ code, and some popular C++ header-only libraries also have such code, which clangd currently cannot handle.

clice will support header context, supporting automatic and user-initiated switching of header file states, and of course will also support non-self-contained header files. We want to achieve the following effect, using the first piece of code as an example. When you jump from b.cpp to a.h, use b.cpp as the context for a.h. Similarly, when you jump from c.cpp to a.h, use c.cpp as the context for a.h.

- +#include "a.h"

a.h itself cannot be compiled, but when embedded in b.cpp, it compiles normally. In this case, clangd will report an error in a.h, unable to find the definition of X. Obviously, this is because it treats a.h as an independent source file. There are many such header files in libstdc++ code, and some popular C++ header-only libraries also have such code, which clangd currently cannot handle.

clice will support header context, supporting automatic and user-initiated switching of header file states, and of course will also support non-self-contained header files. We want to achieve the following effect, using the first piece of code as an example. When you jump from b.cpp to a.h, use b.cpp as the context for a.h. Similarly, when you jump from c.cpp to a.h, use c.cpp as the context for a.h.

+ \ No newline at end of file diff --git a/design/index.html b/design/index.html index d123236f..8700f11e 100644 --- a/design/index.html +++ b/design/index.html @@ -18,8 +18,8 @@ -
Skip to content
- +
Skip to content
+ \ No newline at end of file diff --git a/design/template-resolver.html b/design/template-resolver.html index 0848006b..b4802fa7 100644 --- a/design/template-resolver.html +++ b/design/template-resolver.html @@ -18,14 +18,14 @@ -
Skip to content

Template Resolver

First, there's better template support, which is also the feature I initially wanted clangd to support. Specifically, what problems are there currently in handling templates?

Take code completion as an example. Consider the following code, where ^ represents the cursor position:

cpp
template <typename T>
+    
Skip to content

Template Resolver

First, there's better template support, which is also the feature I initially wanted clangd to support. Specifically, what problems are there currently in handling templates?

Take code completion as an example. Consider the following code, where ^ represents the cursor position:

cpp
template <typename T>
 void foo(std::vector<T> vec) {
     vec.^
 }

In C++, if a type depends on template parameters, we cannot make any accurate assumptions about it before template instantiation. For example, here vector could be either the primary template or the partial specialization of vector<bool>. Which one should we choose? For code compilation, accuracy is always the most important - we cannot use any results that might lead to errors. But for language servers, providing more possible results is often better than providing nothing. We can assume that users use the primary template more often than partial specializations, and thus provide code completion results based on the primary template. Currently, clangd does exactly this - in the above case, it will provide code completion based on the primary template of vector.

Consider a more complex example:

cpp
template <typename T>
 void foo(std::vector<std::vector<T>> vec2) {
     vec2[0].^
-}

From the user's perspective, completion should also be provided here, since the type of vec2[0] is also vector<T>, right? Same as the previous example. But clangd won't provide any completion here. What's the problem? According to the C++ standard, the return type of std::vector<T>::operator[] is std::vector<T>::reference, which is actually a dependent name. Its result seems quite direct - it's T&. But in libstdc++, its definition is nested in dozens of layers of templates, seemingly for compatibility with old standards? So why can't clangd handle this situation?

  1. It's based on primary template assumptions, not considering that partial specializations might make lookup impossible to proceed
  2. It only performs name lookup without template instantiation, so even if it finds the final result, it can't map it back to the original template parameters
  3. It doesn't consider default template parameters, unable to handle dependent names caused by default template parameters

Although we can make exceptions for standard library types to provide related support, I hope that user code can have the same status as standard library code, so we need a universal algorithm to handle dependent types. To solve this problem, I wrote a pseudo-instantiation (pseudo instantiator). It can instantiate dependent types without specific types, achieving the purpose of simplification. For example, in the above example, std::vector<std::vector<T>>::reference can be simplified to std::vector<T>&, and further provide code completion options for users.

- +}

From the user's perspective, completion should also be provided here, since the type of vec2[0] is also vector<T>, right? Same as the previous example. But clangd won't provide any completion here. What's the problem? According to the C++ standard, the return type of std::vector<T>::operator[] is std::vector<T>::reference, which is actually a dependent name. Its result seems quite direct - it's T&. But in libstdc++, its definition is nested in dozens of layers of templates, seemingly for compatibility with old standards? So why can't clangd handle this situation?

  1. It's based on primary template assumptions, not considering that partial specializations might make lookup impossible to proceed
  2. It only performs name lookup without template instantiation, so even if it finds the final result, it can't map it back to the original template parameters
  3. It doesn't consider default template parameters, unable to handle dependent names caused by default template parameters

Although we can make exceptions for standard library types to provide related support, I hope that user code can have the same status as standard library code, so we need a universal algorithm to handle dependent types. To solve this problem, I wrote a pseudo-instantiation (pseudo instantiator). It can instantiate dependent types without specific types, achieving the purpose of simplification. For example, in the above example, std::vector<std::vector<T>>::reference can be simplified to std::vector<T>&, and further provide code completion options for users.

+ \ No newline at end of file diff --git a/dev/build.html b/dev/build.html index 770823b3..7ef34b51 100644 --- a/dev/build.html +++ b/dev/build.html @@ -18,7 +18,7 @@ -
Skip to content

Build from Source

Supported Platforms

  • Windows
  • Linux
  • MacOS

Prerequisites

This section introduces the prerequisites for compiling clice.

Toolchain

  • clang >= 19
  • c++23 compatible standard library
    • MSVC STL >= 19.44(VS 2022 17.4)
    • GCC libstdc++ >= 14
    • Clang libc++ >= 20

clice uses C++23 as the language standard. Please ensure you have an available clang 19 or above compiler, as well as a standard library compatible with C++23.

clice can currently only be compiled with clang. In the future, we will improve this to allow compilation with gcc and msvc.

LLVM Libs

  • 20.1.5 <= llvm libs < 21

Due to the complexity of C++ syntax, writing a new parser from scratch is unrealistic. clice calls clang's API to parse C++ source files and obtain AST, which means it needs to link llvm/clang libs. Additionally, since clice uses clang's private headers, these private headers are not available in llvm's binary release, so you cannot directly use the system's llvm package.

If you can find the llvm commit corresponding to your system's llvm package, copy the following three files from that commit:

  • clang/lib/Sema/CoroutineStmtBuilder.h
  • clang/lib/Sema/TypeLocBuilder.h
  • clang/lib/Sema/TreeTransform.h

Copy them to LLVM_INSTALL_PATH/include/clang/Sema/.

Besides this method, there are two other ways to obtain the llvm libs required by clice:

  1. Use our precompiled version
bash
# .github/workflows/cmake.yml
+    
Skip to content

Build from Source

Supported Platforms

  • Windows
  • Linux
  • MacOS

Prerequisites

This section introduces the prerequisites for compiling clice.

Toolchain

  • clang >= 19
  • c++23 compatible standard library
    • MSVC STL >= 19.44(VS 2022 17.4)
    • GCC libstdc++ >= 14
    • Clang libc++ >= 20

clice uses C++23 as the language standard. Please ensure you have an available clang 19 or above compiler, as well as a standard library compatible with C++23.

clice can currently only be compiled with clang. In the future, we will improve this to allow compilation with gcc and msvc.

LLVM Libs

  • 20.1.5 <= llvm libs < 21

Due to the complexity of C++ syntax, writing a new parser from scratch is unrealistic. clice calls clang's API to parse C++ source files and obtain AST, which means it needs to link llvm/clang libs. Additionally, since clice uses clang's private headers, these private headers are not available in llvm's binary release, so you cannot directly use the system's llvm package.

If you can find the llvm commit corresponding to your system's llvm package, copy the following three files from that commit:

  • clang/lib/Sema/CoroutineStmtBuilder.h
  • clang/lib/Sema/TypeLocBuilder.h
  • clang/lib/Sema/TreeTransform.h

Copy them to LLVM_INSTALL_PATH/include/clang/Sema/.

Besides this method, there are two other ways to obtain the llvm libs required by clice:

  1. Use our precompiled version
bash
# .github/workflows/cmake.yml
 
 # Linux precompiled binary require glibc 2.35 (build on ubuntu 22.04)
 $ mkdir -p ./.llvm
@@ -43,8 +43,8 @@
 ├── include
 ├── lib
 ├── LICENSE
-├── README.md

Hint: launch clice in the docker container by running the following command:

bash
$ docker run --rm -it --entrypoint bash clice
- +├── README.md

Hint: launch clice in the docker container by running the following command:

bash
$ docker run --rm -it --entrypoint bash clice
+ \ No newline at end of file diff --git a/dev/contribution.html b/dev/contribution.html index 7ab1e1bb..6111377f 100644 --- a/dev/contribution.html +++ b/dev/contribution.html @@ -13,13 +13,13 @@ - + -
Skip to content

Contribution

We welcome any contributions!

Please refer to build to build clice, refer to test and debug to test and debug clice.

Code Style

Naming:

  • Variable names: lowercase with underscores
  • Class names, enum names: PascalCase
- +
Skip to content

Contribution

We welcome any contributions!

Please refer to build to build clice, refer to test and debug to test and debug clice.

Code Style

Naming:

  • Variable names: lowercase with underscores
  • Class names, enum names: PascalCase
+ \ No newline at end of file diff --git a/dev/test-and-debug.html b/dev/test-and-debug.html index 6f6d2cd6..85738a87 100644 --- a/dev/test-and-debug.html +++ b/dev/test-and-debug.html @@ -18,7 +18,7 @@ -
Skip to content

Test and Debug

Run Tests

clice has two types of tests: unit tests and integration tests.

  • Run unit tests
bash
$ ./build/bin/unit_tests --test-dir="./tests/data"
  • Run integration tests

We use pytest to run integration tests. Please refer to pyproject.toml to install the required Python libraries.

bash
$ pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice

If you use xmake as your build system, you can run the tests directly with xmake:

shell
$ xmake run --verbose unit_tests
+    
Skip to content

Test and Debug

Run Tests

clice has two types of tests: unit tests and integration tests.

  • Run unit tests
bash
$ ./build/bin/unit_tests --test-dir="./tests/data"
  • Run integration tests

We use pytest to run integration tests. Please refer to pyproject.toml to install the required Python libraries.

bash
$ pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice

If you use xmake as your build system, you can run the tests directly with xmake:

shell
$ xmake run --verbose unit_tests
 $ xmake test --verbose integration_tests/default

Debug

If you want to attach a debugger to clice for debugging, it is recommended to first start clice in socket mode independently, and then connect the client to it.

shell
$ ./build/bin/clice --mode=socket --port=50051

After the server starts, you can connect a client to the server in the following two ways:

  • Connect by running a specific test with pytest

You can run a single integration test case to connect to a running clice instance. This is very useful for reproducing and debugging specific scenarios.

shell
$ pytest -s --log-cli-level=INFO tests/integration/test_file_operation.py::test_did_open --mode=socket --port=50051
  • Use VS Code for practical testing

You can also connect to a running clice service by configuring the clice-vscode extension, allowing you to debug in a real-world usage scenario.

  1. Download the clice-vscode extension from the Marketplace.

  2. Configure settings.json: Create a .vscode/settings.json file in your project's root directory and add the following content:

    jsonc
    {
         // Point this to the clice binary you downloaded.
         "clice.executable": "/path/to/your/clice/executable",
    @@ -31,8 +31,8 @@
         "clangd.path": "",
     }
  3. Reload Window: After modifying the configuration, execute the Developer: Reload Window command in VS Code for the settings to take effect. The extension will automatically connect to the clice instance listening on port 50051.

If you need to modify or debug the clice-vscode extension itself, follow these steps:

  1. Clone and install dependencies:

    shell
    $ git clone https://github.com/clice-io/clice-vscode
     $ cd clice-vscode
    -$ npm install
  2. Open the extension project with VS Code: Open the clice-vscode folder in a new VS Code window.

  3. Create debug configuration: In the clice-vscode project, also create a .vscode/settings.json file with the same content as above.

  4. Press F5. This will launch an [Extension Development Host] window. This is a new VS Code window with your local clice-vscode extension code loaded. Open your C++ project in this new window, and it should automatically connect to clice.

- +$ npm install
  • Open the extension project with VS Code: Open the clice-vscode folder in a new VS Code window.

  • Create debug configuration: In the clice-vscode project, also create a .vscode/settings.json file with the same content as above.

  • Press F5. This will launch an [Extension Development Host] window. This is a new VS Code window with your local clice-vscode extension code loaded. Open your C++ project in this new window, and it should automatically connect to clice.

  • + \ No newline at end of file diff --git a/guide/configuration.html b/guide/configuration.html index 75bcc140..4708c9d6 100644 --- a/guide/configuration.html +++ b/guide/configuration.html @@ -18,8 +18,8 @@ -
    Skip to content

    Configuration

    This is the documentation for clice.toml.

    Server

    Rule

    [[rules]] represents an array of objects, where each object has the following properties:

    NameType
    [rules].patternarray of string

    Glob patterns for matching file paths, following LSP's standard.

    • *: Matches one or more characters in a path segment.
    • ?: Matches a single character in a path segment.
    • **: Matches any number of path segments, including zero.
    • {}: Used for grouping conditions (e.g., **/*.{ts,js} matches all TypeScript and JavaScript files).
    • []: Declares a character range to match in a path segment (e.g., example.[0-9] matches example.0, example.1, etc.).
    • [!...]: Excludes a character range to match in a path segment (e.g., example.[!0-9] matches example.a, example.b, but not example.0).
    NameTypeDefault
    [rules].appendarray of string[]

    Commands to append to the original command list. For example, append = ["-std=c++17"].

    NameTypeDefault
    [rules].removearray of string[]

    Commands to remove from the original command list. For example, remove = ["-std=c++11"].

    NameTypeDefault
    [rules].readonlystring"auto"

    Controls whether the file is treated as read-only. Values can be one of "auto", "always", and "never".

    • "auto": The file is treated as read-only before you edit it.
    • "always": Always treat the file as read-only.
    • "never": Always treat the file as non-read-only.

    Read-only means the file is not editable, and LSP requests like code actions or completions won't be triggered on it. This avoids dynamic computation and allows direct loading of pre-indexed results, improving performance.

    NameTypeDefault
    [rules].headerstring"auto"

    Controls how to handle header files. Values can be one of "auto", "always", and "never".

    • "auto": First try to infer header file context. If no header file context is found, the file will be treated as a regular source file.
    • "always": Always treat the file as a header file. If no header file context is found, an error will be reported.
    • "never": Always treat the file as a source file.

    Header file context refers to the source files or other metadata associated with that header file.

    NameTypeDefault
    [rules].contextsarray of string[]

    Specify additional header file contexts (file paths) for the file.

    Usually, once a file is indexed, header file context is automatically inferred. However, if you need immediate context before indexing is complete, you can manually provide it using this field.

    Cache

    NameTypeDefault
    cache.dirstring"${workspace}/.clice/cache"

    Folder for storing PCH and PCM caches.

    Index

    NameTypeDefault
    index.dirstring"${workspace}/.clice/index"

    Folder for storing index files.

    Feature

    - +
    Skip to content

    Configuration

    This is the documentation for clice.toml.

    Server

    Rule

    [[rules]] represents an array of objects, where each object has the following properties:

    NameType
    [rules].patternarray of string

    Glob patterns for matching file paths, following LSP's standard.

    • *: Matches one or more characters in a path segment.
    • ?: Matches a single character in a path segment.
    • **: Matches any number of path segments, including zero.
    • {}: Used for grouping conditions (e.g., **/*.{ts,js} matches all TypeScript and JavaScript files).
    • []: Declares a character range to match in a path segment (e.g., example.[0-9] matches example.0, example.1, etc.).
    • [!...]: Excludes a character range to match in a path segment (e.g., example.[!0-9] matches example.a, example.b, but not example.0).
    NameTypeDefault
    [rules].appendarray of string[]

    Commands to append to the original command list. For example, append = ["-std=c++17"].

    NameTypeDefault
    [rules].removearray of string[]

    Commands to remove from the original command list. For example, remove = ["-std=c++11"].

    NameTypeDefault
    [rules].readonlystring"auto"

    Controls whether the file is treated as read-only. Values can be one of "auto", "always", and "never".

    • "auto": The file is treated as read-only before you edit it.
    • "always": Always treat the file as read-only.
    • "never": Always treat the file as non-read-only.

    Read-only means the file is not editable, and LSP requests like code actions or completions won't be triggered on it. This avoids dynamic computation and allows direct loading of pre-indexed results, improving performance.

    NameTypeDefault
    [rules].headerstring"auto"

    Controls how to handle header files. Values can be one of "auto", "always", and "never".

    • "auto": First try to infer header file context. If no header file context is found, the file will be treated as a regular source file.
    • "always": Always treat the file as a header file. If no header file context is found, an error will be reported.
    • "never": Always treat the file as a source file.

    Header file context refers to the source files or other metadata associated with that header file.

    NameTypeDefault
    [rules].contextsarray of string[]

    Specify additional header file contexts (file paths) for the file.

    Usually, once a file is indexed, header file context is automatically inferred. However, if you need immediate context before indexing is complete, you can manually provide it using this field.

    Cache

    NameTypeDefault
    cache.dirstring"${workspace}/.clice/cache"

    Folder for storing PCH and PCM caches.

    Index

    NameTypeDefault
    index.dirstring"${workspace}/.clice/index"

    Folder for storing index files.

    Feature

    + \ No newline at end of file diff --git a/guide/quick-start.html b/guide/quick-start.html index 2ef823c2..b9b58b6b 100644 --- a/guide/quick-start.html +++ b/guide/quick-start.html @@ -13,13 +13,13 @@ - + -
    Skip to content

    Quick Start

    Editor Plugins

    clice implements the Language Server Protocol. Any editor that supports this protocol can theoretically work with clice to provide features like code completion, diagnostics, go-to-definition, and more.

    However, beyond the standard protocol, clice also supports some protocol extensions. For better handling of these protocol extensions and better integration with editors, using clice plugins in specific editors is often a better choice. Most of them work out of the box and support clice's protocol extensions.

    Visual Studio Code

    Vim/Neovim

    Others

    Other editors don't have available clice plugins yet (contributions welcome!). To use clice in them, please install clice yourself and refer to the specific editor's documentation on how to use a language server.

    Installation

    If your editor plugin handles clice's download, you can skip this step.

    Download Prebuilt Binary

    Download clice binary version through the Release page.

    Build from Source

    Build clice from source yourself. For specific steps, refer to build.

    Project Setup

    For clice to correctly understand your code (e.g., find header file locations), you need to provide clice with a compile_commands.json file, also known as a compilation database. The compilation database provides compilation options for each source file.

    CMake

    For build systems using cmake, add the -DCMAKE_EXPORT_COMPILE_COMMANDS=ON option during build, for example:

    cmake
    cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

    This will generate a compile_commands.json file in the build directory.

    WARNING

    Note: This option only works when cmake's generator is set to makefile and ninja. For other generators, this option will be ignored, meaning the compilation database cannot be generated.

    Bazel

    Bazel has no native support to generate a compilation database. The recommended solution is to use bazel-compile-commands-extractor. After setting it up, you can generate compile_commands.json with:

    bash
    bazel run @hedron_compile_commands//:refresh_all

    Visual Studio

    TODO:

    Makefile

    TODO:

    Xmake

    Others

    For any other build system, you can try using bear or scan-build to intercept compilation commands and obtain the compilation database (no guarantee of success). We plan to write a new tool in the future that captures compilation commands through a fake compiler approach.

    - +
    Skip to content

    Quick Start

    Editor Plugins

    clice implements the Language Server Protocol. Any editor that supports this protocol can theoretically work with clice to provide features like code completion, diagnostics, go-to-definition, and more.

    However, beyond the standard protocol, clice also supports some protocol extensions. For better handling of these protocol extensions and better integration with editors, using clice plugins in specific editors is often a better choice. Most of them work out of the box and support clice's protocol extensions.

    Visual Studio Code

    Vim/Neovim

    Others

    Other editors don't have available clice plugins yet (contributions welcome!). To use clice in them, please install clice yourself and refer to the specific editor's documentation on how to use a language server.

    Installation

    If your editor plugin handles clice's download, you can skip this step.

    Download Prebuilt Binary

    Download clice binary version through the Release page.

    Build from Source

    Build clice from source yourself. For specific steps, refer to build.

    Project Setup

    For clice to correctly understand your code (e.g., find header file locations), you need to provide clice with a compile_commands.json file, also known as a compilation database. The compilation database provides compilation options for each source file.

    CMake

    For build systems using cmake, add the -DCMAKE_EXPORT_COMPILE_COMMANDS=ON option during build, for example:

    cmake
    cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

    This will generate a compile_commands.json file in the build directory.

    WARNING

    Note: This option only works when cmake's generator is set to makefile and ninja. For other generators, this option will be ignored, meaning the compilation database cannot be generated.

    Bazel

    Bazel has no native support to generate a compilation database. The recommended solution is to use bazel-compile-commands-extractor. After setting it up, you can generate compile_commands.json with:

    bash
    bazel run @hedron_compile_commands//:refresh_all

    Visual Studio

    TODO:

    Makefile

    TODO:

    Xmake

    Others

    For any other build system, you can try using bear or scan-build to intercept compilation commands and obtain the compilation database (no guarantee of success). We plan to write a new tool in the future that captures compilation commands through a fake compiler approach.

    + \ No newline at end of file diff --git a/guide/what-is-clice.html b/guide/what-is-clice.html index 50f41119..6ab44fc1 100644 --- a/guide/what-is-clice.html +++ b/guide/what-is-clice.html @@ -18,8 +18,8 @@ -
    Skip to content

    What is clice?

    clice is a completely new C++ language server designed to address the shortcomings of existing C++ language servers. It provides code navigation and intelligent suggestions for your editor.

    Why a New Language Server?

    So the first question is, why develop a new language server? Is it necessary to reinvent the wheel?

    This question deserves a serious answer. Before this project, I had written many projects, both large and small. But the vast majority of them were toy projects, written only to verify some idea or for personal learning, without solving any real problems. clice is not like that - it genuinely intends to solve existing problems (specific problems will be discussed later), rather than rewriting for the sake of rewriting.

    At the beginning of this year, I wanted to participate in the development of the LLVM project. I wanted to start from what I'm more familiar with - C++, specifically clang. But without requirements, I couldn't just stare at the source code. The normal process in such cases is to start with some "first issues" and gradually get familiar with the project. But I found this boring - I wanted to do something big right from the start, like implementing some new C++ standard feature. However, I found there was almost no place for me to get involved here, as new feature implementations are almost always completed by a few core clang developers. Well, since there's no opportunity here, let's look elsewhere. My attention naturally shifted to clangd, since I mainly use VSCode for development, and the best C++ language server on VSCode is clangd.

    At the time, I knew nothing about clangd, except that I found it seemed to render keyword highlighting incorrectly. So I started reading clangd's source code while browsing through clangd's numerous issues to see if there was anything I could solve. After going through hundreds of issues, I found there were quite a few problems here. At the time, I was particularly interested in an issue about code completion within templates. Why was I interested in this? Readers familiar with me might know that I'm quite an experienced metaprogramming player, having written many related articles before. Naturally, I'm not only curious about how template metaprogramming itself works, but also curious about how clang, as a compiler, implements related features. This issue was a good entry point for me. After spending several weeks exploring prototype implementations, I initially solved that issue, but then I found that there was no one to review the related code!

    After some investigation, I found that clangd's current situation is quite bad. Let's go through the timeline: clangd was originally just a simple small project within LLVM, not outstanding in terms of functionality and usability. As MaskRay mentioned in this blog post about ccls, clangd at the time could only handle single compilation units, and cross-compilation unit requests couldn't be processed. This blog post was published in 2017, which is also one reason why MaskRay chose to write ccls. ccls is also a C/C++ language server that was stronger than clangd at that point. However, later, Google started sending people to improve clangd to meet their internal large codebase needs. At the same time, the LSP standard content was constantly expanding, and clangd was continuously following up on new standard content, but ccls's author seemed to gradually become busy with other things and didn't have much time to maintain ccls. So in the end, clangd had surpassed ccls overall. The turning point occurred around 2023, when clangd seemed to have reached a highly usable state for Google internally, and the original employees responsible for clangd were transferred to do other things. Currently, clangd's issues are mainly handled by only one person, HighCommander4, purely out of passion, not employed by anyone. Since he's not specifically employed to maintain clangd, he can only handle issues in his limited free time, and only limited to answering questions and very limited reviews. As he mentioned in this comment:

    The other part of the reason is lack of resources to pursue the ideas we do have, such as the idea mentioned above of trying to shift more of the burden to disk usage through more aggressive preamble caching. I'm a casual contributor, and the limited time I have to spend on clangd is mostly taken up by answering questions, some code reviews, and the occasional small fix / improvement; I haven't had the bandwidth to drive this type of performance-related experimentation.

    Given this situation, it's not surprising that large PRs like preliminary support for C++20 modules for clangd have been delayed for nearly a year. After realizing this current state, I had the idea of writing a language server myself. I estimated the project size - removing test code, it would take about 20,000 lines to complete, which is a workload that one person can accomplish over a period of time, and there are precedents like ccls and rust-analyzer. Another point is that clangd's code is already dated - despite having many comments, the related logic is still very convoluted, and the time spent on large-scale modifications might not be faster than rewriting.

    So I got to work. I categorized clangd's hundreds of issues to see if there were some problems that were difficult to solve due to clangd's initial architectural design errors and were therefore shelved. If so, could these be considered and solved during redesign? I found that there were indeed some! So in the following time, I spent about two months learning and researching the related mechanisms in clang, exploring solutions to related problems, and exploring prototype implementations. After confirming that the related problems could basically be solved, I officially started the development of clice.

    - +
    Skip to content

    What is clice?

    clice is a completely new C++ language server designed to address the shortcomings of existing C++ language servers. It provides code navigation and intelligent suggestions for your editor.

    Why a New Language Server?

    So the first question is, why develop a new language server? Is it necessary to reinvent the wheel?

    This question deserves a serious answer. Before this project, I had written many projects, both large and small. But the vast majority of them were toy projects, written only to verify some idea or for personal learning, without solving any real problems. clice is not like that - it genuinely intends to solve existing problems (specific problems will be discussed later), rather than rewriting for the sake of rewriting.

    At the beginning of this year, I wanted to participate in the development of the LLVM project. I wanted to start from what I'm more familiar with - C++, specifically clang. But without requirements, I couldn't just stare at the source code. The normal process in such cases is to start with some "first issues" and gradually get familiar with the project. But I found this boring - I wanted to do something big right from the start, like implementing some new C++ standard feature. However, I found there was almost no place for me to get involved here, as new feature implementations are almost always completed by a few core clang developers. Well, since there's no opportunity here, let's look elsewhere. My attention naturally shifted to clangd, since I mainly use VSCode for development, and the best C++ language server on VSCode is clangd.

    At the time, I knew nothing about clangd, except that I found it seemed to render keyword highlighting incorrectly. So I started reading clangd's source code while browsing through clangd's numerous issues to see if there was anything I could solve. After going through hundreds of issues, I found there were quite a few problems here. At the time, I was particularly interested in an issue about code completion within templates. Why was I interested in this? Readers familiar with me might know that I'm quite an experienced metaprogramming player, having written many related articles before. Naturally, I'm not only curious about how template metaprogramming itself works, but also curious about how clang, as a compiler, implements related features. This issue was a good entry point for me. After spending several weeks exploring prototype implementations, I initially solved that issue, but then I found that there was no one to review the related code!

    After some investigation, I found that clangd's current situation is quite bad. Let's go through the timeline: clangd was originally just a simple small project within LLVM, not outstanding in terms of functionality and usability. As MaskRay mentioned in this blog post about ccls, clangd at the time could only handle single compilation units, and cross-compilation unit requests couldn't be processed. This blog post was published in 2017, which is also one reason why MaskRay chose to write ccls. ccls is also a C/C++ language server that was stronger than clangd at that point. However, later, Google started sending people to improve clangd to meet their internal large codebase needs. At the same time, the LSP standard content was constantly expanding, and clangd was continuously following up on new standard content, but ccls's author seemed to gradually become busy with other things and didn't have much time to maintain ccls. So in the end, clangd had surpassed ccls overall. The turning point occurred around 2023, when clangd seemed to have reached a highly usable state for Google internally, and the original employees responsible for clangd were transferred to do other things. Currently, clangd's issues are mainly handled by only one person, HighCommander4, purely out of passion, not employed by anyone. Since he's not specifically employed to maintain clangd, he can only handle issues in his limited free time, and only limited to answering questions and very limited reviews. As he mentioned in this comment:

    The other part of the reason is lack of resources to pursue the ideas we do have, such as the idea mentioned above of trying to shift more of the burden to disk usage through more aggressive preamble caching. I'm a casual contributor, and the limited time I have to spend on clangd is mostly taken up by answering questions, some code reviews, and the occasional small fix / improvement; I haven't had the bandwidth to drive this type of performance-related experimentation.

    Given this situation, it's not surprising that large PRs like preliminary support for C++20 modules for clangd have been delayed for nearly a year. After realizing this current state, I had the idea of writing a language server myself. I estimated the project size - removing test code, it would take about 20,000 lines to complete, which is a workload that one person can accomplish over a period of time, and there are precedents like ccls and rust-analyzer. Another point is that clangd's code is already dated - despite having many comments, the related logic is still very convoluted, and the time spent on large-scale modifications might not be faster than rewriting.

    So I got to work. I categorized clangd's hundreds of issues to see if there were some problems that were difficult to solve due to clangd's initial architectural design errors and were therefore shelved. If so, could these be considered and solved during redesign? I found that there were indeed some! So in the following time, I spent about two months learning and researching the related mechanisms in clang, exploring solutions to related problems, and exploring prototype implementations. After confirming that the related problems could basically be solved, I officially started the development of clice.

    + \ No newline at end of file diff --git a/hashmap.json b/hashmap.json index bf1098b9..ceda1582 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"design_architecture.md":"D8TKjkhM","design_compilation.md":"DgqTpBDl","design_header-context.md":"EUdQ-Lzo","design_index.md":"BgicbTS7","design_template-resolver.md":"IYEsm7Dh","dev_build.md":"BPdhy5jH","dev_contribution.md":"DlEIMhd5","dev_test-and-debug.md":"B85mthGc","guide_configuration.md":"BSfrI2TP","guide_quick-start.md":"B3b6j6Mp","guide_what-is-clice.md":"q1sqlvBr","index.md":"B80amafG","zh_design_architecture.md":"BVcDWyLW","zh_design_compilation.md":"DfE77o6C","zh_design_header-context.md":"BZN78X54","zh_design_index.md":"DHNbht8F","zh_design_template-resolver.md":"BdV6FoQY","zh_dev_build.md":"TwBXt4g8","zh_dev_contribution.md":"DTCatoP3","zh_dev_test-and-debug.md":"C2WjDAQj","zh_guide_configuration.md":"BnQR4J3z","zh_guide_quick-start.md":"BZnA1cly","zh_guide_what-is-clice.md":"DFkS8WKr","zh_index.md":"DOAh-Isq"} +{"design_architecture.md":"D8TKjkhM","design_compilation.md":"DgqTpBDl","design_header-context.md":"EUdQ-Lzo","design_index.md":"BgicbTS7","design_template-resolver.md":"IYEsm7Dh","dev_build.md":"BPdhy5jH","dev_contribution.md":"C0y1pn0v","dev_test-and-debug.md":"B85mthGc","guide_configuration.md":"BSfrI2TP","guide_quick-start.md":"DrPBamDw","guide_what-is-clice.md":"q1sqlvBr","index.md":"B80amafG","zh_design_architecture.md":"BVcDWyLW","zh_design_compilation.md":"DfE77o6C","zh_design_header-context.md":"BZN78X54","zh_design_index.md":"DHNbht8F","zh_design_template-resolver.md":"BdV6FoQY","zh_dev_build.md":"TwBXt4g8","zh_dev_contribution.md":"Dh-sErND","zh_dev_test-and-debug.md":"C2WjDAQj","zh_guide_configuration.md":"BnQR4J3z","zh_guide_quick-start.md":"DXlUe3jK","zh_guide_what-is-clice.md":"DFkS8WKr","zh_index.md":"DOAh-Isq"} diff --git a/index.html b/index.html index 52ac8ca3..6c370d7d 100644 --- a/index.html +++ b/index.html @@ -18,8 +18,8 @@ -
    Skip to content

    cliceNext Generation C++ Language Server

    Development is actively in progress

    clice
    - +
    Skip to content

    cliceNext Generation C++ Language Server

    Development is actively in progress

    clice
    + \ No newline at end of file diff --git a/zh/design/architecture.html b/zh/design/architecture.html index fef6d00e..f8deae2d 100644 --- a/zh/design/architecture.html +++ b/zh/design/architecture.html @@ -18,8 +18,8 @@ -
    Skip to content

    Architecture

    Protocol

    使用 C++ 来描述 Language Server Protocol 中的类型定义。

    AST

    对 clang AST 接口的一些方便的封装

    Async

    使用 C++20 coroutine 对 libuv 协程的封装

    Compiler

    对 clang 编译接口的封装,负责实际的编译过程,以及各种编译信息的获取。

    Feature

    各种 LSP 特性的具体实现。

    Server

    clice 是一个语言服务器,首先是一个服务器。它使用 libuv 作为事件库,采用常见的事件驱动的编译模型。主线程负责处理请求以及分发任务,线程池负责执行耗时的任务,比如编译任务。相关的代码位于 Server 目录下。

    Support

    一些其它的工具库。

    - +
    Skip to content

    Architecture

    Protocol

    使用 C++ 来描述 Language Server Protocol 中的类型定义。

    AST

    对 clang AST 接口的一些方便的封装

    Async

    使用 C++20 coroutine 对 libuv 协程的封装

    Compiler

    对 clang 编译接口的封装,负责实际的编译过程,以及各种编译信息的获取。

    Feature

    各种 LSP 特性的具体实现。

    Server

    clice 是一个语言服务器,首先是一个服务器。它使用 libuv 作为事件库,采用常见的事件驱动的编译模型。主线程负责处理请求以及分发任务,线程池负责执行耗时的任务,比如编译任务。相关的代码位于 Server 目录下。

    Support

    一些其它的工具库。

    + \ No newline at end of file diff --git a/zh/design/compilation.html b/zh/design/compilation.html index f9611890..7f4a230a 100644 --- a/zh/design/compilation.html +++ b/zh/design/compilation.html @@ -18,12 +18,12 @@ -
    Skip to content

    Compilation

    Incremental Parsing

    每当你修改代码时,clice 都必须重新解析文件。clice 使用一种叫做 preamble 的机制实现增量编译以加快重新解析速度。preamble 可被视为 Precompiled Header 的一种特殊情况(内嵌在源文件中)。在打开文件的时候,它会将文件开头的几个预处理指令(被叫做 preamble)构建成 PCH 缓存在磁盘上,后续在解析的时候则可以直接加载 PCH 文件,从而跳过前面几个预处理指令,这样可以大大减少要重新解析的代码量。

    例如,对于如下的代码

    cpp
    #include <iostream>
    +    
    Skip to content

    Compilation

    Incremental Parsing

    每当你修改代码时,clice 都必须重新解析文件。clice 使用一种叫做 preamble 的机制实现增量编译以加快重新解析速度。preamble 可被视为 Precompiled Header 的一种特殊情况(内嵌在源文件中)。在打开文件的时候,它会将文件开头的几个预处理指令(被叫做 preamble)构建成 PCH 缓存在磁盘上,后续在解析的时候则可以直接加载 PCH 文件,从而跳过前面几个预处理指令,这样可以大大减少要重新解析的代码量。

    例如,对于如下的代码

    cpp
    #include <iostream>
     
     int main () {
         std::cout << "Hello world!" << std::endl;
    -}

    iostream 这个头文件大概有 2w 行代码,clice 会先把 #include <iostream> 这一行代码构建成 PCH 文件,在完成之后在使用这个 PCH 文件来解析后面的代码。这样的话后续重新解析的代码量就只剩 5 行了,而不是原本的 2w 行,速度会变得非常快。除非你修改了 preamble 部分的代码,导致需要构建新的 preamble。

    Cancel Compilation

    - +}

    iostream 这个头文件大概有 2w 行代码,clice 会先把 #include <iostream> 这一行代码构建成 PCH 文件,在完成之后在使用这个 PCH 文件来解析后面的代码。这样的话后续重新解析的代码量就只剩 5 行了,而不是原本的 2w 行,速度会变得非常快。除非你修改了 preamble 部分的代码,导致需要构建新的 preamble。

    Cancel Compilation

    + \ No newline at end of file diff --git a/zh/design/header-context.html b/zh/design/header-context.html index 41ce99ec..9fa0d7cb 100644 --- a/zh/design/header-context.html +++ b/zh/design/header-context.html @@ -18,7 +18,7 @@ -
    Skip to content

    Header Context

    为了让 clangd 正常工作,用户往往需要提供一份compile_commands.json文件(后文简称 CDB 文件)。C++ 的传统编译模型的基本编译单元是一个源文件(例如.c.cpp文件),#include只是把头文件的内容粘贴复制到源文件中对应的位置。而上述 CDB 文件里面就储存了各个源文件对应编译命令,当你打开一个源文件的时候,clangd 会使用其在 CDB 中对应的编译命令来编译这个文件。

    那很自然的就有一个疑问,如果 CDB 文件里面只有源文件的编译命令,没有头文件的,那么 clangd 是如何处理头文件的呢?clangd 会把头文件当做一个源文件进行处理,然后呢,根据一些规则,比如使用对应目录下的源文件的编译命令作为该头文件的编译命令。这样的模型简单有效,但是却忽略了一些情况。

    由于头文件是源文件的一部分,那么就会出现它的内容根据它在源文件中前面的内容不同而不同的情况。例如:

    cpp
    // a.h
    +    
    Skip to content

    Header Context

    为了让 clangd 正常工作,用户往往需要提供一份compile_commands.json文件(后文简称 CDB 文件)。C++ 的传统编译模型的基本编译单元是一个源文件(例如.c.cpp文件),#include只是把头文件的内容粘贴复制到源文件中对应的位置。而上述 CDB 文件里面就储存了各个源文件对应编译命令,当你打开一个源文件的时候,clangd 会使用其在 CDB 中对应的编译命令来编译这个文件。

    那很自然的就有一个疑问,如果 CDB 文件里面只有源文件的编译命令,没有头文件的,那么 clangd 是如何处理头文件的呢?clangd 会把头文件当做一个源文件进行处理,然后呢,根据一些规则,比如使用对应目录下的源文件的编译命令作为该头文件的编译命令。这样的模型简单有效,但是却忽略了一些情况。

    由于头文件是源文件的一部分,那么就会出现它的内容根据它在源文件中前面的内容不同而不同的情况。例如:

    cpp
    // a.h
     #ifdef TEST
     struct X { ... };
     #else
    @@ -37,8 +37,8 @@
     
     // b.cpp
     struct X {};
    -#include "a.h"

    a.h自身不能被编译,但是嵌入到b.cpp中的时候就编译正常了。这种情况下 clangd 会在a.h中报错,找不到X的定义。显然这是因为它把a.h当成一个独立的源文件了。在 libstdc++ 中的代码中就有很多这样的头文件,现在流行的一些 C++ 的 header-only 的库也有些有这样的代码,clangd 目前无法处理它们。

    clice 将会支持头文件上下文 (header context),支持自动和用户主动切换头文件的状态,当然也会支持非自包含的头文件。我们想要实现如下的效果,以最开始那份代码为例。当你从b.cpp跳转到a.h的时候使用b.cpp作为a.h的上下文。同理,当你从c.cpp跳转到a.h的时候则使用c.cpp作为a.h的上下文。

    - +#include "a.h"

    a.h自身不能被编译,但是嵌入到b.cpp中的时候就编译正常了。这种情况下 clangd 会在a.h中报错,找不到X的定义。显然这是因为它把a.h当成一个独立的源文件了。在 libstdc++ 中的代码中就有很多这样的头文件,现在流行的一些 C++ 的 header-only 的库也有些有这样的代码,clangd 目前无法处理它们。

    clice 将会支持头文件上下文 (header context),支持自动和用户主动切换头文件的状态,当然也会支持非自包含的头文件。我们想要实现如下的效果,以最开始那份代码为例。当你从b.cpp跳转到a.h的时候使用b.cpp作为a.h的上下文。同理,当你从c.cpp跳转到a.h的时候则使用c.cpp作为a.h的上下文。

    + \ No newline at end of file diff --git a/zh/design/index.html b/zh/design/index.html index fde04bf5..27f6cf8c 100644 --- a/zh/design/index.html +++ b/zh/design/index.html @@ -18,8 +18,8 @@ -
    Skip to content
    - +
    Skip to content
    + \ No newline at end of file diff --git a/zh/design/template-resolver.html b/zh/design/template-resolver.html index f12d2243..a06b51da 100644 --- a/zh/design/template-resolver.html +++ b/zh/design/template-resolver.html @@ -18,14 +18,14 @@ -
    Skip to content

    Template Resolver

    那首先,就是更好的模板支持,这也是我最开始想要 clangd 支持的特性。具体来说目前在处理模板上有什么问题呢?

    以代码补全为例,考虑如下的代码,^代表光标位置:

    cpp
    template <typename T>
    +    
    Skip to content

    Template Resolver

    那首先,就是更好的模板支持,这也是我最开始想要 clangd 支持的特性。具体来说目前在处理模板上有什么问题呢?

    以代码补全为例,考虑如下的代码,^代表光标位置:

    cpp
    template <typename T>
     void foo(std::vector<T> vec) {
         vec.^
     }

    在 C++ 中,如果一个类型依赖于模板参数,那么在模板实例化之前,我们并不能对它做出任何准确的假设。例如这里的vector即可能是主模板也可能是vector<bool>的偏特化,选哪一个呢?对于代码编译来说,准确性永远是最重要的,不能使用任何可能导致错误的结果。但是对于语言服务器来说,提供更多可能的结果往往比什么都不提供更好,我们可以假设用户在更多时候使用主模板而不是偏特化,从而基于主模板来提供代码补全的结果。目前 clangd 也确实是这么做的,在上述情况下它会根据vector的主模板为你提供代码补全。

    再考虑一个更加复杂的例子:

    cpp
    template <typename T>
     void foo(std::vector<std::vector<T>> vec2) {
         vec2[0].^
    -}

    从用户的角度来说,这里也应该提供补全,毕竟vec2[0]的类型不也是vector<T>吗?和前面一个例子一样。但是 clangd 在这里却不会为你提供任何补全,问题出在哪里?根据 C++ 标准,std::vector<T>operator[]返回的类型是std::vector<T>::reference,这其实是一个 dependent name,它的结果似乎相当直接,就是T&。但是 libstdc++ 中它的定义却嵌套了十几层模板,似乎是为了兼容旧标准?那为什么 clangd 不能处理这种情况呢?

    1. 它基于主模板假设,不考虑偏特化可能会使查找无法进行下去
    2. 它只进行名称查找而不进行模板实例化,就算找到了最后的结果,也没法把它和最初的模板参数映射起来
    3. 不考虑默认模板参数,无法处理由默认模板参数导致的依赖名

    尽管我们可以对标准库的类型开洞来提供相关的支持,但是我希望用户的代码能和标准库的代码有相同的地位,那么我们就需要一种通用的算法来处理依赖类型。为了解决这个问题,我编写了一个伪实例化器(pseudo instantiator)。它能在没有具体类型的前提下对依赖类型进行实例化,从而达到化简的目的。比如上面这个例子里面的std::vector<std::vector<T>>::reference就能被化简为std::vector<T>&,进一步就能为用户提供代码补全选项。

    - +}

    从用户的角度来说,这里也应该提供补全,毕竟vec2[0]的类型不也是vector<T>吗?和前面一个例子一样。但是 clangd 在这里却不会为你提供任何补全,问题出在哪里?根据 C++ 标准,std::vector<T>operator[]返回的类型是std::vector<T>::reference,这其实是一个 dependent name,它的结果似乎相当直接,就是T&。但是 libstdc++ 中它的定义却嵌套了十几层模板,似乎是为了兼容旧标准?那为什么 clangd 不能处理这种情况呢?

    1. 它基于主模板假设,不考虑偏特化可能会使查找无法进行下去
    2. 它只进行名称查找而不进行模板实例化,就算找到了最后的结果,也没法把它和最初的模板参数映射起来
    3. 不考虑默认模板参数,无法处理由默认模板参数导致的依赖名

    尽管我们可以对标准库的类型开洞来提供相关的支持,但是我希望用户的代码能和标准库的代码有相同的地位,那么我们就需要一种通用的算法来处理依赖类型。为了解决这个问题,我编写了一个伪实例化器(pseudo instantiator)。它能在没有具体类型的前提下对依赖类型进行实例化,从而达到化简的目的。比如上面这个例子里面的std::vector<std::vector<T>>::reference就能被化简为std::vector<T>&,进一步就能为用户提供代码补全选项。

    + \ No newline at end of file diff --git a/zh/dev/build.html b/zh/dev/build.html index 1ef0c6b9..4dad9862 100644 --- a/zh/dev/build.html +++ b/zh/dev/build.html @@ -18,7 +18,7 @@ -
    Skip to content

    Build from Source

    Supported Platforms

    • Windows
    • Linux
    • MacOS

    Prerequisite

    本小节介绍编译 clice 的前置依赖。

    Toolchain

    • clang >= 19
    • c++23 compitable standard library
      • MSVC STL >= 19.44(VS 2022 17.4)
      • GCC libstdc++ >= 14
      • Clang libc++ >= 20

    clice 使用 C++23 作为语言标准 ,请确保有可用的 clang 19 以及以上的编译器,以及兼容 C++23 的标准库。

    clice 暂时只能使用 clang 编译,在未来我们会改进这一点,使其能使用 gcc 和 msvc 编译。

    LLVM Libs

    • 20.1.5 <= llvm libs < 21

    由于 C++ 的语法太过复杂,自己编写一个新的 parser 是不现实的。clice 调用 clang 的 API 来 parse C++ 源文件获取 AST,这意味它需要链接 llvm/clang libs。另外由于 clice 使用了 clang 的私有头文件,这些私有头文件在 llvm 发布的 binary release 中是没有的,所以不能直接使用系统的 llvm package。

    如果你能找到系统的 llvm package 对应的 llvm commit,将该 commit 下的如下三个文件

    • clang/lib/Sema/CoroutineStmtBuilder.h
    • clang/lib/Sema/TypeLocBuilder.h
    • clang/lib/Sema/TreeTransform.h

    拷贝到 LLVM_INSTALL_PATH/include/clang/Sema/ 中即可。

    除了这种方法以外,还有两种办法获取 clice 所需的 llvm libs:

    1. 使用我们提供的预编译版本
    bash
    # .github/workflows/cmake.yml
    +    
    Skip to content

    Build from Source

    Supported Platforms

    • Windows
    • Linux
    • MacOS

    Prerequisite

    本小节介绍编译 clice 的前置依赖。

    Toolchain

    • clang >= 19
    • c++23 compitable standard library
      • MSVC STL >= 19.44(VS 2022 17.4)
      • GCC libstdc++ >= 14
      • Clang libc++ >= 20

    clice 使用 C++23 作为语言标准 ,请确保有可用的 clang 19 以及以上的编译器,以及兼容 C++23 的标准库。

    clice 暂时只能使用 clang 编译,在未来我们会改进这一点,使其能使用 gcc 和 msvc 编译。

    LLVM Libs

    • 20.1.5 <= llvm libs < 21

    由于 C++ 的语法太过复杂,自己编写一个新的 parser 是不现实的。clice 调用 clang 的 API 来 parse C++ 源文件获取 AST,这意味它需要链接 llvm/clang libs。另外由于 clice 使用了 clang 的私有头文件,这些私有头文件在 llvm 发布的 binary release 中是没有的,所以不能直接使用系统的 llvm package。

    如果你能找到系统的 llvm package 对应的 llvm commit,将该 commit 下的如下三个文件

    • clang/lib/Sema/CoroutineStmtBuilder.h
    • clang/lib/Sema/TypeLocBuilder.h
    • clang/lib/Sema/TreeTransform.h

    拷贝到 LLVM_INSTALL_PATH/include/clang/Sema/ 中即可。

    除了这种方法以外,还有两种办法获取 clice 所需的 llvm libs:

    1. 使用我们提供的预编译版本
    bash
    # .github/workflows/cmake.yml
     
     # Linux precompiled binary require glibc 2.35 (build on ubuntu 22.04)
     $ mkdir -p ./.llvm
    @@ -43,8 +43,8 @@
     ├── include
     ├── lib
     ├── LICENSE
    -├── README.md

    提示:可以使用以下命令进入 clice 容器:

    bash
    $ docker run --rm -it --entrypoint bash clice
    - +├── README.md

    提示:可以使用以下命令进入 clice 容器:

    bash
    $ docker run --rm -it --entrypoint bash clice
    + \ No newline at end of file diff --git a/zh/dev/contribution.html b/zh/dev/contribution.html index b809b69c..1fbddd6e 100644 --- a/zh/dev/contribution.html +++ b/zh/dev/contribution.html @@ -13,13 +13,13 @@ - + -
    Skip to content

    Contribution

    我们欢迎任何贡献!

    请参考 build 来构建 clice,参考 test and debug 来测试和调试 clice。

    Code Style

    命名

    • 变量名:小写下换线
    • 类名,枚举名:大驼峰
    - +
    Skip to content

    Contribution

    我们欢迎任何贡献!

    请参考 build 来构建 clice,参考 test and debug 来测试和调试 clice。

    Code Style

    命名

    • 变量名:小写下换线
    • 类名,枚举名:大驼峰
    + \ No newline at end of file diff --git a/zh/dev/test-and-debug.html b/zh/dev/test-and-debug.html index 416af668..34352864 100644 --- a/zh/dev/test-and-debug.html +++ b/zh/dev/test-and-debug.html @@ -18,7 +18,7 @@ -
    Skip to content

    Test and Debug

    Run Tests

    clice 有两种形式的测试,单元测试和集成测试。

    • 运行单元测试
    bash
    $ ./build/bin/unit_tests --test-dir="./tests/data"
    • 运行集成测试

    我们使用 pytest 来运行集成测试,请参考 pyproject.toml 安装依赖的 python 库

    bash
    $ pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice

    如果你使用 xmake 作为构建系统,可以直接通过 xmake 运行测试:

    shell
    $ xmake run --verbose unit_tests
    +    
    Skip to content

    Test and Debug

    Run Tests

    clice 有两种形式的测试,单元测试和集成测试。

    • 运行单元测试
    bash
    $ ./build/bin/unit_tests --test-dir="./tests/data"
    • 运行集成测试

    我们使用 pytest 来运行集成测试,请参考 pyproject.toml 安装依赖的 python 库

    bash
    $ pytest -s --log-cli-level=INFO tests/integration --executable=./build/bin/clice

    如果你使用 xmake 作为构建系统,可以直接通过 xmake 运行测试:

    shell
    $ xmake run --verbose unit_tests
     $ xmake test --verbose integration_tests/default

    Debug

    如果想在 clice 上附加调试器并进行调试,推荐先单独以 socket 模式启动 clice,然后再将客户端连接到 clice 上

    shell
    $ ./build/bin/clice --mode=socket --port=50051

    在服务器启动之后,可以通过以下两种方式启动客户端连接到服务器

    • 使用 pytest 运行特定测试进行连接

    你可以运行一个单独的集成测试用例来连接正在运行的 clice。这对于复现和调试特定场景非常有用。

    shell
    $ pytest -s --log-cli-level=INFO tests/integration/test_file_operation.py::test_did_open --mode=socket --port=50051
    • 使用 vscode 进行实际的测试

    你也可以通过配置 clice-vscode 插件来连接正在运行的 clice 服务,从而在实际使用场景中进行调试。

    1. 在插件市场下载插件 clice-vscode

    2. 配置 settings.json: 在你的项目根目录下创建 .vscode/settings.json 文件,并填入以下内容:

      jsonc
      {
           // Point this to the clice binary you downloaded.
           "clice.executable": "/path/to/your/clice/executable",
      @@ -31,8 +31,8 @@
           "clangd.path": "",
       }
    3. 重新加载窗口:修改配置后,在 vscode 中执行 Developer: Reload Window 命令使配置生效。插件会自动连接到正在 50051 端口监听的 clice。

    如果你需要修改或调试 clice-vscode 插件本身,可以按以下步骤操作:

    1. 克隆并安装依赖:

      shell
      $ git clone https://github.com/clice-io/clice-vscode
       $ cd clice-vscode
      -$ npm install
    2. 使用 vscode 打开插件项目:用一个新的 vscode 窗口打开 clice-vscode 文件夹

    3. 创建调试配置:在 clice-vscode 项目中,也创建一个 .vscode/settings.json 文件,内容与上方相同

    4. 按下 F5 键。这会启动一个【扩展开发宿主】窗口。这是一个加载了你本地 clice-vscode 插件代码的新的 vscode 窗口,在这个新窗口中打开你的 C++ 项目,它应该会自动连接到 clice

    - +$ npm install
  • 使用 vscode 打开插件项目:用一个新的 vscode 窗口打开 clice-vscode 文件夹

  • 创建调试配置:在 clice-vscode 项目中,也创建一个 .vscode/settings.json 文件,内容与上方相同

  • 按下 F5 键。这会启动一个【扩展开发宿主】窗口。这是一个加载了你本地 clice-vscode 插件代码的新的 vscode 窗口,在这个新窗口中打开你的 C++ 项目,它应该会自动连接到 clice

  • + \ No newline at end of file diff --git a/zh/guide/configuration.html b/zh/guide/configuration.html index fb24e1eb..44286b2c 100644 --- a/zh/guide/configuration.html +++ b/zh/guide/configuration.html @@ -18,8 +18,8 @@ -
    Skip to content

    Configuration

    这是 clice.toml 的文档。

    Server

    Rule

    [[rules]] 表示一个对象数组,其中每个对象都拥有下面这些属性

    名称类型
    [rules].patternarray of string

    用于匹配文件路径的 glob patterns,遵循 LSP 的 标准

    • *: 匹配路径段中的一个或多个字符。
    • ?: 匹配路径段中的单个字符。
    • **: 匹配任意数量的路径段,包括零个。
    • {}: 用于分组条件 (例如,**/*.{ts,js} 匹配所有 TypeScript 和 JavaScript 文件)。
    • []: 声明要匹配的路径段中的字符范围 (例如,example.[0-9] 匹配 example.0, example.1 等)。
    • [!...]: 排除要匹配的路径段中的字符范围 (例如,example.[!0-9] 匹配 example.a, example.b,但不匹配 example.0)。
    名称类型默认值
    [rules].appendarray of string[]

    追加到原始命令列表中的命令。例如,append = ["-std=c++17"]

    名称类型默认值
    [rules].removearray of string[]

    从原始命令列表中移除的命令。例如,remove = ["-std=c++11"]

    名称类型默认值
    [rules].readonlystring"auto"

    控制文件是否被视为只读。值可以是 "auto""always""never" 中的一个。

    • "auto": 在你编辑文件之前,文件被视为只读。
    • "always": 始终将文件视为只读。
    • "never": 始终将文件视为非只读。

    只读意味着文件不可编辑,并且像代码操作 (code actions) 或补全 (completions) 这样的 LSP 请求不会在其上触发。这避免了动态计算,并允许直接加载预先索引的结果,从而提高性能。

    名称类型默认值
    [rules].headerstring"auto"

    控制如何处理头文件。值可以是 "auto""always""never" 中的一个。

    • "auto": 首先尝试推断头文件上下文。如果未找到头文件上下文,文件将被视为普通的源文件。
    • "always": 始终将文件视为头文件。如果未找到头文件上下文,将会报告错误。
    • "never": 始终将文件视为源文件。

    头文件上下文指的是与该头文件相关联的源文件或其他元数据。

    名称类型默认值
    [rules].contextsarray of string[]

    为文件指定额外的头文件上下文 (文件路径)。

    通常,一旦文件被索引,头文件上下文会自动推断。但是,如果你需要在索引完成前获得即时上下文,可以使用此字段手动提供。

    Cache

    名称类型默认值
    cache.dirstring"${workspace}/.clice/cache"

    用于储存 PCH 和 PCM 缓存的文件夹。

    Index

    名称类型默认值
    index.dirstring"${workspace}/.clice/index"

    用于储存索引文件的文件夹。

    Feature

    - +
    Skip to content

    Configuration

    这是 clice.toml 的文档。

    Server

    Rule

    [[rules]] 表示一个对象数组,其中每个对象都拥有下面这些属性

    名称类型
    [rules].patternarray of string

    用于匹配文件路径的 glob patterns,遵循 LSP 的 标准

    • *: 匹配路径段中的一个或多个字符。
    • ?: 匹配路径段中的单个字符。
    • **: 匹配任意数量的路径段,包括零个。
    • {}: 用于分组条件 (例如,**/*.{ts,js} 匹配所有 TypeScript 和 JavaScript 文件)。
    • []: 声明要匹配的路径段中的字符范围 (例如,example.[0-9] 匹配 example.0, example.1 等)。
    • [!...]: 排除要匹配的路径段中的字符范围 (例如,example.[!0-9] 匹配 example.a, example.b,但不匹配 example.0)。
    名称类型默认值
    [rules].appendarray of string[]

    追加到原始命令列表中的命令。例如,append = ["-std=c++17"]

    名称类型默认值
    [rules].removearray of string[]

    从原始命令列表中移除的命令。例如,remove = ["-std=c++11"]

    名称类型默认值
    [rules].readonlystring"auto"

    控制文件是否被视为只读。值可以是 "auto""always""never" 中的一个。

    • "auto": 在你编辑文件之前,文件被视为只读。
    • "always": 始终将文件视为只读。
    • "never": 始终将文件视为非只读。

    只读意味着文件不可编辑,并且像代码操作 (code actions) 或补全 (completions) 这样的 LSP 请求不会在其上触发。这避免了动态计算,并允许直接加载预先索引的结果,从而提高性能。

    名称类型默认值
    [rules].headerstring"auto"

    控制如何处理头文件。值可以是 "auto""always""never" 中的一个。

    • "auto": 首先尝试推断头文件上下文。如果未找到头文件上下文,文件将被视为普通的源文件。
    • "always": 始终将文件视为头文件。如果未找到头文件上下文,将会报告错误。
    • "never": 始终将文件视为源文件。

    头文件上下文指的是与该头文件相关联的源文件或其他元数据。

    名称类型默认值
    [rules].contextsarray of string[]

    为文件指定额外的头文件上下文 (文件路径)。

    通常,一旦文件被索引,头文件上下文会自动推断。但是,如果你需要在索引完成前获得即时上下文,可以使用此字段手动提供。

    Cache

    名称类型默认值
    cache.dirstring"${workspace}/.clice/cache"

    用于储存 PCH 和 PCM 缓存的文件夹。

    Index

    名称类型默认值
    index.dirstring"${workspace}/.clice/index"

    用于储存索引文件的文件夹。

    Feature

    + \ No newline at end of file diff --git a/zh/guide/quick-start.html b/zh/guide/quick-start.html index 590f0564..5473889a 100644 --- a/zh/guide/quick-start.html +++ b/zh/guide/quick-start.html @@ -13,13 +13,13 @@ - + -
    Skip to content

    Quick Start

    Editor Plugins

    clice 实现了 Language Server Protocol,任何支持该协议的编辑器原则上均可以与 clice 一起使用,提供像 code completion, diagnostics, go-to-definition, 等等。

    但是除了标准协议之外,clice 还支持一些协议扩展,为了更好的处理这些协议扩展以及更好的与编辑器集成。使用特定编辑器中的 clice 插件往往是更好的选择,它们大多数都是开箱即用的,并且支持 clice 的协议扩展。

    Visual Studio Code

    Vim/Neovim

    Others

    其它的编辑器还没有可用的 clice 插件(欢迎贡献!),为了在它们中使用 clice,请自行安装 clice 并参考特定编辑器的文档关于如何使用一个语言服务器。

    Installation

    如果你的编辑器插件负责了 clice 的下载,可以跳过这一步。

    Download Prebuilt Binary

    通过 Release 界面下载 clice 二进制版本。

    Build from Source

    自己从源码编译 clice,具体的步骤参考 build

    Project Setup

    为了让 clice 能正确理解你的代码(例如找到头文件的位置),需要为 clice 提供一份 compile_commands.json 文件,也就说所谓的 编译数据库。编译数据库中提供了每个源文件的编译选项。

    CMake

    对于使用 cmake 的构建系统来说,在构建的时候添加 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 选项即可,例如

    cmake
    cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

    这会在 build 目录下生成一份 compile_commands.json 文件。

    WARNING

    注意:只有当 cmake 的生成器选择为 makefile 和 ninja 的时候,这个选项才有作用。对于其它生成器会忽略这个选项,也就是说无法生成编译数据库。

    Bazel

    Bazel 不支持直接生成编译数据库,推荐使用 bazel-compile-commands-extractor。在安装好之后,你可以这样生成 compile_commands.json:

    bash
    bazel run @hedron_compile_commands//:refresh_all

    Visual Studio

    TODO:

    Makefile

    TODO:

    Xmake

    Others

    对于任意其它的构建系统,可以尝试使用 bear 或者 scan-build 来拦截编译命令并获取到编译数据库(不保证成功)。我们计划在未来编写一个新的工具,通过假编译器的方式来实现编译命令的捕获。

    - +
    Skip to content

    Quick Start

    Editor Plugins

    clice 实现了 Language Server Protocol,任何支持该协议的编辑器原则上均可以与 clice 一起使用,提供像 code completion, diagnostics, go-to-definition, 等等。

    但是除了标准协议之外,clice 还支持一些协议扩展,为了更好的处理这些协议扩展以及更好的与编辑器集成。使用特定编辑器中的 clice 插件往往是更好的选择,它们大多数都是开箱即用的,并且支持 clice 的协议扩展。

    Visual Studio Code

    Vim/Neovim

    Others

    其它的编辑器还没有可用的 clice 插件(欢迎贡献!),为了在它们中使用 clice,请自行安装 clice 并参考特定编辑器的文档关于如何使用一个语言服务器。

    Installation

    如果你的编辑器插件负责了 clice 的下载,可以跳过这一步。

    Download Prebuilt Binary

    通过 Release 界面下载 clice 二进制版本。

    Build from Source

    自己从源码编译 clice,具体的步骤参考 build

    Project Setup

    为了让 clice 能正确理解你的代码(例如找到头文件的位置),需要为 clice 提供一份 compile_commands.json 文件,也就说所谓的 编译数据库。编译数据库中提供了每个源文件的编译选项。

    CMake

    对于使用 cmake 的构建系统来说,在构建的时候添加 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 选项即可,例如

    cmake
    cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

    这会在 build 目录下生成一份 compile_commands.json 文件。

    WARNING

    注意:只有当 cmake 的生成器选择为 makefile 和 ninja 的时候,这个选项才有作用。对于其它生成器会忽略这个选项,也就是说无法生成编译数据库。

    Bazel

    Bazel 不支持直接生成编译数据库,推荐使用 bazel-compile-commands-extractor。在安装好之后,你可以这样生成 compile_commands.json:

    bash
    bazel run @hedron_compile_commands//:refresh_all

    Visual Studio

    TODO:

    Makefile

    TODO:

    Xmake

    Others

    对于任意其它的构建系统,可以尝试使用 bear 或者 scan-build 来拦截编译命令并获取到编译数据库(不保证成功)。我们计划在未来编写一个新的工具,通过假编译器的方式来实现编译命令的捕获。

    + \ No newline at end of file diff --git a/zh/guide/what-is-clice.html b/zh/guide/what-is-clice.html index 88b819e0..c6a5d278 100644 --- a/zh/guide/what-is-clice.html +++ b/zh/guide/what-is-clice.html @@ -18,8 +18,8 @@ -
    Skip to content

    What is clice?

    clice 是一个全新的 C++ 的语言服务器,旨在解决现存 C++ 语言服务器的不足,它可以为你的编辑器提供代码导航和智能提示等服务。

    Why a New Language Server?

    那么第一个问题,为什么要去开发一个新的语言服务器?重复造轮子有必要吗?

    这个问题值得去认真回答。在这个项目之前,我自己也编写过或大或小的很多项目。但它们绝大多数都是 toy project,只是为了验证某个想法或者个人学习而编写的,并没有解决任何的实际问题。clice 并不是这样的,它确实打算解决现有的问题(具体的问题后面再说),而不是为了重写而重写。

    在今年年初,我想参与到 llvm 项目的开发中。我想从我较为熟悉的地方,C++,也就是 clang 开始。但是,没需求的话,总不能干瞪源码吧。一般这种时候的正常流程是从一些 first issue 开始,一点点熟悉项目。但是我觉得这样很无聊,一上来我就想来干点大的,比如实现某个 C++ 新标准的特性。但是,我发现这里几乎没有我能插手的地方,新特性的实现几乎总是由几位 clang 的核心开发者完成。好吧,既然这里没什么机会,那就看看别的地方吧。注意力自然而然的转移到了 clangd 身上去了,毕竟我主要使用 vscode 进行开发,而 vscode 上最好用的 C++ 语言服务器就是 clangd 了。

    当时我对 clangd 一无所知,只是发现它似乎对关键字的高亮渲染并不正确。然后呢我就开始一边阅读 clangd 的源码,一边翻翻 clangd 数量众多的 issue 看看有没有什么我能解决的。在翻了几百个 issue 过后,我发现这里的问题还真不少。当时我特别感兴趣的是一个有关模板内代码补全的 issue,为什么我对这个感兴趣呢?熟悉我的读者可能知道,我算是一个资深的元编程玩家了,在这之前也写过很多相关的文章。那很自然的,我不仅仅好奇模板元它本身是如何运作的,也好奇 clang 作为一个编译器是如何实现相关的特性的,这个 issue 对我来说是一个很好的切入点。在花了几个星期摸索原型实现后,我初步解决了那个 issue,但是,这时我发现根本没有人可以 review 相关的代码!

    一番调查过后,我发现 clangd 目前的情况很糟糕。让我们来捋一捋时间线,clangd 最初只是 llvm 内部一个简单的小项目,在功能性和易用性上都不出色。正如 MaskRay 在 ccls 中的这篇 博客 中所说,clangd 在当时只能处理单个编译单元,跨编译单元的请求无法处理。这篇博客发布的时间是 2017 年,这也是为什么 MaskRay 选择编写 ccls 的一个原因。ccls 也是一个 C/C++ 语言服务器,在当时那个节点上是强于 clangd 的。但是,后来,Google 开始派人对 clangd 进行改进以满足他们内部的大型代码库需求。与此同时,LSP 标准的内容也在不断地扩充,clangd 在不断地跟进新标准的内容,但是 ccls 的作者似乎逐渐忙于其它事情没有太多的时间去维护 ccls。于是最后,总体上 clangd 已经超越了 ccls。事情的转折发生在大概 2023 年,clangd 对 Google 内部来说,似乎已经达到了一个高度可用的状态,原先负责 clangd 的几位员工也被调离去做其他事情了。目前来说,clangd 的 issue 主要只有 HighCommander4 一个人在负责处理,纯粹出于热爱,并没有被任何人雇佣。由于并没有被专门雇佣来维护 clangd,所以他只能在有限的空闲时间来处理 issue,而且也仅限于答疑和十分有限的 review。正如他在这条 评论 中提到的一样:

    The other part of the reason is lack of resources to pursue the ideas we do have, such as the idea mentioned above of trying to shift more of the burden to disk usage through more aggressive preamble caching. I'm a casual contributor, and the limited time I have to spend on clangd is mostly taken up by answering questions, some code reviews, and the occasional small fix / improvement; I haven't had the bandwidth to drive this type of performance-related experimentation.

    另一部分原因是缺乏资源去实践我们已有的一些想法,例如上面提到的通过更积极的预编译缓存,将更多的负担转移到磁盘使用上的想法。我只是一个非正式的贡献者,我能投入到 clangd 上的时间非常有限,主要用于回答问题、进行一些代码审查以及偶尔的小修复或改进;我没有足够的精力来推动这种与性能相关的实验。

    既然如此,那么像为 clangd 初步支持 C++20 module 这样的大型 PR 被拖了将近一年也就不奇怪了。意识到这个现状之后,我萌生了自己编写一个语言服务器的想法。我估计了一下项目大小,去除测试代码,大概 2w 行就能完成,是一个人花一段时间能完成的工作量,而且也有先例,例如 ccls 和 rust analyzer。另外一点就是 clangd 的代码已经上了年代了,尽管有非常多的注释,但是相关的逻辑仍然很绕,进行大范围修改所花费的时间可能还不如重写来得快。

    于是说干就干,我对 clangd 的几百个 issue 进行了分类,看看有没有一些问题是因为 clangd 一开始的架构设计错误而导致很难解决,然后被搁置的。如果有的话,是否能在重新设计的时候就考虑这个问题来解决呢?我发现,确实有一些!于是接下来的时间里,我花了大概两个月的时间来学习研究 clang 里面相关的机制,摸索相关问题的解决方法,探索原型实现,在确定相关的问题基本都可以解决之后,正式开始了 clice 的开发。

    - +
    Skip to content

    What is clice?

    clice 是一个全新的 C++ 的语言服务器,旨在解决现存 C++ 语言服务器的不足,它可以为你的编辑器提供代码导航和智能提示等服务。

    Why a New Language Server?

    那么第一个问题,为什么要去开发一个新的语言服务器?重复造轮子有必要吗?

    这个问题值得去认真回答。在这个项目之前,我自己也编写过或大或小的很多项目。但它们绝大多数都是 toy project,只是为了验证某个想法或者个人学习而编写的,并没有解决任何的实际问题。clice 并不是这样的,它确实打算解决现有的问题(具体的问题后面再说),而不是为了重写而重写。

    在今年年初,我想参与到 llvm 项目的开发中。我想从我较为熟悉的地方,C++,也就是 clang 开始。但是,没需求的话,总不能干瞪源码吧。一般这种时候的正常流程是从一些 first issue 开始,一点点熟悉项目。但是我觉得这样很无聊,一上来我就想来干点大的,比如实现某个 C++ 新标准的特性。但是,我发现这里几乎没有我能插手的地方,新特性的实现几乎总是由几位 clang 的核心开发者完成。好吧,既然这里没什么机会,那就看看别的地方吧。注意力自然而然的转移到了 clangd 身上去了,毕竟我主要使用 vscode 进行开发,而 vscode 上最好用的 C++ 语言服务器就是 clangd 了。

    当时我对 clangd 一无所知,只是发现它似乎对关键字的高亮渲染并不正确。然后呢我就开始一边阅读 clangd 的源码,一边翻翻 clangd 数量众多的 issue 看看有没有什么我能解决的。在翻了几百个 issue 过后,我发现这里的问题还真不少。当时我特别感兴趣的是一个有关模板内代码补全的 issue,为什么我对这个感兴趣呢?熟悉我的读者可能知道,我算是一个资深的元编程玩家了,在这之前也写过很多相关的文章。那很自然的,我不仅仅好奇模板元它本身是如何运作的,也好奇 clang 作为一个编译器是如何实现相关的特性的,这个 issue 对我来说是一个很好的切入点。在花了几个星期摸索原型实现后,我初步解决了那个 issue,但是,这时我发现根本没有人可以 review 相关的代码!

    一番调查过后,我发现 clangd 目前的情况很糟糕。让我们来捋一捋时间线,clangd 最初只是 llvm 内部一个简单的小项目,在功能性和易用性上都不出色。正如 MaskRay 在 ccls 中的这篇 博客 中所说,clangd 在当时只能处理单个编译单元,跨编译单元的请求无法处理。这篇博客发布的时间是 2017 年,这也是为什么 MaskRay 选择编写 ccls 的一个原因。ccls 也是一个 C/C++ 语言服务器,在当时那个节点上是强于 clangd 的。但是,后来,Google 开始派人对 clangd 进行改进以满足他们内部的大型代码库需求。与此同时,LSP 标准的内容也在不断地扩充,clangd 在不断地跟进新标准的内容,但是 ccls 的作者似乎逐渐忙于其它事情没有太多的时间去维护 ccls。于是最后,总体上 clangd 已经超越了 ccls。事情的转折发生在大概 2023 年,clangd 对 Google 内部来说,似乎已经达到了一个高度可用的状态,原先负责 clangd 的几位员工也被调离去做其他事情了。目前来说,clangd 的 issue 主要只有 HighCommander4 一个人在负责处理,纯粹出于热爱,并没有被任何人雇佣。由于并没有被专门雇佣来维护 clangd,所以他只能在有限的空闲时间来处理 issue,而且也仅限于答疑和十分有限的 review。正如他在这条 评论 中提到的一样:

    The other part of the reason is lack of resources to pursue the ideas we do have, such as the idea mentioned above of trying to shift more of the burden to disk usage through more aggressive preamble caching. I'm a casual contributor, and the limited time I have to spend on clangd is mostly taken up by answering questions, some code reviews, and the occasional small fix / improvement; I haven't had the bandwidth to drive this type of performance-related experimentation.

    另一部分原因是缺乏资源去实践我们已有的一些想法,例如上面提到的通过更积极的预编译缓存,将更多的负担转移到磁盘使用上的想法。我只是一个非正式的贡献者,我能投入到 clangd 上的时间非常有限,主要用于回答问题、进行一些代码审查以及偶尔的小修复或改进;我没有足够的精力来推动这种与性能相关的实验。

    既然如此,那么像为 clangd 初步支持 C++20 module 这样的大型 PR 被拖了将近一年也就不奇怪了。意识到这个现状之后,我萌生了自己编写一个语言服务器的想法。我估计了一下项目大小,去除测试代码,大概 2w 行就能完成,是一个人花一段时间能完成的工作量,而且也有先例,例如 ccls 和 rust analyzer。另外一点就是 clangd 的代码已经上了年代了,尽管有非常多的注释,但是相关的逻辑仍然很绕,进行大范围修改所花费的时间可能还不如重写来得快。

    于是说干就干,我对 clangd 的几百个 issue 进行了分类,看看有没有一些问题是因为 clangd 一开始的架构设计错误而导致很难解决,然后被搁置的。如果有的话,是否能在重新设计的时候就考虑这个问题来解决呢?我发现,确实有一些!于是接下来的时间里,我花了大概两个月的时间来学习研究 clang 里面相关的机制,摸索相关问题的解决方法,探索原型实现,在确定相关的问题基本都可以解决之后,正式开始了 clice 的开发。

    + \ No newline at end of file diff --git a/zh/index.html b/zh/index.html index efa9009f..18283b2b 100644 --- a/zh/index.html +++ b/zh/index.html @@ -18,8 +18,8 @@ -
    Skip to content

    clice下一代 C++ 语言服务器

    开发正在活跃进行中

    clice
    - +
    Skip to content

    clice下一代 C++ 语言服务器

    开发正在活跃进行中

    clice
    + \ No newline at end of file