-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.cursorrules
More file actions
768 lines (533 loc) · 26.6 KB
/
.cursorrules
File metadata and controls
768 lines (533 loc) · 26.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
# Cursor / Personal Instructions
**Author:** Guillaume DUPONT
**Organization:** Ten Square Software
**Context:** VST/AU plugin development with JUCE & Cursor
**Revision date:** 2026-03-14
---
## Purpose of this document
This file defines my personal instructions for the Cursor AI agent. It guides the agent in code generation, documentation, advice, and responses by establishing a set of rules, standards, and conventions to follow throughout our development sessions.
---
## 1. Communication
### 1.1 Interactions with me
- My name is Guillaume, you can call me by my first name
- Always use informal tone ("tu") in all our exchanges
- Reply to me exclusively in French in our conversations
- Be frank, honest, and factual, even if it means contradicting me
- Absolute priority: help me find the most relevant solution!
### 1.2 Structure of responses
- Clear and didactic structure
- Goal: help me progress
- Ensure longevity and maintainability of generated code
- Optimal solutions compliant with professional standards
- **DO NOT** present code blocks in the chat when modifying/generating
- Prefer summaries and conclusions in natural language only
- The code is already visible in Cursor's editor, no need to repeat it in the chat
---
## 2. Process & Workflow
> ⚠️ **MANDATORY BEFORE ANY CODING**
### 2.1 Three-phase workflow
**STOP!** Always follow this workflow rigorously:
#### Phase 1: DESIGN (mandatory before touching code)
- Identify responsibilities (Single Responsibility Principle)
- Detect potential duplication (Don't Repeat Yourself)
- Choose appropriate abstractions (interfaces, base classes)
- Plan classes/functions with explicit names
- Estimate method sizes (target: < 15 lines)
- Ask myself: "Would Uncle Bob be proud of this design?"
- Mentally sketch the architecture (dependencies, hierarchy)
#### Phase 2: IMPLEMENTATION
- Write code strictly following the Phase 1 design
- Respect quantifiable limits (see dedicated section below)
- If a method exceeds 15 lines → **STOP** and extract helpers IMMEDIATELY
- If I detect duplication (even 3 lines) → **STOP** and factorize IMMEDIATELY
- If a class exceeds 200 lines → **STOP** and re-evaluate the design (SRP likely violated)
- Name each function to reveal its intent (no vague `doStuff()` or `process()`)
#### Phase 3: AUTO-REVIEW (before presenting code to Guillaume)
- Reread with a critical "Uncle Bob" eye
- Verify SOLID for each class (full checklist below)
- Verify that each function does ONE thing only
- Verify there is NO duplication
- Verify names: are they all explicit and reveal intent?
- If review fails → return to Phase 1 (do not present non-Clean code)
### 2.2 Exception: Rapid prototyping
If Guillaume explicitly requests a "rapid prototype", "POC", or "spike", I may skip Phase 1 but I MUST propose a Clean refactoring after validation.
### 2.3 Document metadata
When I modify a file (documentation, code, configuration, etc.) that contains a **revision date** or **version number** in its header or metadata, I MUST update them accordingly.
---
## 3. Quantifiable Limits
> 🚫 **Strict, non-negotiable rules**
### 3.1 Code metrics - MAXIMUM limits
These limits are **HARD LIMITS**, not suggestions:
#### Function/Method: 15 lines MAXIMUM (ideal: 5-10)
- **Exception:** `show()` or orchestration method may go up to 20 lines IF it delegates properly
- **Beyond that:** extract immediately into helpers with explicit names
#### Class: 200 lines MAXIMUM (ideal: < 150)
- **If exceeded:** probable SRP violation → split into multiple classes
#### Function parameters: 3 MAXIMUM
- **If > 3:** create a struct/class of parameters with explicit names
#### Cyclomatic complexity: < 5 per function (ideal: 1-3)
- **Too many `if`/`switch`** → extract into separate functions or use polymorphism
#### Indentation levels: 2 MAXIMUM
- No `if` nested in `for` nested in `if`
- **Solution:** early returns, function extraction, guard clauses
#### Code duplication: zero tolerance
- Even 3 similar lines = mandatory extraction into common function
- Use helpers, templates, or utility functions
### 3.2 Procedure when I exceed a limit
1. **DO NOT** continue writing code
2. **STOP** immediately
3. Ask me: "Why is this limit exceeded?"
4. Answer honestly: "Because this function/class does several things"
5. Apply SRP: extract responsibilities into new functions/classes
6. Start over with the correct design
### 3.3 Warning signs - Indicators of bad design
- I tell myself "this function is a bit long but it's fine" → ❌ **NO**, refactor
- I see similar code elsewhere → ❌ **NO**, factorize immediately
- I can't find a good name for the function → Sign it does too many things
- I need a comment to explain → Code is not clear enough, rename/refactor
- I add a comment like "// Part 1", "// Part 2" → Each part = separate function
---
## 4. Self-Critique
> ✅ **Mandatory checklist before presenting code**
Before presenting code to Guillaume, I MUST systematically verify:
### 4.1 SOLID Check ✓
- [ ] **S**ingle Responsibility: Does each class have ONE well-defined responsibility?
- Test: can I describe the class in one simple sentence without "and"?
- [ ] **O**pen/Closed: Is the code extensible without modifying existing classes?
- Use of abstractions, interfaces, polymorphism?
- [ ] **L**iskov Substitution: Are subclasses substitutable without breaking behavior?
- Do derived classes respect base class contracts?
- [ ] **I**nterface Segregation: No "fat" interfaces forcing implementation of useless methods?
- Small, focused interfaces?
- [ ] **D**ependency Inversion: Dependencies toward abstractions, not concrete implementations?
- Do high-level modules not depend on low-level details?
### 4.2 Clean Code Check ✓
- [ ] All functions < 15 lines? (ideally < 10)
- If NO: extract immediately
- [ ] All names are explicit and reveal intent?
- `calculateTotalPrice()` not `calc()`, `isUserLoggedIn()` not `check()`
- [ ] No code duplication (not even 3 similar lines)?
- If NO: factorize into common function
- [ ] Single level of abstraction per function?
- No mix of high-level calls + low-level manipulations
- [ ] No explanatory comments needed?
- Code must be self-documenting via names
- [ ] No magic numbers?
- All constants are named: `kMaxRetries` not `3`
- [ ] No dead or commented code?
- Remove, versioning (git) keeps history
### 4.3 Architecture & Design Check ✓
- [ ] Dependencies point in the right direction?
- GUI → Core, never Core → GUI
- High-level modules → abstractions ← low-level modules
- [ ] No tight coupling between modules?
- Use of interfaces to decouple
- [ ] Responsibilities clearly separated?
- Rendering, business logic, state, coordination: each in its own class
- [ ] Easily testable?
- No hidden dependencies, dependency injection
### 4.4 Readability Check ✓
- [ ] Can a developer discovering the code understand it without explanation?
- [ ] Do classes and functions have names that document their role?
- [ ] Is the structure logical and predictable?
- [ ] Are there no "surprises" or hidden side effects?
### 4.5 Golden rule
> ❌ **If ONE ✗ → I do NOT present the code**
>
> - I refactor first until ALL ✓ are validated
> - Only then do I present the result to Guillaume
---
## 5. Development Environment
### 5.1 System & Tools
- **Platform:** MacBook Pro M5 with macOS Tahoe
- **IDE:** Cursor (with monthly Agent AI subscription)
- **Compiler:** Xcode 26
- **Build system:** CMake
- **Build directory:** `Builds/` (subfolders `macOS/`, `Windows/`, `Linux/`) — do not use `build/` at root
- **Audio framework:** JUCE 8.0.12
### 5.2 JUCE reference
- **JUCE installation:** `/Applications/JUCE`
- **Official documentation:** https://docs.juce.com
- **Modules:** `/Applications/JUCE/modules/`
- **CMake:** `/Applications/JUCE/CMake/`
### 5.3 Verification of generated code
- Always refer to the most up-to-date JUCE documentation
- Verify that each API, class, and method exists in JUCE 8
- Avoid any obsolete syntax (JUCE 7, JUCE 6, etc.)
- Explicitly report if unsure about an API
---
## 6. C++ Standards & Quality
### 6.1 C++ generalities
- **Standard:** C++17 minimum, compatible with Xcode 26
- **Source code language:** English only
- Variable, function, class names
- All code comments
- No accents, no French in code
### 6.2 Git
#### Commits
- Git Commits: Summary/Description in English only
- **Create commits only on explicit request**
- Never commit proactively or automatically, even after successful refactoring
- Always detail the commit with a main summary line (title) followed by a bullet list of the most important points (same format as annotated tags and releases)
**Format:**
```
Short summary line (imperative mood)
- Bullet point describing the first significant change
- Bullet point describing the second significant change
- etc.
```
- Focus on architectural and significant changes, not minor implementation details
#### GitHub Issues
- GitHub Issues: title and description in English only
- The repository must remain ready for collaboration with other developers (unless special mention otherwise)
#### Annotated tags
When creating an annotated Git tag, use the following format:
- A main description sentence (tag title)
- A short bullet list of the most important points that were reworked in the code
**Example:**
```
Refactor Slider class and improve focus management
- Refactor Slider: simplify drawing code by removing unnecessary constants and helper methods
- Use reduced() for bounds calculations directly in paint() method
- Improve focus management: add ability to remove focus by clicking on background
```
- Do not include implementation details or temporary test widgets
- Focus on architectural improvements and significant code changes
### 6.3 Naming conventions
#### Variables & Methods: lowerCamelCase
- Examples: `audioBuffer`, `processMidiEvents()`, `getSampleRate()`
#### Public variables and constants
- No prefix or suffix
- Examples: `pluginFactory`, `kMaxBufferSize`
#### Private variables and constants
- Underscore suffix (Google Style)
- Examples: `apvts_`, `kWidth_`
#### Classes: PascalCase
- Examples: `PluginProcessor`, `PluginEditor`, `AudioAnalyzer`
#### Enums
- PascalCase for type, `k` prefix for values
- Examples: `enum class ParameterType { kDco, kEnv, kLfo };`
#### Namespaces: PascalCase
- Examples: `namespace Core { }`, `namespace GUI { }`
#### General rule
- Avoid `underscore_case`, except in very rare cases
### 6.4 Magic Numbers - FORBIDDEN
- **NEVER** use magic numbers in code!
- Always define explicit constants with meaningful names
#### Examples to avoid
- ❌ `if (value == -1)` → ✅ `if (value == kNoSysExId)`
- ❌ `for (int i = 0; i < 10; ++i)` → ✅ `for (int i = 0; i < kModulationBusCount; ++i)`
- ✅ `return nullptr;` → OK (nullptr is explicit, not a magic number)
#### Acceptable exceptions
- Obvious self-explanatory literal values: `0`, `1`, `nullptr`, `true`, `false`
- Values in unit tests where the exact value is the subject of the test
### 6.5 Include organization
1. C++ system headers (e.g. `<vector>`, `<memory>`, etc. then blank line)
2. JUCE headers (e.g. `<juce_core/juce_core.h>`, etc. then blank line)
3. Project headers (e.g. `"Core/PatchModel.h"`, etc.)
### 6.6 Include paths
- Use relative paths from project root (configured in CMake)
- **NEVER** use relative paths with multiple `../`
- **Format:** `"GUI/Panels/MainComponent.h"` rather than `"../../../../../GUI/Panels/MainComponent.h"`
- Paths must be clear, readable, and maintainable
- ✅ Correct example: `#include "GUI/Widgets/Slider.h"`
- ❌ Example to avoid: `#include "../../../GUI/Widgets/Slider.h"`
### 6.7 Include Guards
- Always use `#pragma once` as first line of each header
- Do not use old include guards (`#ifndef`/`#define`/`#endif`)
- Example: `#pragma once` as first line, before any other code
### 6.8 Forward Declarations
- Use forward declarations in headers to reduce dependencies
- Include full headers only in `.cpp` when possible
- Example: `class MidiManager;` in header, then `#include "MidiManager.h"` in `.cpp`
### 6.9 .h/.cpp separation
Always separate declaration (`.h`) and definition (`.cpp`) **except for**:
- Templates (definitions in header)
- Explicit inline functions (marked `inline`)
- Small trivial functions (simple getters/setters)
Prefer inline methods in header only if they are short (< 5 lines)
- ✅ Inline example: `int getValue() const { return value_; }` in header
- ✅ Separated example: declaration in `.h`, full definition in `.cpp`
### 6.10 Class organization
- Group members and methods logically in `.h` (e.g. getter/setter pairs)
- Member variables (almost always private) after public/protected methods
- Private methods at the end, after member variables
- Use `JUCE_DECLARE_NON_COPYABLE` if class is not copyable (last element)
- Mark `explicit` for single-parameter constructors
- In `.cpp`, group methods logically (called near their callers)
- Virtual destructors if class is meant to be inherited (polymorphism)
- **Rule of 3/5/0:** if defining destructor/copy/move, define or delete the others explicitly
- Use `= default` for generated default constructors/destructors (more explicit)
- Use `= delete` to explicitly disable copy/move/default constructor
- Examples: `MyClass() = default;` or `MyClass(const MyClass&) = delete;`
### 6.11 SOLID & Clean Code principles
> Reference: Robert C. Martin / Uncle Bob - Clean Code & Clean Architecture
- Readable and human-understandable code
- Names ALWAYS explicit (no cryptic abbreviations)
- Minimize comments: use only when necessary
- Refactor blocks > 15 lines into short functions with explicit names rather than adding comments (which can become obsolete)
- Single level of abstraction per function
- Single responsibility (Single Responsibility Principle)
- No hidden effects, no modified global state
- DRY (Don't Repeat Yourself)
- Respect Law of Demeter
- Prefer delegation over inheritance
- Exceptions > null pointers
- `std::optional` > null
- Handle errors at the appropriate level
### 6.12 Error handling
- Use exceptions for exceptional errors (not for normal flow)
- Use `std::optional` for optional values (avoid `nullptr`)
- Use `std::expected` (C++23) or Result types for operations that can fail
- Never silently ignore errors
- Log errors with appropriate context
- Example: `std::optional<PatchModel> loadPatch(const juce::File& file);`
### 6.13 RAII (Resource Acquisition Is Initialization)
- Always acquire resources in constructors
- Always release resources in destructors
- Use smart pointers for automatic memory management
- Objects must always be in valid state
- Example: `std::unique_ptr<juce::MidiInput> midiInput_;` in class, automatically released
### 6.14 Smart Pointers
- Prefer `unique_ptr` for exclusive ownership
- Use `shared_ptr` only when necessary (shared ownership)
- Use `weak_ptr` to break reference cycles
- Avoid raw pointers except for observing (non-owning)
- Example: `std::unique_ptr<juce::MidiInput> midiInput_;` for ownership, `MidiInput* input` for observer
### 6.15 Move Semantics
- Use `std::move()` to transfer ownership of heavy objects
- Prefer move constructors and move assignment operators
- Mark `noexcept` when appropriate (optimizations, move constraints)
- Example: `void setData(juce::MemoryBlock&& data) { data_ = std::move(data); }`
---
## 7. Formatting & JUCE Style
### 7.1 Formatting and spacing
#### Indentation
- **No tab characters!** Use 4 spaces for indentation
#### Allman style for braces
Opening brace on new line:
```cpp
if (x == 0) // Yes!
{
foobar();
return "zero";
}
```
#### Operators
- Always put space before and after binary operators: `x = 1 + y - 2 * z / 3;`
- Operator `!` must NOT have space after: `if (!foo)` (JUCE standard style)
- Operator `~` must be preceded by space, but not followed
- Operators `++` and `--` have no space between operator and operand: `++i`, `--j`
#### General spacing
- Never space before comma, always space after: `foo(x, y);`
- Always space before opening parenthesis containing text: `foo(123);`
- Never space before empty parenthesis pair: `foo();`
- No space before opening bracket used as array index: `foo[1]`
#### Blank lines
- Blank line before `if`, `for`, `while`, `do` when preceded by another statement
- Blank line after closing brace `}` (except if next line is just another closing brace)
#### `if` statements
Do not write `if` on a single line... **except** for series of similar `if` aligned vertically to show a pattern:
```cpp
if (x == 1) return "one";
if (x == 2) return "two";
if (x == 3) return "three";
```
- Omit braces for trivially simple one-line `if` statements
- In `if-else` with multiple branches, all branches must be formatted the same way (all with or all without braces)
#### NEVER put `else` after `return`
```cpp
// ✅ Good
if (foobar())
return doSomething();
doSomethingElse();
// ❌ Bad
if (foobar())
return doSomething();
else
doSomethingElse();
```
### 7.2 Pointer and reference declarations
- Always put space after type, never before: `SomeObject* myObject`, `SomeObject& myObject`
- Never declare multiple pointers/references of same type in a single declaration
- Prefer smart pointers or typedef to avoid multiple asterisks
### 7.3 const modifier
- Put `const` before type name: `const Thing& t;` (not `Thing const& t;`)
- Mark methods that do not modify state as `const`
- Use `const&` for read-only parameters
- Prefer `const_iterator` for read-only iterations
- Const pointers: `const Type*` (pointer to constant) vs `Type* const` (constant pointer)
### 7.4 Multi-line operators and expressions
When splitting an expression containing operators across lines, each new line must start with the operator:
```cpp
auto xyz = foo + bar
+ func(123)
- def + 4321;
```
Same principle for dot operator (chained methods):
```cpp
auto t = AffineTransform::translation(x, y)
.scaled(2.0f)
.rotated(0.5f);
```
### 7.5 Lambdas
Preferred style:
```cpp
auto myLambda = [] { return 123; };
auto myLambda = [this, &x](int z) -> float { return x + z; };
auto longerLambda = [](int x, int y) -> int
{
// ...multiple lines of stuff...
};
```
- Prefer explicit captures over `[=]` or `[&]` (clearer, avoids accidental captures)
- Avoid capturing large objects by value (prefer `const&` or `&` as appropriate)
- Example: `[this, &x, &y]` rather than `[&]` if only x and y are needed
### 7.6 Null values and pointers
- **NEVER** use `NULL`, `null`, or `0` for null pointer! Always use `nullptr`
- Limit scope of possibly null pointers:
```cpp
if (auto* f = getFoo())
f->doSomething();
// f is out of scope here, impossible to use null pointer by mistake
```
### 7.7 Parameter passing
- For small POD objects, always pass by value, not by reference
- Use `const Foo&` only for complex objects (Array, String, etc.)
- JUCE classes to always pass by value: `Point`, `Time`, `RelativeTime`, `Colour`, `Identifier`, `ModifierKeys`, `JustificationType`, `Range`, `PixelRGB`, `PixelARGB`, `Rectangle`
### 7.8 Increment operators
- Always prefer pre-increment over post-increment: `for (int i = 0; i < 10; ++i)`
### 7.9 Standard library
- Always prefer std versions of functions over old C equivalents
- Use `std::abs`, `std::sqrt`, `std::sin`, `std::cos`, `std::pow` rather than `fabs`, `sqrtf`, `powf`, etc.
### 7.10 Types
- Never use `unsigned` alone - always write `unsigned int`
- Use JUCE types: `int8`, `uint8`, `int16`, `uint16`, `int32`, `uint32`, `int64`, `uint64`
- Prefer range-based for loops over raw for loops to iterate over containers
### 7.11 Auto
"Almost-always-auto" style but avoid in some cases:
- ❌ `auto x = 0;` → not OK (not obvious it's a signed int)
- ✅ `for (int i = 0; i < someNumber; ++i)` → OK (clear it's a signed int)
- ✅ `bool someCondition = false;` → OK (clearer than auto)
- ✅ `auto someResult = thisReturnsABool();` → OK (use auto if RHS is an expression)
### 7.12 Comments
- For short one- or two-line comments, prefer `//` over `/* */`
- Always leave space before text in `//` comment: `// yes!` not `//no!`
- Code names must be self-documenting (avoid comments)
- Use comments only to explain "why", not "how"
- For complex public APIs, consider Doxygen/Javadoc style comments
### 7.13 Macros
- **Do not use macros!** Treat macros as last resort
- Never use a macro just to hold a constant value or to perform a function that could be done as a real inline function
- If macros are used, give them names that won't conflict with other code
- `#undef` macros after use when possible
### 7.14 Templates
- Template parameters must follow their type without space: `vector<int>`
- In template declaration, leave space before opening bracket: `template <typename Type1, typename Type2>`
### 7.15 Dependencies
- Avoid circular dependencies between modules
- Use abstract interfaces to decouple (Dependency Inversion Principle)
- Respect dependency hierarchy (GUI depends on Core, not the reverse)
### 7.16 Enums
- Prefer `enum class` over `enum` for type safety and to avoid collisions
- Name enums in singular (except collections): `enum class ParameterType { kDco, kEnv, kLfo };`
- Prefix values with `k`: `ParameterType::kDco`
- Example: `enum class ClipboardType { kEmpty, kDco, kEnv, kLfo, kPatch };`
### 7.17 Initialization
- Prefer uniform initialization `{}`: `int value {0};` rather than `int value = 0;`
- Initialize members in constructor initializer list
- Initialization order = member declaration order (not list order)
- Example: `MyClass(int x) : member1_ {x}, member2_ {0} {}`
### 7.18 Namespaces
- Use namespaces to organize code by module
- Avoid `using namespace` in headers (global pollution)
- OK in `.cpp` to simplify: `using juce::String;`
- Example: `namespace Core { class MidiManager; }`
### 7.19 Static Members
- Prefer static free functions over static methods when possible
- Use `static constexpr` for class constants
- Example: `static constexpr int kMaxBufferSize {1024};`
### 7.20 Constexpr
- Use `constexpr` for functions computable at compile time
- Use `constexpr` for constant variables computable at compile time
- Prefer `constexpr` over `const` for compile-time computable constants
- `constexpr` implies `const`, but allows compile-time evaluation
- Examples:
```cpp
constexpr int calculateSize(int count) { return count * 4; }
constexpr int kBufferSize {calculateSize(256)};
```
### 7.21 Virtual Functions
- Use `override` explicitly (not `virtual` in derived classes)
- Use `final` to prevent further derivation if appropriate
- Prefer composition over inheritance (SOLID: favor delegation)
- Example: `void paint(Graphics& g) override;`
### 7.22 Assertions
- Use `jassert` (JUCE) or `assert` for development invariants
- Do not use assertions for user error handling (use exceptions)
- Assertions can be disabled in release: do not put critical logic in them
- Example: `jassert(bufferSize > 0);` to verify a precondition
### 7.23 Logging
- Use `juce::Logger` for structured logging
- Appropriate levels: `DBG()`, `Logger::writeToLog()` for debug, critical errors
- Avoid excessive logging in critical loops
- Log with context: include relevant information (file, line, values)
---
## 8. JUCE-Specific Rules
### 8.1 Plugin class naming
- **Processor class:** `PluginProcessor` (files: `PluginProcessor.h`/`.cpp`)
- **UI editor class:** `PluginEditor` (files: `PluginEditor.h`/`.cpp`)
### 8.2 Deprecated JUCE APIs - AVOID
- ~~`getCurrentPlaybackSampleRate()`~~ → `getSampleRate()`
- ~~`setPlayConfigDetails()`~~ → `setBusesLayout()`
- ~~`Timer` with callbacks~~ → `juce::HighResolutionTimer`
- ~~`AudioProcessorValueTreeState`~~ → modern `ValueTree`
- ~~Legacy parameter handling~~ → `juce::AudioProcessorParameter`
> List is non-exhaustive, take time to complete as needed.
### 8.3 Thread-Safety
- Always consider thread-safety in audio code (`processBlock()` called from audio thread)
- Use `juce::MessageManager::callAsync()` to call GUI thread from other threads
- Use `juce::AbstractFifo` or `juce::LockFreeFifo` for inter-thread communication (lock-free)
- APVTS listeners are thread-safe, but GUI updates must be done from message thread
- **NEVER** call GUI methods from audio thread
- Example: `juce::MessageManager::callAsync([this] { updateGUI(); });` from audio thread
### 8.4 Performance
- Avoid memory allocations in `processBlock()` (audio thread)
- Use pre-allocated buffers
- Avoid system calls in critical loops
- Profile before optimizing
- Prefer in-place algorithms when possible
- Avoid unnecessary copies: use `const&` or move semantics
### 8.5 Unit Tests
- Write tests for business classes (not for GUI components)
- Use a test framework (e.g. Catch2, Google Test)
- One test = one responsibility, explicit name
- Isolated tests, no dependencies between tests
- Example: `TEST_CASE("SysExParser validates checksum correctly")`
### 8.6 Complexity
- Maintain cyclomatic complexity < 5 per function (ideal: 1-3)
- If too complex, refactor into sub-functions
- Avoid > 3 parameters: consider a struct/class of parameters
- Prefer early returns to reduce nesting
---
## 9. Markdown Documentation (plans, refactorings)
### 9.1 Location of planning Markdown files
When I create Markdown files for refactoring plans, optimizations, or other planned operations, I store them **directly** in:
`Documentation/Development/Plans/`
The **Plans** folder must be **compartmentalized into subfolders by year**: `2025`, `2026`, etc. Inside each year, create **subfolders by month**: `01`, `02`, `03`, …, `12`. Each plan file is placed in the year then month subfolder corresponding to the plan date (e.g. a plan dated 2026-02-23 → `Documentation/Development/Plans/2026/02/`).
**⚠️ Important procedure:**
- If Cursor automatically generates a plan in `~/.cursor/plans/*.plan.md`, I must **immediately** copy it to `Documentation/Development/Plans/<year>/<month>/` (e.g. `Plans/2026/03/`) with the naming convention below
- This copy must be included in the plan TODOs and marked as done as soon as it's done
- **As soon as all plan TODOs are marked `completed`**, I must **copy** the final `.plan.md` file from `~/.cursor/plans/` to `Documentation/Development/Plans/<year>/<month>/` with the correct name (overwriting the initial copy if necessary)
- Keep original files in `~/.cursor/plans/` so as not to disturb Cursor
- Always have a final archived version in the project with all TODOs up to date
### 9.2 Naming convention
- **Date** at the beginning: `YYYY-MM-DD-`
- **Names in English** only
- **One capital letter at the start of each word**
- **Separator:** hyphen `-`
- **No stop words** (de, la, l', the, of, etc.)
- **Explicit names** reflecting the document content
**Example:** `Documentation/Development/Plans/2026/02/2026-02-03-Theme-To-Skin-Migration-Phase-Six-Summary.md`
---
*End of my personal instructions.*