-
Notifications
You must be signed in to change notification settings - Fork 483
[Housekeeping] Add Co-Pilot Prompts #2575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| # Overview | ||
| This document provides guidelines for using GitHub Copilot to contribute to the .NET MAUI Community Toolkit. It includes instructions on setting up your environment, writing code, and following best practices specific to .NET MAUI. | ||
|
|
||
| ## Prerequisites | ||
| 1. Install the latest stable (.NET SDK)[https://dotnet.microsoft.com/en-us/download]. | ||
| 2. Install .NET MAUI workloads (we recommend using Visual Studio installer). | ||
|
|
||
| ## Setting Up GitHub Copilot | ||
| 1. Ensure you have GitHub Copilot installed and enabled in Visual Studio. | ||
| 2. Familiarize yourself with the basic usage of GitHub Copilot by reviewing the (official documentation)[https://docs.github.com/en/copilot]. | ||
|
|
||
| ## Writing Code with GitHub Copilot | ||
| ### General Guidelines | ||
| * Use GitHub Copilot to assist with code completion, documentation, and generating boilerplate code. | ||
| * Always review and test the code suggested by GitHub Copilot to ensure it meets the project's standards and requirements. | ||
|
|
||
| ### Specific to .NET MAUI | ||
| * Ensure that any UI components or controls are compatible with .NET MAUI. | ||
| * Avoid using Xamarin.Forms-specific code unless there is a direct .NET MAUI equivalent. | ||
| * Follow the project's coding style and best practices as outlined in the (contributing)[https://github.com/CommunityToolkit/Maui/blob/main/CONTRIBUTING.md] document. | ||
|
|
||
| ## Best Practices | ||
| * Use **Trace.WriteLine()** for debug logging instead of **Debug.WriteLine()**. | ||
| * Include a **CancellationToken** as a parameter for methods returning **Task** or **ValueTask**. | ||
| * Use **is** for null checking and type checking. | ||
| * Use file-scoped namespaces to reduce code verbosity. | ||
| * Avoid using the **!** null forgiving operator. | ||
| ** Follow naming conventions for enums and property names. | ||
|
|
||
| ### Debug Logging | ||
| * Always use `Trace.WriteLine()` instead of `Debug.WriteLine` for debug logging because `Debug.WriteLine` is removed by the compiler in Release builds | ||
|
|
||
| ### Methods Returning Task and ValueTask | ||
| * Always include a `CancellationToken` as a parameter to every method returning `Task` or `ValueTask` | ||
| * If the method is public, provide a the default value for the `CancellationToken` (e.g. `CancellationToken token = default`) | ||
| * If the method is not public, do not provide a default value for the `CancellationToken` | ||
| * Use `CancellationToken.ThrowIfCancellationRequested()` to verify the `CancellationToken` | ||
|
|
||
| ### Enums | ||
| * Always use `Unknown` at index 0 for return types that may have a value that is not known | ||
| * Always use `Default` at index 0 for option types that can use the system default option | ||
| * Follow naming guidelines for tense... `SensorSpeed` not `SensorSpeeds` | ||
| * Assign values (0,1,2,3) for all enums | ||
|
|
||
| ### Property Names | ||
| * Include units only if one of the platforms includes it in their implementation. For instance HeadingMagneticNorth implies degrees on all platforms, but PressureInHectopascals is needed since platforms don't provide a consistent API for this. | ||
|
|
||
| ### Units | ||
| * Use the standard units and most well accepted units when possible. For instance Hectopascals are used on UWP/Android and iOS uses Kilopascals so we have chosen Hectopascals. | ||
|
|
||
| ### Pattern matching | ||
| #### Null checking | ||
| * Prefer using `is` when checking for null instead of `==`. | ||
|
|
||
| e.g. | ||
|
|
||
| ```csharp | ||
| // null | ||
| if (something is null) | ||
| { | ||
|
|
||
| } | ||
|
|
||
| // or not null | ||
| if (something is not null) | ||
| { | ||
|
|
||
| } | ||
| ``` | ||
|
|
||
| * Avoid using the `!` [null forgiving operator](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving) to avoid the unintended introduction of bugs. | ||
|
|
||
| #### Type checking | ||
| * Prefer `is` when checking for types instead of casting. | ||
|
|
||
| e.g. | ||
|
|
||
| ```csharp | ||
| if (something is Bucket bucket) | ||
| { | ||
| bucket.Empty(); | ||
| } | ||
| ``` | ||
|
|
||
| ### File Scoped Namespaces | ||
| * Use [file scoped namespaces](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/file-scoped-namespaces) to help reduce code verbosity. | ||
|
|
||
| e.g. | ||
|
|
||
| ```csharp | ||
| namespace CommunityToolkit.Maui.Converters; | ||
|
|
||
| using System; | ||
|
|
||
| class BoolToObjectConverter | ||
| { | ||
| } | ||
| ``` | ||
|
|
||
| ### Braces | ||
| Please use `{ }` after `if`, `for`, `foreach`, `do`, `while`, etc. | ||
|
|
||
| e.g. | ||
|
|
||
| ```csharp | ||
| if (something is not null) | ||
| { | ||
| ActOnIt(); | ||
| } | ||
| ``` | ||
|
|
||
| ### `NotImplementedException` | ||
| * Please avoid adding new code that throws a `NotImplementedException`. According to the [Microsoft Docs](https://docs.microsoft.com/dotnet/api/system.notimplementedexception), we should only "throw a `NotImplementedException` exception in properties or methods in your own types when that member is still in development and will only later be implemented in production code. In other words, a NotImplementedException exception should be synonymous with 'still in development.'" | ||
| In other words, `NotImplementedException` implies that a feature is still in development, indicating that the Pull Request is incomplete. | ||
|
|
||
| ### Bug Fixes | ||
| If you're looking for something to fix, please browse [open issues](https://github.com/CommunityToolkit/Maui/issues). | ||
|
|
||
| Follow the style used by the [.NET Foundation](https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/coding-style.md), with two primary exceptions: | ||
|
|
||
| * We do not use the `private` keyword as it is the default accessibility level in C#. | ||
| * We will **not** use `_` or `s_` as a prefix for internal or private field names | ||
| * We will use `camelCaseFieldName` for naming internal or private fields in both instance and static implementations | ||
|
|
||
| Read and follow our [Pull Request template](https://github.com/CommunityToolkit/Maui/blob/main/.github/PULL_REQUEST_TEMPLATE.md) | ||
|
|
||
| ### Element Positioning | ||
| Please adhere to [Style Cop SA1201](https://docs.github.com/en/copilot/using-github-copilot/code-review/configuring-coding-guidelines#creating-a-coding-guideline) for organizing code in a file. | ||
|
|
||
| Elements at the file root level or within a namespace should be positioned in the following order: | ||
|
|
||
| Extern Alias Directives | ||
| Using Directives | ||
| Namespaces | ||
| Delegates | ||
| Enums | ||
| Interfaces | ||
| Records | ||
| Structs | ||
| Classes | ||
|
|
||
| Within a class, struct, or interface, elements should be positioned in the following order: | ||
|
|
||
| Fields | ||
| Constructors | ||
| Finalizers (Destructors) | ||
| Delegates | ||
| Events | ||
| Enums | ||
| Interfaces | ||
| Properties | ||
| Indexers | ||
| Methods | ||
| Records | ||
| Structs | ||
| Classes | ||
|
|
||
| ## Submitting Contributions | ||
| 1. Fork the repository and create a new branch for your changes. | ||
| 2. Implement your changes using GitHub Copilot as needed. | ||
| 3. Ensure your changes include tests, samples, and documentation. | ||
| 4. Open a pull request and follow the [Pull Request template](https://github.com/CommunityToolkit/Maui/blob/main/.github/PULL_REQUEST_TEMPLATE.md). | ||
|
|
||
| ## Additional Resources | ||
| - [GitHub Copilot Documentation](https://docs.github.com/en/copilot) | ||
| - [.NET MAUI Documentation](https://learn.microsoft.com/en-us/dotnet/maui/) | ||
| By following these guidelines, you can effectively use GitHub Copilot to contribute to the .NET MAUI Community Toolkit. Thank you for your contributions! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| This cheat sheet should serve as a quick reminder of best practices when writing asynchronous code. Following these guidelines will help you avoid common pitfalls such as unobserved exceptions, deadlocks, and unexpected UI blocking. | ||
|
|
||
| --- | ||
|
|
||
| ### ✅ DOs | ||
|
|
||
| - **Always Await Your Tasks:** | ||
| Always use the `await` keyword on async operations to ensure exceptions are captured and to avoid blocking the calling thread. | ||
|
|
||
| - **Use Async Task/Task<T> Methods:** | ||
| Prefer returning `Task` or `Task<T>` over `async void` so that exceptions can be observed, and methods are easily composable and testable. | ||
|
|
||
| - **Name Methods with the "Async" Suffix:** | ||
| Clearly differentiate asynchronous methods (e.g., `GetDataAsync()`) from synchronous ones. | ||
|
|
||
| - **Pass Cancellation Tokens:** | ||
| Allow cancellation by accepting a `CancellationToken` in async methods. | ||
|
|
||
| - **Use ConfigureAwait(false) When Appropriate:** | ||
| In library code or server-side processes, use `ConfigureAwait(false)` to avoid unnecessary context switches and potential deadlocks. | ||
|
|
||
| - **Keep Async Code “Async All the Way”:** | ||
| Propagate async all the way from the entry point (like event handlers or controller actions) rather than mixing sync and async code. | ||
|
|
||
| - **Report Progress and Handle Exceptions Properly:** | ||
| Use tools like `IProgress<T>` to report progress and always catch exceptions at the appropriate level when awaiting tasks. | ||
|
|
||
| --- | ||
|
|
||
| ### ❌ DON'Ts | ||
|
|
||
| - **Avoid async void Methods:** | ||
| Except for event handlers, never use `async void` because their exceptions are not observable and they're difficult to test. | ||
|
|
||
| - **Don't Block on Async Code:** | ||
| Avoid using `.Wait()` or `.Result` as these can lead to deadlocks and wrap exceptions in `AggregateException`. If you must block, consider using `GetAwaiter().GetResult()`. | ||
|
|
||
| - **Don't Mix Blocking and Async Code:** | ||
| Blocking the calling thread in an otherwise async flow (e.g., by mixing synchronous calls with async ones) may cause deadlocks and performance issues. | ||
|
|
||
| - **Avoid Wrapping Return Task in Try/Catch or Using Blocks:** | ||
| When a method returns a `Task`, wrapping it in a try/catch or using block may lead to unexpected behaviour because the task completes outside those blocks. | ||
|
|
||
| - **Don't Overuse Fire-and-Forget Patterns:** | ||
| Unobserved tasks (fire-and-forget) can swallow exceptions and cause race conditions—if needed, use a “safe fire-and-forget” pattern with proper error handling. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Page Life-cycle | ||
|
|
||
| Use `EventToCommandBehavior` from CommunityToolkit.Maui to handle page life-cycle events when using XAML. | ||
|
|
||
| ```xml | ||
| <?xml version="1.0" encoding="utf-8" ?> | ||
| <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
| xmlns:pageModels="clr-namespace:CommunityTemplate.PageModels" | ||
| xmlns:models="clr-namespace:CommunityTemplate.Models" | ||
| xmlns:controls="clr-namespace:CommunityTemplate.Pages.Controls" | ||
| xmlns:pullToRefresh="clr-namespace:Syncfusion.Maui.Toolkit.PullToRefresh;assembly=Syncfusion.Maui.Toolkit" | ||
| xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" | ||
| x:Class="CommunityTemplate.Pages.MainPage" | ||
| x:DataType="pageModels:MainPageModel" | ||
| Title="{Binding Today}"> | ||
|
|
||
| <ContentPage.Behaviors> | ||
| <toolkit:EventToCommandBehavior | ||
| EventName="NavigatedTo" | ||
| Command="{Binding NavigatedToCommand}" /> | ||
| <toolkit:EventToCommandBehavior | ||
| EventName="NavigatedFrom" | ||
| Command="{Binding NavigatedFromCommand}" /> | ||
| <toolkit:EventToCommandBehavior | ||
| EventName="Appearing" | ||
| Command="{Binding AppearingCommand}" /> | ||
| </ContentPage.Behaviors> | ||
| ``` | ||
|
|
||
| ## Control Choices | ||
|
|
||
| * Prefer `Grid` over other layouts to keep the visual tree flatter | ||
| * Use `VerticalStackLayout` or `HorizontalStackLayout`, not `StackLayout` | ||
| * Use `CollectionView` or a `BindableLayout`, not `ListView` or `TableView` | ||
| * Use `Border`, not `Frame` | ||
| * Declare `ColumnDefinitions` and `RowDefinitions` in-line like `<Grid RowDefinitions="*,*,40">` | ||
141 changes: 141 additions & 0 deletions
141
.github/prompts/dotnet/maui/maui-memory-leaks.prompt.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| ## Memory Leaks | ||
|
|
||
| ### Avoid circular references on iOS and Catalyst | ||
|
|
||
| C# objects co-exist with a reference-counted world on Apple platforms, and so a C# object that subclasses `NSObject` can run into situations where they can accidentally live forever -- a memory leak. This situation does not occur on Android or Windows platforms. | ||
|
|
||
| Here is an example of a circular reference: | ||
|
|
||
| ```csharp | ||
| class MyViewSubclass : UIView | ||
| { | ||
| public UIView? Parent { get; set; } | ||
|
|
||
| public void Add(MyViewSubclass subview) | ||
| { | ||
| subview.Parent = this; | ||
| AddSubview(subview); | ||
| } | ||
| } | ||
|
|
||
| //... | ||
|
|
||
| var parent = new MyViewSubclass(); | ||
| var view = new MyViewSubclass(); | ||
| parent.Add(view); | ||
| ``` | ||
|
|
||
| In this case: | ||
|
|
||
| * `parent` -> `view` via `Subviews` | ||
| * `view` -> `parent` via the `Parent` property | ||
| * The reference count of both objects is non-zero | ||
| * Both objects live forever | ||
|
|
||
| This problem isn't limited to a field or property. A similar situation may occur with C# events: | ||
|
|
||
| ```csharp | ||
| class MyView : UIView | ||
| { | ||
| public MyView() | ||
| { | ||
| var picker = new UIDatePicker(); | ||
| AddSubview(picker); | ||
| picker.ValueChanged += OnValueChanged; | ||
| } | ||
|
|
||
| void OnValueChanged(object? sender, EventArgs e) { } | ||
|
|
||
| // Use this instead and it doesn't leak! | ||
| //static void OnValueChanged(object? sender, EventArgs e) { } | ||
| } | ||
| ``` | ||
|
|
||
| In this case: | ||
|
|
||
| * `MyView` -> `UIDatePicker` via `Subviews` | ||
| * `UIDatePicker` -> `MyView` via `ValueChanged` and `EventHandler.Target` | ||
| * Both objects live forever | ||
|
|
||
| A solution for this example, is to make `OnValueChanged` method `static`, which would result in a `null` `Target` on the `EventHandler` instance. | ||
|
|
||
| Another solution, would be to put `OnValueChanged` in a non-`NSObject` subclass: | ||
|
|
||
| ```csharp | ||
| class MyView : UIView | ||
| { | ||
| readonly Proxy _proxy = new(); | ||
|
|
||
| public MyView() | ||
| { | ||
| var picker = new UIDatePicker(); | ||
| AddSubview(picker); | ||
| picker.ValueChanged += _proxy.OnValueChanged; | ||
| } | ||
|
|
||
| class Proxy | ||
| { | ||
| public void OnValueChanged(object? sender, EventArgs e) { } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| If the class subscribing to the events are not an `NSObject` subclass, we can also use a proxy (use weak references to the primary object). | ||
|
|
||
| An example is a "view handler" which maps a "virtual view" to a "platform view". | ||
|
|
||
| The handler will have a readonly field for the proxy, and the proxy will manage the events between the platform view and the virtual view. The proxy should not have a reference to the handler as the handler does not take part in the events. | ||
|
|
||
| * The handler has a strong reference to the proxy | ||
| * The platform view has a strong reference to the proxy via the event handler | ||
| * The proxy has a _weak_ reference to the virtual view | ||
|
|
||
| ```csharp | ||
| class DatePickerHandler : ViewHandler<IDatePicker, UIDatePicker> | ||
| { | ||
| readonly Proxy proxy = new(); | ||
|
|
||
| protected override void ConnectHandler(UIDatePicker platformView) | ||
| { | ||
| proxy.Connect(VirtualView, picker); | ||
|
|
||
| base.ConnectHandler(platformView); | ||
| } | ||
|
|
||
| protected override void DisconnectHandler(UIDatePicker platformView) | ||
| { | ||
| proxy.Disconnect(VirtualView, picker); | ||
|
|
||
| base.DisconnectHandler(platformView); | ||
| } | ||
|
|
||
| void OnValueChanged() { } | ||
|
|
||
| class Proxy | ||
| { | ||
| WeakReference<IDatePicker>? _virtualView; | ||
|
|
||
| IDatePicker? VirtualView => _virtualView is not null && _virtualView.TryGetTarget(out var v) ? v : null; | ||
|
|
||
| public void Connect(IDatePicker handler, UIDatePicker platformView) | ||
| { | ||
| _virtualView = new(handler); | ||
| platformView.ValueChanged += OnValueChanged; | ||
| } | ||
|
|
||
| public void Disconnect(UIDatePicker platformView) | ||
| { | ||
| _virtualView = null; | ||
|
|
||
| platformView.ValueChanged -= OnValueChanged; | ||
| } | ||
|
|
||
| public void OnValueChanged(object? sender, EventArgs e) | ||
| { | ||
| VirtualView?.OnValueChanged(); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| This is the pattern used in most .NET MAUI handlers and other `UIView` subclasses to eliminate circular references. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this xaml is not finished. is it expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@VladislavAntonyuk You are correct, but the example code in the prompts are there only to guide CoPilot and aren't there for direct user reading. Think of them like the old Visual Studio 'snippets'. This one however, is easy to 'complete' and I will complete it.