diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index 5b95c66..e5deb73 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -10,7 +10,7 @@ permissions:
contents: read
env:
- version: '10.9.${{ github.run_number }}'
+ version: '10.10.${{ github.run_number }}'
dotnetVersion: '8'
repoUrl: ${{ github.server_url }}/${{ github.repository }}
vsixPath: src/CodeNav/bin/Release/net472/CodeNav.vsix
diff --git a/src/CodeNav.OutOfProc/CodeNav.OutOfProc.csproj b/src/CodeNav.OutOfProc/CodeNav.OutOfProc.csproj
index b76fd35..94a6565 100644
--- a/src/CodeNav.OutOfProc/CodeNav.OutOfProc.csproj
+++ b/src/CodeNav.OutOfProc/CodeNav.OutOfProc.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/src/CodeNav.OutOfProc/Constants/CodeItemKindEnum.cs b/src/CodeNav.OutOfProc/Constants/CodeItemKindEnum.cs
index a4848e2..df1f42e 100644
--- a/src/CodeNav.OutOfProc/Constants/CodeItemKindEnum.cs
+++ b/src/CodeNav.OutOfProc/Constants/CodeItemKindEnum.cs
@@ -32,6 +32,9 @@ public enum CodeItemKindEnum
[EnumOrder(16)]
Event,
+ [EnumOrder(22)]
+ ExtensionBlock,
+
[EnumOrder(10)]
ImplementedInterface,
diff --git a/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/BaseMapper.cs b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/BaseMapper.cs
index 92ecc68..d5e0d88 100644
--- a/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/BaseMapper.cs
+++ b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/BaseMapper.cs
@@ -104,7 +104,8 @@ private static string MapFullName(SyntaxNode source, string name, SemanticModel
/// String display name
private static string MapName(SyntaxToken? identifier, NameSyntax? nameSyntax, string name = "")
{
- if (identifier != null)
+ if (identifier != null &&
+ !identifier.Value.IsKind(SyntaxKind.None))
{
return identifier.Value.Text;
}
diff --git a/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/DocumentMapper.cs b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/DocumentMapper.cs
index 66ac7da..e873f01 100644
--- a/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/DocumentMapper.cs
+++ b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/DocumentMapper.cs
@@ -133,6 +133,8 @@ SyntaxKind.RecordDeclaration when member is RecordDeclarationSyntax recordSyntax
=> RecordMapper.MapRecord(recordSyntax, semanticModel, codeDocumentViewModel),
SyntaxKind.StructDeclaration when member is StructDeclarationSyntax structSyntax
=> StructMapper.MapStruct(structSyntax, semanticModel, tree, codeDocumentViewModel),
+ SyntaxKind.ExtensionBlockDeclaration when member is ExtensionBlockDeclarationSyntax extensionBlockSyntax
+ => ExtensionBlockMapper.MapExtensionBlock(extensionBlockSyntax, semanticModel, tree, codeDocumentViewModel),
_ => null,
};
diff --git a/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/ExtensionBlockMapper.cs b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/ExtensionBlockMapper.cs
new file mode 100644
index 0000000..9a02302
--- /dev/null
+++ b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/ExtensionBlockMapper.cs
@@ -0,0 +1,51 @@
+using CodeNav.OutOfProc.Constants;
+using CodeNav.OutOfProc.Mappers;
+using CodeNav.OutOfProc.ViewModels;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.VisualStudio.Extensibility;
+
+namespace CodeNav.OutOfProc.Languages.CSharp.Mappers;
+
+public class ExtensionBlockMapper
+{
+ public static CodeClassItem MapExtensionBlock(ExtensionBlockDeclarationSyntax member,
+ SemanticModel semanticModel, SyntaxTree tree, CodeDocumentViewModel codeDocumentViewModel)
+ {
+ var codeItem = BaseMapper.MapBase(member, semanticModel, codeDocumentViewModel,
+ member.Identifier, modifiers: member.Modifiers, name: "Extension");
+ codeItem.Kind = CodeItemKindEnum.ExtensionBlock;
+ codeItem.Moniker = IconMapper.MapMoniker(codeItem.Kind, codeItem.Access);
+ codeItem.OverlayMoniker = ImageMoniker.KnownValues.OverlayLoginDisabled;
+ codeItem.Parameters = ParameterMapper.MapParameters(member.ParameterList);
+ codeItem.Tooltip = TooltipMapper.Map(member, codeItem.Access, string.Empty, codeItem.Name, codeItem.Parameters);
+
+ // Map regions
+ var regions = RegionMapper.MapRegions(tree, member.Span, codeDocumentViewModel);
+
+ // Map block members
+ foreach (var blockMember in member.Members)
+ {
+ var memberItem = DocumentMapper.MapMember(blockMember, tree, semanticModel, codeDocumentViewModel);
+
+ if (memberItem == null)
+ {
+ continue;
+ }
+
+ // Add member to region if it is part of one
+ if (RegionMapper.AddToRegion(regions, memberItem))
+ {
+ continue;
+ }
+
+ // Still here? Add the member to the block
+ codeItem.Members.Add(memberItem);
+ }
+
+ // Add regions to block
+ codeItem.Members.AddRange(regions);
+
+ return codeItem;
+ }
+}
diff --git a/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/MethodMapper.cs b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/MethodMapper.cs
index 53bf16e..a6562fd 100644
--- a/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/MethodMapper.cs
+++ b/src/CodeNav.OutOfProc/Languages/CSharp/Mappers/MethodMapper.cs
@@ -55,7 +55,9 @@ private static CodeItem MapMethod(SyntaxNode node, SyntaxToken identifier,
codeItem.Id = IdMapper.MapId(codeItem.FullName, parameterList);
codeItem.Kind = kind;
- codeItem.Moniker = IconMapper.MapMoniker(codeItem.Kind, codeItem.Access);
+ codeItem.Moniker = IsExtensionMethod(node, semanticModel)
+ ? ImageMoniker.KnownValues.ExtensionMethod
+ : IconMapper.MapMoniker(codeItem.Kind, codeItem.Access);
return codeItem;
}
@@ -70,8 +72,13 @@ public static CodeItem MapConstructor(ConstructorDeclarationSyntax member,
codeItem.Id = IdMapper.MapId(member.Identifier, member.ParameterList);
codeItem.Kind = CodeItemKindEnum.Constructor;
codeItem.Moniker = IconMapper.MapMoniker(codeItem.Kind, codeItem.Access);
- codeItem.OverlayMoniker = ImageMoniker.KnownValues.Add;
return codeItem;
}
+
+ private static bool IsExtensionMethod(SyntaxNode node, SemanticModel semanticModel)
+{
+ var symbol = semanticModel.GetDeclaredSymbol(node);
+ return symbol is IMethodSymbol { IsExtensionMethod: true };
+}
}
diff --git a/src/CodeNav.OutOfProc/Mappers/FilterRuleMapper.cs b/src/CodeNav.OutOfProc/Mappers/FilterRuleMapper.cs
index 41096e2..25ac438 100644
--- a/src/CodeNav.OutOfProc/Mappers/FilterRuleMapper.cs
+++ b/src/CodeNav.OutOfProc/Mappers/FilterRuleMapper.cs
@@ -15,7 +15,8 @@ or CodeItemKindEnum.Region
or CodeItemKindEnum.ImplementedInterface
or CodeItemKindEnum.Constructor
or CodeItemKindEnum.LocalFunction
- or CodeItemKindEnum.BaseClass => [CodeItemAccessEnum.All],
+ or CodeItemKindEnum.BaseClass
+ or CodeItemKindEnum.ExtensionBlock => [CodeItemAccessEnum.All],
CodeItemKindEnum.Struct => [CodeItemAccessEnum.Public, CodeItemAccessEnum.Internal, CodeItemAccessEnum.Private, CodeItemAccessEnum.All],
_ => [.. Enum.GetValues().Except([CodeItemAccessEnum.Unknown])],
};
@@ -29,7 +30,8 @@ or CodeItemKindEnum.Region
or CodeItemKindEnum.ImplementedInterface
or CodeItemKindEnum.Interface
or CodeItemKindEnum.Method
- or CodeItemKindEnum.BaseClass => true,
+ or CodeItemKindEnum.BaseClass
+ or CodeItemKindEnum.ExtensionBlock => true,
_ => false,
};
diff --git a/src/CodeNav.OutOfProc/Mappers/IconMapper.cs b/src/CodeNav.OutOfProc/Mappers/IconMapper.cs
index 240e7d9..f14a304 100644
--- a/src/CodeNav.OutOfProc/Mappers/IconMapper.cs
+++ b/src/CodeNav.OutOfProc/Mappers/IconMapper.cs
@@ -15,13 +15,15 @@ public static ImageMoniker MapMoniker(CodeItemKindEnum kind, CodeItemAccessEnum
CodeItemKindEnum.BaseClass => "ClassShortcut",
CodeItemKindEnum.Class => $"Class{accessString}",
CodeItemKindEnum.Constant => $"Constant{accessString}",
- CodeItemKindEnum.Constructor or CodeItemKindEnum.Method => $"Method{accessString}",
+ CodeItemKindEnum.Constructor => "NewMethod",
CodeItemKindEnum.Delegate => $"Delegate{accessString}",
CodeItemKindEnum.Enum => $"Enumeration{accessString}",
CodeItemKindEnum.EnumMember => $"EnumerationItem{accessString}",
CodeItemKindEnum.Event => $"Event{accessString}",
+ CodeItemKindEnum.ExtensionBlock => $"Class",
CodeItemKindEnum.ImplementedInterface => "ImplementInterface",
CodeItemKindEnum.Interface => $"Interface{accessString}",
+ CodeItemKindEnum.Method => $"Method{accessString}",
CodeItemKindEnum.Namespace => $"Namespace{accessString}",
CodeItemKindEnum.Property or CodeItemKindEnum.Indexer => $"Property{accessString}",
CodeItemKindEnum.Region => $"Numeric",
diff --git a/src/CodeNav.OutOfProc/ToolWindows/Templates/CodeItemStyles.xaml b/src/CodeNav.OutOfProc/ToolWindows/Templates/CodeItemStyles.xaml
index 25676cf..619dc2e 100644
--- a/src/CodeNav.OutOfProc/ToolWindows/Templates/CodeItemStyles.xaml
+++ b/src/CodeNav.OutOfProc/ToolWindows/Templates/CodeItemStyles.xaml
@@ -232,12 +232,16 @@
HorizontalAlignment="Stretch">
-
+
+
+
+
diff --git a/test/CodeNav.Test.csproj b/test/CodeNav.Test.csproj
index 13df255..a48f1a2 100644
--- a/test/CodeNav.Test.csproj
+++ b/test/CodeNav.Test.csproj
@@ -4,6 +4,8 @@
net8.0-windows8.0
enable
enable
+ 14
+ true
diff --git a/test/Files/ExtensionBlock/TestExtensionBlock.cs b/test/Files/ExtensionBlock/TestExtensionBlock.cs
new file mode 100644
index 0000000..bfe26f2
--- /dev/null
+++ b/test/Files/ExtensionBlock/TestExtensionBlock.cs
@@ -0,0 +1,16 @@
+namespace CodeNav.Test.Files.ExtensionBlock;
+
+public static class MyExtensions
+{
+ public static IEnumerable ValuesLessThan(this IEnumerable source, int threshold)
+ => source.Where(x => x < threshold);
+
+ extension(IEnumerable source)
+ {
+ public IEnumerable ValuesGreaterThan(int threshold)
+ => source.Where(x => x > threshold);
+
+ public IEnumerable ValuesGreaterThanZero
+ => source.ValuesGreaterThan(0);
+ }
+}
diff --git a/test/MapperTests/MapperTestExtensionBlock.cs b/test/MapperTests/MapperTestExtensionBlock.cs
new file mode 100644
index 0000000..64119da
--- /dev/null
+++ b/test/MapperTests/MapperTestExtensionBlock.cs
@@ -0,0 +1,21 @@
+namespace CodeNav.Test.MapperTests;
+
+[TestFixture]
+internal class MapperTestExtensionBlock : BaseTest
+{
+ [Test]
+ public async Task TestInlineBaseClassShouldBeOk()
+ {
+ var codeItems = await MapToCodeItems("ExtensionBlock/TestExtensionBlock.cs");
+
+ // First item should be a namespace
+ var namespaceItem = GetNamespace(codeItems);
+
+ var classItem = GetFirstClass(namespaceItem);
+
+ var extensionBlockItem = GetMemberAtIndex(classItem, 1) as CodeClassItem;
+
+ // Extension block item should have 2 members
+ Assert.That(extensionBlockItem!.Members, Has.Count.EqualTo(2));
+ }
+}