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-xrefrc lives) 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

-I options tell c-xrefactory where to find header files, just like the compiler flag:

[my-project]
  -I ../external/headers
Preprocessor defines

-D options define macros, equivalent to the compiler flag. Useful when the project uses conditional compilation.

Excluding subtrees

-prune keeps 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/vendored
The path is anchored to the project root, not matched by name. -prune build skips <root>/build but not a nested src/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.

  1. While browsing variable, you see it passed to function

  2. Place cursor on function and Push to see its definition

  3. Use Next/Previous to navigate all occurrences of function

  4. 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 #include is 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

  • get or *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.