Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add collection return support to ScanForTypes attribute - #55

Merged
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits into
mainfrom
copilot/return-found-types-as-array

Conversation

CopilotAI commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required[ScanForTypes(AssignableTo=typeof(IService))]publicstaticpartialType[]GetServiceTypes();// Generates: return [typeof(MyService1), typeof(MyService2)];// Case 2: map each found type via a Handler[ScanForTypes(AssignableTo=typeof(IService),Handler=nameof(Describe))]publicstaticpartialServiceInfo[]GetServiceInfos();privatestaticServiceInfoDescribe<T>()=>new(typeof(T).Name);// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
CopilotAI changed the title [WIP] Return found types as an array implementationAdd collection return support to ScanForTypes attributeMar 20, 2026
CopilotAI requested a review from DreamescaperMarch 20, 2026 13:44

@DreamescaperDreamescaper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

 public static partial global::System.Type[] GetServiceTypes()
{
return [
typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)
];
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
typeof(global::GeneratorTests.MyService1),
typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

stringsource=(registrations.Count,method.ReturnTypeIsCollection)switch{(>0, _)=>GenerateRegistrationsSource(method,registrations),(_,true)=>GenerateCollectionSource(method,collectionItems),
_ =>GenerateCustomHandlingSource(method,customHandling)};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if(method.ReturnTypeIsCollection)AddCollectionItems(implementationType,matchedTypes,attribute,method,collectionItems);elseif(attribute.CustomHandler!=null)AddCustomHandlerItems(implementationType,matchedTypes,attribute,customHandlers);else{/* registration logic */}

CopilotAI commented Mar 26, 2026

Copy link
Copy Markdown
ContributorAuthor

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

CopilotAI requested a review from DreamescaperMarch 26, 2026 13:01
@Dreamescaper
Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper
Dreamescaper merged commit 405679a into mainMar 26, 2026
1 check passed
@Dreamescaper
Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants

@Dreamescaper