diff --git a/404.html b/404.html index 4bec0140..1d2a3974 100644 --- a/404.html +++ b/404.html @@ -16,7 +16,7 @@
- + \ No newline at end of file diff --git a/assets/dev_build.md.ClJGf1or.js b/assets/dev_build.md.D62hQKxA.js similarity index 93% rename from assets/dev_build.md.ClJGf1or.js rename to assets/dev_build.md.D62hQKxA.js index 71d58571..63093c51 100644 --- a/assets/dev_build.md.ClJGf1or.js +++ b/assets/dev_build.md.D62hQKxA.js @@ -17,9 +17,6 @@ import{_ as i,c as a,o as l,ae as e}from"./chunks/framework.U1Gow_7P.js";const c $ xmake test --verbose integration_tests/default

Or, if you use xmake build the project and do not have uv installed, you can use the following script:

bash
$ pip install pytest pytest-asyncio
 $ xmake f -m debug && xmake build unit_tests
 
-# The environment variable LLVM_INSTALL_DIR may vary in different commits and platforms, 
-# depending on the value of \`set_versions\` in the \`package("llvm")\` section of the xmake.lua
-$ LLVM_INSTALL_DIR=./build/.packages/l/llvm/20.1.5/0181167384bb4acb9e781210294c358d/lib/clang/20/ \\
-  pytest -s --log-cli-level=INFO tests/integration \\
+$ pytest -s --log-cli-level=INFO tests/integration \\
     --executable=./build/linux/x86_64/debug/clice \\
-    --resource-dir=$LLVM_INSTALL_DIR
`,48)]))}const F=i(t,[["render",n]]);export{c as __pageData,F as default}; + --resource-dir=./build/linux/x86_64/debug/lib/clang/20/`,48)]))}const F=i(t,[["render",n]]);export{c as __pageData,F as default}; diff --git a/assets/dev_build.md.ClJGf1or.lean.js b/assets/dev_build.md.D62hQKxA.lean.js similarity index 100% rename from assets/dev_build.md.ClJGf1or.lean.js rename to assets/dev_build.md.D62hQKxA.lean.js diff --git a/assets/zh_dev_build.md.CMjTakSe.js b/assets/zh_dev_build.md.B9MaN1Mj.js similarity index 93% rename from assets/zh_dev_build.md.CMjTakSe.js rename to assets/zh_dev_build.md.B9MaN1Mj.js index ac49731c..9e881304 100644 --- a/assets/zh_dev_build.md.CMjTakSe.js +++ b/assets/zh_dev_build.md.B9MaN1Mj.js @@ -17,9 +17,6 @@ import{_ as i,c as a,o as l,ae as e}from"./chunks/framework.U1Gow_7P.js";const o $ xmake test --verbose integration_tests/default

在使用 xmake 构建和不使用 uv 的情况下, 启动 debug 模式的测试:

shell
$ pip install pytest pytest-asyncio
 $ xmake f -m debug && xmake build unit_tests
 
-# 此处的 LLVM_INSTALL_DIR 在不同版本, 不同平台的 clice 中 sha 值可能不一样,
-# 取决于对应的 xmake.lua 中 \`package("llvm")\` 一节 \`set_versions\` 的取值
-$ LLVM_INSTALL_DIR=./build/.packages/l/llvm/20.1.5/0181167384bb4acb9e781210294c358d/lib/clang/20/ \\
-  pytest -s --log-cli-level=INFO tests/integration \\
+$ pytest -s --log-cli-level=INFO tests/integration \\
     --executable=./build/linux/x86_64/debug/clice \\
-    --resource-dir=$LLVM_INSTALL_DIR
`,48)]))}const F=i(t,[["render",n]]);export{o as __pageData,F as default}; + --resource-dir=./build/linux/x86_64/debug/lib/clang/20/`,48)]))}const F=i(t,[["render",n]]);export{o as __pageData,F as default}; diff --git a/assets/zh_dev_build.md.CMjTakSe.lean.js b/assets/zh_dev_build.md.B9MaN1Mj.lean.js similarity index 100% rename from assets/zh_dev_build.md.CMjTakSe.lean.js rename to assets/zh_dev_build.md.B9MaN1Mj.lean.js diff --git a/design/architecture.html b/design/architecture.html index 4371cf00..94720cb6 100644 --- a/design/architecture.html +++ b/design/architecture.html @@ -19,7 +19,7 @@
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 9616fa12..f37aeff5 100644 --- a/design/compilation.html +++ b/design/compilation.html @@ -23,7 +23,7 @@ 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

- + \ No newline at end of file diff --git a/design/header-context.html b/design/header-context.html index 4e94c63d..19f5a064 100644 --- a/design/header-context.html +++ b/design/header-context.html @@ -38,7 +38,7 @@ // 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.

- + \ No newline at end of file diff --git a/design/index.html b/design/index.html index 0dc8e7a0..6ab6ba8c 100644 --- a/design/index.html +++ b/design/index.html @@ -19,7 +19,7 @@
Skip to content
- + \ No newline at end of file diff --git a/design/template-resolver.html b/design/template-resolver.html index 8d7b1f63..5b191852 100644 --- a/design/template-resolver.html +++ b/design/template-resolver.html @@ -25,7 +25,7 @@ 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.

- + \ No newline at end of file diff --git a/dev/build.html b/dev/build.html index df631f3a..6b982e6e 100644 --- a/dev/build.html +++ b/dev/build.html @@ -13,7 +13,7 @@ - + @@ -37,13 +37,10 @@ $ xmake test --verbose integration_tests/default

Or, if you use xmake build the project and do not have uv installed, you can use the following script:

bash
$ pip install pytest pytest-asyncio
 $ xmake f -m debug && xmake build unit_tests
 
-# The environment variable LLVM_INSTALL_DIR may vary in different commits and platforms, 
-# depending on the value of `set_versions` in the `package("llvm")` section of the xmake.lua
-$ LLVM_INSTALL_DIR=./build/.packages/l/llvm/20.1.5/0181167384bb4acb9e781210294c358d/lib/clang/20/ \
-  pytest -s --log-cli-level=INFO tests/integration \
+$ pytest -s --log-cli-level=INFO tests/integration \
     --executable=./build/linux/x86_64/debug/clice \
-    --resource-dir=$LLVM_INSTALL_DIR
- + --resource-dir=./build/linux/x86_64/debug/lib/clang/20/ + \ No newline at end of file diff --git a/dev/contribution.html b/dev/contribution.html index 1c5b6c2b..2184365d 100644 --- a/dev/contribution.html +++ b/dev/contribution.html @@ -19,7 +19,7 @@
Skip to content

Contribution

We welcome any contributions!

Please refer to build to build clice.

Code Style

Naming:

  • Variable names: lowercase with underscores
  • Class names, enum names: PascalCase
- + \ No newline at end of file diff --git a/guide/configuration.html b/guide/configuration.html index a8300509..634768fe 100644 --- a/guide/configuration.html +++ b/guide/configuration.html @@ -19,7 +19,7 @@
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 e4809544..090db553 100644 --- a/guide/quick-start.html +++ b/guide/quick-start.html @@ -19,7 +19,7 @@
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 61cf9c60..518030d5 100644 --- a/guide/what-is-clice.html +++ b/guide/what-is-clice.html @@ -19,7 +19,7 @@
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 cf0b2b4c..494c7076 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"design_architecture.md":"D8TKjkhM","design_compilation.md":"DgqTpBDl","design_header-context.md":"D86T7s_L","design_index.md":"BgicbTS7","design_template-resolver.md":"IYEsm7Dh","dev_build.md":"ClJGf1or","dev_contribution.md":"D-EaJs3t","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":"Cr4H3x27","zh_design_index.md":"DHNbht8F","zh_design_template-resolver.md":"BdV6FoQY","zh_dev_build.md":"CMjTakSe","zh_dev_contribution.md":"UxN4TV1f","zh_guide_configuration.md":"DM65IMG5","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":"D86T7s_L","design_index.md":"BgicbTS7","design_template-resolver.md":"IYEsm7Dh","dev_build.md":"D62hQKxA","dev_contribution.md":"D-EaJs3t","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":"Cr4H3x27","zh_design_index.md":"DHNbht8F","zh_design_template-resolver.md":"BdV6FoQY","zh_dev_build.md":"B9MaN1Mj","zh_dev_contribution.md":"UxN4TV1f","zh_guide_configuration.md":"DM65IMG5","zh_guide_quick-start.md":"BZnA1cly","zh_guide_what-is-clice.md":"DFkS8WKr","zh_index.md":"DOAh-Isq"} diff --git a/index.html b/index.html index 1610aeb9..4611d545 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,7 @@
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 2e17807e..473776c4 100644 --- a/zh/design/architecture.html +++ b/zh/design/architecture.html @@ -19,7 +19,7 @@
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 7e375aec..5ff5f391 100644 --- a/zh/design/compilation.html +++ b/zh/design/compilation.html @@ -23,7 +23,7 @@ int main () { std::cout << "Hello world!" << std::endl; }

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 e317e0ff..be18ba35 100644 --- a/zh/design/header-context.html +++ b/zh/design/header-context.html @@ -38,7 +38,7 @@ // 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的上下文。

- + \ No newline at end of file diff --git a/zh/design/index.html b/zh/design/index.html index a87987ce..60b3f77c 100644 --- a/zh/design/index.html +++ b/zh/design/index.html @@ -19,7 +19,7 @@
Skip to content
- + \ No newline at end of file diff --git a/zh/design/template-resolver.html b/zh/design/template-resolver.html index a15420dc..de13d4b6 100644 --- a/zh/design/template-resolver.html +++ b/zh/design/template-resolver.html @@ -25,7 +25,7 @@ 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>&,进一步就能为用户提供代码补全选项。

- + \ No newline at end of file diff --git a/zh/dev/build.html b/zh/dev/build.html index 0aa4ccd0..e85839b6 100644 --- a/zh/dev/build.html +++ b/zh/dev/build.html @@ -13,7 +13,7 @@ - + @@ -37,13 +37,10 @@ $ xmake test --verbose integration_tests/default

在使用 xmake 构建和不使用 uv 的情况下, 启动 debug 模式的测试:

shell
$ pip install pytest pytest-asyncio
 $ xmake f -m debug && xmake build unit_tests
 
-# 此处的 LLVM_INSTALL_DIR 在不同版本, 不同平台的 clice 中 sha 值可能不一样,
-# 取决于对应的 xmake.lua 中 `package("llvm")` 一节 `set_versions` 的取值
-$ LLVM_INSTALL_DIR=./build/.packages/l/llvm/20.1.5/0181167384bb4acb9e781210294c358d/lib/clang/20/ \
-  pytest -s --log-cli-level=INFO tests/integration \
+$ pytest -s --log-cli-level=INFO tests/integration \
     --executable=./build/linux/x86_64/debug/clice \
-    --resource-dir=$LLVM_INSTALL_DIR
- + --resource-dir=./build/linux/x86_64/debug/lib/clang/20/ + \ No newline at end of file diff --git a/zh/dev/contribution.html b/zh/dev/contribution.html index d660b8c7..a07645b9 100644 --- a/zh/dev/contribution.html +++ b/zh/dev/contribution.html @@ -19,7 +19,7 @@
Skip to content

Contribution

我们欢迎任何贡献!

请参考 build 来构建 clice

Code Style

命名

  • 变量名:小写下换线
  • 类名,枚举名:大驼峰
- + \ No newline at end of file diff --git a/zh/guide/configuration.html b/zh/guide/configuration.html index 77d39e33..7879aff6 100644 --- a/zh/guide/configuration.html +++ b/zh/guide/configuration.html @@ -19,7 +19,7 @@
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 277c7b96..920b90da 100644 --- a/zh/guide/quick-start.html +++ b/zh/guide/quick-start.html @@ -19,7 +19,7 @@
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 40b68165..513d73df 100644 --- a/zh/guide/what-is-clice.html +++ b/zh/guide/what-is-clice.html @@ -19,7 +19,7 @@
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 1a7061a3..bf6e8c94 100644 --- a/zh/index.html +++ b/zh/index.html @@ -19,7 +19,7 @@
Skip to content

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

开发正在活跃进行中

clice
- + \ No newline at end of file