Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ jobs:
build:
name: "Build & Publish"
runs-on: windows-2022
permissions:
contents: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
Expand All @@ -31,12 +33,32 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Get Version
id: get_version
shell: pwsh
run: |
$version = Select-String -Path gradle.properties -Pattern "PluginVersion=(.*)" | ForEach-Object { $_.Matches.Groups[1].Value }
echo "VERSION=$version" >> $env:GITHUB_OUTPUT

- name: Build Plugin
run: ./gradlew buildPlugin

- name: Run Tests
run: ./gradlew testDotNet

- name: Create GitHub Release
if: github.event_name == 'workflow_dispatch' && inputs.publish == true
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.get_version.outputs.VERSION }}
name: v${{ steps.get_version.outputs.VERSION }}
files: |
output/*.zip
output/*.nupkg
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish Plugin
if: github.event_name == 'workflow_dispatch' && inputs.publish == true
shell: pwsh
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Tests

on:
push:
branches:
- master
pull_request:

jobs:
test:
name: .NET Tests
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Run Tests
run: ./gradlew testDotNet
46 changes: 46 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# AI Project Context: AutoMapper.FindUsage

This file provides essential information for AI agents working on the AutoMapper.FindUsage project.

## Project Overview
AutoMapper.FindUsage is a JetBrains Rider and ReSharper plugin that enables navigation between DTOs and Models based on AutoMapper configurations. It helps developers jump from a property in a destination class to its source in the original model and vice versa.

## Technology Stack
- **C# / .NET**: Main logic for ReSharper/Rider backend.
- **Gradle**: Build system for the entire project.
- **IntelliJ Platform SDK**: For Rider frontend integration.
- **ReSharper SDK**: For static analysis and navigation logic.
- **Kotlin/Java**: Currently minimal or none, as the plugin relies on the ReSharper backend host in Rider.

## Project Structure
- `src/dotnet`: Contains the ReSharper plugin code.
- `ReSharperPlugin.AutoMapper.FindUsage`: Main project containing mapping analysis and navigation logic.
- `Registrations/`: Core logic for finding and caching AutoMapper mappings.
- `ReSharperPlugin.AutoMapper.FindUsage.Tests`: Unit and integration tests.
- `test/data/Navigation/`: C# files used as input for navigation tests.
- `src/rider`: Contains Rider-specific resources and potential frontend code.
- `main/resources/META-INF/plugin.xml`: Plugin metadata and registration of components.
- `build.gradle.kts`: Main Gradle build script.
- `ReSharperPlugin.AutoMapper.FindUsage.sln`: .NET solution file.

## Key Concepts
- **Context Actions**: The plugin provides actions in the `Alt+Enter` menu on property setters (`set`) or `init` accessors.
- **Mapping Analysis**: The plugin scans the codebase for `CreateMap<TSource, TDest>()` calls and handles `.ReverseMap()`.
- **Zones**: ReSharper uses "Zones" for component isolation. See `IAutoMapper.FindUsageZone.cs` and `ZoneMarker.cs`.
- **Navigation**: Implements custom navigation to jump between mapped properties.

## Build and Run
- **Build .NET part**: `./gradlew compileDotNet`
- **Run Rider with plugin**: `./gradlew runIde` (Starts an experimental instance of Rider).
- **Run Tests**: `dotnet test` or via Gradle if configured.

## Development Guidelines for AI
- **Adding new mapping patterns**: Modify `AutoMapperRegistrationFinder.cs` in the `Registrations` folder to support more complex AutoMapper configurations.
- **Extending Navigation**: Look at how `AutoMapperSearchFactory.cs` and `AutoMapperMapping.cs` are implemented.
- **Tests**: When adding features, add a corresponding test case in `src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation`.
- **Code Style**: Adhere to the existing C# coding standards (ReSharper style).

## Useful Documentation
- [ReSharper SDK Documentation](https://www.jetbrains.com/help/resharper/sdk/Welcome.html)
- [Rider SDK Documentation](https://plugins.jetbrains.com/docs/rider/introduction.html)
- [AutoMapper Documentation](https://docs.automapper.org/)
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [0.2] - 2026-06-30
### Changed
- **Breaking Change**: Removed support for Context Actions (`Alt+Enter`). The plugin now integrates exclusively with the standard "Find Usages" action.
- Updated version to 0.2.

## [0.1.3] - 2026-06-30
### Added
- Deep integration with standard "Find Usages" action.
- Support for `init` accessors in navigation.
- Smart grouping of results in the "Find Results" window for properties with multiple mappings.
- New tests for multiple source mappings and `init` property navigation.

### Changed
- Navigation from Source to Destination now requires `.ReverseMap()` to be defined, while Destination to Source always works.

### Fixed
- Prevented self-mapping navigation (e.g., when a type is mapped to itself).
- Improved performance of mapping discovery by deduplicating project files during analysis.

## [0.1.2] - 2026-05-24
### Added
- Support for `Ignore()` method for properties. Navigation to the source property is not suggested if the destination property is marked as ignored in the AutoMapper configuration.
Expand All @@ -21,6 +40,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Automatic use of full type names in the menu when multiple mappings are present.
- Integration with ReSharper 2025.3 SDK.

[0.2]: https://github.com/Backs/AutoMapper.FindUsage/compare/v0.1.3...v0.2
[0.1.3]: https://github.com/Backs/AutoMapper.FindUsage/compare/v0.1.2...v0.1.3
[0.1.2]: https://github.com/Backs/AutoMapper.FindUsage/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/Backs/AutoMapper.FindUsage/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/Backs/AutoMapper.FindUsage/releases/tag/v0.1.0
36 changes: 22 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,38 @@
**AutoMapper.FindUsage** is a plugin for JetBrains Rider and ReSharper that provides seamless navigation between DTOs and Models based on your AutoMapper configurations.

## Features

- **Quick Navigation**: Jump from a property in your destination class directly to its source in the original model.
- **Find Usages**: Deep integration with the standard Find Usages action in Rider and ReSharper.
- **Quick Navigation**: Seamlessly jump between source and destination properties.
- **Fluent API Support**: Automatically recognizes mappings configured via `CreateMap<TSource, TDest>()`.
- **Reverse Mapping**: Full support for `.ReverseMap()`, allowing you to navigate in both directions.
- **Clean UI**: Context actions appear only on `set` or `init` accessors to minimize visual noise in your `Alt+Enter` menu.
- **Smart Grouping**: If a property is mapped from/to multiple types, the plugin groups them and uses full type names for easy identification.
- **Bidirectional Mapping**: Full support for `.ReverseMap()`. Navigation from Destination to Source always works, while Source to Destination navigation is available when a reverse mapping is defined.
- **Smart Grouping**: Mappings are grouped by type in the Find Results window for properties with multiple sources or destinations.

## Usage

1. Open a C# file containing a class used in AutoMapper mappings.
2. Place the caret on the `set` or `init` accessor of a property.
3. Press `Alt + Enter` to open Context Actions.
4. Select **AutoMapper. Navigate to source: <Type>.<Property>** to jump to the corresponding member.
3. Run `Find Usages` action.
4. View the search result for source properties or jump to the corresponding member.

![Find Usages Demo](find_usages.gif)

![Navigate to source](navigate_3.gif)
Find Results window:

![Search Results](find_usages.png)

## Supported Configurations

The plugin analyzes your codebase to find various mapping patterns:

```csharp
// Standard mapping
// Standard mapping (Destination -> Source navigation)
CreateMap<User, UserDto>();

// Reverse mapping support
// Reverse mapping support (Bidirectional navigation)
CreateMap<Order, OrderDto>().ReverseMap();

// Support for init accessors
public class UserDto { public string Name { get; init; } }
```

## Installation
Expand All @@ -45,21 +51,23 @@ To test the plugin in a sandbox environment:

1. **Build the .NET part**:
```bash
.\gradlew.bat compileDotNet
./gradlew compileDotNet
```
*(On Windows, use `.\gradlew.bat compileDotNet`)*

2. **Launch Rider with the plugin**:
```bash
.\gradlew.bat runIde
./gradlew runIde
```
*(On Windows, use `.\gradlew.bat runIde`)*
*This will start an experimental instance of Rider with the plugin installed.*

3. **Verify functionality**:
Open any solution with AutoMapper configurations and test the `Alt+Enter` menu on property setters.
Open any solution with AutoMapper configurations and test the `Find Usages` action.

## License

This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
This project is licensed under the Apache License 2.0 see the [LICENSE](LICENSE) file for details.

## Author

Expand Down
5 changes: 2 additions & 3 deletions ReSharperPlugin.AutoMapper.FindUsage.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ ProjectSection(SolutionItems) = preProject
src\rider\main\resources\META-INF\plugin.xml = src\rider\main\resources\META-INF\plugin.xml
build.gradle.kts = build.gradle.kts
gradle.properties = gradle.properties
navigate_1.png = navigate_1.png
navigate_2.png = navigate_2.png
navigate_3.gif = navigate_3.gif
find_usages.gif = find_usages.gif
find_usages.png = find_usages.png
EndProjectSection
EndProject
Global
Expand Down
33 changes: 23 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import com.jetbrains.plugin.structure.base.utils.isFile
import groovy.ant.FileNameFinder
import org.apache.tools.ant.taskdefs.condition.Os
import org.jetbrains.intellij.platform.gradle.Constants
import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.process.ExecOperations
import java.io.ByteArrayOutputStream

plugins {
Expand Down Expand Up @@ -35,7 +37,7 @@ repositories {
}

tasks.wrapper {
gradleVersion = "8.8"
gradleVersion = "8.13"
distributionType = Wrapper.DistributionType.ALL
distributionUrl = "https://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-${gradleVersion}-all.zip"
}
Expand All @@ -54,8 +56,15 @@ sourceSets {
}
}

tasks.compileKotlin {
kotlinOptions { jvmTarget = "17" }
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21)
}
}

val setBuildTool by tasks.registering {
Expand All @@ -65,7 +74,7 @@ val setBuildTool by tasks.registering {

if (isWindows) {
val stdout = ByteArrayOutputStream()
exec {
serviceOf<ExecOperations>().exec {
executable("${rootDir}\\tools\\vswhere.exe")
args("-latest", "-property", "installationPath", "-products", "*")
standardOutput = stdout
Expand All @@ -91,9 +100,10 @@ val compileDotNet by tasks.registering {
dependsOn(setBuildTool)
doLast {
val executable: String by setBuildTool.get().extra
@Suppress("UNCHECKED_CAST")
val arguments = (setBuildTool.get().extra["args"] as List<String>).toMutableList()
arguments.add("/t:Restore;Rebuild")
exec {
serviceOf<ExecOperations>().exec {
executable(executable)
args(arguments)
workingDir(rootDir)
Expand All @@ -103,7 +113,7 @@ val compileDotNet by tasks.registering {

val testDotNet by tasks.registering {
doLast {
exec {
serviceOf<ExecOperations>().exec {
executable("dotnet")
args("test","${DotnetSolution}","--logger","GitHubActions")
workingDir(rootDir)
Expand All @@ -114,7 +124,7 @@ val testDotNet by tasks.registering {
tasks.buildPlugin {
doLast {
copy {
from("${buildDir}/distributions/${rootProject.name}-${version}.zip")
from(layout.buildDirectory.file("distributions/${rootProject.name}-${version}.zip"))
into("${rootDir}/output")
}

Expand All @@ -128,12 +138,13 @@ tasks.buildPlugin {
}.joinToString("\u2022 ", prefix = "\u2022 ")

val executable: String by setBuildTool.get().extra
@Suppress("UNCHECKED_CAST")
val arguments = (setBuildTool.get().extra["args"] as List<String>).toMutableList()
arguments.add("/t:Pack")
arguments.add("/p:PackageOutputPath=${rootDir}/output")
arguments.add("/p:PackageReleaseNotes=${changeNotes}")
arguments.add("/p:PackageVersion=${version}")
exec {
serviceOf<ExecOperations>().exec {
executable(executable)
args(arguments)
workingDir(rootDir)
Expand All @@ -143,7 +154,9 @@ tasks.buildPlugin {

dependencies {
intellijPlatform {
rider(ProductVersion, useInstaller = false)
rider(ProductVersion) {
useInstaller = false
}
jetbrainsRuntime()

// TODO: add plugins
Expand Down Expand Up @@ -196,7 +209,7 @@ tasks.publishPlugin {
token.set("${PublishToken}")

doLast {
exec {
serviceOf<ExecOperations>().exec {
executable("dotnet")
args("nuget","push","output/${DotnetPluginId}.${version}.nupkg","--api-key","${PublishToken}","--source","https://plugins.jetbrains.com")
workingDir(rootDir)
Expand Down
19 changes: 19 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

# Exit on error
set -e

VERSION=${1:-"9999.0.0"}
SOLUTION="ReSharperPlugin.AutoMapper.FindUsage.sln"
OUTPUT_DIR="$(pwd)/output"

echo "Building solution $SOLUTION with version $VERSION..."

dotnet build "$SOLUTION" \
-c Release \
-p:PackageVersion="$VERSION" \
-p:PackageOutputPath="$OUTPUT_DIR" \
/t:Restore,Rebuild,Pack \
-v minimal

echo "Build finished. Artifacts are in $OUTPUT_DIR"
Binary file added find_usages.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added find_usages.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
DotnetPluginId=ReSharperPlugin.AutoMapper.FindUsage
DotnetSolution=ReSharperPlugin.AutoMapper.FindUsage.sln
RiderPluginId=me.rogatnev.automapper.findusage
PluginVersion=0.1.2
PluginVersion=0.2

BuildConfiguration=Debug

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
Loading
Loading