Lesson 7: Code Navigation#
In this lesson, you’ll learn to navigate large Java codebases efficiently using LSP-powered features.
Learning Objectives#
By the end of this lesson, you’ll be able to:
- Jump to definitions and declarations
- Find all references to a symbol
- Navigate type hierarchies
- Search for symbols across the project
- Use the jump list effectively
Core Navigation Commands#
Go to Definition#
Place cursor on a class, method, or variable and press:
gdExample:
public class Main {
public static void main(String[] args) {
UserService service = new UserService();
// ^^^^^^^ cursor here, press gd
}
}Jumps to the UserService class definition.
Go to Declaration#
gDSimilar to gd, but goes to the declaration rather than the definition. In Java, these are usually the same.
Go to Implementation#
giUseful for interfaces and abstract classes:
List<String> list = new ArrayList<>();
// ^^^^ cursor on List, press giShows all implementations of List interface.
Go to Type Definition#
gyJumps to the type of a variable:
var user = getUserById(1);
// ^^^^ cursor here, press gyJumps to the User class definition.
Finding References#
Find All References#
Place cursor on any symbol and press:
grExample:
public class User {
private String name;
// ^^^^ cursor here, press gr
}Shows all places where name is used:
- Constructor assignments
- Getter/setter methods
- Usage in other classes
Opens in Telescope picker showing all references.
Find References in Quickfix#
Alternative way:
:lua vim.lsp.buf.references()Opens references in quickfix window:
<Enter>- Jump to reference:cn- Next reference:cp- Previous reference:cclose- Close quickfix
Symbol Search#
Document Symbols#
Search symbols in current file:
<Space>ds(Document Symbols)
Shows:
- All classes
- All methods
- All fields
- All variables
Type to filter, press Enter to jump.
Workspace Symbols#
Search symbols across entire project:
<Space>ws(Workspace Symbols)
Example searches:
UserS- Finds UserService, UserServiceImpl, etc.getUser- Finds all methods named getUserMAX_RETRIES- Finds constant definitions
Type Hierarchy#
View Type Hierarchy#
On a class or interface:
:lua vim.lsp.buf.type_hierarchy()Or use code action:
<Space>caSelect “Show type hierarchy”
Shows:
- Superclasses
- Interfaces
- Subclasses
- Implementations
Call Hierarchy#
Incoming Calls#
See what methods call this method:
:lua vim.lsp.buf.incoming_calls()Outgoing Calls#
See what methods this method calls:
:lua vim.lsp.buf.outgoing_calls()Documentation Lookup#
Hover Documentation#
Place cursor on symbol and press:
K(Shift-K)
Shows:
- Method signatures
- Parameter types
- Return types
- JavaDoc comments
- Type information
Press K again to jump into the hover window.
Signature Help#
While editing method parameters:
<Ctrl-k>Shows:
- Expected parameter types
- Parameter names
- Overload options
The Jump List#
Neovim remembers where you’ve been.
Jump Back/Forward#
| Key | Action |
|---|---|
<Ctrl-o> | Jump to previous location (older) |
<Ctrl-i> | Jump to next location (newer) |
Example workflow:
- In Main.java, press
gdon UserService → jumps to UserService.java - Press
gdon User → jumps to User.java - Press
<Ctrl-o>→ back to UserService.java - Press
<Ctrl-o>→ back to Main.java - Press
<Ctrl-i>→ forward to UserService.java
View Jump List#
:jumpsShows history of jumps. Each jump has a number.
Search and Navigation#
Fuzzy File Search#
<Space>ffType partial path or filename:
UserS- matches UserService.javamodel/User- matches com/example/model/User.java
Live Grep#
Search text across all files:
<Space>fgType search query, see results in real-time:
- Shows matching lines
- Preview context
- Press
Enterto jump
Example searches:
public class- Find all class declarationsTODO- Find all TODO comments@Override- Find all overridden methods
Search Current Word#
Place cursor on word and press:
<Space>fwSearches for the word under cursor across entire project.
Practical Navigation Workflows#
Workflow 1: Understanding Unknown Code#
Starting point: Main.java
UserController controller = new UserController();
controller.createUser("Alice", "alice@example.com");Navigation steps:
- Cursor on
UserController, pressgd→ Opens UserController.java - Find
createUsermethod, cursor onUserService, pressgd→ Opens UserService.java - Find implementation details, cursor on
User, pressgd→ Opens User.java - Press
<Ctrl-o>repeatedly to retrace steps back to Main.java
Workflow 2: Refactoring Impact Analysis#
Want to change a method signature:
public void processUser(User user) {
// implementation
}Steps:
- Cursor on
processUser, pressgr- See all callers - Review each reference
- Make changes
- Use
gragain to verify all fixed
Workflow 3: Finding Implementation#
Given an interface:
public interface UserRepository {
User findById(Long id);
}Steps:
- Cursor on interface name, press
gi→ Shows all implementations - Select implementation to view
- Press
<Ctrl-o>to return
Workflow 4: Exploring Dependencies#
In a service class:
@Service
public class OrderService {
private final UserService userService;
private final ProductService productService;
}Steps:
- Press
<Space>dsto see all symbols in file - Find field declarations
- Press
gdon each service to explore - Use
<Ctrl-o>to navigate back
Advanced Navigation Techniques#
Navigate to Related Files#
Use Telescope with smart queries:
:Telescope find_files search=TestFinds all test files.
Search by File Pattern#
<Space>ffThen type patterns:
*Controller.java- All controllers*Test.java- All tests*/model/*.java- All model classes
Custom Symbol Search#
Search specific symbol types:
:lua require('telescope.builtin').lsp_document_symbols({
symbols = 'method'
})Shows only methods.
Marks for Navigation#
Setting Marks#
Mark important locations:
m<letter>Examples:
ma- Set mark ‘a’mb- Set mark ‘b’mM- Set global mark ‘M’ (accessible from any file)
Jumping to Marks#
'<letter>Examples:
'a- Jump to mark ‘a’'M- Jump to global mark ‘M’
View All Marks#
:marksPractical Mark Usage#
- Mark your “home base” in Main.java:
mH - Mark service entry point:
mS - Mark model definition:
mM - Jump between them:
'H,'S,'M
Telescope Navigation Summary#
| Key | Command | Purpose |
|---|---|---|
<Space>ff | Find files | Open any file |
<Space>fg | Live grep | Search text |
<Space>fw | Grep word | Search word under cursor |
<Space>fb | Buffers | Switch buffers |
<Space>fr | Recent files | Recently opened |
<Space>ds | Document symbols | Symbols in file |
<Space>ws | Workspace symbols | Symbols in project |
LSP Navigation Summary#
| Key | Command | Purpose |
|---|---|---|
gd | Go to definition | Jump to where defined |
gD | Go to declaration | Jump to declaration |
gi | Go to implementation | Find implementations |
gy | Go to type definition | Jump to type |
gr | Find references | See all usages |
K | Hover docs | Show documentation |
<Space>ca | Code actions | Context actions |
<Space>rn | Rename | Rename symbol |
Practice Exercises#
Exercise 1: Code Archaeology#
Given a method call in Main.java:
orderService.processOrder(order);Navigate to:
- The
OrderServiceclass definition - The
processOrdermethod implementation - All places that call
processOrder - The
Orderclass definition - Return to Main.java
Exercise 2: Interface Explorer#
Find all implementations of Comparable in your project:
- Create a class implementing Comparable
- Use
gito find all implementations - Explore each one
Exercise 3: Refactoring Exploration#
Pick a method, then:
- Find all callers with
gr - Find what it calls (read the code)
- Find the types it uses with
gd - Navigate the dependency tree
Tips for Effective Navigation#
- Use
gdaggressively - Don’t scroll to find definitions - Trust
<Ctrl-o>- Jump fearlessly, you can always go back - Combine with splits - Open definition in new split with
<Ctrl-w>gd - Use marks for anchors - Mark your starting point before deep dives
- Leverage fuzzy finding - Often faster than tree navigation
- Read hover docs - Press
Kto understand before jumping
What’s Next?#
In Lesson 8: Debugging, you’ll learn to debug Java applications right from Neovim.