diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..32ec89f --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,160 @@ +# Copilot Instructions + +## Project Details + +* .NET SDK pinned in #file:'global.json' +* Common parameters specified in #file:'Directory.Build.props' +* Central NuGet package version management – versions go in #file:'Directory.Packages.props', not in `.fsproj` files +* Build: `dotnet build FSharp.Control.R3.slnx` +* Test: `dotnet test FSharp.Control.R3.slnx` + +## Solution Structure + +```text +/ +├── src/FSharp.Control.R3/ – main library +│ ├── AssemblyInfo.fs – assembly metadata +│ ├── ProcessingOptions.fs – processing configuration helpers +│ ├── Observable.fs – observable wrappers/extensions +│ ├── AsyncObservable.fs – async observable helpers +│ └── TaskObservable.fs – task-based observable helpers +├── tests/FSharp.Control.R3.Tests/ – MSTest test project +│ ├── BuilderTests.fs – builder behavior tests +│ └── ObservableTests.fs – observable behavior tests +├── build/ – FAKE build scripts and release automation +└── docsSrc/ – FSharp.Formatting documentation source +``` + +## Libraries in Use + +* [`R3`](https://github.com/Cysharp/R3) – core reactive primitives +* [`MSTest`](https://github.com/microsoft/testfx) – test framework +* [`Unquote`](https://github.com/SwensenSoftware/unquote) – expressive assertions for complex checks +* [`altcover`](https://github.com/SteveGilham/altcover) – coverage instrumentation in test workflows +* [`FAKE`](https://fake.build/) – build and release scripting + +## F# Coding Guidelines + +### Language Preferences + +* Always use the latest F# 10 features over old syntax. +* Prefer `voption` over `option`. +* Prefer `task` CE over `async` CE. +* Prefer underscore lambda syntax like `Seq.map _.Name` over `Seq.map (fun x -> x.Name)`, but only when the expression is a simple member access. Complex expressions like `Seq.where (fun x -> x.Name = name)` or `Seq.map (fun x -> x.Field1, x.Field2)` cannot be simplified. +* Simplify `Seq.map (fun x -> someFunction x)` to `Seq.map someFunction`. +* When pipe operators are used on a materializable collection multiple times in a row, prefer `Seq` module for the chain and materialize at the end. +* Prefer interpolated strings over `printf` functions for string formatting. +* Use `withNull` for null checks instead of boxing delegates/functions (avoid `isNull (box value)`). + +### Nullable Reference Types + +* Declare variables non-nullable; check for `null` at entry points only. +* Trust the SDK null annotations – do not add null checks when the type system says a value cannot be null. +* Prefer `match` on `null` over `if isNull`: + + ```fsharp + // Preferred + match someObject with + | null -> () + | someObject -> someObject.SomeProperty + ``` + +### Class Constructors + +This is how to define a non-default F# class constructor: + +```fsharp +type DerivedClass = + inherit BaseClass + + new (``arguments here``) as ``created object`` + = + // create any objects used in the base class constructor + let fieldValue = "" + { + inherit + BaseClass (``arguments here``) + } + then + ``created object``.otherField <- fieldValue + + [] + val mutable otherField : FieldType +``` + +### Class Instantiation + +Always prefer F# class initializers over property assignment! **You absolutely must use F# class initializers instead of property assignment**! + +Class declaration: + +```fsharp +type MyClass (someConstructorParam : string) = + member ReadOnlyProperty = someConstructorParam + + member val MutableProperty1 = "" with get, set + member val MutableProperty2 = "" with get, set +``` + +Wrong: + +```fsharp +let myClass = MyClass("some value") +myClass.MutableProperty1 <- "new value" +myClass.MutableProperty2 <- "new value" +``` + +Right: + +```fsharp +let myClass = + MyClass( + // constructor parameters go first without names + "some value", + // then mutable properties go next with names + MutableProperty1 = "new value", + MutableProperty2 = + // operations must be placed into parentheses + (5 |> string) + ) +``` + +### C#-Consumable Extension Members + +```fsharp +// AutoOpen makes the module automatically available without an explicit open statement +// Extension makes the members visible to C# +[] +module MyTypeExtensions = + + type MyType with + + // Extension is visible to C# + // CompiledName makes the method name friendly to C# + [] + member this.ExtensionMethod (param1 : string) : ReturnType = + () +``` + +## Naming Conventions + +* Use PascalCase for modules, types, and public members. +* Use camelCase for `let` bindings, functions, private fields, and local variables. +* Prefix interface names with `I`. +* Do not prefix type parameters with `T` (e.g., use `'Result` instead of `'TResult`). + +## Testing + +* Tests use MSTest 4. +* `CollectionAssert` cannot work with F# lists – use F# array syntax (`[| ... |]`) instead. +* Use Unquote only for complex object/hierarchy assertions; for simple scalar checks prefer standard `Assert.*` APIs. +* Every `Assert.*` call **must include a failure message** so output is self-explanatory. +* Async tests must return `Task`, not `Async` or `Task` – always declare `) : Task = task {`. + +## General + +* Make only high-confidence suggestions when reviewing code changes. +* Write code with good maintainability practices, including comments on why certain design decisions were made. +* Handle edge cases and write clear exception handling. +* Never duplicate code unless explicitly allowed. +* All comments, documentation, README files, and markdown files must be written in **English only**. diff --git a/.github/prompts/create-pr-description.prompt.md b/.github/prompts/create-pr-description.prompt.md new file mode 100644 index 0000000..f13751b --- /dev/null +++ b/.github/prompts/create-pr-description.prompt.md @@ -0,0 +1,53 @@ +--- +mode: agent +description: Generates a pull request description for FSharp.Control.R3 by analyzing the current branch's changes and filling in the project's PR template. +--- + +# Create PR Description + +## Context + +#file:'.github/PULL_REQUEST_TEMPLATE.md' + +> If the `#file:` reference above cannot be resolved, run `Get-Content .github/PULL_REQUEST_TEMPLATE.md` in PowerShell from the repository root to read the template. + +## Task + +Analyze the current branch's git diff and commit history relative to the remote base branch (`origin/main`), then produce a fully filled-in PR description that conforms to the project's `PULL_REQUEST_TEMPLATE.md`. + +## Steps + +1. Read `#file:'.github/PULL_REQUEST_TEMPLATE.md'` to identify the exact section structure, checkbox labels, and required ordering. +2. Run `git diff origin/main...HEAD --stat`, `git diff origin/main...HEAD`, and `git log origin/main...HEAD --oneline` to determine exactly what changed in the current branch. +3. Fill `## Proposed Changes` with a concise 3–6 sentence summary of the concrete behavior, API, test, or documentation changes visible in the diff. +4. Set `## Types of changes` checkboxes strictly from evidence in commits and diff: + - mark bugfix only when existing behavior is corrected + - mark new feature only when new user-facing capability is introduced + - mark breaking change only when existing public behavior or API compatibility is intentionally broken +5. Evaluate `## Checklist` strictly from branch evidence: + - mark test-related items only if tests were added or updated in the diff + - mark documentation-related items only if docs or XML comments were changed + - if build/test execution is not visible from evidence, leave relevant items unchecked and add a brief inline note +6. Replace all placeholder/template text with branch-specific content and include `## Further comments` only when the change is large or architecturally significant. + +## Guidelines + +- Be factual and precise — base every statement on the actual diff and commits, not assumptions. +- Keep the **Proposed Changes** section concise but informative (3–6 sentences max). +- For checklist items that cannot be determined from the diff alone, leave them unchecked and add a short inline note. +- Do not invent issue numbers or links unless they appear in commit messages or branch names. +- Use plain English; avoid vague filler phrases like "various improvements". +- Do not start the summary with "This branch adds". +- Use only en dashes (`–`) for dashes; never use em dashes (`—`). +- Preserve all original template headings, checkbox syntax, and section order exactly. + +## Output + +A single fenced markdown code block containing the fully filled-out PR description, ready to copy and paste directly into GitHub. Do not escape any markdown syntax inside the block. Match the structure of `PULL_REQUEST_TEMPLATE.md` exactly: + +- `## Proposed Changes` — narrative paragraph(s) +- `## Types of changes` — checkboxes with `x` placed in the correct box(es) +- `## Checklist` — checkboxes filled based on evidence from the diff +- `## Further comments` — include only when the change warrants extra explanation; omit the section entirely otherwise + +Do not include any explanation or commentary outside the code block.