Open-source project
MaskRay/ccls avatar
MaskRay/ccls

MaskRay/ccls: a C/C++ language server with cross references and hierarchies

C/C++/ObjC language server supporting cross references, hierarchies, completion and semantic highlighting

4,089 stars280 forksC++Apache-2.0

At a glance

What is it?
ccls is a C/C++/Objective-C language server that builds a project-wide index and answers cross-reference and hierarchy queries. Here is what it does, how to build it, and where clangd fits better.
Who is it for?
Adopt ccls if you want project-wide cross references and call, inheritance and member hierarchies in an editor that speaks LSP, and you are willing to build it from source with CMake and clang. Do not adopt it if you need a packaged installer, a documented upgrade path, or a server that indexes lazily and cheaply: the README points to the wiki for build and setup, and the repository documents no rollback procedure.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 91 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What ccls solves, and for whom

Editing C and C++ in a plain editor is a navigation problem. A symbol's definition may sit in a header, its uses may be scattered across translation units, and the compiler only sees one translation unit at a time. ccls answers those questions over the whole code base rather than a single file. The README states that it has a global view of the code base and supports a lot of cross reference features. It originates from cquery, and it implements the Language Server Protocol, so any editor with an LSP client can talk to it.

The feature list is aimed at people who read code more than they write it: definition and references, symbol rename, document symbols, workspace symbol search, hover information, diagnostics with clang FixIts, semantic highlighting, and preprocessor skipped regions. Three features go beyond what a basic LSP client expects: call hierarchy (caller and callee), inheritance hierarchy (base and derived), and member hierarchy. Those are exposed through non-standard methods prefixed with $ccls, which means the editor client has to know about them. A generic LSP client will get completion and diagnostics but not the hierarchies.

The audience is therefore narrower than "all C++ developers". It is people who already run an LSP-capable editor, are comfortable building a C++ project from source, and care about navigating a large tree. If you want a language server that ships as a package you install and forget, ccls is not that.

How indexing and request serving work

The mechanism that shapes everything else is the index. According to the README, ccls starts indexing the whole project, including subprojects if they exist, in parallel when you open the first file. The main thread can still serve requests before indexing finishes, so the editor is not blocked while the index is being built. Saving a file incrementally updates the index rather than triggering a full rebuild.

That design explains the memory profile. A project-wide index has to hold symbol and reference data for every translation unit it has seen, so memory scales with the size of the tree, not with the size of the file you have open. The README gives figures for projects ccls has been run against: ccls itself at roughly 180MiB resident when idle, glibc, Linux, LLVM at roughly 1800MiB, and musl at roughly 60MiB. Those numbers are noted as of 2018-09-01 and should be read as historical data points, not as a current benchmark. Still, the ordering is the useful part: LLVM's index costs about ten times what musl's does, and that is a property of the code base, not of the editor.

The parallel-start behaviour is a trade-off. You get answers during indexing, but early answers can be incomplete, and the server is doing heavy work while you are trying to type. On a large tree the first minutes after opening a file are the worst time to expect instant cross references.

Building ccls from source with CMake

The README does not contain install instructions. It links to the wiki, with a line that reads "Getting started" and a pointer to the Build and FAQ pages, so the build steps live outside the repository README. What the repository does show is the layout: a top-level CMakeLists.txt, a src/ directory, a third_party/ directory, and .gitmodules, which means the build is CMake-driven and pulls in submodules. The presence of .appveyor.yml indicates Windows CI was configured at some point.

A build therefore starts with a recursive clone so the submodules are present, then a CMake configure and build. The exact flags and dependencies are on the wiki Build page, which this article cannot reproduce. The commands below are the standard CMake invocation against the CMakeLists.txt the repository ships; the wiki Build page is what documents the options that matter for your platform.

bash
git clone --recursive https://github.com/MaskRay/ccls
cd ccls
cmake -B build
cmake --build build -j

After the build, the binary is what your editor's LSP client launches. The wiki Project-Setup page is where the README sends you for examples of wiring a project to the server. There is no documented install target or package in the README, so expect to place the binary somewhere on your PATH yourself.

The first real use is opening a source file in a configured project and letting the index start. Per the README, indexing begins on the first file you open and runs in parallel, so the first observable behaviour is a server that answers some requests immediately and fills in the rest as the index grows. Configuration for the project, such as compile flags, is what the wiki Project-Setup examples cover; the README does not document the configuration file format itself.

Where ccls is the wrong tool

The most concrete limitation is distribution. There is no homepage and no documented binary release channel in the README. The repository has a CMakeLists.txt and a third_party/ directory with submodules, and the build instructions are on the wiki. That means every user is a builder, and every upgrade is a rebuild. For a team that wants to pin a language server version across machines, that is real work with no documented rollback path: the README and the release list do not describe how to revert to an earlier build if a new one misbehaves.

Memory is the second boundary. The README's own figures put LLVM at roughly 1800MiB resident when idle. On a laptop with several editors open, or in a container with a hard memory limit, a server that holds a project-wide index at that size is a liability. The musl figure of roughly 60MiB shows the range, but you cannot know where your project lands without measuring it. Note also that those numbers are dated 2018-09-01 in the README, so they are not a promise about current behaviour.

The third boundary is protocol coverage. The hierarchy features are exposed as $ccls methods, not as standard LSP requests. An editor that only implements the base protocol will not surface call or inheritance hierarchies at all, and the README does not claim otherwise. If your editor's LSP client has no ccls-specific support, a large part of the reason to choose ccls over a plainer server disappears.

ccls versus clangd: the difference in approach

clangd is the obvious comparison, and it is the one people actually search for. The architectural difference is where the index lives and how it is built. clangd is built on the same clang tooling that ccls uses for diagnostics and FixIts, but it is designed around a background index that is populated from compilation database entries and persisted to disk, and it is distributed as part of the LLVM release process. ccls, by contrast, indexes the whole project in parallel when you open the first file, serves requests from the main thread while that runs, and updates incrementally on save.

That difference shows up in day-to-day behaviour. ccls front-loads the cost: the first file open triggers work across the tree, and the payoff is a global view available to cross-reference queries. A disk-persisted index model pays less on each start after the first, because the index survives between sessions. Neither approach is strictly better; they fail differently. ccls's model makes the first session on a large tree expensive, while a persisted-index model makes the initial population expensive and later starts cheap.

The hierarchy methods are the other differentiator. The README documents call, inheritance and member hierarchies as first-class features with their own $ccls methods. If your workflow depends on walking a call graph or an inheritance tree from inside the editor, that is the concrete reason to pick ccls. If your workflow is mostly completion, diagnostics and go-to-definition, the two servers overlap heavily and the deciding factors become packaging, memory and how well your editor client supports each one.

Maintenance, releases and licence

The repository is not archived, and the last push was on 2026-07-02. The release list shows 0.20250815.1 on 2025-11-15, 0.20250815 on 2025-10-13, and 0.20241108 on 2024-11-09. The version strings look date-derived, and the gap between the 2024 release and the 2025 releases is roughly a year, so the cadence is irregular. There is no release in the list after 2025-11-15, though commits continued into 2026-07-02. Anyone pinning a version should read the release notes rather than assume a schedule.

Upgrade cost follows from the build model. Since the README documents no packaged install, upgrading means rebuilding from source at the new revision, with whatever clang and CMake versions that revision expects. The wiki Build page is the place that would carry those requirements; the README does not. Budget for the rebuild time and for re-checking that your editor configuration still matches the server's expectations after a jump.

The licence is Apache-2.0, per the LICENSE file at the repository root. Apache-2.0 is a permissive licence with an explicit patent grant and a requirement to preserve notices and state changes. That is generally friendlier to corporate redistribution than a copyleft licence, but the obligations still exist if you ship the binary. This is a description of the licence, not legal advice; if you are redistributing ccls inside a product, have your own counsel read the LICENSE file.

Editorial conclusion

Adopt ccls if you want project-wide cross references and call, inheritance and member hierarchies in an editor that speaks LSP, and you are willing to build it from source with CMake and clang. Do not adopt it if you need a packaged installer, a documented upgrade path, or a server that indexes lazily and cheaply: the README points to the wiki for build and setup, and the repository documents no rollback procedure. Before committing, check the wiki Build and Project-Setup pages for your platform, confirm the compiler and clang versions your tree needs, and measure the idle memory of the server on your own project, since the only memory figures in the README are dated 2018-09-01.

Frequently asked questions

Which is better, ccls or clangd?

They differ in how the index is built. ccls starts indexing the whole project in parallel when you open the first file and serves requests before indexing completes, while clangd is built on the same clang tooling but distributed through LLVM releases. The README documents call, inheritance and member hierarchies as $ccls methods, which is the clearest reason to pick ccls.

How much memory does ccls use?

The README gives idle resident figures noted on 2018-09-01: roughly 180MiB for ccls itself, roughly 1800MiB for LLVM, and roughly 60MiB for musl. Those are historical data points rather than a current measurement, and the size of your own tree is what determines the cost.

How do I build ccls?

The README does not contain build steps; it links to the wiki Build page. The repository layout shows a top-level CMakeLists.txt, a src/ directory, a third_party/ directory and .gitmodules, so the process is a recursive clone followed by a CMake configure and build.

Does ccls work in Neovim and VS Code?

ccls implements the Language Server Protocol, so any editor with an LSP client can connect to it. The hierarchy features are exposed as $ccls methods rather than standard LSP requests, so an editor client has to support those methods for call, inheritance and member hierarchies to appear.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. MaskRay/ccls on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/maskray-ccls.svg)](https://hysenlabs.com/projects/maskray-ccls)