arc-1 1.6.0 → 1.7.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 (282) hide show
  1. package/README.md +22 -15
  2. package/dist/adt/application-log.d.ts +36 -0
  3. package/dist/adt/application-log.d.ts.map +1 -0
  4. package/dist/adt/application-log.js +143 -0
  5. package/dist/adt/application-log.js.map +1 -0
  6. package/dist/adt/class-structure.d.ts +2 -1
  7. package/dist/adt/class-structure.d.ts.map +1 -1
  8. package/dist/adt/class-structure.js +28 -10
  9. package/dist/adt/class-structure.js.map +1 -1
  10. package/dist/adt/client.d.ts +6 -4
  11. package/dist/adt/client.d.ts.map +1 -1
  12. package/dist/adt/client.js +9 -9
  13. package/dist/adt/client.js.map +1 -1
  14. package/dist/adt/codeintel.d.ts +9 -23
  15. package/dist/adt/codeintel.d.ts.map +1 -1
  16. package/dist/adt/codeintel.js +14 -34
  17. package/dist/adt/codeintel.js.map +1 -1
  18. package/dist/adt/devtools.d.ts +8 -1
  19. package/dist/adt/devtools.d.ts.map +1 -1
  20. package/dist/adt/devtools.js +67 -37
  21. package/dist/adt/devtools.js.map +1 -1
  22. package/dist/adt/diagnostics.d.ts +2 -0
  23. package/dist/adt/diagnostics.d.ts.map +1 -1
  24. package/dist/adt/diagnostics.js +10 -8
  25. package/dist/adt/diagnostics.js.map +1 -1
  26. package/dist/adt/errors.d.ts +5 -1
  27. package/dist/adt/errors.d.ts.map +1 -1
  28. package/dist/adt/errors.js +26 -0
  29. package/dist/adt/errors.js.map +1 -1
  30. package/dist/adt/feed-schema.d.ts +32 -0
  31. package/dist/adt/feed-schema.d.ts.map +1 -0
  32. package/dist/adt/feed-schema.js +123 -0
  33. package/dist/adt/feed-schema.js.map +1 -0
  34. package/dist/adt/fm-parameter-docs.d.ts +7 -0
  35. package/dist/adt/fm-parameter-docs.d.ts.map +1 -0
  36. package/dist/adt/fm-parameter-docs.js +134 -0
  37. package/dist/adt/fm-parameter-docs.js.map +1 -0
  38. package/dist/adt/fm-signature.d.ts +8 -1
  39. package/dist/adt/fm-signature.d.ts.map +1 -1
  40. package/dist/adt/fm-signature.js +37 -28
  41. package/dist/adt/fm-signature.js.map +1 -1
  42. package/dist/adt/http-deadline.d.ts.map +1 -1
  43. package/dist/adt/http-deadline.js +4 -1
  44. package/dist/adt/http-deadline.js.map +1 -1
  45. package/dist/adt/http-retry-after.d.ts +20 -0
  46. package/dist/adt/http-retry-after.d.ts.map +1 -0
  47. package/dist/adt/http-retry-after.js +37 -0
  48. package/dist/adt/http-retry-after.js.map +1 -0
  49. package/dist/adt/http.d.ts +6 -19
  50. package/dist/adt/http.d.ts.map +1 -1
  51. package/dist/adt/http.js +78 -79
  52. package/dist/adt/http.js.map +1 -1
  53. package/dist/adt/include-expansion.d.ts +143 -0
  54. package/dist/adt/include-expansion.d.ts.map +1 -0
  55. package/dist/adt/include-expansion.js +475 -0
  56. package/dist/adt/include-expansion.js.map +1 -0
  57. package/dist/adt/internal-data-operations.d.ts +3 -3
  58. package/dist/adt/internal-data-operations.d.ts.map +1 -1
  59. package/dist/adt/internal-data-operations.js +9 -2
  60. package/dist/adt/internal-data-operations.js.map +1 -1
  61. package/dist/adt/revision-feed.d.ts +8 -0
  62. package/dist/adt/revision-feed.d.ts.map +1 -0
  63. package/dist/adt/revision-feed.js +108 -0
  64. package/dist/adt/revision-feed.js.map +1 -0
  65. package/dist/adt/security-session-gate.d.ts +23 -0
  66. package/dist/adt/security-session-gate.d.ts.map +1 -0
  67. package/dist/adt/security-session-gate.js +56 -0
  68. package/dist/adt/security-session-gate.js.map +1 -0
  69. package/dist/adt/source-diff.d.ts +4 -1
  70. package/dist/adt/source-diff.d.ts.map +1 -1
  71. package/dist/adt/source-diff.js +5 -3
  72. package/dist/adt/source-diff.js.map +1 -1
  73. package/dist/adt/stored-text/chunks.d.ts +8 -0
  74. package/dist/adt/stored-text/chunks.d.ts.map +1 -0
  75. package/dist/adt/stored-text/chunks.js +55 -0
  76. package/dist/adt/stored-text/chunks.js.map +1 -0
  77. package/dist/adt/stored-text/decoder.d.ts +31 -0
  78. package/dist/adt/stored-text/decoder.d.ts.map +1 -0
  79. package/dist/adt/stored-text/decoder.js +319 -0
  80. package/dist/adt/stored-text/decoder.js.map +1 -0
  81. package/dist/adt/stored-text/errors.d.ts +10 -0
  82. package/dist/adt/stored-text/errors.d.ts.map +1 -0
  83. package/dist/adt/stored-text/errors.js +10 -0
  84. package/dist/adt/stored-text/errors.js.map +1 -0
  85. package/dist/adt/stored-text/index.d.ts +7 -0
  86. package/dist/adt/stored-text/index.d.ts.map +1 -0
  87. package/dist/adt/stored-text/index.js +6 -0
  88. package/dist/adt/stored-text/index.js.map +1 -0
  89. package/dist/adt/stored-text/key.d.ts +3 -0
  90. package/dist/adt/stored-text/key.d.ts.map +1 -0
  91. package/dist/adt/stored-text/key.js +23 -0
  92. package/dist/adt/stored-text/key.js.map +1 -0
  93. package/dist/adt/stored-text/reader.d.ts +52 -0
  94. package/dist/adt/stored-text/reader.d.ts.map +1 -0
  95. package/dist/adt/stored-text/reader.js +142 -0
  96. package/dist/adt/stored-text/reader.js.map +1 -0
  97. package/dist/adt/stored-text/source.d.ts +5 -0
  98. package/dist/adt/stored-text/source.d.ts.map +1 -0
  99. package/dist/adt/stored-text/source.js +284 -0
  100. package/dist/adt/stored-text/source.js.map +1 -0
  101. package/dist/adt/stored-text/types.d.ts +38 -0
  102. package/dist/adt/stored-text/types.d.ts.map +1 -0
  103. package/dist/adt/stored-text/types.js +2 -0
  104. package/dist/adt/stored-text/types.js.map +1 -0
  105. package/dist/adt/structure-hierarchy.d.ts.map +1 -1
  106. package/dist/adt/structure-hierarchy.js +11 -27
  107. package/dist/adt/structure-hierarchy.js.map +1 -1
  108. package/dist/adt/syntax-batch.d.ts +66 -0
  109. package/dist/adt/syntax-batch.d.ts.map +1 -0
  110. package/dist/adt/syntax-batch.js +115 -0
  111. package/dist/adt/syntax-batch.js.map +1 -0
  112. package/dist/adt/table-query.d.ts +3 -1
  113. package/dist/adt/table-query.d.ts.map +1 -1
  114. package/dist/adt/table-query.js +32 -4
  115. package/dist/adt/table-query.js.map +1 -1
  116. package/dist/adt/text-elements.js +1 -1
  117. package/dist/adt/text-elements.js.map +1 -1
  118. package/dist/adt/transport-diff.d.ts +16 -25
  119. package/dist/adt/transport-diff.d.ts.map +1 -1
  120. package/dist/adt/transport-diff.js +201 -103
  121. package/dist/adt/transport-diff.js.map +1 -1
  122. package/dist/adt/transport-review-budget.d.ts +15 -0
  123. package/dist/adt/transport-review-budget.d.ts.map +1 -0
  124. package/dist/adt/transport-review-budget.js +57 -0
  125. package/dist/adt/transport-review-budget.js.map +1 -0
  126. package/dist/adt/transport.d.ts +1 -1
  127. package/dist/adt/transport.d.ts.map +1 -1
  128. package/dist/adt/transport.js +2 -2
  129. package/dist/adt/transport.js.map +1 -1
  130. package/dist/adt/types.d.ts +1 -0
  131. package/dist/adt/types.d.ts.map +1 -1
  132. package/dist/adt/user-agent.d.ts +1 -1
  133. package/dist/adt/where-used-native.d.ts +22 -0
  134. package/dist/adt/where-used-native.d.ts.map +1 -0
  135. package/dist/adt/where-used-native.js +111 -0
  136. package/dist/adt/where-used-native.js.map +1 -0
  137. package/dist/adt/xml-parser.d.ts +2 -3
  138. package/dist/adt/xml-parser.d.ts.map +1 -1
  139. package/dist/adt/xml-parser.js +1 -91
  140. package/dist/adt/xml-parser.js.map +1 -1
  141. package/dist/aff/validator.d.ts.map +1 -1
  142. package/dist/aff/validator.js +8 -2
  143. package/dist/aff/validator.js.map +1 -1
  144. package/dist/authz/policy.d.ts.map +1 -1
  145. package/dist/authz/policy.js +10 -2
  146. package/dist/authz/policy.js.map +1 -1
  147. package/dist/cache/sqlite-driver.d.ts +24 -0
  148. package/dist/cache/sqlite-driver.d.ts.map +1 -0
  149. package/dist/cache/sqlite-driver.js +59 -0
  150. package/dist/cache/sqlite-driver.js.map +1 -0
  151. package/dist/cache/sqlite.d.ts +4 -2
  152. package/dist/cache/sqlite.d.ts.map +1 -1
  153. package/dist/cache/sqlite.js +5 -4
  154. package/dist/cache/sqlite.js.map +1 -1
  155. package/dist/cli.js +4 -1
  156. package/dist/cli.js.map +1 -1
  157. package/dist/context/grep.d.ts +2 -0
  158. package/dist/context/grep.d.ts.map +1 -1
  159. package/dist/context/grep.js +8 -7
  160. package/dist/context/grep.js.map +1 -1
  161. package/dist/extract-sap-cookies.js +1 -0
  162. package/dist/extract-sap-cookies.js.map +1 -1
  163. package/dist/handlers/application-log-input.d.ts +32 -0
  164. package/dist/handlers/application-log-input.d.ts.map +1 -0
  165. package/dist/handlers/application-log-input.js +34 -0
  166. package/dist/handlers/application-log-input.js.map +1 -0
  167. package/dist/handlers/application-log.d.ts +4 -0
  168. package/dist/handlers/application-log.d.ts.map +1 -0
  169. package/dist/handlers/application-log.js +14 -0
  170. package/dist/handlers/application-log.js.map +1 -0
  171. package/dist/handlers/cds-hints.d.ts +1 -1
  172. package/dist/handlers/cds-hints.d.ts.map +1 -1
  173. package/dist/handlers/cds-hints.js +21 -63
  174. package/dist/handlers/cds-hints.js.map +1 -1
  175. package/dist/handlers/context.d.ts.map +1 -1
  176. package/dist/handlers/context.js +7 -3
  177. package/dist/handlers/context.js.map +1 -1
  178. package/dist/handlers/diagnose.d.ts.map +1 -1
  179. package/dist/handlers/diagnose.js +10 -1
  180. package/dist/handlers/diagnose.js.map +1 -1
  181. package/dist/handlers/dispatch.d.ts.map +1 -1
  182. package/dist/handlers/dispatch.js +54 -9
  183. package/dist/handlers/dispatch.js.map +1 -1
  184. package/dist/handlers/dump-filter-inputs.d.ts +38 -0
  185. package/dist/handlers/dump-filter-inputs.d.ts.map +1 -0
  186. package/dist/handlers/dump-filter-inputs.js +44 -0
  187. package/dist/handlers/dump-filter-inputs.js.map +1 -0
  188. package/dist/handlers/expanded-read.d.ts +24 -0
  189. package/dist/handlers/expanded-read.d.ts.map +1 -0
  190. package/dist/handlers/expanded-read.js +56 -0
  191. package/dist/handlers/expanded-read.js.map +1 -0
  192. package/dist/handlers/function-parameter-language.d.ts +5 -0
  193. package/dist/handlers/function-parameter-language.d.ts.map +1 -0
  194. package/dist/handlers/function-parameter-language.js +19 -0
  195. package/dist/handlers/function-parameter-language.js.map +1 -0
  196. package/dist/handlers/function-processing.d.ts +5 -0
  197. package/dist/handlers/function-processing.d.ts.map +1 -1
  198. package/dist/handlers/function-processing.js +5 -0
  199. package/dist/handlers/function-processing.js.map +1 -1
  200. package/dist/handlers/hyperfocused.d.ts.map +1 -1
  201. package/dist/handlers/hyperfocused.js +2 -1
  202. package/dist/handlers/hyperfocused.js.map +1 -1
  203. package/dist/handlers/long-text-input.d.ts +113 -0
  204. package/dist/handlers/long-text-input.d.ts.map +1 -0
  205. package/dist/handlers/long-text-input.js +113 -0
  206. package/dist/handlers/long-text-input.js.map +1 -0
  207. package/dist/handlers/long-text.d.ts +5 -0
  208. package/dist/handlers/long-text.d.ts.map +1 -0
  209. package/dist/handlers/long-text.js +122 -0
  210. package/dist/handlers/long-text.js.map +1 -0
  211. package/dist/handlers/navigate.d.ts.map +1 -1
  212. package/dist/handlers/navigate.js +8 -3
  213. package/dist/handlers/navigate.js.map +1 -1
  214. package/dist/handlers/object-types.d.ts.map +1 -1
  215. package/dist/handlers/object-types.js +7 -4
  216. package/dist/handlers/object-types.js.map +1 -1
  217. package/dist/handlers/query-input.d.ts +50 -0
  218. package/dist/handlers/query-input.d.ts.map +1 -0
  219. package/dist/handlers/query-input.js +59 -0
  220. package/dist/handlers/query-input.js.map +1 -0
  221. package/dist/handlers/query-tool.d.ts +11 -0
  222. package/dist/handlers/query-tool.d.ts.map +1 -0
  223. package/dist/handlers/query-tool.js +99 -0
  224. package/dist/handlers/query-tool.js.map +1 -0
  225. package/dist/handlers/query.d.ts +1 -1
  226. package/dist/handlers/query.d.ts.map +1 -1
  227. package/dist/handlers/query.js +7 -1
  228. package/dist/handlers/query.js.map +1 -1
  229. package/dist/handlers/read.d.ts +1 -0
  230. package/dist/handlers/read.d.ts.map +1 -1
  231. package/dist/handlers/read.js +57 -9
  232. package/dist/handlers/read.js.map +1 -1
  233. package/dist/handlers/schemas.d.ts +17 -4
  234. package/dist/handlers/schemas.d.ts.map +1 -1
  235. package/dist/handlers/schemas.js +51 -9
  236. package/dist/handlers/schemas.js.map +1 -1
  237. package/dist/handlers/syntax.d.ts.map +1 -1
  238. package/dist/handlers/syntax.js +8 -0
  239. package/dist/handlers/syntax.js.map +1 -1
  240. package/dist/handlers/tools.d.ts.map +1 -1
  241. package/dist/handlers/tools.js +72 -70
  242. package/dist/handlers/tools.js.map +1 -1
  243. package/dist/handlers/transport-review.d.ts +5 -0
  244. package/dist/handlers/transport-review.d.ts.map +1 -0
  245. package/dist/handlers/transport-review.js +108 -0
  246. package/dist/handlers/transport-review.js.map +1 -0
  247. package/dist/handlers/transport.d.ts.map +1 -1
  248. package/dist/handlers/transport.js +3 -58
  249. package/dist/handlers/transport.js.map +1 -1
  250. package/dist/handlers/where-used-details.d.ts +28 -0
  251. package/dist/handlers/where-used-details.d.ts.map +1 -0
  252. package/dist/handlers/where-used-details.js +67 -0
  253. package/dist/handlers/where-used-details.js.map +1 -0
  254. package/dist/handlers/where-used.d.ts +8 -3
  255. package/dist/handlers/where-used.d.ts.map +1 -1
  256. package/dist/handlers/where-used.js +40 -18
  257. package/dist/handlers/where-used.js.map +1 -1
  258. package/dist/handlers/write/create.d.ts.map +1 -1
  259. package/dist/handlers/write/create.js +15 -12
  260. package/dist/handlers/write/create.js.map +1 -1
  261. package/dist/handlers/write/update-delete.d.ts.map +1 -1
  262. package/dist/handlers/write/update-delete.js +5 -3
  263. package/dist/handlers/write/update-delete.js.map +1 -1
  264. package/dist/server/audit.d.ts.map +1 -1
  265. package/dist/server/audit.js +49 -2
  266. package/dist/server/audit.js.map +1 -1
  267. package/dist/server/config.d.ts.map +1 -1
  268. package/dist/server/config.js +5 -1
  269. package/dist/server/config.js.map +1 -1
  270. package/dist/server/multi-target-tools.d.ts.map +1 -1
  271. package/dist/server/multi-target-tools.js +8 -7
  272. package/dist/server/multi-target-tools.js.map +1 -1
  273. package/dist/server/server.d.ts.map +1 -1
  274. package/dist/server/server.js +3 -2
  275. package/dist/server/server.js.map +1 -1
  276. package/dist/server/tool-auth.d.ts +1 -1
  277. package/dist/server/tool-auth.d.ts.map +1 -1
  278. package/dist/server/tool-auth.js +24 -4
  279. package/dist/server/tool-auth.js.map +1 -1
  280. package/dist/version.d.ts +1 -1
  281. package/dist/version.js +1 -1
  282. package/package.json +13 -9
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Bounded include expansion for `SAPRead expand_includes` on programs and includes (PROG, INCL).
3
+ * Function groups keep their own walk in `AdtClient.getFunctionGroupExpanded`.
4
+ *
5
+ * INCLUDE statements are recognized by abaplint (`Statements.Include`): chains, `IF FOUND`,
6
+ * comments and literals are handled by the parser, and `INCLUDE TYPE`/`INCLUDE STRUCTURE` are
7
+ * type includes. abaplint's structure pass grows faster than linearly with one file (measured on
8
+ * 2.120.71: 10k lines of code ≈ 0.8 s, 100k ≈ 108 s; 40k INCLUDE statements ≈ 3.4 s), so only the
9
+ * statements that mention INCLUDE are handed to it, at their original line numbers and in blocks of
10
+ * at most `PARSE_BLOCK_STATEMENTS`; every other line is blanked. That pre-filter never decides what
11
+ * an include is — it only removes statements that do not contain the keyword.
12
+ *
13
+ * Customer includes are always expanded; SAP includes only under an SAP program, includes of a
14
+ * namespace only under a program of that namespace, and `<...>` system includes never. Customer
15
+ * includes are read first, also those found inside an SAP include; within each group the walk is
16
+ * breadth-first, so a limit cuts deep includes before shallow ones. Every block names the source and
17
+ * line that include it. All reads share one deadline and the request's cancel signal. Every include
18
+ * that is not shown in full is reported, customer code first.
19
+ */
20
+ import type { AdtClient } from './client.js';
21
+ import type { AdtRequestOptions } from './http-deadline.js';
22
+ /** One INCLUDE statement of a source. */
23
+ export interface IncludeStatement {
24
+ /** Include name as written. */
25
+ name: string;
26
+ /** 1-based line of the include name. */
27
+ line: number;
28
+ /** `INCLUDE … IF FOUND`: the include may legitimately not exist. */
29
+ ifFound: boolean;
30
+ }
31
+ export interface IncludeExpansionLimits {
32
+ /** Sources read, the root included; an `IF FOUND` include that does not exist counts as one. */
33
+ maxBlocks: number;
34
+ /** Include levels below the root. */
35
+ maxDepth: number;
36
+ /** Characters of source over all blocks. */
37
+ maxChars: number;
38
+ /** Milliseconds for reading the program and its includes. */
39
+ maxMs: number;
40
+ }
41
+ /**
42
+ * Time for reading the program and its includes. 80 sources took about 4 s on SAP_BASIS 7.50 and 7.58,
43
+ * so 20 s leaves room for slower systems and still ends far below the 120 s timeout of one request. It is
44
+ * set on its own; `REVIEW_DEADLINE_MS` bounds one transport-review page with the same value.
45
+ */
46
+ export declare const EXPANSION_DEADLINE_MS = 20000;
47
+ /**
48
+ * Text output must fit what MCP clients show inline: a client with a 25,000-token result limit refused
49
+ * an 81,000-character SAPMV45A expansion and saved a 62 KB one to a file, because comment-heavy SAP code
50
+ * tokenizes far below 4 characters per token. 40,000 characters stayed inline in the live tests.
51
+ */
52
+ export declare const EXPANSION_TEXT_LIMITS: IncludeExpansionLimits;
53
+ /** grep reads more than it returns; the character limit only bounds memory and SAP work. */
54
+ export declare const EXPANSION_SEARCH_LIMITS: IncludeExpansionLimits;
55
+ export interface IncludeExpansionOptions {
56
+ limits?: Partial<IncludeExpansionLimits>;
57
+ /** Cut the block that crosses `maxChars` at a line boundary instead of keeping it whole. */
58
+ shorten?: boolean;
59
+ /** Epoch milliseconds after which no further include is read; defaults to now + `maxMs`. */
60
+ deadline?: number;
61
+ /** Cancels the walk together with the MCP request. */
62
+ signal?: AbortSignal;
63
+ }
64
+ export interface ExpansionBlock {
65
+ name: string;
66
+ source: string;
67
+ /** Source and line of the INCLUDE statement that names it; absent for the root. */
68
+ includedAt?: {
69
+ parent: string;
70
+ line: number;
71
+ };
72
+ /** The read failed; `source` is a placeholder. */
73
+ unreadable?: true;
74
+ /** Read from the user's unactivated draft. */
75
+ draft?: true;
76
+ }
77
+ export type IncludeSkipReason = 'system' | 'sap' | 'namespace' | 'name' | 'depth' | 'blocks' | 'size' | 'time';
78
+ /** An include that is referenced but not shown in full. */
79
+ export interface IncludeIssue {
80
+ kind: 'skipped' | 'absent' | 'unreadable' | 'cycle' | 'shortened';
81
+ include: string;
82
+ /** Block whose INCLUDE statement names the include. */
83
+ parent: string;
84
+ line: number;
85
+ reason?: IncludeSkipReason;
86
+ /** For `unreadable`: HTTP status or error class, never SAP's message text. */
87
+ detail?: string;
88
+ /** For `shortened`: lines kept and total lines. */
89
+ shownLines?: number;
90
+ totalLines?: number;
91
+ }
92
+ export interface IncludeExpansionResult {
93
+ blocks: ExpansionBlock[];
94
+ issues: IncludeIssue[];
95
+ /** A limit left includes unread or shortened a block. */
96
+ truncated: boolean;
97
+ limits: IncludeExpansionLimits;
98
+ }
99
+ export interface IncludeExpansionRoot {
100
+ /** Block heading, e.g. `PROG ZREPORT`. */
101
+ label: string;
102
+ /** Program holding the root source; names it in issue locations and cycles. */
103
+ ref: string;
104
+ /** Object name deciding which includes share its owner. */
105
+ owner: string;
106
+ source: string;
107
+ }
108
+ export type IncludeReader = (name: string, optional: boolean, request: AdtRequestOptions) => Promise<string>;
109
+ /** INCLUDE statements of an ABAP source in source order (type includes excluded). */
110
+ export declare function findIncludeStatements(source: string): IncludeStatement[];
111
+ /** `system` (`<...>`), `customer`, a `/NAMESPACE/`, or `sap`. */
112
+ export declare function includeOwner(name: string): string;
113
+ /** The expansion of `root` reads `include`, limits aside (owner and name rules). */
114
+ export declare function expandsInclude(include: string, root: string): boolean;
115
+ /**
116
+ * Walk the include graph of `root`, customer includes first: one found inside an SAP include goes
117
+ * ahead of the SAP includes still waiting. A read that fails because the deadline passed ends the
118
+ * walk with the rest listed under the time limit; other failures except safety refusals and
119
+ * cancellation become placeholder blocks, and a 404 for `INCLUDE … IF FOUND` is reported as absent.
120
+ */
121
+ export declare function expandIncludes(root: IncludeExpansionRoot, read: IncludeReader, options?: IncludeExpansionOptions): Promise<IncludeExpansionResult>;
122
+ export interface ProgramExpansionVersions {
123
+ /** Version of the program or include itself. */
124
+ root?: 'active' | 'inactive';
125
+ /** It is read from the user's draft. */
126
+ rootDraft?: boolean;
127
+ /** Version of one include (default: the root's), and whether that is the user's draft. */
128
+ include?: (name: string) => {
129
+ version: 'active' | 'inactive';
130
+ draft: boolean;
131
+ };
132
+ }
133
+ /**
134
+ * Read a program or include and every include it names, customer includes first, within one deadline.
135
+ * Blocks read from the user's drafts are marked `draft`.
136
+ */
137
+ export declare function readProgramExpanded(client: AdtClient, type: 'PROG' | 'INCL', name: string, versions: ProgramExpansionVersions, options?: Omit<IncludeExpansionOptions, 'deadline' | 'signal'>): Promise<IncludeExpansionResult>;
138
+ /**
139
+ * Explain every include that is not shown (or searched) in full, customer code first, or undefined
140
+ * when there is none. `searched` words the notes for grep output.
141
+ */
142
+ export declare function expansionNotes(result: IncludeExpansionResult, searched: boolean): string | undefined;
143
+ //# sourceMappingURL=include-expansion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"include-expansion.d.ts","sourceRoot":"","sources":["../../src/adt/include-expansion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,475 @@
1
+ /**
2
+ * Bounded include expansion for `SAPRead expand_includes` on programs and includes (PROG, INCL).
3
+ * Function groups keep their own walk in `AdtClient.getFunctionGroupExpanded`.
4
+ *
5
+ * INCLUDE statements are recognized by abaplint (`Statements.Include`): chains, `IF FOUND`,
6
+ * comments and literals are handled by the parser, and `INCLUDE TYPE`/`INCLUDE STRUCTURE` are
7
+ * type includes. abaplint's structure pass grows faster than linearly with one file (measured on
8
+ * 2.120.71: 10k lines of code ≈ 0.8 s, 100k ≈ 108 s; 40k INCLUDE statements ≈ 3.4 s), so only the
9
+ * statements that mention INCLUDE are handed to it, at their original line numbers and in blocks of
10
+ * at most `PARSE_BLOCK_STATEMENTS`; every other line is blanked. That pre-filter never decides what
11
+ * an include is — it only removes statements that do not contain the keyword.
12
+ *
13
+ * Customer includes are always expanded; SAP includes only under an SAP program, includes of a
14
+ * namespace only under a program of that namespace, and `<...>` system includes never. Customer
15
+ * includes are read first, also those found inside an SAP include; within each group the walk is
16
+ * breadth-first, so a limit cuts deep includes before shallow ones. Every block names the source and
17
+ * line that include it. All reads share one deadline and the request's cancel signal. Every include
18
+ * that is not shown in full is reported, customer code first.
19
+ */
20
+ import { Expressions, MemoryFile, Registry, Statements } from '@abaplint/core';
21
+ import { getDefaultAbaplintConfig } from '../lint/abaplint-config-cache.js';
22
+ import { getCurrentContext } from '../server/context.js';
23
+ import { AdtApiError, AdtNetworkError, AdtSafetyError, isNotFoundError } from './errors.js';
24
+ import { ABAPLINT_MAX_RELEASE, mapSapReleaseToAbaplintVersion } from './features.js';
25
+ /**
26
+ * Time for reading the program and its includes. 80 sources took about 4 s on SAP_BASIS 7.50 and 7.58,
27
+ * so 20 s leaves room for slower systems and still ends far below the 120 s timeout of one request. It is
28
+ * set on its own; `REVIEW_DEADLINE_MS` bounds one transport-review page with the same value.
29
+ */
30
+ export const EXPANSION_DEADLINE_MS = 20_000;
31
+ /**
32
+ * Text output must fit what MCP clients show inline: a client with a 25,000-token result limit refused
33
+ * an 81,000-character SAPMV45A expansion and saved a 62 KB one to a file, because comment-heavy SAP code
34
+ * tokenizes far below 4 characters per token. 40,000 characters stayed inline in the live tests.
35
+ */
36
+ export const EXPANSION_TEXT_LIMITS = {
37
+ maxBlocks: 80,
38
+ maxDepth: 5,
39
+ maxChars: 40_000,
40
+ maxMs: EXPANSION_DEADLINE_MS,
41
+ };
42
+ /** grep reads more than it returns; the character limit only bounds memory and SAP work. */
43
+ export const EXPANSION_SEARCH_LIMITS = { ...EXPANSION_TEXT_LIMITS, maxChars: 4_000_000 };
44
+ const LIMIT_REASONS = new Set(['depth', 'blocks', 'size', 'time']);
45
+ const KEYWORD = 'INCLUDE';
46
+ const MAX_NAME_LENGTH = 40;
47
+ const MAX_LISTED = 20;
48
+ // SAP names module includes after their field, e.g. MV45AO0R_RV45A-KEDATU_AUSGABE.
49
+ const FETCHABLE_NAME = /^(?:\/[A-Z0-9_]+\/)?[A-Z0-9_%#]+(?:-[A-Z0-9_]+)*$/i;
50
+ const DEFAULT_VERSION = mapSapReleaseToAbaplintVersion(String(ABAPLINT_MAX_RELEASE));
51
+ function isWordChar(code) {
52
+ return (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === 95;
53
+ }
54
+ /** INCLUDE statements per abaplint parse; its structure pass grows faster than linearly with them. */
55
+ const PARSE_BLOCK_STATEMENTS = 2_000;
56
+ /**
57
+ * Row ranges, in order and without overlap, of the statements that contain the INCLUDE keyword
58
+ * outside comments and literals. Statement boundaries are the ABAP periods outside literals; a range
59
+ * ends at a line whose last statement is complete, so no fragment can merge into a following INCLUDE
60
+ * statement and the ranges can be parsed in separate blocks.
61
+ */
62
+ function includeStatementRanges(lines) {
63
+ const pendingAtEnd = new Array(lines.length).fill(false);
64
+ const ranges = [];
65
+ let pending = false;
66
+ let mentions = false;
67
+ let start = 0;
68
+ let templateText = false;
69
+ let templateDepth = 0;
70
+ const open = (row) => {
71
+ if (!pending)
72
+ start = row;
73
+ pending = true;
74
+ };
75
+ for (let row = 0; row < lines.length; row++) {
76
+ const line = lines[row];
77
+ // A later INCLUDE on this line must keep the prefix of a multiline statement too.
78
+ const rowStart = pending ? start : row;
79
+ if (!templateText && line.startsWith('*')) {
80
+ pendingAtEnd[row] = pending;
81
+ continue;
82
+ }
83
+ let i = 0;
84
+ while (i < line.length) {
85
+ const char = line[i];
86
+ // Keep template state across rows; { resumes code and } resumes text, including nested templates.
87
+ if (templateText) {
88
+ if (char === '\\')
89
+ i += 2;
90
+ else {
91
+ if (char === '|')
92
+ templateDepth--;
93
+ if (char === '|' || char === '{')
94
+ templateText = false;
95
+ i++;
96
+ }
97
+ continue;
98
+ }
99
+ if (char === '"')
100
+ break;
101
+ if (char === '|' || (char === '}' && templateDepth > 0)) {
102
+ open(rowStart);
103
+ if (char === '|')
104
+ templateDepth++;
105
+ templateText = true;
106
+ i++;
107
+ }
108
+ else if (char === "'" || char === '`') {
109
+ open(rowStart);
110
+ let j = i + 1;
111
+ while (j < line.length && line[j] !== char)
112
+ j++;
113
+ i = j + 1;
114
+ }
115
+ else if (char === '.') {
116
+ if (pending && mentions)
117
+ ranges.push([start, row]);
118
+ pending = false;
119
+ mentions = false;
120
+ i++;
121
+ }
122
+ else if (isWordChar(line.charCodeAt(i))) {
123
+ open(rowStart);
124
+ let j = i + 1;
125
+ while (j < line.length && isWordChar(line.charCodeAt(j)))
126
+ j++;
127
+ if (j - i === KEYWORD.length && line.slice(i, j).toUpperCase() === KEYWORD)
128
+ mentions = true;
129
+ i = j;
130
+ }
131
+ else {
132
+ if (char !== ' ' && char !== '\t' && char !== '\r')
133
+ open(rowStart);
134
+ i++;
135
+ }
136
+ }
137
+ pendingAtEnd[row] = pending;
138
+ }
139
+ if (pending && mentions)
140
+ ranges.push([start, lines.length - 1]);
141
+ const merged = [];
142
+ for (const [from, to] of ranges) {
143
+ let end = to;
144
+ while (pendingAtEnd[end] && end + 1 < lines.length)
145
+ end++;
146
+ const last = merged.at(-1);
147
+ if (last && from <= last[1])
148
+ last[1] = Math.max(last[1], end);
149
+ else
150
+ merged.push([from, end]);
151
+ }
152
+ return merged;
153
+ }
154
+ /** INCLUDE statements of an ABAP source in source order (type includes excluded). */
155
+ export function findIncludeStatements(source) {
156
+ if (!/include/i.test(source))
157
+ return [];
158
+ const lines = source.replace(/\r\n/g, '\n').split('\n');
159
+ const ranges = includeStatementRanges(lines);
160
+ const statements = [];
161
+ for (let first = 0; first < ranges.length; first += PARSE_BLOCK_STATEMENTS) {
162
+ const block = ranges.slice(first, first + PARSE_BLOCK_STATEMENTS);
163
+ const offset = block[0][0];
164
+ const rows = new Array(block.at(-1)[1] - offset + 1).fill('');
165
+ for (const [from, to] of block)
166
+ for (let row = from; row <= to; row++)
167
+ rows[row - offset] = lines[row];
168
+ statements.push(...parseIncludeStatements(rows.join('\n'), offset));
169
+ }
170
+ return statements;
171
+ }
172
+ /** abaplint's INCLUDE statements in `text`, with their lines moved down by `offset` rows. */
173
+ function parseIncludeStatements(text, offset) {
174
+ const registry = new Registry(getDefaultAbaplintConfig(DEFAULT_VERSION));
175
+ registry.addFile(new MemoryFile('zarc1_include_scan.prog.abap', text));
176
+ registry.parse();
177
+ const statements = [];
178
+ for (const object of registry.getObjects()) {
179
+ const file = object.getMainABAPFile?.();
180
+ for (const statement of file?.getStatements() ?? []) {
181
+ if (!(statement.get() instanceof Statements.Include))
182
+ continue;
183
+ const name = statement.findFirstExpression(Expressions.IncludeName);
184
+ if (!name)
185
+ continue;
186
+ const words = statement.getTokens().map((token) => token.getStr().toUpperCase());
187
+ statements.push({
188
+ name: name.concatTokens(),
189
+ line: name.getFirstToken().getRow() + offset,
190
+ ifFound: words.some((word, index) => word === 'IF' && words[index + 1] === 'FOUND'),
191
+ });
192
+ }
193
+ }
194
+ return statements;
195
+ }
196
+ /** `system` (`<...>`), `customer`, a `/NAMESPACE/`, or `sap`. */
197
+ export function includeOwner(name) {
198
+ const upper = name.toUpperCase();
199
+ if (upper.startsWith('<'))
200
+ return 'system';
201
+ const namespace = /^\/[^/]+\//.exec(upper);
202
+ if (namespace)
203
+ return namespace[0];
204
+ // Customer name ranges: Z*/Y*, module pools SAPMZ*/MZ*, function groups SAPLZ*/LZ*.
205
+ return /^(?:SAP[LM]|[LM])?[YZ]/.test(upper) ? 'customer' : 'sap';
206
+ }
207
+ function skipReason(include, rootOwner) {
208
+ const owner = includeOwner(include);
209
+ if (owner === 'system')
210
+ return 'system';
211
+ if (owner !== 'customer' && owner !== rootOwner)
212
+ return owner === 'sap' ? 'sap' : 'namespace';
213
+ if (include.length > MAX_NAME_LENGTH || !FETCHABLE_NAME.test(include))
214
+ return 'name';
215
+ return undefined;
216
+ }
217
+ /** The expansion of `root` reads `include`, limits aside (owner and name rules). */
218
+ export function expandsInclude(include, root) {
219
+ return skipReason(include, includeOwner(root)) === undefined;
220
+ }
221
+ /** Lines of `source` that fit `maxChars`, or undefined when only blank lines would be left out. */
222
+ function shortenSource(source, maxChars) {
223
+ const lines = source.split('\n');
224
+ let used = 0;
225
+ let shownLines = 0;
226
+ while (shownLines < lines.length && used + lines[shownLines].length + 1 <= maxChars) {
227
+ used += lines[shownLines].length + 1;
228
+ shownLines++;
229
+ }
230
+ if (lines.slice(shownLines).every((line) => line.trim() === ''))
231
+ return undefined;
232
+ return { text: lines.slice(0, shownLines).join('\n'), shownLines, totalLines: lines.length };
233
+ }
234
+ /** The read was cut off by its deadline (an `AbortSignal.timeout`), detected as in the transport review. */
235
+ function isDeadlineError(err) {
236
+ return err instanceof AdtNetworkError && err.cause?.name === 'TimeoutError';
237
+ }
238
+ /** Customer includes first, then the others; each group breadth-first in the order found. */
239
+ function byPriority(a, b) {
240
+ return Number(b.customer) - Number(a.customer) || a.depth - b.depth || a.order - b.order;
241
+ }
242
+ /**
243
+ * Walk the include graph of `root`, customer includes first: one found inside an SAP include goes
244
+ * ahead of the SAP includes still waiting. A read that fails because the deadline passed ends the
245
+ * walk with the rest listed under the time limit; other failures except safety refusals and
246
+ * cancellation become placeholder blocks, and a 404 for `INCLUDE … IF FOUND` is reported as absent.
247
+ */
248
+ export async function expandIncludes(root, read, options = {}) {
249
+ const limits = { ...EXPANSION_SEARCH_LIMITS, ...options.limits };
250
+ const deadline = options.deadline ?? Date.now() + limits.maxMs;
251
+ const request = { deadline, ...(options.signal ? { signal: options.signal } : {}) };
252
+ const timeUp = () => Date.now() >= deadline;
253
+ const rootOwner = includeOwner(root.owner);
254
+ const blocks = [];
255
+ const issues = [];
256
+ const rootKey = root.ref.toUpperCase();
257
+ const seen = new Set([rootKey]);
258
+ const cycles = new Set();
259
+ const pending = [];
260
+ let found = 0;
261
+ /** `IF FOUND` includes that do not exist: no block, but a read like any other. */
262
+ let absent = 0;
263
+ let chars = 0;
264
+ let stop;
265
+ /** Add a block; false when not even the first line of an include fits, which is then listed instead. */
266
+ const add = (name, source, at) => {
267
+ const cut = options.shorten && chars + source.length > limits.maxChars
268
+ ? shortenSource(source, limits.maxChars - chars)
269
+ : undefined;
270
+ if (cut?.shownLines === 0 && at) {
271
+ issues.push({ kind: 'skipped', reason: 'size', include: name, ...at });
272
+ stop = 'size';
273
+ return false;
274
+ }
275
+ const includedAt = at ? { includedAt: at } : {};
276
+ if (!cut) {
277
+ blocks.push({ name, source, ...includedAt });
278
+ }
279
+ else {
280
+ const rest = `lines ${cut.shownLines + 1}-${cut.totalLines} not shown (character limit)`;
281
+ blocks.push({ name, source: `${cut.text}${cut.text ? '\n' : ''}[... ${rest}]`, ...includedAt });
282
+ issues.push({
283
+ kind: 'shortened',
284
+ include: name,
285
+ parent: at?.parent ?? '',
286
+ line: at?.line ?? 0,
287
+ shownLines: cut.shownLines,
288
+ totalLines: cut.totalLines,
289
+ });
290
+ }
291
+ chars += source.length;
292
+ if (chars >= limits.maxChars)
293
+ stop = 'size';
294
+ return true;
295
+ };
296
+ /** Queue the includes that `source` names, or report why one is not read. */
297
+ const discover = (ref, source, depth, ancestors) => {
298
+ for (const statement of findIncludeStatements(source)) {
299
+ const key = statement.name.toUpperCase();
300
+ const at = { parent: ref, line: statement.line };
301
+ if (ancestors.has(key)) {
302
+ if (!cycles.has(`${ref.toUpperCase()} ${key}`))
303
+ issues.push({ kind: 'cycle', include: statement.name, ...at });
304
+ cycles.add(`${ref.toUpperCase()} ${key}`);
305
+ continue;
306
+ }
307
+ if (seen.has(key))
308
+ continue;
309
+ seen.add(key);
310
+ const reason = skipReason(statement.name, rootOwner) ?? stop ?? (depth >= limits.maxDepth ? 'depth' : undefined);
311
+ if (reason) {
312
+ issues.push({ kind: 'skipped', reason, include: statement.name, ...at });
313
+ continue;
314
+ }
315
+ const customer = includeOwner(statement.name) === 'customer';
316
+ pending.push({
317
+ statement,
318
+ at,
319
+ depth: depth + 1,
320
+ ancestors: new Set([...ancestors, key]),
321
+ customer,
322
+ order: found++,
323
+ });
324
+ }
325
+ };
326
+ add(root.label, root.source);
327
+ discover(root.ref, root.source, 0, new Set([rootKey]));
328
+ while (pending.length > 0) {
329
+ let first = 0;
330
+ for (let i = 1; i < pending.length; i++)
331
+ if (byPriority(pending[i], pending[first]) < 0)
332
+ first = i;
333
+ const next = pending.splice(first, 1)[0];
334
+ const { statement, at } = next;
335
+ if (!stop && timeUp())
336
+ stop = 'time';
337
+ const reason = stop ?? (blocks.length + absent >= limits.maxBlocks ? 'blocks' : undefined);
338
+ if (reason) {
339
+ // Nothing more is read: list the rest once, in the order it would have been read.
340
+ for (const left of [next, ...pending.sort(byPriority)]) {
341
+ issues.push({ kind: 'skipped', reason, include: left.statement.name, ...left.at });
342
+ }
343
+ break;
344
+ }
345
+ let source;
346
+ try {
347
+ source = await read(statement.name, statement.ifFound, request);
348
+ }
349
+ catch (err) {
350
+ if (err instanceof AdtSafetyError || options.signal?.aborted)
351
+ throw err;
352
+ if (isDeadlineError(err)) {
353
+ stop = 'time';
354
+ issues.push({ kind: 'skipped', reason: 'time', include: statement.name, ...at });
355
+ }
356
+ else if (statement.ifFound && isNotFoundError(err)) {
357
+ absent++;
358
+ issues.push({ kind: 'absent', include: statement.name, ...at });
359
+ }
360
+ else {
361
+ blocks.push({
362
+ name: statement.name,
363
+ source: `[Could not read include "${statement.name}"]`,
364
+ includedAt: at,
365
+ unreadable: true,
366
+ });
367
+ const detail = err instanceof AdtApiError ? `HTTP ${err.statusCode}` : err?.name || 'error';
368
+ issues.push({ kind: 'unreadable', detail, include: statement.name, ...at });
369
+ }
370
+ continue;
371
+ }
372
+ if (add(statement.name, source, at))
373
+ discover(statement.name, source, next.depth, next.ancestors);
374
+ }
375
+ const truncated = issues.some((issue) => issue.kind === 'shortened' || (issue.kind === 'skipped' && LIMIT_REASONS.has(issue.reason)));
376
+ return { blocks, issues, truncated, limits };
377
+ }
378
+ /**
379
+ * Read a program or include and every include it names, customer includes first, within one deadline.
380
+ * Blocks read from the user's drafts are marked `draft`.
381
+ */
382
+ export async function readProgramExpanded(client, type, name, versions, options = {}) {
383
+ const limits = { ...EXPANSION_SEARCH_LIMITS, ...options.limits };
384
+ const signal = getCurrentContext()?.signal;
385
+ const deadline = Date.now() + limits.maxMs;
386
+ const rootRead = { deadline, ...(signal ? { signal } : {}), version: versions.root };
387
+ const { source } = type === 'PROG' ? await client.getProgram(name, rootRead) : await client.getInclude(name, rootRead);
388
+ const versionOf = (include) => versions.include?.(include) ?? { version: versions.root, draft: false };
389
+ const read = async (include, optional, request) => (await client.getInclude(include, {
390
+ ...request,
391
+ version: versionOf(include).version,
392
+ suppressNotFoundLog: optional,
393
+ })).source;
394
+ const root = { label: `${type} ${name}`, ref: name, owner: name, source };
395
+ const result = await expandIncludes(root, read, { ...options, limits, deadline, signal });
396
+ const blocks = result.blocks.map((block, index) => {
397
+ const draft = index === 0 ? versions.rootDraft === true : !block.unreadable && versionOf(block.name).draft;
398
+ return draft ? { ...block, draft: true } : block;
399
+ });
400
+ return { ...result, blocks };
401
+ }
402
+ /** Customer includes first, then other namespaces, SAP and system includes. */
403
+ function ownerRank(include) {
404
+ const owner = includeOwner(include);
405
+ return owner === 'customer' ? 0 : owner.startsWith('/') ? 1 : owner === 'sap' ? 2 : 3;
406
+ }
407
+ function listed(issues) {
408
+ const ordered = [...issues].sort((a, b) => ownerRank(a.include) - ownerRank(b.include));
409
+ const names = ordered
410
+ .slice(0, MAX_LISTED)
411
+ .map((issue) => `${issue.include} (${issue.parent} line ${issue.line}${issue.detail ? `, ${issue.detail}` : ''})`);
412
+ if (issues.length > MAX_LISTED)
413
+ names.push(`${issues.length - MAX_LISTED} more`);
414
+ return names.join(', ');
415
+ }
416
+ /**
417
+ * Explain every include that is not shown (or searched) in full, customer code first, or undefined
418
+ * when there is none. `searched` words the notes for grep output.
419
+ */
420
+ export function expansionNotes(result, searched) {
421
+ const verb = searched ? 'searched' : 'expanded';
422
+ const of = (predicate) => result.issues.filter(predicate);
423
+ const lines = [];
424
+ const shortened = of((issue) => issue.kind === 'shortened');
425
+ if (result.truncated) {
426
+ const { maxBlocks, maxDepth, maxChars, maxMs } = result.limits;
427
+ const unread = of((issue) => issue.kind === 'skipped' && LIMIT_REASONS.has(issue.reason));
428
+ const hit = new Set(unread.map((issue) => issue.reason));
429
+ if (shortened.length)
430
+ hit.add('size');
431
+ const phrases = {
432
+ blocks: `${maxBlocks} sources`,
433
+ depth: `${maxDepth} levels`,
434
+ size: `${maxChars.toLocaleString('en-US')} characters`,
435
+ time: `${maxMs / 1000} s`,
436
+ };
437
+ const reached = ['blocks', 'depth', 'size', 'time'].filter((reason) => hit.has(reason));
438
+ lines.push(`Limit reached (${reached.map((reason) => phrases[reason]).join(', ')}).` +
439
+ (shortened.length
440
+ ? ` ${shortened.map((issue) => `${issue.include} shows lines 1-${issue.shownLines} of ${issue.totalLines}`).join('; ')}.`
441
+ : '') +
442
+ (unread.length ? ` Not ${verb}: ${listed(unread)}; includes inside them are not listed.` : ''));
443
+ }
444
+ const unreadable = of((issue) => issue.kind === 'unreadable');
445
+ if (unreadable.length)
446
+ lines.push(`Could not be read: ${listed(unreadable)}.`);
447
+ const absent = of((issue) => issue.kind === 'absent');
448
+ if (absent.length)
449
+ lines.push(`Not present (INCLUDE ... IF FOUND): ${listed(absent)}.`);
450
+ const cycle = of((issue) => issue.kind === 'cycle');
451
+ if (cycle.length)
452
+ lines.push(`Cycle (includes an enclosing include again; read once): ${listed(cycle)}.`);
453
+ const namespace = of((issue) => issue.reason === 'namespace');
454
+ if (namespace.length)
455
+ lines.push(`Not ${verb} (other namespace; may be customer code): ${listed(namespace)}.`);
456
+ const sap = of((issue) => issue.reason === 'sap');
457
+ if (sap.length)
458
+ lines.push(`Not ${verb} (SAP include): ${listed(sap)}.`);
459
+ const system = of((issue) => issue.reason === 'system');
460
+ if (system.length)
461
+ lines.push(`Not ${verb} (system include): ${listed(system)}.`);
462
+ const name = of((issue) => issue.reason === 'name');
463
+ if (name.length)
464
+ lines.push(`Not ${verb} (unsupported include name): ${listed(name)}.`);
465
+ if (lines.length === 0)
466
+ return undefined;
467
+ if (searched)
468
+ lines.push('Search one include with SAPRead(type="INCL", name="...", grep="...").');
469
+ else if (shortened.length === 0)
470
+ lines.push('Read one include with SAPRead(type="INCL", name="...").');
471
+ else
472
+ lines.push('Read one include with SAPRead(type="INCL", name="..."); lineStart/lineEnd read the rest of a shortened one.');
473
+ return `=== [include expansion] ===\n${lines.join('\n')}`;
474
+ }
475
+ //# sourceMappingURL=include-expansion.js.map