@sap-ux/fiori-annotation-api 0.1.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 (211) hide show
  1. package/LICENSE +201 -0
  2. package/Readme.md +35 -0
  3. package/dist/avt/annotations.d.ts +72 -0
  4. package/dist/avt/annotations.d.ts.map +1 -0
  5. package/dist/avt/annotations.js +508 -0
  6. package/dist/avt/annotations.js.map +1 -0
  7. package/dist/avt/expressions.d.ts +13 -0
  8. package/dist/avt/expressions.d.ts.map +1 -0
  9. package/dist/avt/expressions.js +34 -0
  10. package/dist/avt/expressions.js.map +1 -0
  11. package/dist/avt/find.d.ts +39 -0
  12. package/dist/avt/find.d.ts.map +1 -0
  13. package/dist/avt/find.js +130 -0
  14. package/dist/avt/find.js.map +1 -0
  15. package/dist/avt/index.d.ts +9 -0
  16. package/dist/avt/index.d.ts.map +1 -0
  17. package/dist/avt/index.js +32 -0
  18. package/dist/avt/index.js.map +1 -0
  19. package/dist/avt/metadata.d.ts +10 -0
  20. package/dist/avt/metadata.d.ts.map +1 -0
  21. package/dist/avt/metadata.js +346 -0
  22. package/dist/avt/metadata.js.map +1 -0
  23. package/dist/avt/pointer.d.ts +12 -0
  24. package/dist/avt/pointer.d.ts.map +1 -0
  25. package/dist/avt/pointer.js +188 -0
  26. package/dist/avt/pointer.js.map +1 -0
  27. package/dist/avt/to-internal.d.ts +81 -0
  28. package/dist/avt/to-internal.d.ts.map +1 -0
  29. package/dist/avt/to-internal.js +340 -0
  30. package/dist/avt/to-internal.js.map +1 -0
  31. package/dist/avt/types.d.ts +3 -0
  32. package/dist/avt/types.d.ts.map +1 -0
  33. package/dist/avt/types.js +3 -0
  34. package/dist/avt/types.js.map +1 -0
  35. package/dist/avt/utils.d.ts +61 -0
  36. package/dist/avt/utils.d.ts.map +1 -0
  37. package/dist/avt/utils.js +87 -0
  38. package/dist/avt/utils.js.map +1 -0
  39. package/dist/cds/adapter.d.ts +112 -0
  40. package/dist/cds/adapter.d.ts.map +1 -0
  41. package/dist/cds/adapter.js +703 -0
  42. package/dist/cds/adapter.js.map +1 -0
  43. package/dist/cds/cds-compiler-tokens.d.ts +30 -0
  44. package/dist/cds/cds-compiler-tokens.d.ts.map +1 -0
  45. package/dist/cds/cds-compiler-tokens.js +57 -0
  46. package/dist/cds/cds-compiler-tokens.js.map +1 -0
  47. package/dist/cds/change.d.ts +347 -0
  48. package/dist/cds/change.d.ts.map +1 -0
  49. package/dist/cds/change.js +232 -0
  50. package/dist/cds/change.js.map +1 -0
  51. package/dist/cds/comments.d.ts +15 -0
  52. package/dist/cds/comments.d.ts.map +1 -0
  53. package/dist/cds/comments.js +56 -0
  54. package/dist/cds/comments.js.map +1 -0
  55. package/dist/cds/deletion.d.ts +59 -0
  56. package/dist/cds/deletion.d.ts.map +1 -0
  57. package/dist/cds/deletion.js +821 -0
  58. package/dist/cds/deletion.js.map +1 -0
  59. package/dist/cds/document.d.ts +52 -0
  60. package/dist/cds/document.d.ts.map +1 -0
  61. package/dist/cds/document.js +98 -0
  62. package/dist/cds/document.js.map +1 -0
  63. package/dist/cds/indent.d.ts +20 -0
  64. package/dist/cds/indent.d.ts.map +1 -0
  65. package/dist/cds/indent.js +86 -0
  66. package/dist/cds/indent.js.map +1 -0
  67. package/dist/cds/index.d.ts +3 -0
  68. package/dist/cds/index.d.ts.map +1 -0
  69. package/dist/cds/index.js +21 -0
  70. package/dist/cds/index.js.map +1 -0
  71. package/dist/cds/pointer.d.ts +23 -0
  72. package/dist/cds/pointer.d.ts.map +1 -0
  73. package/dist/cds/pointer.js +438 -0
  74. package/dist/cds/pointer.js.map +1 -0
  75. package/dist/cds/preprocessor.d.ts +12 -0
  76. package/dist/cds/preprocessor.d.ts.map +1 -0
  77. package/dist/cds/preprocessor.js +338 -0
  78. package/dist/cds/preprocessor.js.map +1 -0
  79. package/dist/cds/references.d.ts +35 -0
  80. package/dist/cds/references.d.ts.map +1 -0
  81. package/dist/cds/references.js +413 -0
  82. package/dist/cds/references.js.map +1 -0
  83. package/dist/cds/service.d.ts +11 -0
  84. package/dist/cds/service.d.ts.map +1 -0
  85. package/dist/cds/service.js +37 -0
  86. package/dist/cds/service.js.map +1 -0
  87. package/dist/cds/utils.d.ts +35 -0
  88. package/dist/cds/utils.d.ts.map +1 -0
  89. package/dist/cds/utils.js +75 -0
  90. package/dist/cds/utils.js.map +1 -0
  91. package/dist/cds/writer.d.ts +104 -0
  92. package/dist/cds/writer.d.ts.map +1 -0
  93. package/dist/cds/writer.js +1086 -0
  94. package/dist/cds/writer.js.map +1 -0
  95. package/dist/change-converter.d.ts +54 -0
  96. package/dist/change-converter.d.ts.map +1 -0
  97. package/dist/change-converter.js +639 -0
  98. package/dist/change-converter.js.map +1 -0
  99. package/dist/error.d.ts +35 -0
  100. package/dist/error.d.ts.map +1 -0
  101. package/dist/error.js +64 -0
  102. package/dist/error.js.map +1 -0
  103. package/dist/fiori-service.d.ts +130 -0
  104. package/dist/fiori-service.d.ts.map +1 -0
  105. package/dist/fiori-service.js +362 -0
  106. package/dist/fiori-service.js.map +1 -0
  107. package/dist/index.d.ts +5 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +25 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/protected.d.ts +3 -0
  112. package/dist/protected.d.ts.map +1 -0
  113. package/dist/protected.js +11 -0
  114. package/dist/protected.js.map +1 -0
  115. package/dist/types/adapter.d.ts +46 -0
  116. package/dist/types/adapter.d.ts.map +1 -0
  117. package/dist/types/adapter.js +3 -0
  118. package/dist/types/adapter.js.map +1 -0
  119. package/dist/types/change.d.ts +187 -0
  120. package/dist/types/change.d.ts.map +1 -0
  121. package/dist/types/change.js +52 -0
  122. package/dist/types/change.js.map +1 -0
  123. package/dist/types/index.d.ts +7 -0
  124. package/dist/types/index.d.ts.map +1 -0
  125. package/dist/types/index.js +33 -0
  126. package/dist/types/index.js.map +1 -0
  127. package/dist/types/internal-change.d.ts +98 -0
  128. package/dist/types/internal-change.d.ts.map +1 -0
  129. package/dist/types/internal-change.js +18 -0
  130. package/dist/types/internal-change.js.map +1 -0
  131. package/dist/types/project-info.d.ts +6 -0
  132. package/dist/types/project-info.d.ts.map +1 -0
  133. package/dist/types/project-info.js +3 -0
  134. package/dist/types/project-info.js.map +1 -0
  135. package/dist/types/service.d.ts +27 -0
  136. package/dist/types/service.d.ts.map +1 -0
  137. package/dist/types/service.js +3 -0
  138. package/dist/types/service.js.map +1 -0
  139. package/dist/types/text-file.d.ts +12 -0
  140. package/dist/types/text-file.d.ts.map +1 -0
  141. package/dist/types/text-file.js +3 -0
  142. package/dist/types/text-file.js.map +1 -0
  143. package/dist/utils/constants.d.ts +2 -0
  144. package/dist/utils/constants.d.ts.map +1 -0
  145. package/dist/utils/constants.js +24 -0
  146. package/dist/utils/constants.js.map +1 -0
  147. package/dist/utils/indent.d.ts +10 -0
  148. package/dist/utils/indent.d.ts.map +1 -0
  149. package/dist/utils/indent.js +36 -0
  150. package/dist/utils/indent.js.map +1 -0
  151. package/dist/utils/index.d.ts +7 -0
  152. package/dist/utils/index.d.ts.map +1 -0
  153. package/dist/utils/index.js +16 -0
  154. package/dist/utils/index.js.map +1 -0
  155. package/dist/utils/path.d.ts +8 -0
  156. package/dist/utils/path.d.ts.map +1 -0
  157. package/dist/utils/path.js +31 -0
  158. package/dist/utils/path.js.map +1 -0
  159. package/dist/utils/pointer.d.ts +11 -0
  160. package/dist/utils/pointer.d.ts.map +1 -0
  161. package/dist/utils/pointer.js +26 -0
  162. package/dist/utils/pointer.js.map +1 -0
  163. package/dist/utils/reference.d.ts +10 -0
  164. package/dist/utils/reference.d.ts.map +1 -0
  165. package/dist/utils/reference.js +20 -0
  166. package/dist/utils/reference.js.map +1 -0
  167. package/dist/utils/text-edits.d.ts +12 -0
  168. package/dist/utils/text-edits.d.ts.map +1 -0
  169. package/dist/utils/text-edits.js +29 -0
  170. package/dist/utils/text-edits.js.map +1 -0
  171. package/dist/vocabularies.d.ts +11 -0
  172. package/dist/vocabularies.d.ts.map +1 -0
  173. package/dist/vocabularies.js +26 -0
  174. package/dist/vocabularies.js.map +1 -0
  175. package/dist/xml/adapter.d.ts +85 -0
  176. package/dist/xml/adapter.d.ts.map +1 -0
  177. package/dist/xml/adapter.js +579 -0
  178. package/dist/xml/adapter.js.map +1 -0
  179. package/dist/xml/changes.d.ts +117 -0
  180. package/dist/xml/changes.d.ts.map +1 -0
  181. package/dist/xml/changes.js +34 -0
  182. package/dist/xml/changes.js.map +1 -0
  183. package/dist/xml/comments.d.ts +17 -0
  184. package/dist/xml/comments.d.ts.map +1 -0
  185. package/dist/xml/comments.js +49 -0
  186. package/dist/xml/comments.js.map +1 -0
  187. package/dist/xml/document.d.ts +11 -0
  188. package/dist/xml/document.d.ts.map +1 -0
  189. package/dist/xml/document.js +3 -0
  190. package/dist/xml/document.js.map +1 -0
  191. package/dist/xml/index.d.ts +3 -0
  192. package/dist/xml/index.d.ts.map +1 -0
  193. package/dist/xml/index.js +8 -0
  194. package/dist/xml/index.js.map +1 -0
  195. package/dist/xml/pointer.d.ts +10 -0
  196. package/dist/xml/pointer.d.ts.map +1 -0
  197. package/dist/xml/pointer.js +29 -0
  198. package/dist/xml/pointer.js.map +1 -0
  199. package/dist/xml/references.d.ts +9 -0
  200. package/dist/xml/references.d.ts.map +1 -0
  201. package/dist/xml/references.js +87 -0
  202. package/dist/xml/references.js.map +1 -0
  203. package/dist/xml/service.d.ts +12 -0
  204. package/dist/xml/service.d.ts.map +1 -0
  205. package/dist/xml/service.js +55 -0
  206. package/dist/xml/service.js.map +1 -0
  207. package/dist/xml/writer.d.ts +39 -0
  208. package/dist/xml/writer.d.ts.map +1 -0
  209. package/dist/xml/writer.js +855 -0
  210. package/dist/xml/writer.js.map +1 -0
  211. package/package.json +47 -0
@@ -0,0 +1,821 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDeletionRangeForNode = exports.getTextEditsForDeletionRanges = void 0;
4
+ const odata_annotation_core_1 = require("@sap-ux/odata-annotation-core");
5
+ const odata_annotation_core_types_1 = require("@sap-ux/odata-annotation-core-types");
6
+ const cds_odata_annotation_converter_1 = require("@sap-ux/cds-odata-annotation-converter");
7
+ const utils_1 = require("./utils");
8
+ const cdsKeywords = [
9
+ 'ABSTRACT',
10
+ 'ACTION',
11
+ 'ANNOTATE',
12
+ 'ANNOTATION',
13
+ 'ASPECT',
14
+ 'CONTEXT',
15
+ 'DEFINE',
16
+ 'ENTITY',
17
+ 'EVENT',
18
+ 'EXTEND',
19
+ 'FUNCTION',
20
+ 'SERVICE',
21
+ 'TYPE',
22
+ 'USING',
23
+ 'VIEW'
24
+ ];
25
+ const separatorForKind = {
26
+ Undefined: '',
27
+ TermName: ',',
28
+ TermFqName: ',',
29
+ Annotation: '',
30
+ TargetElement: ';',
31
+ TargetBoundAction: ';',
32
+ TargetParameter: ',',
33
+ TargetArtifact: ';',
34
+ TargetFromWith: ''
35
+ };
36
+ /**
37
+ * Converts deletion ranges to text edits.
38
+ *
39
+ * @param deletionRanges - Deletion ranges.
40
+ * @param vocabularyAliases - Vocabulary aliases.
41
+ * @param tokens - All tokens in the document.
42
+ * @param annotationFile - Internal representation root.
43
+ * @param includeTarget - Flag indicating if the target should also be deleted.
44
+ * @returns Expanded text edit.
45
+ */
46
+ function getTextEditsForDeletionRanges(deletionRanges, vocabularyAliases, tokens, annotationFile, includeTarget) {
47
+ let changed;
48
+ // ranges need to be in order to be properly merged
49
+ deletionRanges.sort((a, b) => a.termRange.start - b.termRange.start);
50
+ do {
51
+ expandDeletionRanges(vocabularyAliases, tokens, deletionRanges, annotationFile, includeTarget);
52
+ changed = mergeDeletionRanges(tokens, deletionRanges);
53
+ } while (changed);
54
+ // build list of text edits for deletion (include neighboring sibling separators)
55
+ const textEdits = [];
56
+ for (const deletionRange of deletionRanges) {
57
+ const { deletionRange: deletionRangeWithSeparator, previousSeparatorIncluded, siblingSeparatorFound } = includeSiblingSeparator(tokens, deletionRange);
58
+ const range = getRangeFromTokenRange(tokens, deletionRangeWithSeparator.tokenRange, previousSeparatorIncluded, siblingSeparatorFound);
59
+ if (range) {
60
+ textEdits.push(odata_annotation_core_types_1.TextEdit.del(range));
61
+ }
62
+ }
63
+ return textEdits;
64
+ }
65
+ exports.getTextEditsForDeletionRanges = getTextEditsForDeletionRanges;
66
+ /**
67
+ * Creates deletion ranges for a node.
68
+ *
69
+ * @param vocabularyService - Vocabulary API.
70
+ * @param vocabularyAliases - Vocabulary aliases.
71
+ * @param termIndex - Term index in the target.
72
+ * @param tokens - All tokens in the document.
73
+ * @param annotation - Annotation to be deleted.
74
+ * @param target - Target name.
75
+ * @returns Deletion range for the node.
76
+ */
77
+ function getDeletionRangeForNode(vocabularyService, vocabularyAliases, termIndex, tokens, annotation, target) {
78
+ var _a;
79
+ if (annotation === null || annotation === void 0 ? void 0 : annotation.range) {
80
+ const termValue = annotation.term.value;
81
+ const nativeCdsTermName = (_a = vocabularyService.cdsVocabulary.reverseNameMap.get(termValue)) !== null && _a !== void 0 ? _a : '';
82
+ const tokenRange = (0, utils_1.getTokenRange)(tokens, annotation.range);
83
+ const kind = findTermKind(vocabularyAliases, tokens, tokenRange, nativeCdsTermName);
84
+ return {
85
+ kind,
86
+ printPattern: (0, cds_odata_annotation_converter_1.resolveTarget)(target).printPattern,
87
+ termRange: { start: termIndex, end: termIndex },
88
+ tokenRange,
89
+ isExpanded: false
90
+ };
91
+ }
92
+ return undefined;
93
+ }
94
+ exports.getDeletionRangeForNode = getDeletionRangeForNode;
95
+ function findTermKind(vocabularyAliases, tokens, tokenRange, nativeCdsTermName) {
96
+ let deletionRangeKind;
97
+ for (let i = tokenRange.start; i <= tokenRange.end && !deletionRangeKind; i++) {
98
+ if (nativeCdsTermName && tokens[i].text === nativeCdsTermName) {
99
+ deletionRangeKind = "TermFqName" /* DeletionRangeKind.TERM_FQ_NAME */; // e.g. @title
100
+ }
101
+ else if (vocabularyAliases.has(tokens[i].text) && tokens[i + 1].text === '.') {
102
+ deletionRangeKind = "TermFqName" /* DeletionRangeKind.TERM_FQ_NAME */;
103
+ }
104
+ else if (/^\w+$/.test(tokens[i].text)) {
105
+ // TODO use better regular expression for OData simple identifier
106
+ deletionRangeKind = "TermName" /* DeletionRangeKind.TERM_NAME */;
107
+ }
108
+ else {
109
+ deletionRangeKind = "Undefined" /* DeletionRangeKind.UNDEFINED */;
110
+ }
111
+ }
112
+ return deletionRangeKind !== null && deletionRangeKind !== void 0 ? deletionRangeKind : "Undefined" /* DeletionRangeKind.UNDEFINED */;
113
+ }
114
+ function expandDeletionRanges(vocabularyAliases, tokens, deletionRanges, annotationFile, includeTarget) {
115
+ deletionRanges
116
+ .filter((deletionRange) => !deletionRange.isExpanded)
117
+ .forEach((deletionRange) => {
118
+ let changed;
119
+ do {
120
+ changed = true;
121
+ const options = {
122
+ separator: separatorForKind[deletionRange.kind],
123
+ beforeTokenIndex: deletionRange.tokenRange.start - 1,
124
+ afterTokenIndex: deletionRange.tokenRange.end + 1,
125
+ vocabularyAliases,
126
+ includeTarget
127
+ };
128
+ if (canIncludePrecedingComment(tokens, options)) {
129
+ // include comment before annotation (full lines, also block comments)
130
+ includePrecedingComment(deletionRange, options);
131
+ }
132
+ else if (canIncludeSubsequentComment(deletionRange, tokens, options)) {
133
+ // include comment after annotation (line comment only; in same line)
134
+ includeSubsequentComment(deletionRange, options);
135
+ }
136
+ else if (canIncludeSurroundingBrackets(deletionRange, tokens, options, true)) {
137
+ // include surrounding { }, maybe also <vocabularyAlias>:
138
+ includeSurroundingBrackets(deletionRange, tokens, options, true);
139
+ }
140
+ else if (canIncludeSurroundingBrackets(deletionRange, tokens, options)) {
141
+ // include surrounding ( )
142
+ includeSurroundingBrackets(deletionRange, tokens, options);
143
+ }
144
+ else if (deletionRange.kind === "TermFqName" /* DeletionRangeKind.TERM_FQ_NAME */ &&
145
+ tokens[options.beforeTokenIndex].text === '@') {
146
+ // @<fqTermNameWithValue> : include prefix @
147
+ deletionRange.tokenRange.start = options.beforeTokenIndex;
148
+ deletionRange.kind = "Annotation" /* DeletionRangeKind.ANNOTATION */;
149
+ }
150
+ else if (canIncludeSimpleTargetName(deletionRange, tokens, options)) {
151
+ // <element|boundAction|param> <annotation> : include name before annotation
152
+ changed = includeSimpleTargetName(deletionRange, tokens, options);
153
+ }
154
+ else if (canIncludeSimpleTargetName(deletionRange, tokens, options, true)) {
155
+ // <annotation> <element|boundAction|param> : include name after annotation
156
+ changed = includeSimpleTargetName(deletionRange, tokens, options, true);
157
+ }
158
+ else if (canIncludeAnnotateWithStatement(deletionRange, tokens, options)) {
159
+ // annotate <entity> with <annotation> : include 'annotate <entity> with ' before annotation
160
+ // annotate <entity> with <elementAnnotations> : include 'annotate <entity> with ' before annotation
161
+ changed = includeAnnotateWithStatement(deletionRange, tokens, options, annotationFile);
162
+ }
163
+ else if (canIncludeAnnotateWithStatement(deletionRange, tokens, options, true)) {
164
+ // annotate <entity> with actions <actionAnnotations> : include 'annotate <entity> with actions' before annotation
165
+ // find index of token 'annotate', and accept any target in the form <segment1>.<segment2>.<entityName>
166
+ changed = includeAnnotateWithStatement(deletionRange, tokens, options, annotationFile, true);
167
+ }
168
+ else if (canIncludeAnnotateWithStatement(deletionRange, tokens, options, true, true)) {
169
+ // annotate <entity> with <otherAnnos> actions <actionAnnotations> : include 'actions' before annotation
170
+ changed = includeAnnotateWithStatement(deletionRange, tokens, options, annotationFile, true, true);
171
+ }
172
+ else {
173
+ // TODO remove orphaned target elements/entities ?
174
+ changed = false;
175
+ }
176
+ } while (changed);
177
+ deletionRange.isExpanded = true;
178
+ });
179
+ }
180
+ function mergeDeletionRanges(tokens, deletionRanges) {
181
+ let changed = false;
182
+ for (let rangeIndex = 0; rangeIndex < deletionRanges.length; rangeIndex++) {
183
+ const current = deletionRanges[rangeIndex];
184
+ let doMerge;
185
+ do {
186
+ doMerge = false;
187
+ const next = rangeIndex + 1 < deletionRanges.length ? deletionRanges[rangeIndex + 1] : null;
188
+ if (next && current.termRange.end + 1 === next.termRange.start) {
189
+ // merge possible if at most a single separator token is between deletion ranges
190
+ const nonCommentTokenIndexes = [];
191
+ let nextIndex = current.tokenRange.end;
192
+ do {
193
+ nextIndex = getNeighboringTokenIndex(tokens, nextIndex, +1);
194
+ if (nextIndex > 0 && nextIndex < next.tokenRange.start) {
195
+ nonCommentTokenIndexes.push(nextIndex);
196
+ }
197
+ } while (nextIndex > 0 && nextIndex < next.tokenRange.start);
198
+ if (nonCommentTokenIndexes.length === 0 ||
199
+ (nonCommentTokenIndexes.length === 1 &&
200
+ tokens[nonCommentTokenIndexes[0]].text === separatorForKind[current.kind])) {
201
+ doMerge = true;
202
+ }
203
+ if (doMerge) {
204
+ // do the merge
205
+ current.termRange.end = next.termRange.end;
206
+ current.tokenRange.end = next.tokenRange.end;
207
+ current.isExpanded = false;
208
+ deletionRanges.splice(rangeIndex + 1, 1);
209
+ changed = true;
210
+ }
211
+ }
212
+ } while (doMerge);
213
+ }
214
+ return changed;
215
+ }
216
+ function getNeighborIndex(tokens, index, next = false) {
217
+ let neighboringIndex = index + (next ? +1 : -1);
218
+ if (!tokens[neighboringIndex]) {
219
+ return -1;
220
+ }
221
+ while (isComment(tokens[neighboringIndex])) {
222
+ neighboringIndex = neighboringIndex + (next ? +1 : -1);
223
+ if (!tokens[neighboringIndex]) {
224
+ return -1;
225
+ }
226
+ }
227
+ return neighboringIndex;
228
+ }
229
+ /**
230
+ * Get neighboring token index (ignoring comment tokens).
231
+ *
232
+ * @param tokens - All tokens in the document.
233
+ * @param index - current token index.
234
+ * @param offset - Token offset.
235
+ * @returns Index of a token.
236
+ */
237
+ function getNeighboringTokenIndex(tokens, index, offset) {
238
+ let resultIndex = index;
239
+ for (let count = 0; count < Math.abs(offset) && resultIndex >= 0; count++) {
240
+ resultIndex = getNeighborIndex(tokens, resultIndex, offset > 0);
241
+ }
242
+ return resultIndex;
243
+ }
244
+ /**
245
+ * Can simple target name before/after annotation be included into deletion range?
246
+ * i.e. <elementName|boundActionName|paramName> <annotation> or
247
+ * <boundActionName> <annotatedParameter>.
248
+ *
249
+ * @param deletionRange - Deletion range.
250
+ * @param tokens - All tokens in the document.
251
+ * @param options - Deletion options.
252
+ * @param nameIsAfter - Flag indicating where name is located.
253
+ * @returns True if target name can be included in deletion range.
254
+ */
255
+ function canIncludeSimpleTargetName(deletionRange, tokens, options, nameIsAfter) {
256
+ var _a, _b;
257
+ if (!options.includeTarget) {
258
+ return false;
259
+ }
260
+ if (isEmbedded(tokens, deletionRange.tokenRange.start)) {
261
+ return false;
262
+ }
263
+ if (deletionRange.printPattern === "boundParameter" /* PrintPattern.boundParameter */) {
264
+ // cases: annotation on bound parameter and parameter bound action
265
+ if (!["Annotation" /* DeletionRangeKind.ANNOTATION */, "TargetParameter" /* DeletionRangeKind.TARGET_PARAMETER */].includes(deletionRange.kind)) {
266
+ return false;
267
+ }
268
+ }
269
+ else if (deletionRange.kind !== "Annotation" /* DeletionRangeKind.ANNOTATION */) {
270
+ // for all other print patterns: deletion range needs to represent whole annotation
271
+ return false;
272
+ }
273
+ let indexIdentifier = nameIsAfter ? options.afterTokenIndex : options.beforeTokenIndex;
274
+ // skip comments token in between
275
+ while (isComment(tokens[indexIdentifier])) {
276
+ indexIdentifier = indexIdentifier + (nameIsAfter ? +1 : -1);
277
+ }
278
+ // check there are no further annotations targeting identifier
279
+ let firstTokenIndex, lastTokenIndex;
280
+ if (nameIsAfter) {
281
+ firstTokenIndex = deletionRange.tokenRange.start;
282
+ lastTokenIndex = indexIdentifier;
283
+ }
284
+ else {
285
+ firstTokenIndex = indexIdentifier;
286
+ lastTokenIndex = deletionRange.tokenRange.end;
287
+ }
288
+ if (["element" /* PrintPattern.element */].includes(deletionRange.printPattern)) {
289
+ if (!['{', ';'].includes(getNeighboringToken(tokens, firstTokenIndex, -1).text) ||
290
+ !['}', ';'].includes(getNeighboringToken(tokens, lastTokenIndex, 1).text)) {
291
+ return false;
292
+ }
293
+ }
294
+ switch ((_a = tokens[indexIdentifier]) === null || _a === void 0 ? void 0 : _a.isIdentifier) {
295
+ case 'Element':
296
+ case 'ExtElement':
297
+ return deletionRange.printPattern === "element" /* PrintPattern.element */;
298
+ case 'Param':
299
+ case 'ExtParam':
300
+ return ["parameter" /* PrintPattern.parameter */, "boundParameter" /* PrintPattern.boundParameter */].includes(deletionRange.printPattern);
301
+ case 'BoundAction':
302
+ case 'ExtBoundAction':
303
+ if (deletionRange.kind === "TargetParameter" /* DeletionRangeKind.TARGET_PARAMETER */) {
304
+ // target name is bound action - make sure it has no annotations itself
305
+ if (!['{', ';'].includes((_b = getNeighboringToken(tokens, indexIdentifier, -1)) === null || _b === void 0 ? void 0 : _b.text)) {
306
+ return false;
307
+ }
308
+ }
309
+ return ["boundAction" /* PrintPattern.boundAction */, "boundParameter" /* PrintPattern.boundParameter */].includes(deletionRange.printPattern);
310
+ default:
311
+ return false;
312
+ }
313
+ }
314
+ /**
315
+ * Include simple target name before/after annotation into deletion range
316
+ * i.e. <elementName|boundActionName|paramName> <annotation> or
317
+ * <boundActionName> <annotatedParameter>.
318
+ *
319
+ * @param deletionRange - Deletion range.
320
+ * @param tokens - All tokens in the document.
321
+ * @param options - Deletion options.
322
+ * @param nameIsAfter - Flag indicating where name is located.
323
+ * @returns True if target name can be added to deletion range.
324
+ */
325
+ function includeSimpleTargetName(deletionRange, tokens, options, nameIsAfter) {
326
+ var _a, _b;
327
+ // <element|boundAction|param> <annotation> : include name before/after annotation
328
+ let changed = true;
329
+ const checkIndex = nameIsAfter ? options.afterTokenIndex + 1 : options.afterTokenIndex;
330
+ if (deletionRange.printPattern === "boundAction" /* PrintPattern.boundAction */ && hasParameterList(tokens, checkIndex)) {
331
+ changed = false; // to be deleted bound action annotation is followed by parameter list - don't delete bound action name
332
+ }
333
+ else if (nameIsAfter) {
334
+ deletionRange.tokenRange.end = getNeighboringTokenIndex(tokens, deletionRange.tokenRange.end, +1);
335
+ deletionRange.kind = mapTokenIdentifierToKind((_a = tokens[deletionRange.tokenRange.end]) === null || _a === void 0 ? void 0 : _a.isIdentifier);
336
+ }
337
+ else {
338
+ deletionRange.tokenRange.start = getNeighboringTokenIndex(tokens, deletionRange.tokenRange.start, -1);
339
+ deletionRange.kind = mapTokenIdentifierToKind((_b = tokens[deletionRange.tokenRange.start]) === null || _b === void 0 ? void 0 : _b.isIdentifier);
340
+ }
341
+ return changed;
342
+ }
343
+ /**
344
+ * Can include 'annotate with' statement
345
+ * i.e. annotate <entity> with <annotation> : include 'annotate <entity> with ' before annotation
346
+ * annotate <entity> with <elementAnnotations> : include 'annotate <entity> with ' before annotation
347
+ * annotate <entity> with actions <actionAnnotations> : include 'annotate <entity> with actions' before annotation.
348
+ *
349
+ * @param deletionRange - Deletion range.
350
+ * @param tokens - All tokens in the document.
351
+ * @param options - Deletion options.
352
+ * @param forAction - Check actions.
353
+ * @param includeActionOnly - Delete only actions.
354
+ * @returns True if `annotate with` can be deleted.
355
+ */
356
+ function canIncludeAnnotateWithStatement(deletionRange, tokens, options, forAction, includeActionOnly) {
357
+ var _a, _b, _c, _d, _e;
358
+ if (!options.includeTarget) {
359
+ return false;
360
+ }
361
+ if (deletionRange.kind === "Annotation" /* DeletionRangeKind.ANNOTATION */) {
362
+ if (deletionRange.printPattern !== "artifact" /* PrintPattern.artifact */) {
363
+ return false;
364
+ }
365
+ }
366
+ else if (deletionRange.kind === "TargetElementsList" /* DeletionRangeKind.TARGET_ELEMENTS_LIST */) {
367
+ if (deletionRange.printPattern !== "element" /* PrintPattern.element */) {
368
+ return false;
369
+ }
370
+ }
371
+ else if (deletionRange.kind === "TargetParameter" /* DeletionRangeKind.TARGET_PARAMETER */) {
372
+ if (deletionRange.printPattern !== "parameter" /* PrintPattern.parameter */) {
373
+ return false;
374
+ }
375
+ }
376
+ else if (deletionRange.kind === "TargetBoundAction" /* DeletionRangeKind.TARGET_BOUND_ACTION */) {
377
+ if (!["boundAction" /* PrintPattern.boundAction */, "boundParameter" /* PrintPattern.boundParameter */].includes(deletionRange.printPattern)) {
378
+ return false;
379
+ }
380
+ }
381
+ else {
382
+ return false; // unsupported combination
383
+ }
384
+ if (getClosingTokenIndex(tokens, options.afterTokenIndex) < 0) {
385
+ return false;
386
+ } // annotate statement must be finished
387
+ let beforeTokenIndex = options.beforeTokenIndex;
388
+ while (isComment(tokens[beforeTokenIndex])) {
389
+ --beforeTokenIndex;
390
+ }
391
+ if (forAction) {
392
+ if (includeActionOnly) {
393
+ return tokens[beforeTokenIndex].text.toUpperCase() === 'ACTIONS';
394
+ }
395
+ else {
396
+ return (tokens[beforeTokenIndex].text.toUpperCase() === 'ACTIONS' &&
397
+ ((_a = getNeighboringToken(tokens, beforeTokenIndex, -1)) === null || _a === void 0 ? void 0 : _a.text.toUpperCase()) === 'WITH' &&
398
+ ['Annotate', 'Extend', 'Ext'].includes((_c = (_b = getNeighboringToken(tokens, beforeTokenIndex, -2)) === null || _b === void 0 ? void 0 : _b.isIdentifier) !== null && _c !== void 0 ? _c : ''));
399
+ }
400
+ }
401
+ else {
402
+ return (tokens[beforeTokenIndex].text.toUpperCase() === 'WITH' &&
403
+ ['Annotate', 'Extend', 'Ext'].includes((_e = (_d = getNeighboringToken(tokens, beforeTokenIndex, -1)) === null || _d === void 0 ? void 0 : _d.isIdentifier) !== null && _e !== void 0 ? _e : ''));
404
+ }
405
+ }
406
+ /**
407
+ * Check if annotation is present before tokenIndex (pointing to 'annotate' keyword)
408
+ * which targets the target of the 'annotate' statement.
409
+ *
410
+ * @param position - Start position of 'annotate' keyword.
411
+ * @param annotationFile - Internal representation root.
412
+ * @returns True if there are relevant annotations before the position.
413
+ */
414
+ function annotationsPresentBefore(position, annotationFile) {
415
+ var _a, _b;
416
+ // find target for annotation term positioned immediately before tokenIndex
417
+ let targetOfPreviousAnnotation;
418
+ let previousTermEndPosition; // closest term end position before tokenIndex
419
+ annotationFile.targets.forEach((target) => {
420
+ target.terms.forEach((term) => {
421
+ var _a, _b, _c;
422
+ const termRangeEndPosition = (_c = (_b = (_a = term.attributes) === null || _a === void 0 ? void 0 : _a['Term']) === null || _b === void 0 ? void 0 : _b.valueRange) === null || _c === void 0 ? void 0 : _c.end;
423
+ if (termRangeEndPosition && (0, odata_annotation_core_1.isBefore)(termRangeEndPosition, position)) {
424
+ if (!previousTermEndPosition || (0, odata_annotation_core_1.isBefore)(previousTermEndPosition, termRangeEndPosition)) {
425
+ previousTermEndPosition = termRangeEndPosition;
426
+ targetOfPreviousAnnotation = target;
427
+ }
428
+ }
429
+ });
430
+ });
431
+ // if that target is starting after tokenIndexPosition, then current 'annotate' statement must not be deleted
432
+ // example:
433
+ // @title: 'foo' // this annotation targets "AdminService.Books"
434
+ // annotate AdminService.Books with { ... // don't delete, otherwise @title is orphaned
435
+ return (!!((_a = targetOfPreviousAnnotation === null || targetOfPreviousAnnotation === void 0 ? void 0 : targetOfPreviousAnnotation.nameRange) === null || _a === void 0 ? void 0 : _a.end) && (0, odata_annotation_core_1.isBefore)(position, (_b = targetOfPreviousAnnotation === null || targetOfPreviousAnnotation === void 0 ? void 0 : targetOfPreviousAnnotation.nameRange) === null || _b === void 0 ? void 0 : _b.end));
436
+ }
437
+ /**
438
+ * Can preceding comment be included in deletion range ?
439
+ *
440
+ * @param tokens - All tokens in the document.
441
+ * @param options - Deletion options.
442
+ * @returns True if preceding comment can be added to deletion range.
443
+ */
444
+ function canIncludePrecedingComment(tokens, options) {
445
+ const { beforeTokenIndex } = options;
446
+ const commentToken = tokens[beforeTokenIndex];
447
+ if (!commentToken || !isComment(commentToken)) {
448
+ return false;
449
+ }
450
+ // token represents a comment
451
+ if (beforeTokenIndex === 0) {
452
+ return true;
453
+ } // file starts with this comment
454
+ // comments are "associated" with current annotation only if they spans the whole line
455
+ // (otherwise it is considered line end comment of previous content)
456
+ const precedingNonCommentToken = getNeighboringToken(tokens, beforeTokenIndex, -1);
457
+ return !precedingNonCommentToken || precedingNonCommentToken.line < commentToken.line;
458
+ }
459
+ /**
460
+ * Include preceding comment.
461
+ *
462
+ * @param deletionRange - Deletion range.
463
+ * @param options - Deletion options.
464
+ */
465
+ function includePrecedingComment(deletionRange, options) {
466
+ deletionRange.tokenRange.start = options.beforeTokenIndex;
467
+ }
468
+ /**
469
+ * Can subsequent comment be included in deletion range ?
470
+ *
471
+ * @param deletionRange - Deletion range.
472
+ * @param tokens - All tokens in the document.
473
+ * @param options - Deletion options.
474
+ * @returns True if subsequent comment can be deleted.
475
+ */
476
+ function canIncludeSubsequentComment(deletionRange, tokens, options) {
477
+ const { afterTokenIndex } = options;
478
+ const commentToken = tokens[afterTokenIndex];
479
+ if (!isComment(commentToken)) {
480
+ return false;
481
+ }
482
+ // token represents a comment
483
+ // comments are "associated" with current range only if it is in same line as range end and next non-comment token is in next line
484
+ if (commentToken.line > tokens[deletionRange.tokenRange.end].line) {
485
+ return false;
486
+ } // comment not in same line
487
+ const nextToken = getNeighboringToken(tokens, afterTokenIndex, +1);
488
+ return !nextToken || nextToken.line > commentToken.line; // make sure no further non-comment token after comment
489
+ }
490
+ /**
491
+ * Include subsequent comment.
492
+ *
493
+ * @param deletionRange - Deletion range.
494
+ * @param options - Deletion options.
495
+ */
496
+ function includeSubsequentComment(deletionRange, options) {
497
+ deletionRange.tokenRange.end = options.afterTokenIndex;
498
+ }
499
+ /**
500
+ * Can surrounding brackets be included in deletion range ?
501
+ *
502
+ * @param deletionRange - Deletion range.
503
+ * @param tokens - All tokens in the document.
504
+ * @param options - Deletion options.
505
+ * @param asCurlyBracket - True if `{}` should be checked otherwise `() will be checked.
506
+ * @returns True if surrounding brackets can be deleted.
507
+ */
508
+ function canIncludeSurroundingBrackets(deletionRange, tokens, options, asCurlyBracket) {
509
+ var _a;
510
+ const { separator } = options;
511
+ const beforeTokenIndex = getNeighboringTokenIndex(tokens, deletionRange.tokenRange.start, -1);
512
+ const afterTokenIndex = getNeighboringTokenIndex(tokens, deletionRange.tokenRange.end, +1);
513
+ const openingBracket = asCurlyBracket ? '{' : '(';
514
+ const closingBracket = asCurlyBracket ? '}' : ')';
515
+ const supportedKinds = asCurlyBracket
516
+ ? ["TermName" /* DeletionRangeKind.TERM_NAME */, "TargetElement" /* DeletionRangeKind.TARGET_ELEMENT */, "TargetBoundAction" /* DeletionRangeKind.TARGET_BOUND_ACTION */]
517
+ : ["TermFqName" /* DeletionRangeKind.TERM_FQ_NAME */, "TargetParameter" /* DeletionRangeKind.TARGET_PARAMETER */];
518
+ if (!supportedKinds.includes(deletionRange.kind)) {
519
+ return false;
520
+ }
521
+ if (tokens[beforeTokenIndex].text !== openingBracket) {
522
+ return false;
523
+ }
524
+ return (tokens[afterTokenIndex].text === closingBracket ||
525
+ (tokens[afterTokenIndex].text === separator &&
526
+ ((_a = getNeighboringToken(tokens, afterTokenIndex, +1)) === null || _a === void 0 ? void 0 : _a.text) === closingBracket));
527
+ }
528
+ /**
529
+ * Include surrounding brackets into deletion range.
530
+ *
531
+ * @param deletionRange - Deletion range.
532
+ * @param tokens - All tokens in the document.
533
+ * @param options - Deletion options.
534
+ * @param asCurlyBracket - True if `{}` should be checked otherwise `() will be checked.
535
+ */
536
+ function includeSurroundingBrackets(deletionRange, tokens, options, asCurlyBracket) {
537
+ var _a, _b;
538
+ // { <termNameWithValue> } : extend range to include surrounding curly brackets
539
+ // { <targetElement with annotations> } : extend range to include surrounding curly brackets
540
+ const { vocabularyAliases } = options;
541
+ const beforeTokenIndex = getNeighboringTokenIndex(tokens, deletionRange.tokenRange.start, -1);
542
+ const afterTokenIndex = getNeighboringTokenIndex(tokens, deletionRange.tokenRange.end, +1);
543
+ const closingBracket = asCurlyBracket ? '}' : ')';
544
+ deletionRange.tokenRange = {
545
+ start: beforeTokenIndex,
546
+ end: tokens[afterTokenIndex].text === closingBracket
547
+ ? afterTokenIndex
548
+ : getNeighboringTokenIndex(tokens, afterTokenIndex, +1)
549
+ };
550
+ if (asCurlyBracket &&
551
+ deletionRange.kind === "TermName" /* DeletionRangeKind.TERM_NAME */ &&
552
+ ((_a = getNeighboringToken(tokens, beforeTokenIndex, -1)) === null || _a === void 0 ? void 0 : _a.text) === ':' &&
553
+ vocabularyAliases.has((_b = getNeighboringToken(tokens, beforeTokenIndex, -2)) === null || _b === void 0 ? void 0 : _b.text)) {
554
+ // <VocabularyALias>: <termNameWithValue> : include vocabulary alias name and colon
555
+ deletionRange.tokenRange.start = getNeighboringTokenIndex(tokens, beforeTokenIndex, -2);
556
+ deletionRange.kind = "TermFqName" /* DeletionRangeKind.TERM_FQ_NAME */;
557
+ }
558
+ else if (asCurlyBracket && deletionRange.kind === "TargetElement" /* DeletionRangeKind.TARGET_ELEMENT */) {
559
+ deletionRange.kind = "TargetElementsList" /* DeletionRangeKind.TARGET_ELEMENTS_LIST */;
560
+ }
561
+ }
562
+ /**
563
+ * Include 'annotate with' statement
564
+ * i.e. annotate <entity> with <annotation> : include 'annotate <entity> with ' before annotation
565
+ * annotate <entity> with <elementAnnotations> : include 'annotate <entity> with ' before annotation
566
+ * annotate <entity> with actions <actionAnnotations> : include 'annotate <entity> with actions' before annotation.
567
+ *
568
+ * @param deletionRange - Deletion range.
569
+ * @param tokens - All tokens in the document.
570
+ * @param options - Deletion options.
571
+ * @param annotationFile - Internal representation root.
572
+ * @param forAction - Deletion of actions.
573
+ * @param withActionOnly - Deletes only actions.
574
+ * @returns True if annotate statement can be deleted.
575
+ */
576
+ function includeAnnotateWithStatement(deletionRange, tokens, options, annotationFile, forAction, withActionOnly) {
577
+ var _a;
578
+ let beforeTokenIndex = options.beforeTokenIndex;
579
+ while (isComment(tokens[beforeTokenIndex])) {
580
+ --beforeTokenIndex;
581
+ }
582
+ let startDeletionTokenIndex = beforeTokenIndex;
583
+ if (!withActionOnly) {
584
+ startDeletionTokenIndex = getNeighboringTokenIndex(tokens, startDeletionTokenIndex, forAction ? -3 : -2);
585
+ }
586
+ let changed = false;
587
+ if (!withActionOnly) {
588
+ // find index of token 'annotate', and accept any target in the form <segment1>.<segment2>.<entityName>
589
+ let done = false;
590
+ while (!done) {
591
+ if (tokens[startDeletionTokenIndex].text.toUpperCase() === 'ANNOTATE') {
592
+ done = true;
593
+ }
594
+ else if (tokens[startDeletionTokenIndex].text === '.') {
595
+ startDeletionTokenIndex--; // accepted as part of target
596
+ }
597
+ else if (['Annotate', 'Extend', 'Ext'].includes(((_a = tokens[startDeletionTokenIndex]) === null || _a === void 0 ? void 0 : _a.isIdentifier) || '')) {
598
+ startDeletionTokenIndex--; // accepted as part of target
599
+ }
600
+ else if (isComment(tokens[startDeletionTokenIndex])) {
601
+ startDeletionTokenIndex--; // include comment
602
+ }
603
+ else {
604
+ startDeletionTokenIndex = -1; // anything else found: don't delete this
605
+ done = true;
606
+ }
607
+ }
608
+ }
609
+ const closingSemicolonIndex = getClosingTokenIndex(tokens, deletionRange.tokenRange.end);
610
+ if (startDeletionTokenIndex > -1 && closingSemicolonIndex > -1) {
611
+ // semicolon will be deleted as neighboring sibling separator
612
+ deletionRange.tokenRange.end = closingSemicolonIndex - 1;
613
+ const startDeletionPosition = getPositionFromToken(tokens[startDeletionTokenIndex]);
614
+ if (withActionOnly) {
615
+ deletionRange.tokenRange.start = startDeletionTokenIndex;
616
+ deletionRange.kind = "TargetFromWith" /* DeletionRangeKind.TARGET_FROM_WITH */;
617
+ }
618
+ else if (startDeletionPosition && annotationsPresentBefore(startDeletionPosition, annotationFile)) {
619
+ // only remove from the 'with' token - keep 'annotate <target>;' for preceding annotations
620
+ deletionRange.tokenRange.start = forAction
621
+ ? getNeighboringTokenIndex(tokens, beforeTokenIndex, -1)
622
+ : beforeTokenIndex;
623
+ deletionRange.kind = "TargetFromWith" /* DeletionRangeKind.TARGET_FROM_WITH */;
624
+ }
625
+ else {
626
+ deletionRange.tokenRange.start = startDeletionTokenIndex;
627
+ deletionRange.kind = "TargetArtifact" /* DeletionRangeKind.TARGET_ARTIFACT */;
628
+ }
629
+ changed = true;
630
+ }
631
+ return changed;
632
+ }
633
+ /**
634
+ * UTILS
635
+ */
636
+ /**
637
+ * Find out if annotation is embedded
638
+ * - i.e. search until preceding ';' or begin of file
639
+ * - when finding 'annotate': not embedded
640
+ * - when finding 'action' or 'function': embedded
641
+ * - when finding none: consider embedded (to not delete metadata, can happen for element in element list).
642
+ *
643
+ * @param tokens - All tokens in the document.
644
+ * @param startIndex - Start token index.
645
+ * @returns True if annotation is embedded.
646
+ */
647
+ function isEmbedded(tokens, startIndex) {
648
+ let annotateFound = false;
649
+ let actionFunctionFound = false;
650
+ let semicolonFound = false;
651
+ let index = startIndex;
652
+ while (index && !annotateFound && !actionFunctionFound && !semicolonFound) {
653
+ index--;
654
+ const token = tokens[index];
655
+ if (token.text.toUpperCase() === 'ANNOTATE') {
656
+ annotateFound = true;
657
+ }
658
+ else if (['ACTION', 'FUNCTION'].includes(token.text.toUpperCase())) {
659
+ actionFunctionFound = true;
660
+ }
661
+ else if (token.text === ';') {
662
+ semicolonFound = true;
663
+ }
664
+ }
665
+ return actionFunctionFound ? true : !annotateFound;
666
+ }
667
+ function isComment(token) {
668
+ var _a, _b;
669
+ return ((_a = token === null || token === void 0 ? void 0 : token.text) === null || _a === void 0 ? void 0 : _a.startsWith('//')) || ((_b = token === null || token === void 0 ? void 0 : token.text) === null || _b === void 0 ? void 0 : _b.startsWith('/*'));
670
+ }
671
+ /**
672
+ * Get neighboring token (ignoring comment tokens).
673
+ *
674
+ * @param tokens - All tokens in the document.
675
+ * @param index - Current token index.
676
+ * @param offset - Token offset.
677
+ * @returns Neighboring token.
678
+ */
679
+ function getNeighboringToken(tokens, index, offset) {
680
+ return tokens[getNeighboringTokenIndex(tokens, index, offset)];
681
+ }
682
+ /**
683
+ * Get closing token index
684
+ * - returns -1 if '(' or '{' is present before start of next statement (or end of file)
685
+ * - looks forward in token stream until next cds key word, '@', '(', '{', ';' or end of file.
686
+ *
687
+ * @param tokens - All tokens in the document.
688
+ * @param startIndex - Start token index.
689
+ * @returns Token index.
690
+ */
691
+ function getClosingTokenIndex(tokens, startIndex) {
692
+ let annoOrBracketPresent = false;
693
+ let closingTokenIndex = -1;
694
+ let commentStartIndex = -1; // start index of trailing comments
695
+ let index = startIndex;
696
+ while (index && tokens[index] && closingTokenIndex === -1 && !annoOrBracketPresent) {
697
+ const token = tokens[index];
698
+ if ([';', '@'].includes(token.text)) {
699
+ // ';': semicolon terminates current annotate statement (but is not mandatory!)
700
+ // '@': starts annotation targeting next statement
701
+ // (this is not possible: element/parameter list could be followed by annotations of entity/action)
702
+ // only include trailing comments if they are followed by a semicolon
703
+ closingTokenIndex = token.text !== ';' && commentStartIndex > 0 ? commentStartIndex : index;
704
+ }
705
+ else if (cdsKeywords.includes((token.text || '').toUpperCase())) {
706
+ // start of next CDS statement
707
+ closingTokenIndex = commentStartIndex > 0 ? commentStartIndex : index;
708
+ }
709
+ else if (['(', '{'].includes(token.text)) {
710
+ // '(': action or bound action as target could be followed by parameter list
711
+ // '{': entity could be followed by element list
712
+ annoOrBracketPresent = true;
713
+ }
714
+ if (commentStartIndex < 0 && isComment(token)) {
715
+ commentStartIndex = index;
716
+ }
717
+ if (commentStartIndex > 0 && !isComment(token)) {
718
+ commentStartIndex = -1;
719
+ }
720
+ index++;
721
+ }
722
+ return closingTokenIndex;
723
+ }
724
+ /**
725
+ * Map token identifier (token for name which is included into deletion range) to DeletionRangeKind.
726
+ *
727
+ * @param identifierKind - Identifier kind.
728
+ * @returns Deletion range kind.
729
+ */
730
+ function mapTokenIdentifierToKind(identifierKind) {
731
+ switch (identifierKind) {
732
+ case 'Element':
733
+ case 'ExtElement':
734
+ return "TargetElement" /* DeletionRangeKind.TARGET_ELEMENT */;
735
+ case 'BoundAction':
736
+ case 'ExtBoundAction':
737
+ return "TargetBoundAction" /* DeletionRangeKind.TARGET_BOUND_ACTION */;
738
+ case 'Param':
739
+ case 'ExtParam':
740
+ return "TargetParameter" /* DeletionRangeKind.TARGET_PARAMETER */;
741
+ default:
742
+ return "Undefined" /* DeletionRangeKind.UNDEFINED */;
743
+ }
744
+ }
745
+ /**
746
+ * Find out if parameter list starts i.e. if '(' occurs before '}'.
747
+ *
748
+ * @param tokens - All tokens in the document.
749
+ * @param startIndex - Start token index.
750
+ * @returns True if annotate statement contains parameter list.
751
+ */
752
+ function hasParameterList(tokens, startIndex) {
753
+ let bracketPresent = false;
754
+ let curlyBracketFound = false;
755
+ let index = startIndex;
756
+ while (index && tokens[index] && !curlyBracketFound && !bracketPresent) {
757
+ const token = tokens[index];
758
+ if (token.text === '}') {
759
+ curlyBracketFound = true;
760
+ }
761
+ else if (token.text === '(') {
762
+ // '(': action or bound action as target could be followed by parameter list
763
+ bracketPresent = true;
764
+ }
765
+ index++;
766
+ }
767
+ return bracketPresent;
768
+ }
769
+ function getPositionFromToken(token, endPosition = false) {
770
+ if (!token) {
771
+ return undefined;
772
+ }
773
+ const offset = endPosition ? token.text.length : 0;
774
+ return odata_annotation_core_types_1.Position.create(token.line - 1, token.column + offset);
775
+ }
776
+ function getRangeFromTokenRange(tokens, tokenRange, previousSeparatorIncluded, siblingSeparatorFound) {
777
+ const doNotExpandToAfterEndToken = !siblingSeparatorFound && previousSeparatorIncluded; // the case when last annotation in a group is being deleted, to preserve group closing bracket indentation
778
+ const startToken = tokens[tokenRange.start];
779
+ const endToken = tokens[tokenRange.end + (doNotExpandToAfterEndToken ? 0 : 1)];
780
+ const startPosition = getPositionFromToken(startToken);
781
+ const endPosition = getPositionFromToken(endToken, doNotExpandToAfterEndToken);
782
+ if (startPosition && endPosition) {
783
+ return odata_annotation_core_types_1.Range.create(startPosition, endPosition);
784
+ }
785
+ return undefined;
786
+ }
787
+ function includeSiblingSeparator(tokens, deletionRange) {
788
+ const separator = separatorForKind[deletionRange.kind];
789
+ if (!separator) {
790
+ return { deletionRange };
791
+ }
792
+ const endTokenIndex = deletionRange.tokenRange.end;
793
+ const nextTokenIndex = getNeighboringTokenIndex(tokens, endTokenIndex, +1);
794
+ const nextToken = tokens[nextTokenIndex];
795
+ let siblingSeparatorFound = false;
796
+ let previousSeparatorIncluded = false;
797
+ if (nextToken && nextToken.text === separator) {
798
+ // extend deletion range to include separator
799
+ deletionRange.tokenRange.end = nextTokenIndex;
800
+ siblingSeparatorFound = true;
801
+ // include line comment in same line
802
+ const commentToken = tokens[deletionRange.tokenRange.end + 1];
803
+ if (commentToken === null || commentToken === void 0 ? void 0 : commentToken.text.startsWith('//')) {
804
+ if (tokens[deletionRange.tokenRange.end].line === commentToken.line) {
805
+ ++deletionRange.tokenRange.end;
806
+ }
807
+ }
808
+ }
809
+ if (!siblingSeparatorFound) {
810
+ const startTokenIndex = deletionRange.tokenRange.start;
811
+ const previousTokenIndex = getNeighboringTokenIndex(tokens, startTokenIndex, -1);
812
+ const previousToken = tokens[previousTokenIndex];
813
+ if (previousToken.text === separator && deletionRange.kind !== "TargetArtifact" /* DeletionRangeKind.TARGET_ARTIFACT */) {
814
+ // extend deletion range to include separator (except for whole annotate statement, there ';' might be needed)
815
+ deletionRange.tokenRange.start = previousTokenIndex;
816
+ previousSeparatorIncluded = true;
817
+ }
818
+ }
819
+ return { deletionRange, siblingSeparatorFound, previousSeparatorIncluded };
820
+ }
821
+ //# sourceMappingURL=deletion.js.map