|
| 1 | + |
| 2 | +# Copilot Instructions |
| 3 | + |
| 4 | +## Code Style Rules |
| 5 | + |
| 6 | +### Line Length |
| 7 | + |
| 8 | +- All `.cs` source files must adhere to the following rule: |
| 9 | + - No line of code should exceed **120 characters** in length. |
| 10 | + - This includes comments, string literals, and code. |
| 11 | + - Exception: automatically generated files may be ignored if they cannot be reformatted safely. |
| 12 | +- **How to measure (raw file characters, per-line only):** |
| 13 | + - Count based on **raw file characters**, not editor rendering. |
| 14 | + - **Tabs count as 4 characters** for measurement. |
| 15 | + - Trailing whitespace must be removed. |
| 16 | + - **Per-physical-line measurement ONLY.** The unit of measurement is a **single newline-delimited line**. |
| 17 | + - **Never** add or aggregate the lengths of multiple lines. |
| 18 | + - A wrapped invocation is compliant if **each** physical line is ≤ 120 characters. |
| 19 | + - Ignore soft wrapping (on-screen wrapping that doesn't insert a newline). |
| 20 | + |
| 21 | +### Code Formatting |
| 22 | + |
| 23 | +- Single-line instructions must follow each other with **no blank lines** in between. |
| 24 | +- **New rule (clarified):** A multi-line instruction must be preceded by **exactly one blank line _only when it begins a new statement_**. |
| 25 | + Do **not** require a blank line before a multi-line **continuation** of an existing statement. |
| 26 | +- If a multi-line instruction is followed by further instructions, it must also be followed by **exactly one blank line**. |
| 27 | +- **Exception (block first statement):** If a statement is the **first statement inside a block**—i.e., directly after `{`—**no preceding blank line** is required. |
| 28 | +- Any C# `return` statement must be preceded by **exactly one blank line** unless it is the first statement in a block. |
| 29 | +- If a constructor/method name would push a line past 120 characters, move `new`, the call, or the arguments to the next line. |
| 30 | +- Always format so that **no single physical line exceeds 120 characters**, even when calls span multiple lines. |
| 31 | +- **Definition of a blank line (updated):** |
| 32 | + A blank line is any physical line that contains **no visible characters**. After trimming whitespace, the line must be empty. |
| 33 | + Lines containing only spaces or tabs **are valid blank lines**. |
| 34 | +- **Method separation:** Method declarations must be preceded by **exactly one blank line** after the closing brace of the previous member. |
| 35 | +- **Argument indentation:** |
| 36 | + - For multi-line method or constructor calls, the first line ends before the first argument. |
| 37 | + - Each wrapped argument line must be indented **one additional indentation level** (usually 4 spaces). |
| 38 | + - Do **not** use extra indentation levels. |
| 39 | + - The closing `)` must align with the start of the call. |
| 40 | +- **Continuation clarification (applies across all checks):** |
| 41 | + - A line is considered a **continuation of the same statement** and must **not** be flagged for a missing blank line when **both** are true: |
| 42 | + 1) The previous non-empty trimmed line **does not** end with `;` or `}`, **and** |
| 43 | + 2) The current line, after trimming leading whitespace, **starts with a continuation indicator**, such as: |
| 44 | + `.`, `??`, `?`, `:`, `+`, `-`, `*`, `/`, `%`, `&&`, `||`, `=>`, `,`, `)`, `]`, |
| 45 | + or any identifier/keyword when the previous line ends with an incomplete construct (e.g., open `(`, interpolated start `$"`, method/constructor call, LINQ chain). |
| 46 | + - Only when a **new statement** begins (i.e., the previous trimmed line **ends** with `;` or `}`) and the **next statement is multi-line** should an **exactly one** blank line be required before it. |
| 47 | + |
| 48 | +### Enforcement |
| 49 | + |
| 50 | +- Copilot should **not generate code** that exceeds the 120-character line limit. |
| 51 | +- When writing new C# code, Copilot should: |
| 52 | + - Break up long method/constructor calls across multiple lines. |
| 53 | + - Use string interpolation or verbatim strings with proper line breaks where needed. |
| 54 | + - Format long LINQ queries across multiple lines. |
| 55 | + - Wrap parameters and arguments for readability. |
| 56 | + - Insert a blank line before any `return` following other statements. |
| 57 | + - Prefer moving `new` or the method invocation to the next line when appropriate. |
| 58 | + |
| 59 | +### Review Guidelines (strict) |
| 60 | + |
| 61 | +- Copilot must: |
| 62 | + - Evaluate **each physical line independently**. |
| 63 | + - Flag a violation only when a **single physical line** exceeds 120 characters. |
| 64 | + - When flagging, include line number and measured character count. |
| 65 | + - Suggest multiline formatting only when the offending line exceeds 120. |
| 66 | + - **Not** flag whitespace-only lines; they are valid blank lines. |
| 67 | + - **Continuation Detection (unambiguous):** Do **not** require a blank line before a line that is a continuation of the same statement. |
| 68 | + Treat a line as a continuation when **both** of the following hold: |
| 69 | + 1) The previous non-empty trimmed line **does not** end with `;` or `}`, **and** |
| 70 | + 2) The current line (after trimming leading whitespace) **begins with** a continuation indicator: |
| 71 | + `.`, `??`, `?`, `:`, `+`, `-`, `*`, `/`, `%`, `&&`, `||`, `=>`, `,`, `)`, `]`, |
| 72 | + or any identifier/keyword when the previous line ends with an incomplete construct (e.g., open `(`, start of `$"`, method/constructor call, LINQ chain). |
| 73 | + - Flag missing blank lines before `return` **only** when `return` is the first token on the line **and** the previous non-empty trimmed line ended with `;` or `}`. |
| 74 | +- **Operator lines:** |
| 75 | + - Measure compliance per physical line. |
| 76 | + - Do not combine operator lines with continuations. |
| 77 | + - Operator-at-end style is preferred. |
| 78 | +- **Block-first statement exemption:** |
| 79 | + - Do not require a preceding blank line if the previous meaningful line ends with `{`. |
| 80 | + |
| 81 | +### Examples |
| 82 | + |
| 83 | +#### ✅ Correct (first statement inside a block; no blank line required) |
| 84 | + |
| 85 | +```csharp |
| 86 | +public void Foo() |
| 87 | +{ |
| 88 | + DoSomething( |
| 89 | + x, |
| 90 | + y); |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +#### ❌ Incorrect (blank line required between two statements) |
| 95 | + |
| 96 | +```csharp |
| 97 | +DoSomething(); |
| 98 | +DoSomethingElse( |
| 99 | + x, |
| 100 | + y); |
| 101 | +``` |
| 102 | + |
| 103 | +#### ✅ Correct (wrapped invocation; each line ≤ 120) |
| 104 | + |
| 105 | +```csharp |
| 106 | +Validate( |
| 107 | + createException: () => new InvalidDecisionPollException( |
| 108 | + message: "Invalid decisionPoll. Please correct the errors and try again."), |
| 109 | + (Rule: IsInvalid(decisionPoll.Id), Parameter: nameof(DecisionPoll.Id))); |
| 110 | +``` |
| 111 | + |
| 112 | +#### ❌ Incorrect (single line > 120) |
| 113 | + |
| 114 | +```csharp |
| 115 | +Validate(createException: () => new InvalidDecisionPollException(message: "Invalid decisionPoll. Please correct the errors and try again.")); |
| 116 | +``` |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +### Code Formatting Rule Examples |
| 121 | + |
| 122 | +#### ✅ Correct (return with blank line) |
| 123 | + |
| 124 | +```csharp |
| 125 | +var user = users.FirstOrDefault(u => u.Id == id); |
| 126 | + |
| 127 | +return user; |
| 128 | +``` |
| 129 | + |
| 130 | +#### ❌ Incorrect |
| 131 | + |
| 132 | +```csharp |
| 133 | +var user = users.FirstOrDefault(u => u.Id == id); |
| 134 | +return user; |
| 135 | +``` |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +### Argument Indentation Examples |
| 140 | + |
| 141 | +#### ✅ Correct |
| 142 | + |
| 143 | +```csharp |
| 144 | +DoSomething( |
| 145 | + firstArgument: "value1", |
| 146 | + secondArgument: "value2", |
| 147 | + thirdArgument: "value3"); |
| 148 | +``` |
| 149 | + |
| 150 | +#### ❌ Incorrect (extra indentation) |
| 151 | + |
| 152 | +```csharp |
| 153 | +DoSomething( |
| 154 | + firstArgument: "value1", |
| 155 | + secondArgument: "value2", |
| 156 | + thirdArgument: "value3"); |
| 157 | +``` |
| 158 | + |
| 159 | +#### ❌ Incorrect (misaligned closing parenthesis) |
| 160 | + |
| 161 | +```csharp |
| 162 | +DoSomething( |
| 163 | + firstArgument: "value1", |
| 164 | + secondArgument: "value2", |
| 165 | + thirdArgument: "value3" |
| 166 | + ); |
| 167 | +``` |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +### More Formatting Examples |
| 172 | + |
| 173 | +#### ✅ Correct |
| 174 | + |
| 175 | +```csharp |
| 176 | +var filteredUsers = users |
| 177 | + .Where(u => u.IsActive && u.LastLoginDate >= DateTime.UtcNow.AddDays(-30)) |
| 178 | + .OrderByDescending(u => u.LastLoginDate) |
| 179 | + .Select(u => new |
| 180 | + { |
| 181 | + u.Id, |
| 182 | + u.Name, |
| 183 | + u.Email, |
| 184 | + LastSeen = u.LastLoginDate.ToString("yyyy-MM-dd HH:mm:ss") |
| 185 | + }) |
| 186 | + .ToList(); |
| 187 | +``` |
| 188 | + |
| 189 | +#### ❌ Incorrect |
| 190 | + |
| 191 | +```csharp |
| 192 | +var filteredUsers = users.Where(u => u.IsActive && u.LastLoginDate >= DateTime.UtcNow.AddDays(-30)).OrderByDescending(u => u.LastLoginDate).Select(u => new { u.Id, u.Name, u.Email, LastSeen = u.LastLoginDate.ToString("yyyy-MM-dd HH:mm:ss") }).ToList(); |
| 193 | +``` |
| 194 | + |
| 195 | +--- |
| 196 | + |
| 197 | +### Rationale |
| 198 | + |
| 199 | +- **Per-line measurement** prevents false positives in wrapped calls. |
| 200 | +- **Tabs count as 4 characters** ensures consistent line-length calculation. |
| 201 | +- **Whitespace-only blank lines count as blank** and match VS behaviour. |
| 202 | +- **Return visibility** improves readability. |
| 203 | +- **Argument indentation** improves consistency. |
| 204 | +- **Block-first patterns** avoid unnecessary whitespace noise. |
| 205 | + |
| 206 | +--- |
| 207 | + |
| 208 | +## Supporting .editorconfig Settings |
| 209 | + |
| 210 | +```ini |
| 211 | +[*.{cs,vb,ts,tsx}] |
| 212 | +guidelines = 120 |
| 213 | +indent_style = space |
| 214 | +indent_size = 4 |
| 215 | +trim_trailing_whitespace = true |
| 216 | +end_of_line = crlf |
| 217 | + |
| 218 | +dotnet_sort_system_directives_first = true |
| 219 | +``` |
0 commit comments