agent-docs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.cursor/plans/OPTIMISE.md +379 -0
  2. package/.cursor/plans/VERSIONING.md +207 -0
  3. package/.cursor/rules/IMPORTANT.mdc +97 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +13 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  6. package/.github/dependabot.yml +38 -0
  7. package/.github/pull_request_template.md +10 -0
  8. package/.github/workflows/format.yml +35 -0
  9. package/CODE_OF_CONDUCT.md +64 -0
  10. package/CONTRIBUTING.md +52 -0
  11. package/LICENSE.md +20 -0
  12. package/PLAN.md +707 -0
  13. package/README.md +133 -0
  14. package/SECURITY.md +21 -0
  15. package/docs/APEXANNOTATIONS.md +472 -0
  16. package/docs/APEXDOC.md +198 -0
  17. package/docs/CML.md +877 -0
  18. package/docs/CODEANALYZER.md +435 -0
  19. package/docs/CONTEXTDEFINITIONS.md +617 -0
  20. package/docs/ESLINT.md +827 -0
  21. package/docs/ESLINTJSDOC.md +520 -0
  22. package/docs/FIELDSERVICE.md +4452 -0
  23. package/docs/GRAPHBINARY.md +208 -0
  24. package/docs/GRAPHENGINE.md +616 -0
  25. package/docs/GRAPHML.md +337 -0
  26. package/docs/GRAPHSON.md +302 -0
  27. package/docs/GREMLIN.md +490 -0
  28. package/docs/GRYO.md +232 -0
  29. package/docs/HUSKY.md +106 -0
  30. package/docs/JEST.md +387 -0
  31. package/docs/JORJE.md +537 -0
  32. package/docs/JSDOC.md +621 -0
  33. package/docs/PMD.md +910 -0
  34. package/docs/PNPM.md +409 -0
  35. package/docs/PRETTIER.md +716 -0
  36. package/docs/PRETTIERAPEX.md +874 -0
  37. package/docs/REVENUETRANSACTIONMANAGEMENT.md +887 -0
  38. package/docs/TINKERPOP.md +252 -0
  39. package/docs/VITEST.md +706 -0
  40. package/docs/VSCODE.md +231 -0
  41. package/docs/XPATH31.md +213 -0
  42. package/package.json +32 -0
  43. package/postinstall.mjs +51 -0
  44. package/prettier.config.js +18 -0
@@ -0,0 +1,874 @@
1
+ # Prettier Plugin Apex - AI Agent Reference
2
+
3
+ > **Version**: 1.0.0
4
+
5
+ > **Quick Info**: Prettier plugin for Apex using Jorje (Salesforce's parser).
6
+ > Correctness over configurability. **Source**:
7
+ > https://github.com/dangmai/prettier-plugin-apex
8
+
9
+ ---
10
+
11
+ ## Monorepo Structure
12
+
13
+ | Package | Purpose |
14
+ | -------------------------------------- | --------------------------- |
15
+ | `prettier-plugin-apex` | Main plugin |
16
+ | `apex-ast-serializer` | Java/Gradle Jorje wrapper |
17
+ | `playground` | Web testing interface |
18
+ | `@prettier-apex/apex-ast-serializer-*` | Platform-native executables |
19
+
20
+ ## Core Files
21
+
22
+ | File | Role |
23
+ | ---------------- | ----------------------------------- |
24
+ | `parser.ts` | Source → Jorje AST → enriched AST |
25
+ | `printer.ts` | AST → Prettier Doc → formatted code |
26
+ | `comments.ts` | Comment attachment/handling |
27
+ | `util.ts` | Helpers, precedence, AST massage |
28
+ | `constants.ts` | Node types, operators |
29
+ | `pragma.ts` | `@format`/`@prettier` detection |
30
+ | `http-server.ts` | Standalone parser server |
31
+
32
+ ---
33
+
34
+ ## Parser
35
+
36
+ ### Exported Parsers
37
+
38
+ ```typescript
39
+ export const parsers = {
40
+ apex: {
41
+ astFormat: 'apex',
42
+ parse,
43
+ locStart,
44
+ locEnd,
45
+ hasPragma,
46
+ preprocess: (text) => text.trim(),
47
+ },
48
+ 'apex-anonymous': {
49
+ astFormat: 'apex',
50
+ parse,
51
+ locStart,
52
+ locEnd,
53
+ hasPragma,
54
+ preprocess: (text) => text.trim(),
55
+ },
56
+ };
57
+ ```
58
+
59
+ ### Parser Methods
60
+
61
+ | Method | Type | Description |
62
+ | ------------ | --------------------------------- | ---------------------------------------------------- |
63
+ | `parse` | `(text, options) => Promise<AST>` | **Required**. Source → enriched AST |
64
+ | `locStart` | `(node) => number` | **Required**. Node start index |
65
+ | `locEnd` | `(node) => number` | **Required**. Node end index |
66
+ | `hasPragma` | `(text) => boolean` | Optional. Detects `@format`/`@prettier` |
67
+ | `preprocess` | `(text, options) => string` | Optional. Transform before parse (default: `trim()`) |
68
+
69
+ ### Parsing Modes
70
+
71
+ | Mode | Option Value | Speed | Notes |
72
+ | ----------- | ------------ | ------- | ---------------------------------- |
73
+ | Java CLI | `'none'` | Slowest | Most compatible, requires JRE ≥11 |
74
+ | Native | `'native'` | Fastest | Default. Falls back to Java |
75
+ | HTTP Server | `'built-in'` | Medium | Reuses JVM, needs separate process |
76
+
77
+ ### Parse Flow
78
+
79
+ 1. `preprocess` → `text.trim()`
80
+ 2. External parse → Jorje (via Java/native/HTTP)
81
+ 3. JSON deserialize → AST object
82
+ 4. Error check → throw if `parseErrors.length > 0`
83
+ 5. Comment extract → from `hiddenTokenMap`
84
+ 6. Enrich locations → `nodeLocationVisitor`
85
+ 7. Resolve line indices → `lineIndexVisitor`
86
+ 8. Add metadata → `metadataVisitor`
87
+
88
+ ### Location Functions
89
+
90
+ ```typescript
91
+ // Access location
92
+ const loc = node.loc ?? node.location;
93
+ const start = loc.startIndex;
94
+ const end = loc.endIndex;
95
+
96
+ // Plugin exports
97
+ locStart: (node) => node.loc?.startIndex ?? node.location?.startIndex;
98
+ locEnd: (node) => node.loc?.endIndex ?? node.location?.endIndex;
99
+ ```
100
+
101
+ ### External Parse Functions
102
+
103
+ ```typescript
104
+ // Spawn (none/native mode)
105
+ parseTextWithSpawn(executable: string, text: string, anonymous: boolean): Promise<{stdout, stderr}>
106
+ // Args: anonymous ? ['-a'] : []
107
+
108
+ // HTTP (built-in mode)
109
+ parseTextWithHttp(text: string, host: string, port: number, protocol: string, anonymous: boolean): Promise<string>
110
+ // POST to ${protocol}://${host}:${port}/api/ast
111
+ // Body: { sourceCode: text, anonymous, prettyPrint: false }
112
+ ```
113
+
114
+ ### AST Enrichment Visitors
115
+
116
+ Applied via `dfsPostOrderApply(ast, [visitor1, visitor2, ...])` - post-order
117
+ DFS.
118
+
119
+ ```typescript
120
+ type DfsVisitor<R, C> = {
121
+ accumulator?: (entry, accumulated) => R;
122
+ apply: (node, accumulatedResult, context, childrenContext) => R;
123
+ gatherChildrenContext?: (node, currentContext) => C;
124
+ };
125
+ ```
126
+
127
+ | Visitor | Purpose |
128
+ | ------------------------------------------- | --------------------------------------------------------- |
129
+ | `nodeLocationVisitor(sourceCode, comments)` | Fix/validate node locations |
130
+ | `lineIndexVisitor(lineIndexes)` | Add `startLine`, `endLine`, `column` |
131
+ | `metadataVisitor(emptyLineLocations)` | Add `trailingEmptyLine`, `forcedHardline`, `ifBlockIndex` |
132
+
133
+ ### Metadata Flags
134
+
135
+ | Flag | Purpose |
136
+ | ------------------- | ------------------------------ |
137
+ | `trailingEmptyLine` | Preserve empty line after node |
138
+ | `forcedHardline` | Force line breaks (SOQL/SOSL) |
139
+ | `ifBlockIndex` | Distinguish `if` vs `else if` |
140
+ | `insideParenthesis` | Flag for parameter nodes |
141
+
142
+ ### Location Handlers
143
+
144
+ | Handler | Purpose |
145
+ | --------------------------------------- | --------------------------------- |
146
+ | `handleNodeSurroundedByCharacters` | Find actual `(` and `)` positions |
147
+ | `handleNodeEndedWithCharacter` | Extend to closing `}`, `)`, `;` |
148
+ | `handleWhereCompoundExpressionLocation` | SOQL WHERE clause |
149
+ | `handleLimitValueLocation` | SOQL LIMIT value |
150
+
151
+ ---
152
+
153
+ ## Printer
154
+
155
+ ### Exported Printer
156
+
157
+ ```typescript
158
+ export const printers = {
159
+ apex: {
160
+ print,
161
+ massageAstNode,
162
+ hasPrettierIgnore,
163
+ insertPragma,
164
+ isBlockComment,
165
+ canAttachComment,
166
+ printComment,
167
+ willPrintOwnComments,
168
+ handleComments: { ownLine, endOfLine, remaining },
169
+ },
170
+ };
171
+ ```
172
+
173
+ ### Printer Methods
174
+
175
+ | Method | Type | Description |
176
+ | ---------------------- | ---------------------------------------- | ---------------------------- |
177
+ | `print` | `(path, options, print, args?) => Doc` | **Required**. AST → Doc |
178
+ | `massageAstNode` | `(original, cloned, parent) => any` | Normalize for debug-check |
179
+ | `hasPrettierIgnore` | `(path) => boolean` | Check `prettier-ignore` |
180
+ | `insertPragma` | `(text) => string` | Insert `@format` pragma |
181
+ | `isBlockComment` | `(comment) => boolean` | Is `/* */` comment |
182
+ | `canAttachComment` | `(node, ancestors) => boolean` | Can attach comment to node |
183
+ | `printComment` | `(commentPath, options) => Doc` | Print comment node |
184
+ | `willPrintOwnComments` | `(path) => boolean` | Node prints own comments |
185
+ | `printPrettierIgnored` | `(path, options, print, args?) => Doc` | Format ignored regions |
186
+ | `embed` | `(path, options) => Fn\|Doc\|null` | Embedded language formatting |
187
+ | `preprocess` | `(ast, options) => ast` | Transform before printing |
188
+ | `getCommentChildNodes` | `(node, options) => nodes[]` | Comment attachment traversal |
189
+ | `getVisitorKeys` | `(node, nonTraversableKeys) => string[]` | AST traversal keys |
190
+
191
+ ### Print Dispatch
192
+
193
+ 1. Get `node['@class']`
194
+ 2. Lookup in `nodeHandler` table
195
+ 3. If not found → `getParentType()` for parent lookup
196
+ 4. Call handler → `(path, print, options) => Doc`
197
+ 5. Apply `handleTrailingEmptyLines()`
198
+
199
+ ### Handler Types
200
+
201
+ ```typescript
202
+ type SingleNodeHandler = (
203
+ path: AstPath,
204
+ print: PrintFn,
205
+ options: ParserOptions,
206
+ ) => Doc;
207
+ type ChildNodeHandler = (
208
+ childClass: string,
209
+ path: AstPath,
210
+ print: PrintFn,
211
+ options: ParserOptions,
212
+ ) => Doc;
213
+ ```
214
+
215
+ ### Handler Patterns
216
+
217
+ ```typescript
218
+ // Passthrough
219
+ handlePassthroughCall('propertyDecl') // path.call(print, "propertyDecl")
220
+
221
+ // Inline
222
+ () => "break;"
223
+ (path, print) => ["throw", " ", path.call(print, "expr"), ";"]
224
+
225
+ // Child class lookup
226
+ (childClass: string) => MODIFIER[childClass]
227
+
228
+ // Complex
229
+ function handleBinaryishExpression(path, print): Doc { /* ... */ }
230
+ ```
231
+
232
+ ### Doc Builders
233
+
234
+ From Prettier: `group`, `indent`, `hardline`, `softline`, `line`, `join`,
235
+ `align`, `fill`, `ifBreak`
236
+
237
+ ---
238
+
239
+ ## Node Handlers Reference
240
+
241
+ ### Statements
242
+
243
+ | Type | Handler |
244
+ | -------------------------------- | ----------------------------------------------- |
245
+ | `IF_ELSE_BLOCK` | `handleIfElseBlock` |
246
+ | `IF_BLOCK` | `handleIfBlock` |
247
+ | `ELSE_BLOCK` | `handleElseBlock` |
248
+ | `EXPRESSION_STATEMENT` | `handleExpressionStatement` |
249
+ | `RETURN_STATEMENT` | `handleReturnStatement` |
250
+ | `BREAK_STATEMENT` | `() => "break;"` |
251
+ | `CONTINUE_STATEMENT` | `() => "continue;"` |
252
+ | `THROW_STATEMENT` | `["throw", " ", path.call(print, "expr"), ";"]` |
253
+ | `BLOCK_STATEMENT` | `handleBlockStatement` |
254
+ | `VARIABLE_DECLARATION_STATEMENT` | `handlePassthroughCall("variableDecls")` |
255
+ | `STATEMENT` | `handleStatement` |
256
+ | `DML_MERGE_STATEMENT` | `handleDmlMergeStatement` |
257
+
258
+ ### Loops
259
+
260
+ | Type | Handler |
261
+ | ---------------------- | -------------------------- |
262
+ | `WHILE_LOOP` | `handleWhileLoop` |
263
+ | `DO_LOOP` | `handleDoLoop` |
264
+ | `FOR_LOOP` | `handleForLoop` |
265
+ | `FOR_C_STYLE_CONTROL` | `handleForCStyleControl` |
266
+ | `FOR_ENHANCED_CONTROL` | `handleForEnhancedControl` |
267
+ | `FOR_INITS` | `handleForInits` |
268
+ | `FOR_INIT` | `handleForInit` |
269
+
270
+ ### Try-Catch-Finally
271
+
272
+ | Type | Handler |
273
+ | ------------------------- | ---------------------------- |
274
+ | `TRY_CATCH_FINALLY_BLOCK` | `handleTryCatchFinallyBlock` |
275
+ | `CATCH_BLOCK` | `handleCatchBlock` |
276
+ | `FINALLY_BLOCK` | `handleFinallyBlock` |
277
+
278
+ ### Switch
279
+
280
+ | Type | Handler |
281
+ | ------------------ | ------------------------------- |
282
+ | `SWITCH_STATEMENT` | `handleSwitchStatement` |
283
+ | `VALUE_WHEN` | `handleValueWhen` |
284
+ | `ELSE_WHEN` | `handleElseWhen` |
285
+ | `TYPE_WHEN` | `handleTypeWhen` |
286
+ | `ENUM_CASE` | `handleEnumCase` |
287
+ | `LITERAL_CASE` | `handlePassthroughCall("expr")` |
288
+
289
+ ### Declarations
290
+
291
+ | Type | Handler |
292
+ | ----------------------- | ----------------------------------- |
293
+ | `CLASS_DECLARATION` | `handleClassDeclaration` |
294
+ | `INTERFACE_DECLARATION` | `handleInterfaceDeclaration` |
295
+ | `METHOD_DECLARATION` | `handleMethodDeclaration` |
296
+ | `VARIABLE_DECLARATION` | `handleVariableDeclaration` |
297
+ | `ENUM_DECLARATION` | `handleEnumDeclaration` |
298
+ | `PROPERTY_DECLATION` | `handlePropertyDeclaration` |
299
+ | `PROPERTY_GETTER` | `handlePropertyGetterSetter("get")` |
300
+ | `PROPERTY_SETTER` | `handlePropertyGetterSetter("set")` |
301
+
302
+ ### Compilation Units
303
+
304
+ | Type | Handler |
305
+ | ---------------------------- | ------------------------------- |
306
+ | `TRIGGER_DECLARATION_UNIT` | `handleTriggerDeclarationUnit` |
307
+ | `CLASS_DECLARATION_UNIT` | `handlePassthroughCall("body")` |
308
+ | `ENUM_DECLARATION_UNIT` | `handlePassthroughCall("body")` |
309
+ | `INTERFACE_DECLARATION_UNIT` | `handlePassthroughCall("body")` |
310
+ | `ANONYMOUS_BLOCK_UNIT` | `handleAnonymousBlockUnit` |
311
+
312
+ ### Block Members
313
+
314
+ | Type | Handler |
315
+ | ------------------------------- | ---------------------------------------- |
316
+ | `PROPERTY_MEMBER` | `handlePassthroughCall("propertyDecl")` |
317
+ | `FIELD_MEMBER` | `handlePassthroughCall("variableDecls")` |
318
+ | `STATEMENT_BLOCK_MEMBER` | `handleStatementBlockMember()` |
319
+ | `STATIC_STATEMENT_BLOCK_MEMBER` | `handleStatementBlockMember("static")` |
320
+ | `METHOD_MEMBER` | `handlePassthroughCall("methodDecl")` |
321
+ | `INNER_CLASS_MEMBER` | `handlePassthroughCall("body")` |
322
+ | `INNER_ENUM_MEMBER` | `handlePassthroughCall("body")` |
323
+ | `INNER_INTERFACE_MEMBER` | `handlePassthroughCall("body")` |
324
+
325
+ ### Expressions
326
+
327
+ | Type | Handler |
328
+ | ----------------------------- | ------------------------------------------------ |
329
+ | `BINARY_EXPRESSION` | `handleBinaryishExpression` |
330
+ | `BOOLEAN_EXPRESSION` | `handleBinaryishExpression` |
331
+ | `ASSIGNMENT_EXPRESSION` | `handleAssignmentExpression` |
332
+ | `TERNARY_EXPRESSION` | `handleTernaryExpression` |
333
+ | `NESTED_EXPRESSION` | `handleNestedExpression` |
334
+ | `VARIABLE_EXPRESSION` | `handleVariableExpression` |
335
+ | `JAVA_VARIABLE_EXPRESSION` | `handleJavaVariableExpression` |
336
+ | `LITERAL_EXPRESSION` | `handleLiteralExpression` |
337
+ | `TRIGGER_VARIABLE_EXPRESSION` | `["Trigger", ".", path.call(print, "variable")]` |
338
+ | `THIS_VARIABLE_EXPRESSION` | `() => "this"` |
339
+ | `SUPER_VARIABLE_EXPRESSION` | `() => "super"` |
340
+ | `POSTFIX_EXPRESSION` | `handlePostfixExpression` |
341
+ | `PREFIX_EXPRESSION` | `handlePrefixExpression` |
342
+ | `CAST_EXPRESSION` | `handleCastExpression` |
343
+ | `INSTANCE_OF_EXPRESSION` | `handleInstanceOfExpression` |
344
+ | `PACKAGE_VERSION_EXPRESSION` | `handlePackageVersionExpression` |
345
+ | `ARRAY_EXPRESSION` | `handleArrayExpression` |
346
+ | `CLASS_REF_EXPRESSION` | `[path.call(print, "type"), ".", "class"]` |
347
+ | `NULL_COALESCING_EXPRESSION` | `handleNullCoalescingExpression` |
348
+
349
+ ### Method Calls
350
+
351
+ | Type | Handler |
352
+ | ------------------------------ | --------------------------------- |
353
+ | `METHOD_CALL_EXPRESSION` | `handleMethodCallExpression` |
354
+ | `JAVA_METHOD_CALL_EXPRESSION` | `handleJavaMethodCallExpression` |
355
+ | `SUPER_METHOD_CALL_EXPRESSION` | `handleSuperMethodCallExpression` |
356
+ | `THIS_METHOD_CALL_EXPRESSION` | `handleThisMethodCallExpression` |
357
+ | `NEW_EXPRESSION` | `handleNewExpression` |
358
+ | `NEW_LIST_INIT` | `handleNewListInit` |
359
+ | `NEW_MAP_INIT` | `handleNewMapInit` |
360
+ | `NEW_SET_INIT` | `handleNewSetInit` |
361
+ | `NEW_LIST_LITERAL` | `handleNewListLiteral` |
362
+ | `NEW_MAP_LITERAL` | `handleNewMapLiteral` |
363
+ | `NEW_SET_LITERAL` | `handleNewSetLiteral` |
364
+ | `NEW_STANDARD` | `handleNewStandard` |
365
+ | `NEW_KEY_VALUE` | `handleNewKeyValue` |
366
+ | `SOQL_EXPRESSION` | `handleSoqlExpression` |
367
+ | `SOSL_EXPRESSION` | `handleSoslExpression` |
368
+
369
+ ### Types
370
+
371
+ | Type | Handler |
372
+ | ---------------- | -------------------- |
373
+ | `TYPE_REF` | `handleTypeRef` |
374
+ | `ARRAY_TYPE_REF` | `handleArrayTypeRef` |
375
+ | `CLASS_TYPE_REF` | `handleClassTypeRef` |
376
+
377
+ ### Annotations
378
+
379
+ | Type | Handler |
380
+ | ------------------------ | -------------------------- |
381
+ | `ANNOTATION` | `handleAnnotation` |
382
+ | `ANNOTATION_KEY_VALUE` | `handleAnnotationKeyValue` |
383
+ | `ANNOTATION_VALUE` | `handleAnnotationValue` |
384
+ | `ANNOTATION_TRUE_VALUE` | `() => "true"` |
385
+ | `ANNOTATION_FALSE_VALUE` | `() => "false"` |
386
+
387
+ ### Modifiers
388
+
389
+ | Type | Handler |
390
+ | ---------- | -------------------------------------- |
391
+ | `MODIFIER` | `(childClass) => MODIFIER[childClass]` |
392
+
393
+ **Modifier constants:**
394
+
395
+ ```typescript
396
+ const MODIFIER = {
397
+ PUBLIC: 'public',
398
+ PRIVATE: 'private',
399
+ PROTECTED: 'protected',
400
+ ABSTRACT: 'abstract',
401
+ FINAL: 'final',
402
+ GLOBAL: 'global',
403
+ INHERITED_SHARING: 'inherited sharing',
404
+ OVERRIDE: 'override',
405
+ STATIC: 'static',
406
+ TEST_METHOD: 'testMethod',
407
+ TRANSIENT: 'transient',
408
+ VIRTUAL: 'virtual',
409
+ WEB_SERVICE: 'webService',
410
+ WITH_SHARING: 'with sharing',
411
+ WITHOUT_SHARING: 'without sharing',
412
+ };
413
+ ```
414
+
415
+ ### DML
416
+
417
+ | Type | Handler |
418
+ | ------------------------ | -------------------------------- |
419
+ | `DML_INSERT_STATEMENT` | `handleDmlStatement("insert")` |
420
+ | `DML_UPDATE_STATEMENT` | `handleDmlStatement("update")` |
421
+ | `DML_UPSERT_STATEMENT` | `handleDmlUpsertStatement` |
422
+ | `DML_DELETE_STATEMENT` | `handleDmlStatement("delete")` |
423
+ | `DML_UNDELETE_STATEMENT` | `handleDmlStatement("undelete")` |
424
+
425
+ ### SOQL
426
+
427
+ | Type | Handler |
428
+ | ----------------------------- | ----------------------------------------------------- |
429
+ | `QUERY` | `handleQuery` |
430
+ | `SELECT_COLUMN` | `handleSelectColumn` |
431
+ | `SELECT_INNER_QUERY` | `handleSelectInnerQuery` |
432
+ | `FIELD` | `handleField` |
433
+ | `FIELD_IDENTIFIER` | `handleFieldIdentifier` |
434
+ | `FROM_CLAUSE` | `handleFromClause` |
435
+ | `FROM_EXPRESSION` | `handleFromExpression` |
436
+ | `WHERE_CLAUSE` | `handleWhereClause` |
437
+ | `WHERE_INNER_EXPRESSION` | `handleWhereInnerExpression` |
438
+ | `WHERE_OPERATION_EXPRESSION` | `handleWhereOperationExpression` |
439
+ | `WHERE_OPERATION_EXPRESSIONS` | `handleWhereOperationExpressions` |
440
+ | `WHERE_COMPOUND_EXPRESSION` | `handleWhereCompoundExpression` |
441
+ | `WHERE_UNARY_EXPRESSION` | `handleWhereUnaryExpression` |
442
+ | `WHERE_DISTANCE_EXPRESSION` | `handleWhereDistanceExpression` |
443
+ | `ORDER_BY` | `handleOrderBy` |
444
+ | `ORDER_BY_VALUE` | `handleOrderByValue` |
445
+ | `GROUP_BY` | `handleGroupBy` |
446
+ | `GROUP_BY_VALUE` | `handleGroupByValue` |
447
+ | `GROUP_BY_TYPE` | `handleGroupByType` |
448
+ | `HAVING_CLAUSE` | `handleHavingClause` |
449
+ | `LIMIT_VALUE` | `handleLimitValue` |
450
+ | `OFFSET_VALUE` | `handleOffsetValue` |
451
+ | `WITH_VALUE` | `handleWithValue` |
452
+ | `WITH_DATA_CATEGORY` | `handleWithDataCategory` |
453
+ | `DATA_CATEGORY` | `handleDataCategory` |
454
+ | `DATA_CATEGORY_OPERATOR` | `(childClass) => DATA_CATEGORY_OPERATORS[childClass]` |
455
+ | `FOR_CLAUSE` | `handleForClause` |
456
+ | `UPDATE_STATS_CLAUSE` | `handleUpdateStatsClause` |
457
+ | `BIND_CLAUSE` | `handleBindClause` |
458
+ | `BIND_EXPRESSION` | `handleBindExpression` |
459
+
460
+ **SOQL Operators:**
461
+
462
+ ```typescript
463
+ const QUERY = {
464
+ AND: 'AND',
465
+ OR: 'OR',
466
+ NOT: 'NOT',
467
+ INCLUDES: 'INCLUDES',
468
+ EXCLUDES: 'EXCLUDES',
469
+ LIKE: 'LIKE',
470
+ IN: 'IN',
471
+ NOT_IN: 'NOT IN',
472
+ '=': '=',
473
+ '!=': '!=',
474
+ '<>': '<>',
475
+ '<': '<',
476
+ '>': '>',
477
+ '<=': '<=',
478
+ '>=': '>=',
479
+ ASC: 'ASC',
480
+ DESC: 'DESC',
481
+ NULLS_FIRST: 'NULLS FIRST',
482
+ NULLS_LAST: 'NULLS LAST',
483
+ };
484
+
485
+ const DATA_CATEGORY_OPERATORS = {
486
+ AT: 'AT',
487
+ ABOVE: 'ABOVE',
488
+ BELOW: 'BELOW',
489
+ ABOVE_OR_BELOW: 'ABOVE_OR_BELOW',
490
+ };
491
+ ```
492
+
493
+ ### SOSL
494
+
495
+ | Type | Handler |
496
+ | ----------------------------- | --------------------------------- |
497
+ | `SEARCH` | `handleSearch` |
498
+ | `FIND_CLAUSE` | `handleFindClause` |
499
+ | `SEARCH_WITH_CLAUSE` | `handleSearchWithClause` |
500
+ | `SEARCH_WITH_CLAUSE_VALUE` | `handleSearchWithClauseValue` |
501
+ | `DIVISION_VALUE` | `handleDivisionValue` |
502
+ | `RETURNING_CLAUSE` | `handleReturningClause` |
503
+ | `RETURNING_EXPRESSION` | `handleReturningExpression` |
504
+ | `RETURNING_SELECT_EXPRESSION` | `handleReturningSelectExpression` |
505
+ | `SEARCH_USING_CLAUSE` | `handleSearchUsingClause` |
506
+
507
+ ---
508
+
509
+ ## Comments
510
+
511
+ ### Comment Types
512
+
513
+ | Type | Detection |
514
+ | ------- | ---------------------------------------------- |
515
+ | Block | `node['@class'] === APEX_TYPES.BLOCK_COMMENT` |
516
+ | Inline | `node['@class'] === APEX_TYPES.INLINE_COMMENT` |
517
+ | ApexDoc | Block comment starting with `/**` |
518
+
519
+ ### Comment Handlers
520
+
521
+ ```typescript
522
+ handleComments: {
523
+ ownLine: (comment, text, options, ast, isLast) => boolean, // Own line
524
+ endOfLine: (comment, text, options, ast, isLast) => boolean, // End of line
525
+ remaining: (comment, text, options, ast, isLast) => boolean // Between code
526
+ }
527
+ // Return true = handled; false = let Prettier handle
528
+ ```
529
+
530
+ ### Comment Properties
531
+
532
+ ```typescript
533
+ interface AnnotatedComment {
534
+ value: string; // Comment text
535
+ location: { startIndex; endIndex };
536
+ trailing?: boolean; // After code
537
+ leading?: boolean; // Before code
538
+ printed?: boolean; // Already printed
539
+ enclosingNode?: any; // Containing node
540
+ followingNode?: any; // Next node
541
+ precedingNode?: any; // Previous node
542
+ placement?: string; // "ownLine" | "endOfLine" | "remaining"
543
+ trailingEmptyLine?: boolean; // Preserve empty line after
544
+ }
545
+ ```
546
+
547
+ ### Prettier-Ignore
548
+
549
+ ```typescript
550
+ // Detection: exact match "prettier-ignore"
551
+ // Formats: // prettier-ignore OR /* prettier-ignore */
552
+ hasPrettierIgnore: (path) => {
553
+ const comment = path.getValue()?.comments?.find((c) => isPrettierIgnore(c));
554
+ return !!comment;
555
+ };
556
+ ```
557
+
558
+ ### Comment Attachment Constants
559
+
560
+ ```typescript
561
+ const ALLOW_DANGLING_COMMENTS = [BLOCK_STATEMENT, CLASS_DECLARATION, INTERFACE_DECLARATION, ENUM_DECLARATION, ...];
562
+ ```
563
+
564
+ ### Special Comment Cases
565
+
566
+ | Case | Handler | Behavior |
567
+ | -------------- | ---------------------------------------------------- | ---------------------------- |
568
+ | Dangling | `handleDanglingComment` | Empty block comments |
569
+ | Block leading | `handleBlockStatementLeadingComment` | Move before `{` inside |
570
+ | Method chain | `handleLongChainComment` | Move to trailing of previous |
571
+ | Binary expr | `handleBinaryishExpressionRightChildTrailingComment` | Attach to right child |
572
+ | Continue/Break | `handleContinueBreakDanglingComment` | Move to trailing |
573
+
574
+ ---
575
+
576
+ ## Options
577
+
578
+ ### Apex-Specific Options
579
+
580
+ | Option | Type | Default | Description |
581
+ | ------------------------ | ------------------------------ | ------------- | ----------------- |
582
+ | `apexStandaloneParser` | `'none'\|'native'\|'built-in'` | `'native'` | Parser mode |
583
+ | `apexStandaloneHost` | `string` | `'localhost'` | HTTP server host |
584
+ | `apexStandalonePort` | `number` | `2117` | HTTP server port |
585
+ | `apexStandaloneProtocol` | `'http'\|'https'` | `'http'` | HTTP protocol |
586
+ | `apexInsertFinalNewline` | `boolean` | `true` | Add final newline |
587
+
588
+ ### Standard Prettier Options (Apex-Relevant)
589
+
590
+ | Option | Type | Default | Description |
591
+ | --------------- | --------- | ------- | ----------------------- |
592
+ | `printWidth` | `number` | `80` | Line wrap width |
593
+ | `tabWidth` | `number` | `4` | Tab spaces |
594
+ | `useTabs` | `boolean` | `false` | Use tabs |
595
+ | `requirePragma` | `boolean` | `false` | Only format with pragma |
596
+ | `insertPragma` | `boolean` | `false` | Add pragma to output |
597
+
598
+ ### Options Definition
599
+
600
+ ```typescript
601
+ export const options: SupportOptions = {
602
+ apexStandaloneParser: {
603
+ type: 'choice',
604
+ category: 'apex',
605
+ default: 'native',
606
+ choices: [
607
+ { value: 'none', description: 'Java CLI' },
608
+ { value: 'native', description: 'Native executables' },
609
+ { value: 'built-in', description: 'HTTP server' },
610
+ ],
611
+ description: 'Parser mode',
612
+ },
613
+ // ...
614
+ };
615
+ ```
616
+
617
+ ---
618
+
619
+ ## Constants
620
+
621
+ ### APEX_TYPES
622
+
623
+ Node type constants mapping `@class` strings. Pattern: `apex.jorje.data.ast.*`
624
+ or `apex.jorje.parser.impl.*`
625
+
626
+ ```typescript
627
+ // Example entries
628
+ APEX_TYPES.CLASS_DECLARATION = 'apex.jorje.data.ast.ClassDecl';
629
+ APEX_TYPES.METHOD_DECLARATION = 'apex.jorje.data.ast.MethodDecl';
630
+ APEX_TYPES.BINARY_EXPRESSION = 'apex.jorje.data.ast.BinaryExpr';
631
+ // ... 140+ types
632
+ ```
633
+
634
+ **Parent type lookup**: Split on `$` (e.g., `Modifier$Annotation` → parent
635
+ `Modifier`)
636
+
637
+ ### Operator Constants
638
+
639
+ ```typescript
640
+ const BINARY = { ADDITION: '+', SUBTRACTION: '-', MULTIPLICATION: '*', DIVISION: '/', ... };
641
+ const BOOLEAN = { DOUBLE_EQUAL: '==', TRIPLE_EQUAL: '===', NOT_EQUAL: '!=', ... };
642
+ const ASSIGNMENT = { EQUALS: '=', ADDITION_EQUALS: '+=', SUBTRACTION_EQUALS: '-=', ... };
643
+ const PREFIX = { NEGATIVE: '-', POSITIVE: '+', NOT: '!', BITWISE_NOT: '~', INCREMENT: '++', DECREMENT: '--' };
644
+ const POSTFIX = { INCREMENT: '++', DECREMENT: '--' };
645
+ ```
646
+
647
+ ### Precedence (Apex-specific)
648
+
649
+ ```
650
+ 1. || (lowest)
651
+ 2. &&
652
+ 3. | ^ &
653
+ 4. == === != !== <> < > <= >= (SAME tier in Apex!)
654
+ 5. >> << >>>
655
+ 6. + -
656
+ 7. * / % (highest)
657
+ ```
658
+
659
+ ### Metadata Arrays
660
+
661
+ ```typescript
662
+ const ALLOW_TRAILING_EMPTY_LINE = [CLASS_DECLARATION, METHOD_DECLARATION, ...];
663
+ const ALLOW_DANGLING_COMMENTS = [BLOCK_STATEMENT, CLASS_DECLARATION, ...];
664
+ ```
665
+
666
+ ---
667
+
668
+ ## Utilities
669
+
670
+ ### Key Functions
671
+
672
+ | Function | Purpose |
673
+ | ----------------------------------------------- | ---------------------------- |
674
+ | `isBinaryish(node)` | Is binary/boolean expression |
675
+ | `isApexDocComment(comment)` | Is `/**` ApexDoc |
676
+ | `getPrecedence(op)` | Operator precedence level |
677
+ | `getParentType(className)` | Parent from `$` split |
678
+ | `findNextUncommentedCharacter(text, idx, char)` | Skip comments when searching |
679
+ | `shouldDottedExpressionBreak(path)` | Method chain breaking logic |
680
+ | `getEmptyLineLocations(src)` | Array of empty line numbers |
681
+ | `getLineIndexes(src)` | Array of line start indices |
682
+
683
+ ### AST Massage (Debug-Check)
684
+
685
+ Normalizes AST for comparison:
686
+
687
+ ```typescript
688
+ massageAstNode(original, cloned, parent) {
689
+ // Remove: loc, comments, trailingEmptyLine, forcedHardline, etc.
690
+ // Normalize: scope → uppercase
691
+ // Flatten: WHERE compound expressions, dotted expression names
692
+ // Normalize: ApexDoc whitespace
693
+ }
694
+ ```
695
+
696
+ ---
697
+
698
+ ## Plugin API
699
+
700
+ ### Exported Interface
701
+
702
+ ```typescript
703
+ export default {
704
+ languages: [{ name: 'Apex', parsers: ['apex', 'apex-anonymous'], extensions: ['.cls', '.trigger'] }],
705
+ parsers: { apex, 'apex-anonymous' },
706
+ printers: { apex },
707
+ options: { apexStandaloneParser, ... },
708
+ defaultOptions: { tabWidth: 4 }
709
+ };
710
+ ```
711
+
712
+ ### Override Plugin
713
+
714
+ ```typescript
715
+ import originalPlugin from 'prettier-plugin-apex';
716
+
717
+ export default {
718
+ ...originalPlugin,
719
+ parsers: {
720
+ ...originalPlugin.parsers,
721
+ apex: {
722
+ ...originalPlugin.parsers.apex,
723
+ parse: async (text, options) => {
724
+ /* custom */
725
+ },
726
+ preprocess: (text, options) => {
727
+ /* custom */
728
+ },
729
+ },
730
+ },
731
+ printers: {
732
+ ...originalPlugin.printers,
733
+ apex: {
734
+ ...originalPlugin.printers.apex,
735
+ print: (path, options, print) => {
736
+ /* custom */
737
+ },
738
+ },
739
+ },
740
+ };
741
+ ```
742
+
743
+ ---
744
+
745
+ ## Override Points Summary
746
+
747
+ | Category | Point | Location |
748
+ | ------------- | ---------------------- | ----------------------------------------- |
749
+ | **Parser** | `parse` | `parsers.apex.parse` |
750
+ | | `locStart` | `parsers.apex.locStart` |
751
+ | | `locEnd` | `parsers.apex.locEnd` |
752
+ | | `hasPragma` | `parsers.apex.hasPragma` |
753
+ | | `preprocess` | `parsers.apex.preprocess` |
754
+ | **Printer** | `print` | `printers.apex.print` |
755
+ | | `massageAstNode` | `printers.apex.massageAstNode` |
756
+ | | `hasPrettierIgnore` | `printers.apex.hasPrettierIgnore` |
757
+ | | `insertPragma` | `printers.apex.insertPragma` |
758
+ | | `isBlockComment` | `printers.apex.isBlockComment` |
759
+ | | `canAttachComment` | `printers.apex.canAttachComment` |
760
+ | | `printComment` | `printers.apex.printComment` |
761
+ | | `willPrintOwnComments` | `printers.apex.willPrintOwnComments` |
762
+ | **Comments** | `ownLine` | `handleComments.ownLine` |
763
+ | | `endOfLine` | `handleComments.endOfLine` |
764
+ | | `remaining` | `handleComments.remaining` |
765
+ | **Location** | handlers | `parser.ts` - `locationGenerationHandler` |
766
+ | **Visitors** | `nodeLocationVisitor` | `parser.ts` |
767
+ | | `lineIndexVisitor` | `parser.ts` |
768
+ | | `metadataVisitor` | `parser.ts` |
769
+ | **Handlers** | `nodeHandler` | `printer.ts` |
770
+ | **Utils** | `getPrecedence` | `util.ts` |
771
+ | | `isBinaryish` | `util.ts` |
772
+ | | `isApexDocComment` | `util.ts` |
773
+ | **Options** | `options` | `index.ts` |
774
+ | | `defaultOptions` | `index.ts` |
775
+ | **Constants** | `APEX_TYPES` | `constants.ts` |
776
+ | | operators | `constants.ts` |
777
+
778
+ ---
779
+
780
+ ## Binary Expression Handling
781
+
782
+ ### Precedence Check
783
+
784
+ ```typescript
785
+ function shouldFlatten(parent, node) {
786
+ const parentOp = parent.op?.['@class'];
787
+ const nodeOp = node.op?.['@class'];
788
+ return getPrecedence(parentOp) === getPrecedence(nodeOp);
789
+ }
790
+ ```
791
+
792
+ ### Grouping Logic
793
+
794
+ ```typescript
795
+ // Same precedence: flatten
796
+ // Different precedence: group child
797
+ // Left/right asymmetry: special handling for readability
798
+ ```
799
+
800
+ ---
801
+
802
+ ## SOQL/SOSL Formatting
803
+
804
+ - Preserves user line breaks via `forcedHardline`
805
+ - Complex WHERE clause: flattening + indentation
806
+ - Query literals: special string handling
807
+ - Order/Group by: consistent formatting
808
+
809
+ ---
810
+
811
+ ## CLI / Usage
812
+
813
+ ```bash
814
+ # Format file
815
+ npx prettier --plugin prettier-plugin-apex --write MyClass.cls
816
+
817
+ # Check formatting
818
+ npx prettier --plugin prettier-plugin-apex --check MyClass.cls
819
+
820
+ # Debug output
821
+ npx prettier --plugin prettier-plugin-apex --debug-check MyClass.cls
822
+
823
+ # Start HTTP server (for built-in mode)
824
+ npx start-apex-server --host localhost --port 2117
825
+ ```
826
+
827
+ ### Config (.prettierrc)
828
+
829
+ ```json
830
+ {
831
+ "plugins": ["prettier-plugin-apex"],
832
+ "apexStandaloneParser": "native",
833
+ "apexInsertFinalNewline": true,
834
+ "printWidth": 120,
835
+ "tabWidth": 4
836
+ }
837
+ ```
838
+
839
+ ---
840
+
841
+ ## Best Practices
842
+
843
+ ### Parser
844
+
845
+ 1. Preserve AST structure
846
+ 2. Throw descriptive errors
847
+ 3. Ensure location accuracy
848
+ 4. Use native executables when possible
849
+ 5. Test with `--debug-check`
850
+
851
+ ### Printer
852
+
853
+ 1. Use Doc builders correctly
854
+ 2. Handle precedence/parentheses
855
+ 3. Preserve comments
856
+ 4. Apply trailing empty lines
857
+
858
+ ### Overrides
859
+
860
+ 1. Call original functions unless replacing
861
+ 2. Maintain TypeScript types
862
+ 3. Preserve error handling
863
+ 4. Test with `--debug-check`
864
+ 5. Document changes
865
+ 6. Consider version compatibility
866
+
867
+ ---
868
+
869
+ ## Related Docs
870
+
871
+ - `docs/JORJE.md` - Jorje AST details
872
+ - `docs/PRETTIER.md` - Prettier architecture
873
+ - `docs/ESLINT.md` - ESLint reference
874
+ - `docs/VITEST.md` - Vitest testing