AiGen & AiFind in CodeRush for Visual Studio

In the 25.1 release of CodeRush, we’re introducing two new features designed to radically improve how you write and find code — AiGen and AiFind.

Built into CodeRush (a free extension for Visual Studio), AiGen and AiFind help you generate, modify, and navigate code using natural language prompts. These features bring the power of AI directly into your development environment, delivering faster AI responses at a lower cost.

No separate tool windows, no context switching, no copying/pasting, no typing (using optional voice input), and more focus on solving real problems.

Let’s dive in.

Setup Notes

Setup requires an OpenAI API key for AI features and an Azure Cognitive Services Speech API key for voice-to-text. Full setup details are here.

The instructions here are for voice prompts (which is the easiest way to code with AI), however if you prefer to type your prompts you can press Caps+G to bring up the AiGen Prompt window (assuming you have enabled the CodeRush's Caps as a Modifier feature - if not you can create a new shortcut binding to the AiGenFromPrompt command).

AiGen - Your Coding Assistant is Really Here

Just double-tap and hold the right Ctrl key inside the Visual Studio editor, and say what you need (release the Ctrl key when you have finished your voice request). You can ask for new classes, new methods, XAML layout changes or styles, or modifications to existing code. AiGen will integrate the AI response directly with your existing code. Here are some of my favorite AiGen use cases:

  • Creating new classes
  • Creating test cases
  • Adding DebuggerDisplay[] attributes
  • Creating data entry forms
  • Changing a window/form's design and its associated code behind in a single step
  • Modifying a group of methods or statements (e.g., consolidating/optimizing/transforming similar blocks of code)
  • Upgrading legacy code to a more modern approach (e.g., changing old test case asserts to fluent assertions)
  • Anything tedious so I stay focused on the big picture

AiGen can do almost anything you need, just ask. If AiGen can't do it, it will let you know (I'll include a limitations section below).

Let's explore some practical examples.

Follow Along

In the upcoming examples, I'll demonstrate sample prompts and responses. You might want to follow along and try these examples, however note that because we are using AI there is a certain non-deterministic element to these examples. Your results may vary. And AI results may not always be accurate.

Debugger Display Attribute

This is one of my favorite examples, because it is the smallest change, but it demonstrates the speed and intelligence of AI even for a small change. Consider the following class in C#:

public class Fraction {
    public int Numerator { get; set; }
    public int Denominator { get; set; }

    public Fraction(int numerator, int denominator) {
        if (denominator == 0) {
            throw new ArgumentException("Denominator cannot be zero.", nameof(denominator));
        }
        Numerator = numerator;
        Denominator = denominator;
    }
}

This class is a great start, but at debug time inspecting every instance will reveal the same value "{ Fraction }", regardless of what it contains. We need a DebuggerDisplay attribute so we can see the value for each instance at a glance.

Place the caret inside this class, double-tap and hold the right Ctrl key and say something like "Can I get a debugger display attribute?". As you speak you'll see feedback that CodeRush is listening. After you release the Ctrl key, you should see the "Thinking..." animation feedback. This means your query (along with pertinent context) has been sent to AI for processing. Within a few moments (about 5 seconds when I tried it just now) AiGen should insert an attribute into the code that looks like this:

[DebuggerDisplay("{Numerator}/{Denominator}")]
public class Fraction {
...

This is one of the things I love about working with AI -- you can supply a hint of what you want and it regularly infers and delivers exactly what you need. And in this example, it's in a place where there is no IntelliSense (inside the string parameter), so an error-prone location where precision and care must otherwise be used to avoid time-consuming mistakes. Could I add this myself in five seconds by hand? Unlikely. It would take nearly five seconds to just move the caret into the correct location.

You also might have noticed the AiGen Navigation window appear:

This UI appears after any AiGen or AiFind query, and allows you to navigate through all the changes.  Since there was only one change in this example, we can close this window. You can also ignore it -- it will auto-close 10 seconds after making any change in VS. Regardless, we will talk about this window later in this post.

Adding Operator Overloads

Let's stay in the Fraction class for a moment. With the caret inside this class, double-tap and hold the right Ctrl key and say: "Can I get some math operator overloads?"

After a moment you should see methods like the following inserted into your Fraction class:

[DebuggerDisplay("{Numerator}/{Denominator}")]
public class Fraction
{
	public static Fraction operator +(Fraction a, Fraction b)
	{
		int numerator = a.Numerator * b.Denominator + b.Numerator * a.Denominator;
		int denominator = a.Denominator * b.Denominator;
		return new Fraction(numerator, denominator);
	}

	public static Fraction operator -(Fraction a, Fraction b)
	{
		int numerator = a.Numerator * b.Denominator - b.Numerator * a.Denominator;
		int denominator = a.Denominator * b.Denominator;
		return new Fraction(numerator, denominator);
	}

	public static Fraction operator *(Fraction a, Fraction b)
	{
		int numerator = a.Numerator * b.Numerator;
		int denominator = a.Denominator * b.Denominator;
		return new Fraction(numerator, denominator);
	}

	public static Fraction operator /(Fraction a, Fraction b)
	{
		if (b.Numerator == 0)
		{
			throw new DivideByZeroException("Cannot divide by a fraction with zero numerator.");
		}
		int numerator = a.Numerator * b.Denominator;
		int denominator = a.Denominator * b.Numerator;
		return new Fraction(numerator, denominator);
	}
	public int Numerator { get; set; }
	public int Denominator { get; set; }

	public Fraction(int numerator, int denominator)
	{
		if (denominator == 0)
		{
			throw new ArgumentException("Denominator cannot be zero.", nameof(denominator));
		}

		Numerator = numerator;
		Denominator = denominator;
	}
}

Adding Test Cases

With the new operator overloads, our improved Fraction class is easier to use. Now let's verify that with some test cases. Double-tap and hold the right Ctrl key and say something like, "How about some test cases?".

And within seconds you should have a new class and file, FractionTests.cs, added to your project.

On my machine, with the NUnit framework already referenced, AI built this:

[TestFixture]
public class FractionTests
{
    [Test]
    public void TestAddition()
    {
        var fraction1 = new Fraction(1, 2);
        var fraction2 = new Fraction(1, 3);
        var result = fraction1 + fraction2;
        Assert.AreEqual(5, result.Numerator);
        Assert.AreEqual(6, result.Denominator);
    }

    [Test]
    public void TestSubtraction()
    {
        var fraction1 = new Fraction(1, 2);
        var fraction2 = new Fraction(1, 3);
        var result = fraction1 - fraction2;
        Assert.AreEqual(1, result.Numerator);
        Assert.AreEqual(6, result.Denominator);
    }

    [Test]
    public void TestMultiplication()
    {
        var fraction1 = new Fraction(1, 2);
        var fraction2 = new Fraction(2, 3);
        var result = fraction1 * fraction2;
        Assert.AreEqual(2, result.Numerator);
        Assert.AreEqual(6, result.Denominator);
    }

    [Test]
    public void TestDivision()
    {
        var fraction1 = new Fraction(1, 2);
        var fraction2 = new Fraction(3, 4);
        var result = fraction1 / fraction2;
        Assert.AreEqual(4, result.Numerator);
        Assert.AreEqual(6, result.Denominator);
    }

    [Test]
    public void TestZeroDenominatorThrowsException()
    {
        Assert.Throws<ArgumentException>(() => new Fraction(1, 0));
    }

    [Test]
    public void TestZeroNumeratorInDivisionThrowsException()
    {
        var fraction1 = new Fraction(1, 2);
        var fraction2 = new Fraction(0, 1);
        Assert.Throws<DivideByZeroException>(() => fraction1 / fraction2);
    }
}

Notice in the highlighted lines above where we are exercising our new operator overloads

Legacy Code and New Frameworks

To upgrade legacy code or use a new framework, open that file and simply say what you need. For example, in the test class we just created, we might want to convert all those Assert.AreEqual calls to something more fluent.

For example, double-tap and hold the right Ctrl key and say something like, "Hey, can we convert all these Assert.AreEqual calls to Assert.That calls?"

Within seconds, AiGen changes every method in the class to look like this:

[TestFixture]
public class FractionTests
{
    [Test]
	public void TestAddition()
	{
		var fraction1 = new Fraction(1, 2);
		var fraction2 = new Fraction(1, 3);
		var result = fraction1 + fraction2;
		Assert.That(result.Numerator, Is.EqualTo(5));
		Assert.That(result.Denominator, Is.EqualTo(6));
	}

    [Test]
	public void TestSubtraction()
	{
		var fraction1 = new Fraction(1, 2);
		var fraction2 = new Fraction(1, 3);
		var result = fraction1 - fraction2;
		Assert.That(result.Numerator, Is.EqualTo(1));
		Assert.That(result.Denominator, Is.EqualTo(6));
	}

    [Test]
	public void TestMultiplication()
	{
		var fraction1 = new Fraction(1, 2);
		var fraction2 = new Fraction(2, 3);
		var result = fraction1 * fraction2;
		Assert.That(result.Numerator, Is.EqualTo(2));
		Assert.That(result.Denominator, Is.EqualTo(6));
	}

    [Test]
	public void TestDivision()
	{
		var fraction1 = new Fraction(1, 2);
		var fraction2 = new Fraction(3, 4);
		var result = fraction1 / fraction2;
		Assert.That(result.Numerator, Is.EqualTo(4));
		Assert.That(result.Denominator, Is.EqualTo(6));
	}

    [Test]
    public void TestZeroDenominatorThrowsException()
    {
        Assert.Throws<ArgumentException>(() => new Fraction(1, 0));
    }

    [Test]
    public void TestZeroNumeratorInDivisionThrowsException()
    {
        var fraction1 = new Fraction(1, 2);
        var fraction2 = new Fraction(0, 1);
        Assert.Throws<DivideByZeroException>(() => fraction1 / fraction2);
    }
}

Undo and Redo in a Single Step

Now might be a good time to take a look at the undo stack.

Notice that every AiGen operation is prefixed and labeled, explaining each change. AiGen operations, even those spanning multiple files and locations, will appear as a single entry in the stack and can be easily undone (or redone) in a single step.

When you redo an AiGen operation, the Navigator reappears so you can see a summary of changes.

Creating A New Class

Creating a new class is easy. Just double-tap and hold the right Ctrl key and say something like this: "I need a new class named user with properties for first name, last name, and birth date. And I would like a unique ID property that's set to a new guid any time an instance is created."

If you're following along, try this in a new WPF application. AI builds this new class, wrapping it in the default project namespace, and adds its file to the project:

using System;

namespace WpfDX {
    public class User {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public Guid UniqueID { get; }

        public User() {
            UniqueID = Guid.NewGuid();
        }
    }
}

Note we're increasing the complexity a bit, providing multiple specifications to describe the code we want in the same prompt.

Creating a User Interface

Now, let's copy this class to the clipboard. Placing a class on the clipboard is one way to let AI know you want to work with this class. To quickly copy this class to the clipboard, place the caret on the first public keyword (on the class declaration) and press Ctrl+C. If you have CodeRush's smart copy feature enabled, the entire class will be selected and placed on the clipboard. 

Next, switch to the MainWindow.xaml file. In my sample project, my XAML looks like this (I've created a DockPanel on line 9, but if yours is showing a Grid that's fine too):

<Window x:Class="WpfDX.MainWindow"
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
		xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:local="clr-namespace:WpfDX"
		mc:Ignorable="d"
		Title="MainWindow" Height="450" Width="800">
	<DockPanel/>
</Window>

For reference, my code-behind file starts off relatively empty, looking like this:

using System;
using System.Windows;

namespace WpfDX {
	public partial class MainWindow : Window {
		public MainWindow() {
			InitializeComponent();
		}
	}
}

Double-tap and hold the right Ctrl key and say "I want to create a data table for the user class, which I've placed on the clipboard. Can we populate this form with, say 50 users filled with sample data?"

After a few seconds, changes should be integrated in both the XAML and the code behind, and the AiGen Navigator appears. My version for this demo looks like this:

The AiGen/AiFind Navigator

The Navigator window provides a summary of all code changes in this operation in the Results tree view on the left. In my screenshot above, it shows three changes to my code behind file (MainWindow.xaml.cs), and one change to my designer file (MainWindow.xaml).

Each change is prefixed by an icon that shows whether it's an addition (plus symbol), a change (a delta symbol), or a deletion (minus symbol). You can click on a change and the Navigator will highlight that change in the code, or you can press F7 and F8 to navigate backward and forward through the results.

Tip: If the change is large and does not fit onscreen, you can turn on Selection Previews to see both the top and bottom of the selection (open the CodeRush Options window and navigate to the "Editor->All Languages->Selection" options page.

Feel free to explore the changes by clicking on the results, or Run your application. I got this:

Not bad for zero lines of hand-written code.

As an expert in UI, I'm immediately noticing that the border contrast is too high. We'll upgrade to a more professional look in a minute, but before we go there, let's dive into the Difference View.

The Difference View

Often it's sufficient to navigate to the change. But sometimes its useful to see a difference. If a difference view is available, the "Show Difference View" button will be available (turned off by default to conserve screen space). Click this button (or press F9).

You can see in the Difference View (diff view) above, CodeRush has opened up the <DockPanel> tag and added a single DataGrid child.

Once you turn on the Difference View, it stays on until the Navigator is closed. You can always hide it in the right-click context menu.

Triggered Prompts

You may recall in our last example to create the data table we used the word "clipboard" in our prompt. When CodeRush matches certain regular expression triggers to your spoken/written prompts, it can send additional instructions to AI. In this case, it sent the clipboard contents as part of a rich context, so AI can generate the highest quality code.

You have full control over this behavior (and other triggered prompt modifications) on the "Triggered Prompts" options page (open the CodeRush Options window and navigate to the "IDE->Cognitive" section to find it).

You can use Triggered Prompts to provide conditional instructions, or "always on" instructions (just set the trigger to the universal regular expression matching pattern ".*").

Taking it to the Next Level

So far we've created a data table with some sample data. Next I'm going to switch over to the MainWindow.xaml file, double-tap and hold the right Ctrl key, and say "I'd like to convert these controls over to their DevExpress equivalents." Follow along if you have the DevExpress controls installed. If not, you can download a free trial here.

For me, this request took about 20 seconds. When it completed, the first thing I noticed was an "Invalid Markup" message in the XAML. But not to worry, because the AiGen Navigator is up and it's presenting a NuGet package install page.

Installing NuGet Packages

The NuGet Packages page lets you install packages needed to support the AI generated changes.

I can just click these Install buttons to start the NuGet package install. You can install these packages in any order (and you can certainly choose not to install at all).

The Navigator confirms successful installation by placing checkmarks next to each package:

As soon as these packages finish installing, the XAML designer preview updates showing the DevExpress controls. The designer preview now looks like this:

Excellent. This may be a good time for a reminder that we haven't typed any lines of code yet. Before we run, let's return to the AiGen Navigator and take a closer look at the changes.

AI Conversion to DevExpress Controls

If you've been following along with our example, so far we've created a new User class, we created a DataGrid and initialized it with sample data. Next we converted our application to use DevExpress controls instead of the ordinary WPF controls that come with .NET.

In my example, the Navigator shows two changes to the XAML and one to the code behind (interesting!). First, let's look at the XAML changes using the Diff View (F9 to toggle).

First, you can see AiGen removed the DataGrid control and replaced it with a GridControl that's parenting a TableView. Careful readers may also notice in my screen shot that it renamed the grid control to "userGridControl" (another interesting choice).

It also added the dxg namespace (you can see this by clicking the xmlns:dxg attribute entry in the Navigator tree view.

The last change is in the code behind. Using F8 (or simply clicking) to navigate to this change, the diff view reveals why the code behind was changed.

Ah... perfect. It renamed the control on the designer to match the new DevExpress WPF control type, and also updated the reference in the code behind. Nice.

I should point out that AI may not get it right every time. Sometimes you may have to make minor adjustments to the code or undo altogether (you can also click the Edit Prompt button on the Navigator if you want to try again).

Trying Out the New Application

So far we used AI to convert our DataGrid to a DevExpress GridControl . Now it's time to see it in action. Run the application and try it out. Here's what I have:

The default cell border contrast with the DevExpress controls is already better out of the box.

AiFind - Your AI-Powered Search

Just double-tap and hold the right Ctrl key inside the Visual Studio editor in the file you want to search, and state what you are looking for. For example, you might say "I'm looking for security issues in this code." or "Show me any methods involved in preparing an order for processing" or "I need to see any buttons that do not yet have a style assigned". AiFind will search the active file and highlight the code elements satisfying your search in the AiFind Navigator.

The F8 and F7 shortcuts (for Next/Previous AiFind result) are really useful for high-speed navigation.

Limitations

As with anything involving AI, there are some limitations to what AI assistance can do in this release.

  • AiFind currently works in the active file.
  • C# and XAML code folding agents are supplied
  • You can create new types contained in their own files, but there is no ability yet to create new files holding other non-C# type content.
  • Cannot delete files or remove files from the project.
  • Aside from installing NuGet packages, you cannot modify the project file or the solution file (so no way to create or add new projects).
  • Cannot perform refactorings on references spanning multiple files (use solid refactoring tools for this instead).
  • Contextual code awareness is currently limited to designer + code behind + clipboard (if you mention "clipboard" in your prompt), plus a project overview (language, framework, ui framework, and references).
  • There are the usual limitations and disclaimers on the power of AI itself. Sometimes AI "hallucinates" (this is especially true when generating code based on frameworks that have changed or where the API has broken over time). Also, it is possible to have a request that is so complex AI has difficulty implementing it correctly.

More Voice Features

CodeRush contains powerful features for voice interaction in Visual Studio. Other posts documenting voice features in CodeRush:

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.