Language server
ripscope lsp is a language server for C. Editors that speak the Language Server
Protocol, such as Helix and Neovim, can use Ripscope through it with no plugin. It
answers four requests:
| Request | Answered by |
|---|---|
| Go to definition | The definition search (field 1) |
| Find references | The symbol search (field 0) |
| Incoming and outgoing calls (call hierarchy) | The callers and callees searches (fields 3 and 2) |
| Workspace symbols | The definition search, for every definition whose name holds what you type |
Results are the same rows the searches give, with Ripscope’s paths and lines. Other requests, such as hover and completion, are not answered.
Requirements: the ripscope executable on PATH, from the
installer or a system package. It has been
checked with Helix 25.07.1 and Neovim 0.12.6 on Linux and Windows.
Helix
Add this to your languages.toml, in ~/.config/helix/ on Linux or
%AppData%\helix\ on Windows:
[language-server.ripscope]
command = "ripscope"
args = ["lsp"]
[[language]]
name = "c"
language-servers = ["ripscope"]
Open a C file and press gd on a name to go to its definition, gr to find its
references, or Space S to search the workspace symbols. hx --health c shows
whether Helix finds the server. Helix 25.07 has no call hierarchy.
To keep clangd for everything else, list Ripscope first. Helix asks the first server in the list for definitions and references, and shows workspace symbols from every server unless you turn them off for clangd:
[[language]]
name = "c"
language-servers = ["ripscope", { name = "clangd", except-features = ["workspace-symbols"] }]
Neovim
Neovim 0.11 or newer starts the server with this in your init.lua:
vim.lsp.config('ripscope', {
cmd = { 'ripscope', 'lsp' },
filetypes = { 'c' },
root_markers = { 'cscope.files', '.git' },
})
vim.lsp.enable('ripscope')
Neovim maps grr to find references, and Ctrl-] goes to a definition.
:lua vim.lsp.buf.incoming_calls() and
:lua vim.lsp.buf.outgoing_calls() show the call hierarchy, and
:lua vim.lsp.buf.workspace_symbol() searches the definitions. :checkhealth vim.lsp
shows whether the server is running. Neovim lists outgoing calls in the file you are
in, so ask for them on the function’s definition rather than on a call to it.
Neovim asks every server attached to a file and lists all their answers. Beside clangd, you can leave these requests to Ripscope by turning them off for clangd:
vim.lsp.config('clangd', {
on_init = function(client)
local capabilities = client.server_capabilities
capabilities.definitionProvider = false
capabilities.referencesProvider = false
capabilities.callHierarchyProvider = false
capabilities.workspaceSymbolProvider = false
end,
})
You can use the server and the Neovim plugin together. They share the index.
Projects and the index
Each request goes to the project of the file it names, found by the same rule the editor integrations use. The server keeps one session per project, so several projects can be open at once. The index lives in your cache directory, and the Vim, Neovim and Emacs integrations use the same one.
The editor’s workspace folder matters only where that rule would use the editor’s
current directory: a file outside any cscope.files project or Git repository
belongs to the innermost workspace folder holding it. A workspace symbol search names
no file, so it searches every project you have opened a file in.
When you open the first file of a project that has no index yet, the server starts
indexing it in the background, and the editor’s log shows
ripscope: indexing ... for the first time. A request waits up to 10 seconds for
that to finish. On a large project it may say still indexing ... for the first time
instead, so try again a little later. Later requests see your saved edits, as a
session does.
Names and ranges
The name under the cursor comes from the text in the editor, so an unsaved edit does not move it. Results come from the saved files.
The index keeps lines, not columns. The server finds the name in each result’s line as the file is saved now and marks it. Where that line no longer holds the name, the result covers the whole line.
Workspace symbols ignore letter case and take only letters, digits and _. An empty
search lists nothing, rather than every definition in every project. A symbol’s kind,
such as function or variable, is a guess from its line.