Mert Özen Further With Every Line
Developer Tools IDEs — #03

Being Fast in the IDE: Navigation and Shortcuts Compared Across VS 2026, Rider and VS Code

Mert Özen Nov 8, 2026 9 min 4 views
Being Fast in the IDE: Navigation and Shortcuts Compared Across VS 2026, Rider and VS Code

The answer to "who uses this" is Find All References, not Find in Files: in the Bookstore sample grep found eight lines while Roslyn's SymbolFinder API returned only four real references. The queries behind Go to Definition, Implementation, References and Call Hierarchy, a Windows and macOS shortcut table for Visual Studio 2026, Rider and VS Code, multi-caret, column selection, bookmarks and switching keymaps.

Version note: The code in this post was built and run with .NET 10 and Microsoft.CodeAnalysis.CSharp.Workspaces 5.9.0. Shortcuts come from the official shortcut documentation of Visual Studio 2026 (18.11), JetBrains Rider 2026.2 and Visual Studio Code (VS Code) 1.145; Visual Studio 2026 is Windows-only and Rider is not installed on this machine (macOS), so those two IDEs (Integrated Development Environment) were not run.

Guess First

You are about to change how the Order.Total property is computed in Bookstore, and first you want to know who uses it. You run grep -rn -w "Total" in the repo:

Invoice.cs:5:    public decimal Total { get; } = total;
Checkout.cs:16:        Console.WriteLine($"Order {order.Id} charged. Total: {order.Total}");
Checkout.cs:17:        return new Invoice(order.Total);
Checkout.cs:20:    public static string ToJson(Invoice invoice) => $$"""{ "Total": {{invoice.Total}} }""";
Payments.cs:12:    public Task<bool> ChargeAsync(Order order, CancellationToken ct) => Task.FromResult(order.Total > 0);
Payments.cs:17:    public Task<bool> ChargeAsync(Order order, CancellationToken ct) => Task.FromResult(order.Total <= 500);
Order.cs:8:    // Total is computed, never stored
Order.cs:9:    public decimal Total => Items.Sum(i => i.Quantity * i.UnitPrice);

Eight lines. How many of them actually reference Order.Total? Write down your guess; in the first section we ask the same question through the Roslyn API (Application Programming Interface) that IDEs use for Find All References.

Semantic Search vs Text Search: Why Find All References Is Different

What makes an IDE's navigation commands valuable is that they search the symbols the compiler sees, not the text. The C# support in Visual Studio and VS Code does this through Roslyn's SymbolFinder API; Rider uses its own ReSharper engine, but the question is the same. We loaded the four files of the Bookstore sample into an AdhocWorkspace and ran the queries behind three navigation commands ourselves:

Compilation compilation = (await project.GetCompilationAsync())!;
Solution solution = project.Solution;

// Find All References: Order.Total
INamedTypeSymbol order = compilation.GetTypeByMetadataName("Bookstore.Ordering.Order")!;
ISymbol total = order.GetMembers("Total").Single();
IEnumerable<ReferencedSymbol> references = await SymbolFinder.FindReferencesAsync(total, solution);

// Go to Implementation: IPaymentGateway
INamedTypeSymbol gateway = compilation.GetTypeByMetadataName("Bookstore.Payments.IPaymentGateway")!;
IEnumerable<INamedTypeSymbol> implementations = await SymbolFinder.FindImplementationsAsync(gateway, solution);

// Call Hierarchy (incoming calls): IPaymentGateway.ChargeAsync
IMethodSymbol charge = (IMethodSymbol)gateway.GetMembers("ChargeAsync").Single();
IEnumerable<SymbolCallerInfo> callers = await SymbolFinder.FindCallersAsync(charge, solution);
Compilation errors: 0
Find All References: Bookstore.Ordering.Order.Total
  Checkout.cs:16
  Checkout.cs:17
  Payments.cs:12
  Payments.cs:17
Go to Implementation: IPaymentGateway -> CardGateway, GiftCardGateway
Call Hierarchy: ChargeAsync <- Bookstore.Checkout.CheckoutService.PlaceAsync(Bookstore.Ordering.Order, System.Threading.CancellationToken)

The answer is four. Three of the eight lines grep found are something else: Invoice.Total is a different property with the same name, the "Total" in the JSON (JavaScript Object Notation) string is text, and Order.cs:8 is a comment. Order.cs:9 is the definition itself; Find All References shows it separately as the "definition". The reverse also holds: grep counts once per line and doesn't distinguish two Totals on the same line; at Checkout.cs:16 Roslyn counts only order.Total, not the word "Total:" inside the string.

This difference gives the most important shortcut rule of the post: for "who uses this", use Find All References, not Find in Files. Find in Files is for things that are not symbols: log messages, config keys, route strings, column names inside SQL (Structured Query Language). Mixing the two up is also the subject of the next post, which explains why rename misses some places.

Moving Through Code: Definition, Implementation, References, Hierarchy

The navigation commands have different names in the three IDEs but the same function. The table below is compiled from the Visual Studio shortcut list on Microsoft Learn, JetBrains' Rider keymap page and VS Code's default keybindings page. The Rider columns use Rider's default keymap on Windows and, on macOS, IntelliJ (macOS), one of the macOS keymaps JetBrains recommends; Visual Studio 2026 is Windows-only, so it has a single column.

CommandVisual Studio 2026Rider (Windows)Rider (macOS)VS Code (Windows)VS Code (macOS)
Go to DefinitionF12Ctrl+B⌘BF12F12
Go to ImplementationCtrl+F12Ctrl+Alt+B⌥⌘BCtrl+F12⌘F12
Find All References / Find UsagesShift+F12Alt+F7⌥F7Shift+Alt+F12⇧⌥F12
Peek Definition / ReferencesAlt+F12Ctrl+Alt+F7 (Show Usages)⌥Space (Quick Definition)Alt+F12 / Shift+F12⌥F12 / ⇧F12
Search everythingCtrl+T, Ctrl+,, Ctrl+PDouble ShiftDouble ⇧Ctrl+P, Ctrl+T (symbol)⌘P, ⌘T (symbol)
Search commandsCtrl+QCtrl+Shift+A⇧⌘ACtrl+Shift+P⇧⌘P
Go to fileCtrl+Shift+TCtrl+Shift+N⇧⌘OCtrl+P⌘P
Recent filesCtrl+T, RCtrl+E⌘ECtrl+Tab⌃Tab
Back / ForwardCtrl+- / Ctrl+Shift+-Ctrl+Alt+Left / Ctrl+Alt+Right⌘[ / ⌘]Alt+Left / Alt+Right⌃- / ⌃⇧-
Find in FilesCtrl+Shift+FCtrl+Shift+F⇧⌘FCtrl+Shift+F⇧⌘F
Add caret at next matchShift+Alt+.Alt+J⌃GCtrl+D⌘D
Move lineAlt+Up / Alt+DownAlt+Shift+Up / Alt+Shift+Down⌥⇧↑ / ⌥⇧↓Alt+Up / Alt+Down⌥↑ / ⌥↓
Duplicate lineCtrl+DCtrl+D⌘DShift+Alt+Down⇧⌥↓
Toggle bookmarkCtrl+K, Ctrl+KF11F3Not built inNot built in

Call Hierarchy is not in the table because it lives in a different place in each IDE. In Visual Studio, View Call Hierarchy is Ctrl+K, Ctrl+T; in VS Code, Show Call Hierarchy is Shift+Alt+H on Windows and ⇧⌥H on macOS; in Rider you pick Incoming Calls and Outgoing Calls from the Inspect This (Ctrl+Alt+Shift+A) menu. All of them are the FindCallersAsync query above, shown as a tree.

Telling Go to Implementation apart from Go to Definition saves dozens of clicks a day in a codebase that uses DI (Dependency Injection). On the gateway.ChargeAsync call in CheckoutService, F12 takes you to the interface, where there is nothing to see; Ctrl+F12 lists CardGateway and GiftCardGateway directly. In Rider you get the same result with Go to Implementation, and Go to Super Method (Ctrl+U) goes the other way, to the base member.

Search Everything and Search Commands

The three IDEs have two most-used boxes: one searches the code (files, types, members, symbols), the other searches the IDE's own commands. In Visual Studio they are Code Search and Feature Search, in Rider Search Everywhere and Find Action, in VS Code Quick Open and the Command Palette. Instead of hunting through menus for something whose shortcut you don't know, open the command box and type its name; the result row shows the shortcut too. Do this for a week and the shortcuts of your ten most-used commands memorize themselves.

The boxes have small languages too. Visual Studio Code Search has the t:, m:, f: prefixes and going to a line with Order:43 (we saw them in the previous post). In VS Code's Quick Open, @ brings the open file's symbols, # workspace symbols, : a line, and > commands. Rider's Search Everywhere window is split into All, Types, Files, Symbols and Actions tabs, switched with Tab; it understands camel case abbreviations, so typing CheSe finds CheckoutService.

Editing: Multi-Caret, Column Selection and Line Operations

After navigation, the biggest time saver is making the same change in several places at once. The "add caret at next match" shortcut in the table puts a second caret on the next occurrence of the selected word; in Visual Studio Shift+Alt+; adds carets to all matches at once, the VS Code equivalent is Ctrl+Shift+L (⇧⌘L on macOS), and in Rider it is Select All Occurrences, Ctrl+Alt+Shift+J.

Column (box) selection helps with aligned code: in Visual Studio and VS Code on Windows you drag with the mouse while holding Shift+Alt (⇧⌥ in VS Code on macOS), and in Visual Studio Shift+Alt+Down extends the column downward from the keyboard. In Rider, dragging with Alt held makes a rectangular selection, and Alt+Shift+Insert toggles column selection mode.

A warning: multi-caret works on text. If you want to change the same name in ten places, that is a rename, and the semantic rename (F2, Ctrl+R, Ctrl+R, Shift+F6) doesn't touch unrelated symbols that share the name. In the example above, selecting the Totals with multi-caret and changing them would also break Invoice.Total and the JSON field.

Bookmarks leave a trail during a long reading session. In Visual Studio you set one with Ctrl+K, Ctrl+K and jump to the next with Ctrl+K, Ctrl+N; in Rider F11 sets an anonymous bookmark and Ctrl+F11 one with a letter or digit, and Ctrl+[digit] jumps straight to it. VS Code has no built-in bookmarks; you either rely on the Go Back/Go Forward history or install an extension.

Switching Keymaps: Taking Your Muscle Memory With You

When you move from one IDE to another, the biggest cost is muscle memory. All three IDEs offer a way to reduce it. In Rider you choose the keymap under Settings (Ctrl+Alt+S) > Keymap; JetBrains' own recommendation is the Visual Studio 2022 keymap if you have plain Visual Studio experience without ReSharper, IntelliJ if you come from the IntelliJ family, and the VS Code keymap if you come from VS Code; on macOS choose the variant with macOS in its name. In VS Code, the File > Preferences > Migrate Keyboard Shortcuts from... menu lists popular keymap extensions; for Visual Studio shortcuts there is Microsoft's Visual Studio Keymap extension (ms-vscode.vs-keybindings). In Visual Studio you change your shortcuts under Tools > Options > Environment > Keyboard, and with Tools > Import and Export Settings you can export just the shortcuts to a .vssettings file and hand it to a teammate.

My recommendation: if you use more than one IDE, standardize on a single keymap across all of them. Someone who works mostly in Visual Studio on Windows keeps the same F12, Shift+F12, Ctrl+F12 trio with the Visual Studio 2022 keymap in Rider and the Visual Studio Keymap in VS Code. There is a trade-off: Rider's own documentation and help pages assume the default keymap; when you choose a different one, remember to switch the keymap selector at the top of the page.

In Production

Semantic search isn't free in a large solution. Find All References scans the whole solution through the compiler's eyes; in a hundred-project codebase the first call can take seconds, and if a project isn't loaded (solution filter, unloaded project), references in that project are missing from the result. Before you say "nobody uses this", make sure all projects are loaded.

Code reached through reflection, DI registrations or strings (places that don't use nameof, Razor and Angular templates, JSON property names) may not show up in Find All References. Before deleting something or changing a signature, do a Find in Files text search as well and compare the two results.

Over a remote connection (Remote Desktop, Dev Container, SSH (Secure Shell)) some shortcuts are swallowed by the operating system or the terminal; on macOS, for example, keys like ⌃← may go to Mission Control. If a shortcut doesn't work, first check for a shortcut conflict in the operating system.

Common Pitfalls

Doing everything with the mouse. Expanding folders in Solution Explorer to find a file spends minutes on a one-second Go to File job. For a week, every time you reach for a menu, try the command box instead.

Confusing Find in Files with Find All References. The first searches text, the second searches symbols. In the example grep returned eight lines while there were four real references; in the other direction, code called through a string shows up in a text search but not in Find All References.

Renaming with multi-caret. It changes different symbols that share the same text together; use semantic rename.

Keymap confusion. Someone using the IntelliJ keymap in Rider, the default in VS Code and the default in Visual Studio presses three different "Go to Definition" keys in a single day. Pick one keymap and apply it everywhere.

Looking for Windows shortcuts on macOS. If you pick a Windows keymap for Rider on macOS, Ctrl shortcuts can clash with system shortcuts; that is why JetBrains recommends the keymaps with macOS in their name.

A Small Experiment

★☆☆ Try the fourteen shortcuts from the table in your own IDE and mark the ones that don't work. Find out whether the cause is the keymap or the operating system.

★★☆ Pick a frequently used property in your own codebase. Search for it first with Find in Files, then with Find All References; sort the difference between the two result lists into categories: another symbol with the same name, a string, a comment, reflection.

★★★ Clone Microsoft's open-source eShop reference app (dotnet/eShop on GitHub) and, using only the keyboard, trace the path of the add-to-basket request: start at the HTTP (HyperText Transfer Protocol) endpoint and go down to the handler, the domain object and the code that writes to the database. Note which shortcuts you used and how often; how many times did you reach for the mouse?

We have learned to move through code quickly. In the next post we move on to changing that code safely: refactoring with Quick Actions and Alt+Enter, code style with .editorconfig, and the places rename can't catch.