diff --git a/404.html b/404.html index 0fe971c7..16a82ab5 100644 --- a/404.html +++ b/404.html @@ -16,7 +16,7 @@
- + \ No newline at end of file diff --git a/assets/dev_build.md.BVixu1OK.js b/assets/dev_build.md.BVixu1OK.js deleted file mode 100644 index 58a10a2b..00000000 --- a/assets/dev_build.md.BVixu1OK.js +++ /dev/null @@ -1,36 +0,0 @@ -import{_ as i,c as a,o as l,ae as e}from"./chunks/framework.D8webtH5.js";const c=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"dev/build.md","filePath":"en/dev/build.md"}'),n={name:"dev/build.md"};function t(h,s,p,r,k,o){return l(),a("div",null,s[0]||(s[0]=[e(`

Build from Source

Supported Platforms

Prerequisites

This section introduces the prerequisites for compiling clice.

Toolchain

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

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:

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
-$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x86_64-linux-gnu-release.tar.xz" | tar -xJ -C ./.llvm
-
-# MacOS precompiled binary require macos15+
-$ mkdir -p ./.llvm
-$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/arm64-macosx-apple-release.tar.xz" | tar -xJ -C ./.llvm
-
-# Windows precompiled binary only MD runtime support
-$ curl -O -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x64-windows-msvc-release.7z"
-$ 7z x x64-windows-msvc-release.7z "-o.llvm"

IMPORTANT

For debug versions of llvm libs, we enabled address sanitizer during build, and address sanitizer depends on compiler rt, which is very sensitive to compiler versions. So if using debug versions, please ensure your clang's compiler rt version is strictly consistent with what we used during build.

  1. Compile llvm/clang from scratch

This is the most recommended approach, ensuring environment consistency and avoiding crash issues caused by ABI inconsistencies. We provide a script for building the llvm libs required by clice: build-llvm-libs.py.

bash
$ cd llvm-project
-$ python3 <clice>/scripts/build-llvm-libs.py debug

You can also refer to llvm's official build tutorial Building LLVM with CMake.

GCC Toolchain

clice requires GCC libstdc++ >= 14. You could use a different GCC toolchain and also link statically against its libstdc++:

bash
cmake .. -DCMAKE_C_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \\
-         -DCMAKE_CXX_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \\
-         -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++"

Building

After handling the prerequisites, you can start building clice. We provide two build methods: cmake/xmake.

CMake

Below are the cmake parameters supported by clice:

For example:

bash
$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_INSTALL_PATH="./.llvm" -DCLICE_ENABLE_TEST=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
-$ cmake --build build

Xmake

Use the following command to build clice:

bash
$ xmake f -c --dev=true --mode=debug --toolchain=clang --llvm="./.llvm" --enable_test=true
-$ xmake build --all

--llvm is optional. If not specified, xmake will automatically download our precompiled binary

Building Docker Image

Use the following command to build docker image:

bash
$ docker build -t clice .

Run docker image by running the following command:

bash
$ docker run --rm -it clice --help
-OVERVIEW: clice is a new generation of language server for C/C++
-...

The directory structure of the docker image is as follows:

/opt/clice
-├── bin
-│   ├── clice -> /usr/local/bin/clice
-├── 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

Development Container

We provide Docker images as a pre-configured environment to streamline the setup process. You can use the following scripts to manage the development container. These scripts can be run from the project root directory.

bash
# Build the development image
-./docker/linux/build.sh
-
-# Run the container with the clang toolchain
-./docker/linux/run.sh --compiler clang
-
-# Run the container with the gcc toolchain
-./docker/linux/run.sh --compiler gcc
-
-# Reset the container (stops and removes the existing one)
-./docker/linux/run.sh --reset

NOTE

This feature is currently in a preview stage and only supports Linux. Windows support will be provided in the future, and the functionality may be subject to change.

`,50)]))}const F=i(n,[["render",t]]);export{c as __pageData,F as default}; diff --git a/assets/dev_build.md.BVixu1OK.lean.js b/assets/dev_build.md.BVixu1OK.lean.js deleted file mode 100644 index cac2e514..00000000 --- a/assets/dev_build.md.BVixu1OK.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,c as a,o as l,ae as e}from"./chunks/framework.D8webtH5.js";const c=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"dev/build.md","filePath":"en/dev/build.md"}'),n={name:"dev/build.md"};function t(h,s,p,r,k,o){return l(),a("div",null,s[0]||(s[0]=[e("",50)]))}const F=i(n,[["render",t]]);export{c as __pageData,F as default}; diff --git a/assets/dev_build.md.pZgZRssl.js b/assets/dev_build.md.pZgZRssl.js new file mode 100644 index 00000000..35e95831 --- /dev/null +++ b/assets/dev_build.md.pZgZRssl.js @@ -0,0 +1,4 @@ +import{_ as t,c as l,o as i,ae as a}from"./chunks/framework.D8webtH5.js";const u=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"dev/build.md","filePath":"en/dev/build.md"}'),s={name:"dev/build.md"};function n(r,e,o,d,h,c){return i(),l("div",null,e[0]||(e[0]=[a(`

Build from Source

Supported Platforms

Prerequisite

clice uses C++23 as its language standard. Please ensure you have a clang 20 (or higher) compiler and a C++23 compatible standard library available. clice depends on lld as its linker. Please ensure your clang toolchain can find it (clang distributions usually bundle lld, or you may need to install the lld-20 package separately).

clice is currently only guaranteed to compile with clang (as ensured by CI testing). We do our best to maintain compatibility with gcc and msvc, but we do not add corresponding tests in CI. Contributions are welcome if you encounter any issues.

CMake

Use the following commands to build clice

shell
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
+cmake --build build

Optional build options:

OptionDefaultDescription
LLVM_INSTALL_PATH""Build clice using llvm libs from a custom path
CLICE_ENABLE_TESTOFFWhether to build clice's unit tests
CLICE_USE_LIBCXXOFFWhether to build clice with libc++ (adds -std=libc++). If enabled, ensure that the llvm libs were also compiled with libc++.
CLICE_CI_ENVIRONMENTOFFWhether to enable the CLICE_CI_ENVIRONMENT macro. Some tests only run in a CI environment.

XMake

Use the following commands to build clice

bash
xmake f -c --mode=releasedbg --toolchain=clang
+xmake build --all

Optional build options:

OptionDefaultDescription
--llvm""Build clice using llvm libs from a custom path
--enable_testfalseWhether to build clice's unit tests
--cifalseWhether to enable CLICE_CI_ENVIRONMENT

A Note on LLVM Libs

Due to the complexity of C++ syntax, writing a new parser from scratch is unrealistic. clice calls clang's APIs to parse C++ source files and obtain the AST, which means it needs to link against llvm/clang libs. Because clice uses clang's private headers, which are not included in the binary releases published by LLVM, you cannot use the system's llvm package directly.

  1. We publish pre-compiled binaries for the LLVM version we use on clice-llvm, which are used for CI or release builds. By default, cmake and xmake will download and use the llvm libs from here during the build.

IMPORTANT

For debug builds of llvm libs, we enable address sanitizer. Address sanitizer depends on compiler-rt, which is highly sensitive to the compiler version.

Therefore, if you use a debug build, please ensure your clang's compiler-rt version is strictly identical to the one used in our build.

You can refer to the cmake and xmake files in our CI as a reference, as they maintain an environment strictly consistent with the pre-compiled llvm libs.

  1. Build llvm/clang yourself to match your current environment. If the default pre-compiled binaries (Method 1) fail to run on your system due to ABI or library version (e.g., glibc) incompatibility, or if you need a custom Debug build, we recommend you use this method to compile llvm libs from scratch. We provide a script to build the llvm libs required by clice: build-llvm-libs.py.
bash
cd llvm-project
+python3 <clice>/scripts/build-llvm-libs.py debug

You can also refer to LLVM's official build tutorial: Building LLVM with CMake.

`,24)]))}const b=t(s,[["render",n]]);export{u as __pageData,b as default}; diff --git a/assets/dev_build.md.pZgZRssl.lean.js b/assets/dev_build.md.pZgZRssl.lean.js new file mode 100644 index 00000000..3b32c533 --- /dev/null +++ b/assets/dev_build.md.pZgZRssl.lean.js @@ -0,0 +1 @@ +import{_ as t,c as l,o as i,ae as a}from"./chunks/framework.D8webtH5.js";const u=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"dev/build.md","filePath":"en/dev/build.md"}'),s={name:"dev/build.md"};function n(r,e,o,d,h,c){return i(),l("div",null,e[0]||(e[0]=[a("",24)]))}const b=t(s,[["render",n]]);export{u as __pageData,b as default}; diff --git a/assets/zh_dev_build.md.C5-ImQeZ.js b/assets/zh_dev_build.md.C5-ImQeZ.js new file mode 100644 index 00000000..5977867f --- /dev/null +++ b/assets/zh_dev_build.md.C5-ImQeZ.js @@ -0,0 +1,4 @@ +import{_ as i,c as a,o as t,ae as e}from"./chunks/framework.D8webtH5.js";const k=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/build.md","filePath":"zh/dev/build.md"}'),s={name:"zh/dev/build.md"};function r(d,l,n,h,c,o){return t(),a("div",null,l[0]||(l[0]=[e(`

Build from Source

Supported Platforms

Prerequisite

clice 使用 C++23 作为语言标准,请确保有可用的 clang 20 以及以上的编译器,以及兼容 C++23 的标准库。clice 依赖 lld 作为链接器。请确保你的 clang 工具链可以找到它(通常 clang 发行版会自带 lld,或者你需要单独安装 lld-20 包)。

clice 目前只保证能使用 clang 编译(CI 测试保证)。对于 gcc 和 msvc 的兼容,我们尽力而为,但不会在 CI 中添加对应的测试。如果遇到任何问题,欢迎贡献。

CMake

使用如下的命令构建 clice

shell
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
+cmake --build build

可选的构建选项:

选项默认值效果
LLVM_INSTALL_PATH""使用自定义路径的 llvm libs 来构建 clice
CLICE_ENABLE_TESTOFF是否构建 clice 的单元测试
CLICE_USE_LIBCXXOFF是否使用 libc++ 来构建 clice(添加 -std=libc++),如果开启,请确保 llvm libs 也是使用 libc++ 编译的
CLICE_CI_ENVIRONMENTOFF是否打开 CLICE_CI_ENVIRONMENT 这个宏,有些测试在 CI 环境才会执行

XMake

使用如下的命令即可构建 clice

bash
xmake f -c --mode=releasedbg --toolchain=clang
+xmake build --all

可选的构建选项:

选项默认值效果
--llvm""使用自定义路径的 llvm libs 来构建 clice
--enable_testfalse是否构建 clice 的单元测试
--cifalse是否打开 CLICE_CI_ENVIRONMENT

A Note on LLVM Libs

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

  1. 我们在 clice-llvm 上会发布使用的 llvm 版本的预编译二进制,用于 CI 或者 release 构建。在构建时 cmake 和 xmake 默认会从此处下载 llvm libs 然后使用,

IMPORTANT

对于 debug 版本的 llvm libs,构建的时候我们开启了 address sanitizer,而 address sanitizer 依赖于 compiler rt,它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本和我们构建的时候严格一致

可以参考 CI 中的 cmakexmake 文件作为参考,它们与预编译 llvm libs 的环境保持严格一致。

  1. 自己重新一个与当前环境一致的 llvm/clang。如果默认的预编译二进制文件(方法 1)在你的系统上因 ABI 或库版本(如 glibc)不兼容而运行失败,或者你需要一个自定义的 Debug 版本,那么我们推荐你使用此方法从头编译 llvm libs。我们提供了一个脚本,用于构建 clice 所需要的 llvm libs:build-llvm-libs.py
bash
cd llvm-project
+python3 <clice>/scripts/build-llvm-libs.py debug

也可以参考 llvm 的官方构建教程 Building LLVM with CMake

`,24)]))}const b=i(s,[["render",r]]);export{k as __pageData,b as default}; diff --git a/assets/zh_dev_build.md.C5-ImQeZ.lean.js b/assets/zh_dev_build.md.C5-ImQeZ.lean.js new file mode 100644 index 00000000..5cf98e7c --- /dev/null +++ b/assets/zh_dev_build.md.C5-ImQeZ.lean.js @@ -0,0 +1 @@ +import{_ as i,c as a,o as t,ae as e}from"./chunks/framework.D8webtH5.js";const k=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/build.md","filePath":"zh/dev/build.md"}'),s={name:"zh/dev/build.md"};function r(d,l,n,h,c,o){return t(),a("div",null,l[0]||(l[0]=[e("",24)]))}const b=i(s,[["render",r]]);export{k as __pageData,b as default}; diff --git a/assets/zh_dev_build.md.xrKp7NV4.js b/assets/zh_dev_build.md.xrKp7NV4.js deleted file mode 100644 index 888ad933..00000000 --- a/assets/zh_dev_build.md.xrKp7NV4.js +++ /dev/null @@ -1,33 +0,0 @@ -import{_ as i,c as a,o as l,ae as n}from"./chunks/framework.D8webtH5.js";const o=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/build.md","filePath":"zh/dev/build.md"}'),e={name:"zh/dev/build.md"};function t(h,s,p,k,r,c){return l(),a("div",null,s[0]||(s[0]=[n(`

Build from Source

Supported Platforms

Prerequisite

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

Toolchain

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

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

LLVM Libs

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

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

拷贝到 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
-$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x86_64-linux-gnu-release.tar.xz" | tar -xJ -C ./.llvm
-
-# MacOS precompiled binary require macos15+
-$ mkdir -p ./.llvm
-$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/arm64-macosx-apple-release.tar.xz" | tar -xJ -C ./.llvm
-
-# Windows precompiled binary only MD runtime support
-$ curl -O -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x64-windows-msvc-release.7z"
-$ 7z x x64-windows-msvc-release.7z "-o.llvm"

IMPORTANT

对于 debug 版本的 llvm libs,构建的时候我们开启了 address sanitizer,而 address sanitizer 依赖于 compiler rt,它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本和我们构建的时候严格一致

  1. 自己从头编译 llvm/clang

这是最推荐的方式,可以保证环境一致性,避免因为 ABI 不一致而导致的崩溃问题。我们提供了一个脚本,用于构建 clice 所需要的 llvm libs:build-llvm-libs.py

bash
$ cd llvm-project
-$ python3 <clice>/scripts/build-llvm-libs.py debug

也可以参考 llvm 的官方构建教程 Building LLVM with CMake

GCC Toolchain

clice 要求 GCC libstdc++ >= 14。以下命令使用不同的 GCC 工具链并静态链接其 libstdc++:

bash
cmake .. -DCMAKE_C_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \\
-         -DCMAKE_CXX_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \\
-         -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++"

Building

在处理好前置依赖之后,可以开始构建 clice 了,我们提供 cmake/xmake 两种构建方式。

CMake

下面是 clice 支持的 cmake 参数

例如

bach
$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_INSTALL_PATH="./.llvm" -DCLICE_ENABLE_TEST=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
-$ cmake --build build

Xmake

使用如下的命令即可构建 clice

bash
$ xmake f -c --dev=true --mode=debug --toolchain=clang --llvm="./.llvm" --enable_test=true
-$ xmake build --all

--llvm 是可选的,如果不指定的话,xmake 会自动下载我们编译好的预编译二进制

Dev Container

我们提供了 docker 镜像作为预装环境解决方案,可以有效地解决环境配置问题,可通过下列命令使用(不限脚本调用路径,可以直接运行 ./build.sh):

bash
# construct container
-docker/linux/build.sh
-# run clang container
-docker/linux/run.sh --compiler clang
-# run gcc container
-docker/linux/run.sh --compiler gcc
-# reset container(delete exist container and reset)
-docker/linux/run.sh --reset

NOTE

当前该功能仍处于 Preview 阶段,仅支持 Linux,后续会提供 Windows 平台版本,并可能存在功能改动

Building Docker Image

使用以下命令构建 docker 镜像:

bash
$ docker build -t clice .

运行 docker 镜像:

bash
$ docker run --rm -it clice --help
-OVERVIEW: clice is a new generation of language server for C/C++
-...

docker 镜像的目录结构如下:

/opt/clice
-├── bin
-│   ├── clice -> /usr/local/bin/clice
-├── include
-├── lib
-├── LICENSE
-├── README.md

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

bash
$ docker run --rm -it --entrypoint bash clice
`,50)]))}const F=i(e,[["render",t]]);export{o as __pageData,F as default}; diff --git a/assets/zh_dev_build.md.xrKp7NV4.lean.js b/assets/zh_dev_build.md.xrKp7NV4.lean.js deleted file mode 100644 index 82f3bfba..00000000 --- a/assets/zh_dev_build.md.xrKp7NV4.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,c as a,o as l,ae as n}from"./chunks/framework.D8webtH5.js";const o=JSON.parse('{"title":"Build from Source","description":"","frontmatter":{},"headers":[],"relativePath":"zh/dev/build.md","filePath":"zh/dev/build.md"}'),e={name:"zh/dev/build.md"};function t(h,s,p,k,r,c){return l(),a("div",null,s[0]||(s[0]=[n("",50)]))}const F=i(e,[["render",t]]);export{o as __pageData,F as default}; diff --git a/design/architecture.html b/design/architecture.html index 7ca0951d..8f774a1c 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 7420de11..6de3e805 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 5d488f5f..9f8475d4 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 14eccd33..0c3e5ba6 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 128664c2..92c30d1c 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 cf5c6163..fe68d364 100644 --- a/dev/build.html +++ b/dev/build.html @@ -13,48 +13,16 @@ - + -
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
-$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x86_64-linux-gnu-release.tar.xz" | tar -xJ -C ./.llvm
-
-# MacOS precompiled binary require macos15+
-$ mkdir -p ./.llvm
-$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/arm64-macosx-apple-release.tar.xz" | tar -xJ -C ./.llvm
-
-# Windows precompiled binary only MD runtime support
-$ curl -O -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x64-windows-msvc-release.7z"
-$ 7z x x64-windows-msvc-release.7z "-o.llvm"

IMPORTANT

For debug versions of llvm libs, we enabled address sanitizer during build, and address sanitizer depends on compiler rt, which is very sensitive to compiler versions. So if using debug versions, please ensure your clang's compiler rt version is strictly consistent with what we used during build.

  • Windows currently has no debug build of llvm libs because it doesn't support building clang as a dynamic library. Related progress can be found here
  • Linux uses clang20
  • MacOS uses homebrew llvm@20, definitely don't use apple clang
  1. Compile llvm/clang from scratch

This is the most recommended approach, ensuring environment consistency and avoiding crash issues caused by ABI inconsistencies. We provide a script for building the llvm libs required by clice: build-llvm-libs.py.

bash
$ cd llvm-project
-$ python3 <clice>/scripts/build-llvm-libs.py debug

You can also refer to llvm's official build tutorial Building LLVM with CMake.

GCC Toolchain

clice requires GCC libstdc++ >= 14. You could use a different GCC toolchain and also link statically against its libstdc++:

bash
cmake .. -DCMAKE_C_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \
-         -DCMAKE_CXX_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \
-         -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++"

Building

After handling the prerequisites, you can start building clice. We provide two build methods: cmake/xmake.

CMake

Below are the cmake parameters supported by clice:

  • LLVM_INSTALL_PATH specifies the installation path of llvm libs
  • CLICE_ENABLE_TEST whether to build clice's unit tests

For example:

bash
$ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_INSTALL_PATH="./.llvm" -DCLICE_ENABLE_TEST=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
-$ cmake --build build

Xmake

Use the following command to build clice:

bash
$ xmake f -c --dev=true --mode=debug --toolchain=clang --llvm="./.llvm" --enable_test=true
-$ xmake build --all

--llvm is optional. If not specified, xmake will automatically download our precompiled binary

Building Docker Image

Use the following command to build docker image:

bash
$ docker build -t clice .

Run docker image by running the following command:

bash
$ docker run --rm -it clice --help
-OVERVIEW: clice is a new generation of language server for C/C++
-...

The directory structure of the docker image is as follows:

/opt/clice
-├── bin
-│   ├── clice -> /usr/local/bin/clice
-├── 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

Development Container

We provide Docker images as a pre-configured environment to streamline the setup process. You can use the following scripts to manage the development container. These scripts can be run from the project root directory.

bash
# Build the development image
-./docker/linux/build.sh
-
-# Run the container with the clang toolchain
-./docker/linux/run.sh --compiler clang
-
-# Run the container with the gcc toolchain
-./docker/linux/run.sh --compiler gcc
-
-# Reset the container (stops and removes the existing one)
-./docker/linux/run.sh --reset

NOTE

This feature is currently in a preview stage and only supports Linux. Windows support will be provided in the future, and the functionality may be subject to change.

- +
Skip to content

Build from Source

Supported Platforms

  • Windows
  • Linux
  • MacOS

Prerequisite

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

clice uses C++23 as its language standard. Please ensure you have a clang 20 (or higher) compiler and a C++23 compatible standard library available. clice depends on lld as its linker. Please ensure your clang toolchain can find it (clang distributions usually bundle lld, or you may need to install the lld-20 package separately).

clice is currently only guaranteed to compile with clang (as ensured by CI testing). We do our best to maintain compatibility with gcc and msvc, but we do not add corresponding tests in CI. Contributions are welcome if you encounter any issues.

CMake

Use the following commands to build clice

shell
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
+cmake --build build

Optional build options:

OptionDefaultDescription
LLVM_INSTALL_PATH""Build clice using llvm libs from a custom path
CLICE_ENABLE_TESTOFFWhether to build clice's unit tests
CLICE_USE_LIBCXXOFFWhether to build clice with libc++ (adds -std=libc++). If enabled, ensure that the llvm libs were also compiled with libc++.
CLICE_CI_ENVIRONMENTOFFWhether to enable the CLICE_CI_ENVIRONMENT macro. Some tests only run in a CI environment.

XMake

Use the following commands to build clice

bash
xmake f -c --mode=releasedbg --toolchain=clang
+xmake build --all

Optional build options:

OptionDefaultDescription
--llvm""Build clice using llvm libs from a custom path
--enable_testfalseWhether to build clice's unit tests
--cifalseWhether to enable CLICE_CI_ENVIRONMENT

A Note on LLVM Libs

Due to the complexity of C++ syntax, writing a new parser from scratch is unrealistic. clice calls clang's APIs to parse C++ source files and obtain the AST, which means it needs to link against llvm/clang libs. Because clice uses clang's private headers, which are not included in the binary releases published by LLVM, you cannot use the system's llvm package directly.

  1. We publish pre-compiled binaries for the LLVM version we use on clice-llvm, which are used for CI or release builds. By default, cmake and xmake will download and use the llvm libs from here during the build.

IMPORTANT

For debug builds of llvm libs, we enable address sanitizer. Address sanitizer depends on compiler-rt, which is highly sensitive to the compiler version.

Therefore, if you use a debug build, please ensure your clang's compiler-rt version is strictly identical to the one used in our build.

  • Windows does not currently have debug builds for llvm libs, as it does not support building clang as a dynamic library. Related progress is tracked here.
  • Linux uses clang20
  • MacOS uses homebrew llvm@20. Do not use apple clang.

You can refer to the cmake and xmake files in our CI as a reference, as they maintain an environment strictly consistent with the pre-compiled llvm libs.

  1. Build llvm/clang yourself to match your current environment. If the default pre-compiled binaries (Method 1) fail to run on your system due to ABI or library version (e.g., glibc) incompatibility, or if you need a custom Debug build, we recommend you use this method to compile llvm libs from scratch. We provide a script to build the llvm libs required by clice: build-llvm-libs.py.
bash
cd llvm-project
+python3 <clice>/scripts/build-llvm-libs.py debug

You can also refer to LLVM's official build tutorial: Building LLVM with CMake.

+ \ No newline at end of file diff --git a/dev/contribution.html b/dev/contribution.html index ca20a0e6..e9f489d6 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, 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 63a8fabc..3b00250a 100644 --- a/dev/test-and-debug.html +++ b/dev/test-and-debug.html @@ -32,7 +32,7 @@ }
  • 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.

    - + \ No newline at end of file diff --git a/guide/configuration.html b/guide/configuration.html index 3edeabc6..f222f7ac 100644 --- a/guide/configuration.html +++ b/guide/configuration.html @@ -19,7 +19,7 @@
    Skip to content

    Configuration

    This is the documentation for clice.toml.

    Project

    NameTypeDefault
    project.cache_dirstring"${workspace}/.clice/cache"

    Folder for storing PCH and PCM caches.

    NameTypeDefault
    project.index_dirstring"${workspace}/.clice/index"

    Folder for storing index files.

    Rule

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

    NameType
    [rules].patternsarray 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"].

    - + \ No newline at end of file diff --git a/guide/quick-start.html b/guide/quick-start.html index e66a2eb6..0321b8a8 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 e6ea675e..85fa41b9 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 f892a887..b9f11978 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"design_architecture.md":"B1nOS2BF","design_compilation.md":"CCvAsCts","design_header-context.md":"BxMpdImS","design_index.md":"iLhzg_qd","design_template-resolver.md":"BWRB2TFl","dev_build.md":"BVixu1OK","dev_contribution.md":"BDlGKyx4","dev_test-and-debug.md":"BDhcoSZa","guide_configuration.md":"BX4EXUeC","guide_quick-start.md":"GKsihDbV","guide_what-is-clice.md":"BMyw_-9E","index.md":"JdQy7MpZ","zh_design_architecture.md":"BVGXXj2L","zh_design_compilation.md":"BpkOwlT8","zh_design_header-context.md":"6ANkDXaY","zh_design_index.md":"CRxI2HZv","zh_design_template-resolver.md":"C8xOYUnS","zh_dev_build.md":"xrKp7NV4","zh_dev_contribution.md":"DouHFQDZ","zh_dev_test-and-debug.md":"CECWy0Lv","zh_guide_configuration.md":"YRqjuVKw","zh_guide_quick-start.md":"BIzdzWD6","zh_guide_what-is-clice.md":"D_fcfWi8","zh_index.md":"DZmqz5yo"} +{"design_architecture.md":"B1nOS2BF","design_compilation.md":"CCvAsCts","design_header-context.md":"BxMpdImS","design_index.md":"iLhzg_qd","design_template-resolver.md":"BWRB2TFl","dev_build.md":"pZgZRssl","dev_contribution.md":"BDlGKyx4","dev_test-and-debug.md":"BDhcoSZa","guide_configuration.md":"BX4EXUeC","guide_quick-start.md":"GKsihDbV","guide_what-is-clice.md":"BMyw_-9E","index.md":"JdQy7MpZ","zh_design_architecture.md":"BVGXXj2L","zh_design_compilation.md":"BpkOwlT8","zh_design_header-context.md":"6ANkDXaY","zh_design_index.md":"CRxI2HZv","zh_design_template-resolver.md":"C8xOYUnS","zh_dev_build.md":"C5-ImQeZ","zh_dev_contribution.md":"DouHFQDZ","zh_dev_test-and-debug.md":"CECWy0Lv","zh_guide_configuration.md":"YRqjuVKw","zh_guide_quick-start.md":"BIzdzWD6","zh_guide_what-is-clice.md":"D_fcfWi8","zh_index.md":"DZmqz5yo"} diff --git a/index.html b/index.html index 0a815d72..effbf1ee 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 6ee2e806..9660b267 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 79676c1a..1d867478 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 a25ccfe0..15b9acab 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 3880a569..44da7925 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 301af7e2..550dbd3c 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 3c72d5cc..a98c97d1 100644 --- a/zh/dev/build.html +++ b/zh/dev/build.html @@ -13,45 +13,16 @@ - + -
    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
    -$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x86_64-linux-gnu-release.tar.xz" | tar -xJ -C ./.llvm
    -
    -# MacOS precompiled binary require macos15+
    -$ mkdir -p ./.llvm
    -$ curl -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/arm64-macosx-apple-release.tar.xz" | tar -xJ -C ./.llvm
    -
    -# Windows precompiled binary only MD runtime support
    -$ curl -O -L "https://github.com/clice-io/llvm-binary/releases/download/20.1.5/x64-windows-msvc-release.7z"
    -$ 7z x x64-windows-msvc-release.7z "-o.llvm"

    IMPORTANT

    对于 debug 版本的 llvm libs,构建的时候我们开启了 address sanitizer,而 address sanitizer 依赖于 compiler rt,它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本和我们构建的时候严格一致

    • Windows 暂时无 debug 构建的 llvm libs,因为它不支持将 clang 构建为动态库,相关的进展可以在 这里 找到
    • Linux 使用 clang20
    • MacOS 使用 homebrew llvm@20,一定不要使用 apple clang
    1. 自己从头编译 llvm/clang

    这是最推荐的方式,可以保证环境一致性,避免因为 ABI 不一致而导致的崩溃问题。我们提供了一个脚本,用于构建 clice 所需要的 llvm libs:build-llvm-libs.py

    bash
    $ cd llvm-project
    -$ python3 <clice>/scripts/build-llvm-libs.py debug

    也可以参考 llvm 的官方构建教程 Building LLVM with CMake

    GCC Toolchain

    clice 要求 GCC libstdc++ >= 14。以下命令使用不同的 GCC 工具链并静态链接其 libstdc++:

    bash
    cmake .. -DCMAKE_C_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \
    -         -DCMAKE_CXX_FLAGS="--gcc-toolchain=/usr/local/gcc-14.3.0/" \
    -         -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++"

    Building

    在处理好前置依赖之后,可以开始构建 clice 了,我们提供 cmake/xmake 两种构建方式。

    CMake

    下面是 clice 支持的 cmake 参数

    • LLVM_INSTALL_PATH 用于指定 llvm libs 的安装路径
    • CLICE_ENABLE_TEST 是否构建 clice 的单元测试

    例如

    bach
    $ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_INSTALL_PATH="./.llvm" -DCLICE_ENABLE_TEST=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
    -$ cmake --build build

    Xmake

    使用如下的命令即可构建 clice

    bash
    $ xmake f -c --dev=true --mode=debug --toolchain=clang --llvm="./.llvm" --enable_test=true
    -$ xmake build --all

    --llvm 是可选的,如果不指定的话,xmake 会自动下载我们编译好的预编译二进制

    Dev Container

    我们提供了 docker 镜像作为预装环境解决方案,可以有效地解决环境配置问题,可通过下列命令使用(不限脚本调用路径,可以直接运行 ./build.sh):

    bash
    # construct container
    -docker/linux/build.sh
    -# run clang container
    -docker/linux/run.sh --compiler clang
    -# run gcc container
    -docker/linux/run.sh --compiler gcc
    -# reset container(delete exist container and reset)
    -docker/linux/run.sh --reset

    NOTE

    当前该功能仍处于 Preview 阶段,仅支持 Linux,后续会提供 Windows 平台版本,并可能存在功能改动

    Building Docker Image

    使用以下命令构建 docker 镜像:

    bash
    $ docker build -t clice .

    运行 docker 镜像:

    bash
    $ docker run --rm -it clice --help
    -OVERVIEW: clice is a new generation of language server for C/C++
    -...

    docker 镜像的目录结构如下:

    /opt/clice
    -├── bin
    -│   ├── clice -> /usr/local/bin/clice
    -├── include
    -├── lib
    -├── LICENSE
    -├── README.md

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

    bash
    $ docker run --rm -it --entrypoint bash clice
    - +
    Skip to content

    Build from Source

    Supported Platforms

    • Windows
    • Linux
    • MacOS

    Prerequisite

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

    clice 使用 C++23 作为语言标准,请确保有可用的 clang 20 以及以上的编译器,以及兼容 C++23 的标准库。clice 依赖 lld 作为链接器。请确保你的 clang 工具链可以找到它(通常 clang 发行版会自带 lld,或者你需要单独安装 lld-20 包)。

    clice 目前只保证能使用 clang 编译(CI 测试保证)。对于 gcc 和 msvc 的兼容,我们尽力而为,但不会在 CI 中添加对应的测试。如果遇到任何问题,欢迎贡献。

    CMake

    使用如下的命令构建 clice

    shell
    cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
    +cmake --build build

    可选的构建选项:

    选项默认值效果
    LLVM_INSTALL_PATH""使用自定义路径的 llvm libs 来构建 clice
    CLICE_ENABLE_TESTOFF是否构建 clice 的单元测试
    CLICE_USE_LIBCXXOFF是否使用 libc++ 来构建 clice(添加 -std=libc++),如果开启,请确保 llvm libs 也是使用 libc++ 编译的
    CLICE_CI_ENVIRONMENTOFF是否打开 CLICE_CI_ENVIRONMENT 这个宏,有些测试在 CI 环境才会执行

    XMake

    使用如下的命令即可构建 clice

    bash
    xmake f -c --mode=releasedbg --toolchain=clang
    +xmake build --all

    可选的构建选项:

    选项默认值效果
    --llvm""使用自定义路径的 llvm libs 来构建 clice
    --enable_testfalse是否构建 clice 的单元测试
    --cifalse是否打开 CLICE_CI_ENVIRONMENT

    A Note on LLVM Libs

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

    1. 我们在 clice-llvm 上会发布使用的 llvm 版本的预编译二进制,用于 CI 或者 release 构建。在构建时 cmake 和 xmake 默认会从此处下载 llvm libs 然后使用,

    IMPORTANT

    对于 debug 版本的 llvm libs,构建的时候我们开启了 address sanitizer,而 address sanitizer 依赖于 compiler rt,它对编译器版本十分敏感。所以如果使用 debug 版本,请确保你的 clang 的 compiler rt 版本和我们构建的时候严格一致

    • Windows 暂时没有 debug 构建的 llvm libs,因为它不支持将 clang 构建为动态库,相关的进展在 这里 跟踪
    • Linux 使用 clang20
    • MacOS 使用 homebrew llvm@20,不要使用 apple clang

    可以参考 CI 中的 cmakexmake 文件作为参考,它们与预编译 llvm libs 的环境保持严格一致。

    1. 自己重新一个与当前环境一致的 llvm/clang。如果默认的预编译二进制文件(方法 1)在你的系统上因 ABI 或库版本(如 glibc)不兼容而运行失败,或者你需要一个自定义的 Debug 版本,那么我们推荐你使用此方法从头编译 llvm libs。我们提供了一个脚本,用于构建 clice 所需要的 llvm libs:build-llvm-libs.py
    bash
    cd llvm-project
    +python3 <clice>/scripts/build-llvm-libs.py debug

    也可以参考 llvm 的官方构建教程 Building LLVM with CMake

    + \ No newline at end of file diff --git a/zh/dev/contribution.html b/zh/dev/contribution.html index a6aac2f7..74e3c5cf 100644 --- a/zh/dev/contribution.html +++ b/zh/dev/contribution.html @@ -19,7 +19,7 @@
    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 d99d4f9a..d4a41dcb 100644 --- a/zh/dev/test-and-debug.html +++ b/zh/dev/test-and-debug.html @@ -32,7 +32,7 @@ }
  • 重新加载窗口:修改配置后,在 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

    - + \ No newline at end of file diff --git a/zh/guide/configuration.html b/zh/guide/configuration.html index e97d3d79..9e8c92a2 100644 --- a/zh/guide/configuration.html +++ b/zh/guide/configuration.html @@ -19,7 +19,7 @@
    Skip to content

    Configuration

    这是 clice.toml 的文档。

    Project

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

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

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

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

    Rule

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

    名称类型
    [rules].patternsarray 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"]

    - + \ No newline at end of file diff --git a/zh/guide/quick-start.html b/zh/guide/quick-start.html index 538944a8..239c6cc3 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 14f24648..4053a752 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 fb23a7c0..e06250f6 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