1. Introduction
C-xrefactory is a code browser and refactoring tool for C and Yacc, integrated with Emacs. It lets you navigate symbols, find usages, complete code and refactor it.
All functions are available from the C-xref menu in Emacs.
2. Getting Started
2.1. Installation
Clone and load in Emacs:
git clone -b stable https://github.com/thoni56/c-xrefactory
make -j -C c-xrefactory
emacs -l c-xrefactory/load.el
Or via el-get: M-x el-get-install RET c-xrefactory RET
See the README for platform-specific details.
2.2. Projects
C-xrefactory is "project-based", meaning that it handles files within one project.
C-xrefactory auto-detects projects based on the file you’re editing when you do your initial C-xrefactory operation.
The path of the file is investigated upwards until a .c-xrefrc (C-xrefactory project config) file is found.
If no config file could be found, you’ll be prompted to create a project. Project > Show Active shows the active project.
If the references seem off, use Project > Remove References and Restart Server. It removes the project’s saved references, and the following operations analyse what they need again.
2.3. Project Configuration
The .c-xrefrc file marks the root of a source tree.
The directory containing .c-xrefrc and its subdirectories are always scanned for compilation units.
A minimal config only needs a section header naming the project:
[my-project]
The section header […] names the project.
Lines below it configure the project:
- Source directories
-
The project root (where
.c-xrefrclives) and its subdirectories are always scanned automatically. Additional source directories only need to be listed for multi-directory projects, e.g. when source files live outside the project root:[my-project] ../shared/library - Include paths
-
-Ioptions tell c-xrefactory where to find header files, just like the compiler flag:[my-project] -I ../external/headers - Preprocessor defines
-
-Doptions define macros, equivalent to the compiler flag. Useful when the project uses conditional compilation. - Excluding subtrees
-
-prunekeeps a directory subtree out of the scan. The argument is a path relative to the project root (the directory holding.c-xrefrc), matched exactly; the named directory and everything beneath it are skipped. Useful for build outputs or vendored third-party code:[my-project] -prune build -prune third_party/vendoredThe path is anchored to the project root, not matched by name. -prune buildskips<root>/buildbut not a nestedsrc/build.
Paths in .c-xrefrc are resolved relative to the config file’s directory, so the configuration is portable and can be checked into version control.
|
2.4. When Something Does Not Parse
Missing references are usually caused by a file that could not be analysed properly.
What C-xrefactory could not parse is shown in the *c-xref-diagnostics* buffer.
Most of the time it points to something missing in the configuration, e.g. an include path (-I) or a define (-D).
The mode line shows how many messages there are, e.g. "C-xref:2". Click it to see them. Step through them with M-g n and M-g p, or use RET on a message in the buffer.
The messages are grouped under a heading for each file that was analysed. When a file is analysed again, its group is replaced.
A message about code from a macro is given where the macro is used.
The note: lines below it lead to the macro definitions.
M-g n skips the notes (as it does with compiler output), so use RET on a note to go there.
3. Browsing
Browsing uses a list of references (occurrences of a symbol), including definition, declarations and usages, which allows you to navigate that list.
c-xrefactory handles not only normal C variables, types etc. but also labels and include file references.
In addition Yacc non-terminals can be browsed.
3.1. Basic Navigation
The core workflow uses four actions:
-
Push This Symbol and Goto Definition — resolve the symbol at cursor, create the list of references and jump to its definition
-
Pop Symbol and Return — return to where you were before pushing, discarding the current list and picking up the previous list of references
-
Next Reference — step to the next occurrence of the current symbol
-
Previous Reference — step to the previous occurrence
Place cursor on a symbol, push it, then step through references. When done, pop to return.
3.2. The Browser Stack
You can browse multiple symbols simultaneously. Each push adds to a stack level. Each stack level holds the references for that symbol or referenceable item.
-
While browsing
variable, you see it passed tofunction -
Place cursor on
functionand Push to see its definition -
Use Next/Previous to navigate all occurrences of
function -
Pop to return and continue browsing
variable
You can Re-push Popped Symbol to get back the symbol you just popped.
3.3. Push by Name
Push Symbol by Name finds symbols by name only, ignoring scope. Useful when you know a symbol’s name but aren’t looking at an occurrence of it.
3.4. Browser Dialog
Display Browser opens a visual representation of the browser stack with two panes:
-
Symbol Pane — shows matched symbols and where they’re defined
-
References Pane — lists all occurrences with source context
Click a reference to jump to it. Multi-select symbols to combine their references into a navigable list of references.
In the Symbol Pane, the keys 0 to 2 filter which symbols are listed:
-
2— only the browsed symbol -
1— also the other included files, when an#includeis browsed -
0— all symbols with the same name, e.g. a static variable in another file
In the References Pane, the keys 0 to 3 filter which references are listed:
-
0— all references -
1— not where a variable is only read -
2— not where its address is taken either, so only where it is written, declared or defined -
3— only declarations and definitions
The filter starts at 0 for every symbol, and goes back to 0 when you change the filter in the Symbol Pane.
For a type, 1 also shows where it is used at the top level, i.e. in the type of a global variable or the return type of a function.
4. Searching
Searching finds symbols by pattern matching across your entire project.
Use it when you:
-
Can’t remember a symbol’s exact name
-
Want to find symbols from libraries
There are two search operations available from the menu:
- Search Symbol
-
Finds all symbols whose name matches the pattern — functions, variables, types, macros, and any other symbol known to c-xrefactory.
- Search Definition
-
Finds only symbols that have a definition within the project. Symbols that are merely used but not defined (e.g. called from an external library) are excluded.
4.1. Navigating Results
The search results are presented in a list. Use up/down arrow keys to move between entries. Press RET or Space to inspect a symbol — this navigates to its definition and enters the reference browser where Next/Previous Reference can be used to visit all references.
Pressing p and n navigates the search history — returning to the results of a previous or next search, not moving between entries in the current list.
q or Escape closes the list.
4.2. Patterns
Patterns use shell-style wildcards:
-
*— any string (including empty) -
?— any single character -
[abc]— one of the enclosed characters -
[a-z]— character range -
[^0-9]— negation (any non-digit)
Searching is case-insensitive, also within [].
4.3. Examples
-
getor*get*— symbols containing "get" -
init*— symbols starting with "init" -
get_?— five-letter symbols starting with "get_"
5. Completion
Press the completion key (default F8) to complete the identifier at cursor.
C-xrefactory understands context - it offers appropriate completions for function parameters, variable definitions, struct members, etc.
5.1. Completion Dialog
When multiple completions exist, a dialog appears showing each candidate with its full declaration.
-
Return — insert the selected completion
-
Space — inspect the symbol’s definition
-
Escape — cancel and return to editing
-
Arrow keys — navigate candidates
6. Refactoring
All refactorings are invoked from the Refactor menu with cursor on the relevant symbol. C-xrefactory checks that a change does not alter what the program does (that’s at least the intention).
6.1. Renaming
Rename Symbol — changes all occurrences of a variable, function, type, or macro throughout the project.
Rename Included File — renames a header file and updates all #include directives that reference it.
Rename Module — with the cursor on the #include of a module’s header, renames the header and its .c file, and updates the include guard and all #include directives.
6.2. Function Signatures
These work on functions and macros:
Add Parameter — adds a new parameter at a specified position, inserting the value you give at all call sites.
Delete Parameter — removes a parameter from the signature and all calls.
Move Parameter — reorders parameters, updating all calls accordingly.
6.3. Extracting Code
Extract Function / Extract Macro — select a region of code, then extract it into a new function or macro. C-xrefactory analyses the code to determine parameters and return values.
Extract Variable — select an expression, then replace it with a new variable that is initialised to it.
Extract Variable is not finished.
It suggests int as the type, so you might need to change it when it asks for the declaration.
The declaration might not end up where it should for some statements, e.g. a statement spanning several lines.
If the new code does not parse, the extraction is undone and a window shows what happened.
|
6.4. Moving Code
Move Function — moves the function at cursor to another place, possibly in another file. Mark the place first with Set Target for Next Moving Refactoring.
When it moves to another file, the static functions and variables that are only used by that function move along with it, and stay static.
If it uses a static that something else in the file also uses, it will refuse the move and name those.
The target’s header gets an extern for the function, and the source file includes that header.
If the target has no header, and the function is still used somewhere else, the header is created and saved.
If there is a comment above what is moved, Move Function asks whether to move the comments too. A comment is taken up to a blank line. A comment on the same line after the function always moves with it.
6.5. Includes
Organize Includes — sorts and groups the #include directives of the file.
6.6. Undo
Undo Last Refactoring — undoes the last refactoring, in all the buffers it changed. Doing it again redoes what was undone. It is also in the C-xref menu.
6.7. Finding Unused Code
Unused Symbols → … in File — finds unused local variables, parameters, and labels in the current file.
Unused Symbols → … in Project — finds unused symbols across the project.
7. Quick Reference
7.1. Default Key Bindings
| Key | Action |
|---|---|
F6 |
Push This Symbol and Goto Definition |
C-F6 |
Push This Symbol and Display Browser |
M-F6 |
Search Definition |
F5 |
Pop Symbol and Return |
C-F5 |
Re-push Popped Symbol |
F3 |
Previous Reference |
F4 |
Next Reference |
C-F3 |
Previous Reference to This Symbol (or Alternatives) |
C-F4 |
Next Reference to This Symbol (or Alternatives) |
F7 |
Delete One Window |
F8 |
Complete Identifier |
F11 |
Refactor Menu |
7.2. Common Workflows
Find where a function is called: Push the function name → step through references with Next/Previous
Rename a symbol safely: Cursor on symbol → Refactor → Rename Symbol → enter new name
Extract code: Select the code → Refactor → Extract Function → name the new function
Find all globals starting with "config":
Search Symbol → enter config* → browse results
Find out why references are missing:
M-g n → read the message → add the missing -I or -D to .c-xrefrc
8. Feedback
Issues and contributions welcome at GitHub.