Skip to content

Commit 5df690b

Browse files
committed
Add project files.
1 parent 7b1bf10 commit 5df690b

29 files changed

+2269
-0
lines changed

README.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# 📃 AddWithXmlDoc
2+
A Visual Studio extension that lets you generate common methods in C# - parameterless constructors, `GetHashCode`, `Equals`, and equality operators - with XML documentation automatically inserted for you.
3+
4+
# ❓ How does it work?
5+
When you install the extension, you get four new code actions for classes and structs!
6+
7+
- `XML: Add Equality Methods to Fields`: When you click this, it generates `GetHashCode`, `Equals`, `IEquatable`, and equality operators for all fields, + XML documentation.
8+
- `XML: Add Equality Methods to Properties`: Same as the one above, but for properties.
9+
- `XML: Add Equality Methods to Members`: Same as the one above, but for both Fields and Properties.
10+
- `XML: Add Parameterless Constructor`: Adds an empty constructor without any parameters, with XML documentation
11+
12+
You no longer have to manually write XML documentation for methods like `GetHashCode` or `Equals` - even an `<inheritdoc ... />` can take some time to write! AddWithXmlDoc does that for you.
13+
14+
# ✨ Example
15+
Imagine we have a class:
16+
```cs
17+
public class Class1
18+
{
19+
public string Greeting { get; set; }
20+
= "Welcome to AddWithXmlDoc!"
21+
}
22+
```
23+
24+
Right click on `Class1`. Click `XML: Add Equality Methods to Members`.
25+
Our class now looks like this. With just one click.
26+
27+
```cs
28+
public class Class1 : IEquatable<Class1>
29+
{
30+
public string Greeting { get; set; }
31+
= "Welcome to AddWithXmlDoc!"
32+
33+
/// <summary>
34+
/// Determines if this instance is equal to the other instance of type <see cref = "Class1"/>.
35+
/// </summary>
36+
/// <param name = "other">The other value to compare this instance with.</param>
37+
/// <returns>A boolean that, if <see langword="true"/>, indicates that <paramref name = "other"/> is equal to this instance, is of same type as this instance, and is not null.</returns>
38+
public override bool Equals(object? other)
39+
{
40+
return other is Class1 value && Equals(value);
41+
}
42+
43+
/// <summary>
44+
/// Determines if this instance is equal to the other instance of type <see cref = "Class1"/>.
45+
/// </summary>
46+
/// <param name = "other">The other instance to compare this instance with.</param>
47+
/// <returns>A boolean that, if <see langword="true"/>, indicates that <paramref name = "other"/> is equal to this instance.</returns>
48+
public bool Equals(Class1? other)
49+
{
50+
return this.Greeting == other?.Greeting;
51+
}
52+
53+
/// <summary>
54+
/// Computes the hash code for this object.
55+
/// </summary>
56+
/// <returns>This object's hash code.</returns>
57+
public override int GetHashCode()
58+
{
59+
var hashCode = new HashCode();
60+
hashCode.Add(this.Greeting);
61+
return hashCode.ToHashCode();
62+
}
63+
64+
/// <summary>
65+
/// Determines if <paramref name = "left"/> is equal to <paramref name = "right"/>.
66+
/// </summary>
67+
/// <param name = "left">The value to compare from.</param>
68+
/// <param name = "right">The value to compare with.</param>
69+
/// <returns><see langword="true"/> if the left parameter is same as the right parameter, otherwise <see langword="false"/>.</returns>
70+
public static bool operator ==(Class1 left, Class1 right)
71+
{
72+
return left.Equals(right);
73+
}
74+
75+
/// <summary>
76+
/// Determines if <paramref name = "left"/> is not equal to <paramref name = "right"/>.
77+
/// </summary>
78+
/// <param name = "left">The value to compare from.</param>
79+
/// <param name = "right">The value to compare with.</param>
80+
/// <returns><see langword="true"/> if the left parameter is different compared to the right parameter, otherwise <see langword="false"/> if both are same.</returns>
81+
public static bool operator !=(Class1 left, Class1 right)
82+
{
83+
return !(left == right);
84+
}
85+
}
86+
```
87+
88+
Typing all of this would've took a while!
89+
90+
Or, instead, click `XML: Add Parameterless Constructor`. Our class now looks like this.
91+
92+
```cs
93+
public class Class1
94+
{
95+
public string Greeting { get; set; }
96+
= "Welcome to AddWithXmlDoc!"
97+
98+
/// <summary>
99+
/// Initializes a new instance of the <see cref="Class1" /> class.
100+
/// </summary>
101+
public Class1()
102+
{
103+
}
104+
}
105+
```
106+
107+
# 🧱 Prerequisites
108+
AddWithXmlDoc supports all Visual Studio versions starting from Visual Studio 2017. You should have
109+
Visual Studio versions at least 2017 to use AddWithXmlDoc.
110+
111+
# 📦 Building
112+
You'll have to have Visual Studio workloads `.NET Desktop Development` and `Visual Studio extension development` prior to building AddWithXmlDoc.
113+
After that, open the solution VSAddWithXmlDoc.sln, and click Ctrl+Shift+B in Visual Studio to build the entire solution!
114+
115+
# ❔ Got any questions?
116+
Feel free to create an Issue post on the GitHub Repository if you have any questions, found a bug, or proposing a suggestion!
117+
118+
# 🐞 Known Issues
119+
One known problem is that when you click to generate equality methods, the class's base list can become incomplete, like so:
120+
```cs
121+
public class Class1 :
122+
```
123+
This seems to be a bug with the `NormalizeWhitespace()` method in Roslyn. The issue is that we're using an older version, 3.3.1, because
124+
apparently Visual Studio does not load AddWithXmlDoc if it uses a newer version of Roslyn (i.e. 4.14.0), so we have to use 3.3.1. If you're aware
125+
on potential fixes of this problem, please let me know by creating an Issue post on the GitHub Repository!
126+
127+
But it's not a major problem. It just takes 2-4 seconds to manually add:
128+
```cs
129+
public class Class1 : IEquatable<Class1>
130+
```
131+
132+
# 🤗 Author
133+
[winscripter](https://github.com/winscripter)
134+
135+
# 🪪 License
136+
Copyright (c) 2023-2025, winscripter
137+
138+
See the file LICENSE.txt for licensing information.

VsAddWithXmlDoc.sln

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.13.35931.197
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddWithXmlDoc.Core", "src\AddWithXmlDoc.Core\AddWithXmlDoc.Core.csproj", "{8B83C092-2156-4CCF-88EE-3B1C727703D7}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSAddWithXmlDoc", "src\VSAddWithXmlDoc\VSAddWithXmlDoc\VSAddWithXmlDoc.csproj", "{F1FE4A00-4DBD-4FF3-A205-3385D0CE4BBD}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSAddWithXmlDoc.Vsix", "src\VSAddWithXmlDoc\VSAddWithXmlDoc.Vsix\VSAddWithXmlDoc.Vsix.csproj", "{86DB2713-2417-4931-8F20-96297C622AFF}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddWithXmlDoc.ConsoleTest", "src\AddWithXmlDoc.ConsoleTest\AddWithXmlDoc.ConsoleTest.csproj", "{5071B20D-00B6-43F6-92A0-D5CA7CE6F427}"
15+
EndProject
16+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
17+
ProjectSection(SolutionItems) = preProject
18+
README.md = README.md
19+
EndProjectSection
20+
EndProject
21+
Global
22+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
23+
Debug|Any CPU = Debug|Any CPU
24+
Release|Any CPU = Release|Any CPU
25+
EndGlobalSection
26+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
27+
{8B83C092-2156-4CCF-88EE-3B1C727703D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28+
{8B83C092-2156-4CCF-88EE-3B1C727703D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
29+
{8B83C092-2156-4CCF-88EE-3B1C727703D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
30+
{8B83C092-2156-4CCF-88EE-3B1C727703D7}.Release|Any CPU.Build.0 = Release|Any CPU
31+
{F1FE4A00-4DBD-4FF3-A205-3385D0CE4BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32+
{F1FE4A00-4DBD-4FF3-A205-3385D0CE4BBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
33+
{F1FE4A00-4DBD-4FF3-A205-3385D0CE4BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
34+
{F1FE4A00-4DBD-4FF3-A205-3385D0CE4BBD}.Release|Any CPU.Build.0 = Release|Any CPU
35+
{86DB2713-2417-4931-8F20-96297C622AFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36+
{86DB2713-2417-4931-8F20-96297C622AFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
37+
{86DB2713-2417-4931-8F20-96297C622AFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{86DB2713-2417-4931-8F20-96297C622AFF}.Release|Any CPU.Build.0 = Release|Any CPU
39+
{5071B20D-00B6-43F6-92A0-D5CA7CE6F427}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40+
{5071B20D-00B6-43F6-92A0-D5CA7CE6F427}.Debug|Any CPU.Build.0 = Debug|Any CPU
41+
{5071B20D-00B6-43F6-92A0-D5CA7CE6F427}.Release|Any CPU.ActiveCfg = Release|Any CPU
42+
{5071B20D-00B6-43F6-92A0-D5CA7CE6F427}.Release|Any CPU.Build.0 = Release|Any CPU
43+
EndGlobalSection
44+
GlobalSection(SolutionProperties) = preSolution
45+
HideSolutionNode = FALSE
46+
EndGlobalSection
47+
GlobalSection(NestedProjects) = preSolution
48+
{8B83C092-2156-4CCF-88EE-3B1C727703D7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
49+
{F1FE4A00-4DBD-4FF3-A205-3385D0CE4BBD} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
50+
{86DB2713-2417-4931-8F20-96297C622AFF} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
51+
{5071B20D-00B6-43F6-92A0-D5CA7CE6F427} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
52+
EndGlobalSection
53+
GlobalSection(ExtensibilityGlobals) = postSolution
54+
SolutionGuid = {C149D727-4D4E-423E-9CE1-490C7AC23998}
55+
EndGlobalSection
56+
EndGlobal
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\AddWithXmlDoc.Core\AddWithXmlDoc.Core.csproj" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using AddWithXmlDoc.ConsoleTest;
2+
using AddWithXmlDoc.Core;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
6+
Console.WriteLine("Default snippet:");
7+
Console.WriteLine(Snippets.Snippet);
8+
9+
var parsed = CSharpSyntaxTree.ParseText(Snippets.Snippet);
10+
11+
var csharpLSC = LanguageServicesContainer.CreateCSharp();
12+
13+
var type = parsed.GetRoot().DescendantNodes().OfType<TypeDeclarationSyntax>().First();
14+
15+
csharpLSC.AddEqualityMembersToFields.ProvideRootNode(type);
16+
csharpLSC.AddEqualityMembersToFields.Invoke((newNode) =>
17+
{
18+
Console.WriteLine("Equality to Fields:");
19+
Console.WriteLine(newNode.ToString());
20+
});
21+
22+
csharpLSC.AddEqualityMembersToProperties.ProvideRootNode(type);
23+
csharpLSC.AddEqualityMembersToProperties.Invoke((newNode) =>
24+
{
25+
Console.WriteLine("Equality to Properties:");
26+
Console.WriteLine(newNode.ToString());
27+
});
28+
29+
csharpLSC.AddEqualityMembersToMembers.ProvideRootNode(type);
30+
csharpLSC.AddEqualityMembersToMembers.Invoke((newNode) =>
31+
{
32+
Console.WriteLine("Equality to Members:");
33+
Console.WriteLine(newNode.ToString());
34+
});
35+
36+
csharpLSC.AddParameterlessConstructor.ProvideRootNode(type);
37+
csharpLSC.AddParameterlessConstructor.Invoke((newNode) =>
38+
{
39+
Console.WriteLine("Parameterless Constructor:");
40+
Console.WriteLine(newNode.ToString());
41+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace AddWithXmlDoc.ConsoleTest;
2+
3+
internal static class Snippets
4+
{
5+
public const string Snippet =
6+
"""
7+
public sealed class MyClass {
8+
public string MyProperty { get; set; } = "Hello!";
9+
private int myField;
10+
public int MyIntProperty => 5;
11+
}
12+
""";
13+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace AddWithXmlDoc.Core.Abstractions;
4+
5+
/// <summary>
6+
/// Base service to add values to types with XML documentation.
7+
/// </summary>
8+
public interface IXmlAdd
9+
{
10+
/// <summary>
11+
/// Provides the root node to use.
12+
/// <example>
13+
/// <para><em>Example</em></para>
14+
///
15+
/// If <paramref name="syntaxNode"/> depicts a class or struct,
16+
/// things will be added into it with XML documentation.
17+
/// </example>
18+
/// </summary>
19+
/// <param name="syntaxNode">The root node.</param>
20+
/// <remarks>
21+
/// The provided node must be language-specific. If this
22+
/// <see cref="IXmlAdd"/> implementation receives C#-only syntax
23+
/// nodes, a Visual Basic syntax node cannot be provided.
24+
/// </remarks>
25+
void ProvideRootNode(SyntaxNode syntaxNode);
26+
27+
/// <summary>
28+
/// The language that this <see cref="IXmlAdd"/> implementation supports.
29+
/// The language name is provided by the <see cref="LanguageNames"/> class.
30+
/// </summary>
31+
/// <remarks>
32+
/// The language influences what nodes can be passed to the
33+
/// <see cref="ProvideRootNode(SyntaxNode)"/> method.
34+
/// </remarks>
35+
string Language { get; }
36+
37+
/// <summary>
38+
/// Begins the operation.
39+
/// </summary>
40+
/// <param name="del">Once the operation is complete, the new node is sent here.</param>
41+
void Invoke(NewNodeDelegate del);
42+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AddWithXmlDoc.Core.Abstractions;
2+
3+
/// <summary>
4+
/// <see cref="IXmlAdd"/> implementation for "XML: Add Equality Members to all Fields"
5+
/// </summary>
6+
public interface IXmlAddEqualityMembersToFields : IXmlAdd
7+
{
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AddWithXmlDoc.Core.Abstractions;
2+
3+
/// <summary>
4+
/// <see cref="IXmlAdd"/> implementation for "XML: Add Equality Members to all Members"
5+
/// </summary>
6+
public interface IXmlAddEqualityMembersToMembers : IXmlAdd
7+
{
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AddWithXmlDoc.Core.Abstractions;
2+
3+
/// <summary>
4+
/// <see cref="IXmlAdd"/> implementation for "XML: Add Equality Members to all Properties"
5+
/// </summary>
6+
public interface IXmlAddEqualityMembersToProperties : IXmlAdd
7+
{
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AddWithXmlDoc.Core.Abstractions;
2+
3+
/// <summary>
4+
/// <see cref="IXmlAdd"/> implementation for parameterless constructors.
5+
/// </summary>
6+
public interface IXmlAddParameterlessConstructor : IXmlAdd
7+
{
8+
}

0 commit comments

Comments
 (0)