@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,1086 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.CDSWriter = void 0;
14
+ const path_1 = require("path");
15
+ const url_1 = require("url");
16
+ const project_access_1 = require("@sap-ux/project-access");
17
+ const odata_annotation_core_1 = require("@sap-ux/odata-annotation-core");
18
+ const ux_cds_compiler_facade_1 = require("@sap/ux-cds-compiler-facade");
19
+ const cds_annotation_parser_1 = require("@sap-ux/cds-annotation-parser");
20
+ const cds_odata_annotation_converter_1 = require("@sap-ux/cds-odata-annotation-converter");
21
+ const utils_1 = require("../utils");
22
+ const error_1 = require("../error");
23
+ const document_1 = require("./document");
24
+ const deletion_1 = require("./deletion");
25
+ const pointer_1 = require("./pointer");
26
+ const change_1 = require("./change");
27
+ const preprocessor_1 = require("./preprocessor");
28
+ const cds_compiler_tokens_1 = require("./cds-compiler-tokens");
29
+ const indent_1 = require("./indent");
30
+ const printOptions = Object.assign(Object.assign({}, odata_annotation_core_1.printOptions), { useSnippetSyntax: false });
31
+ const ANNOTATION_START_PATTERN = /^@/i;
32
+ /**
33
+ *
34
+ */
35
+ class CDSWriter {
36
+ /**
37
+ *
38
+ * @param facade - CDS compiler facade instance.
39
+ * @param vocabularyService - Vocabulary API.
40
+ * @param document - CDS document AST root.
41
+ * @param comments - All comments of the document.
42
+ * @param tokens - All tokens in the document.
43
+ * @param textDocument - TextDocument instance.
44
+ * @param projectRoot - Absolute path to the project root.
45
+ * @param annotationFile - Internal representation of the annotation file.
46
+ */
47
+ constructor(facade, vocabularyService, document, comments, tokens, textDocument, projectRoot,
48
+ /**
49
+ * This should be removed once it is no longer needed by deletion logic
50
+ *
51
+ * @deprecated
52
+ */
53
+ annotationFile) {
54
+ this.facade = facade;
55
+ this.vocabularyService = vocabularyService;
56
+ this.document = document;
57
+ this.comments = comments;
58
+ this.tokens = tokens;
59
+ this.textDocument = textDocument;
60
+ this.projectRoot = projectRoot;
61
+ this.annotationFile = annotationFile;
62
+ this.changes = [];
63
+ this.edits = [];
64
+ this.indentLevelCache = {};
65
+ this.vocabularyAliases = new Set();
66
+ this.deletionRangesMapForTarget = new Map();
67
+ this.uniqueInserts = new Set();
68
+ this.processedChanges = [];
69
+ // Change Handlers
70
+ this[_a] = (change) => {
71
+ if (!this.document.range) {
72
+ return;
73
+ }
74
+ let prefix = '';
75
+ const position = this.document.range.end;
76
+ if (this.document.range.end.character > 0) {
77
+ prefix = '\n';
78
+ position.character = 0;
79
+ }
80
+ const newElements = prefix + (0, cds_odata_annotation_converter_1.printTarget)(change.target) + '\n';
81
+ const edits = [odata_annotation_core_1.TextEdit.insert(position, newElements)];
82
+ this.edits.push(...edits);
83
+ };
84
+ this[_b] = (change, reversePath) => {
85
+ const [astNode] = reversePath;
86
+ const indentLevel = this.getIndentLevel(change.pointer);
87
+ // inserting new record as value eg: annotate IncidentService.Incidents with @UI.Chart;
88
+ if (((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.ANNOTATION_TYPE || (astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.RECORD_PROPERTY_TYPE) && astNode.range) {
89
+ const recordText = ' : ' +
90
+ (0, cds_odata_annotation_converter_1.indent)((0, cds_odata_annotation_converter_1.print)(change.element, printOptions, false), {
91
+ level: indentLevel,
92
+ skipFirstLine: true
93
+ });
94
+ const position = astNode.range.end;
95
+ this.edits.push(odata_annotation_core_1.TextEdit.insert(position, recordText));
96
+ }
97
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.COLLECTION_TYPE) {
98
+ const content = getContainerContent(astNode, this.comments, this.tokens);
99
+ const insertEdits = insertIntoNodeWithContent(content, astNode, [change], indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
100
+ this.edits.push(...insertEdits);
101
+ }
102
+ };
103
+ this[_c] = (change, reversePath) => {
104
+ var _0;
105
+ const [astNode] = reversePath;
106
+ const indentLevel = this.getIndentLevel(change.pointer);
107
+ const collectionText = change.element.content.length ? (0, cds_odata_annotation_converter_1.print)(change.element) : '[]';
108
+ const textWithPrefix = ` : ${collectionText}`;
109
+ const position = (_0 = astNode === null || astNode === void 0 ? void 0 : astNode.range) === null || _0 === void 0 ? void 0 : _0.end;
110
+ if (position) {
111
+ const text = (0, cds_odata_annotation_converter_1.indent)(textWithPrefix, { level: indentLevel + 1, skipFirstLine: true });
112
+ this.edits.push(odata_annotation_core_1.TextEdit.insert(position, text));
113
+ }
114
+ };
115
+ this[_d] = (change, reversePath) => {
116
+ const [astNode] = reversePath;
117
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) !== cds_odata_annotation_converter_1.TARGET_TYPE) {
118
+ return;
119
+ }
120
+ const indentLevel = this.getIndentLevel(change.pointer);
121
+ const content = getContainerContent(astNode, this.comments, this.tokens);
122
+ const insertEdits = convertInsertNodeToTextEdits(content, astNode, [change], indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
123
+ this.edits.push(...insertEdits);
124
+ };
125
+ this[_e] = (change, reversePath) => {
126
+ var _0;
127
+ const [astNode] = reversePath;
128
+ const indentLevel = this.getIndentLevel(change.pointer);
129
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.RECORD_TYPE) {
130
+ const content = getContainerContent(astNode, this.comments, this.tokens);
131
+ const insertEdits = insertIntoNodeWithContent(content, astNode, [change], indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
132
+ this.edits.push(...insertEdits);
133
+ }
134
+ else if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.ANNOTATION_TYPE) {
135
+ const value = astNode.value;
136
+ let adjustedIndentLevel = indentLevel;
137
+ if (willTargetAnnotationIncreaseIndent(this.processedChanges, change.pointer)) {
138
+ adjustedIndentLevel++;
139
+ }
140
+ if ((value === null || value === void 0 ? void 0 : value.type) === cds_annotation_parser_1.RECORD_TYPE) {
141
+ const content = getContainerContent(value, this.comments, this.tokens);
142
+ // existing value is record
143
+ const insertEdits = insertIntoNodeWithContent(content, value, [change], adjustedIndentLevel, this.isFirstInsert(change.pointer, value, change.index));
144
+ this.edits.push(...insertEdits);
145
+ }
146
+ else if ((_0 = astNode.value) === null || _0 === void 0 ? void 0 : _0.range) {
147
+ // existing value is primitive
148
+ const indent = ' '.repeat(adjustedIndentLevel * printOptions.tabWidth);
149
+ const valueIndent = ' '.repeat((adjustedIndentLevel + 1) * printOptions.tabWidth);
150
+ const annotationText = (0, cds_odata_annotation_converter_1.printCsdlNode)(change.element, printOptions);
151
+ const insertEdits = [
152
+ odata_annotation_core_1.TextEdit.insert(astNode.value.range.start, `{\n${valueIndent}$value : `),
153
+ odata_annotation_core_1.TextEdit.insert(astNode.value.range.end, `,\n${valueIndent}${annotationText}\n${indent}}`)
154
+ ];
155
+ const valuePointer = [change.pointer, 'value'].join('/');
156
+ const replacementChange = this.processedChanges.find((c) => c.pointer === valuePointer && c.type === change_1.REPLACE_NODE_CHANGE_TYPE);
157
+ if (!replacementChange) {
158
+ let line = astNode.value.range.start.line + 1;
159
+ const contentIndent = ' '.repeat(printOptions.tabWidth);
160
+ while (line <= astNode.value.range.end.line) {
161
+ insertEdits.push(odata_annotation_core_1.TextEdit.insert(odata_annotation_core_1.Position.create(line, 0), contentIndent));
162
+ line++;
163
+ }
164
+ }
165
+ this.edits.push(...insertEdits);
166
+ }
167
+ }
168
+ };
169
+ this[_f] = (change, reversePath) => {
170
+ const [astNode] = reversePath;
171
+ const indentLevel = this.getIndentLevel(change.pointer);
172
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) !== cds_annotation_parser_1.RECORD_TYPE) {
173
+ return;
174
+ }
175
+ const content = getContainerContent(astNode, this.comments, this.tokens);
176
+ const insertEdits = convertInsertNodeToTextEdits(content, astNode, [change], indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
177
+ this.edits.push(...insertEdits);
178
+ };
179
+ this[_g] = (change, reversePath) => {
180
+ const [astNode] = reversePath;
181
+ const indentLevel = this.getIndentLevel(change.pointer);
182
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.COLLECTION_TYPE) {
183
+ const content = getContainerContent(astNode, this.comments, this.tokens);
184
+ const insertEdits = convertInsertNodeToTextEdits(content, astNode, [change], indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
185
+ this.edits.push(...insertEdits);
186
+ }
187
+ else if (((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.RECORD_PROPERTY_TYPE || (astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.ANNOTATION_TYPE) && astNode.range) {
188
+ const textNode = change.element.content[0];
189
+ const value = (textNode === null || textNode === void 0 ? void 0 : textNode.type) === odata_annotation_core_1.TEXT_TYPE ? textNode.text : '';
190
+ const recordText = ' : ' + (0, cds_odata_annotation_converter_1.indent)((0, cds_odata_annotation_converter_1.printPrimitiveValue)(change.element.name, value));
191
+ this.edits.push(odata_annotation_core_1.TextEdit.insert(astNode.range.end, recordText));
192
+ }
193
+ };
194
+ this[_h] = (change, reversePath) => {
195
+ const [astNode] = reversePath;
196
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.ANNOTATION_TYPE && astNode.term.range) {
197
+ this.edits.push(odata_annotation_core_1.TextEdit.insert(astNode.term.range.end, ` #${change.value}`));
198
+ }
199
+ };
200
+ this[_j] = (change) => __awaiter(this, void 0, void 0, function* () {
201
+ const { position, prependNewLine } = getInsertReferencePosition(this.document.references);
202
+ // TODO: this breaks memfs concept
203
+ const text = `${prependNewLine ? '\n' : ''}${yield getTextEditForMissingRefs(change.references, this.document.uri, this.projectRoot)}`;
204
+ this.edits.push(odata_annotation_core_1.TextEdit.insert(position, text));
205
+ });
206
+ this[_k] = (_change, reversePath) => {
207
+ const [astNode] = reversePath;
208
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) !== cds_odata_annotation_converter_1.TARGET_TYPE || !astNode.range || !astNode.nameRange) {
209
+ return;
210
+ }
211
+ // TODO: check if we can relay on target.kind instead of going through edmx conversions
212
+ const { edmxPath } = this.facade.collectMetadataForAbsolutePath(astNode.name, astNode.kind, (0, ux_cds_compiler_facade_1.createMetadataCollector)(new Map(), this.facade));
213
+ const ranges = astNode.assignments
214
+ .flatMap((assignment) => (assignment.type === cds_annotation_parser_1.ANNOTATION_TYPE ? [assignment] : assignment.items.items))
215
+ .map((annotation, i) => {
216
+ return (0, deletion_1.getDeletionRangeForNode)(this.vocabularyService, this.vocabularyAliases, i, this.tokens, annotation, edmxPath);
217
+ })
218
+ .filter((range) => !!range);
219
+ const targetDeletions = (0, deletion_1.getTextEditsForDeletionRanges)(ranges, this.vocabularyAliases, this.tokens, this.annotationFile, true);
220
+ if (targetDeletions) {
221
+ this.edits.push(...targetDeletions);
222
+ }
223
+ };
224
+ this[_l] = (change, reversePath) => {
225
+ this.deleteNode(change.pointer, reversePath);
226
+ };
227
+ this[_m] = (change, reversePath) => {
228
+ this.deleteNode(change.pointer, reversePath);
229
+ };
230
+ this[_o] = (change, reversePath) => {
231
+ var _0;
232
+ const [astNode, parent] = reversePath;
233
+ const segments = change.pointer.split('/');
234
+ const lastIndex = (_0 = segments.pop()) !== null && _0 !== void 0 ? _0 : '';
235
+ const index = parseInt(lastIndex, 10);
236
+ if (Number.isNaN(index) || (astNode === null || astNode === void 0 ? void 0 : astNode.type) !== cds_annotation_parser_1.ANNOTATION_GROUP_TYPE || (parent === null || parent === void 0 ? void 0 : parent.type) !== cds_odata_annotation_converter_1.TARGET_TYPE) {
237
+ return;
238
+ }
239
+ const content = getContainerContent(parent, this.comments, this.tokens);
240
+ const { startContentIndex } = findContentIndices(content, index, index);
241
+ deleteBlock(this.edits, content, startContentIndex);
242
+ };
243
+ this[_p] = (change, reversePath) => {
244
+ const [annotation, parent, , greatGrandParent] = reversePath;
245
+ const segments = change.pointer.split('/');
246
+ const lastIndex = segments.pop();
247
+ const isInAnnotationGroup = (greatGrandParent === null || greatGrandParent === void 0 ? void 0 : greatGrandParent.type) === cds_odata_annotation_converter_1.TARGET_TYPE;
248
+ const target = isInAnnotationGroup ? greatGrandParent : parent;
249
+ if (!lastIndex || (annotation === null || annotation === void 0 ? void 0 : annotation.type) !== cds_annotation_parser_1.ANNOTATION_TYPE || target.type !== cds_odata_annotation_converter_1.TARGET_TYPE) {
250
+ return;
251
+ }
252
+ const targetPointer = change.pointer.split('/').slice(0, 3).join('/');
253
+ const assignmentIndex = parseInt(lastIndex, 10);
254
+ if (Number.isNaN(assignmentIndex)) {
255
+ return;
256
+ }
257
+ let index = 0;
258
+ // We need to calculate correct indices for the annotations
259
+ for (let i = 0; i < target.assignments.length; i++) {
260
+ const assignment = target.assignments[i];
261
+ if (assignment.type === cds_annotation_parser_1.ANNOTATION_GROUP_TYPE) {
262
+ index += assignment.items.items.length;
263
+ }
264
+ else {
265
+ if (i === assignmentIndex) {
266
+ break;
267
+ }
268
+ index++;
269
+ }
270
+ }
271
+ const range = this.getDeletionRange(annotation, target, index);
272
+ if (!range) {
273
+ return;
274
+ }
275
+ let targetDeletionRanges = this.deletionRangesMapForTarget.get(targetPointer);
276
+ if (!targetDeletionRanges) {
277
+ targetDeletionRanges = [];
278
+ this.deletionRangesMapForTarget.set(targetPointer, targetDeletionRanges);
279
+ }
280
+ targetDeletionRanges.push(range);
281
+ };
282
+ this[_q] = (change, reversePath) => {
283
+ var _0, _1;
284
+ const [astNode, parent] = reversePath;
285
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.ANNOTATION_TYPE && parent.type === cds_annotation_parser_1.RECORD_TYPE) {
286
+ // Transform $value back to normal primitive value representation if last embedded annotation is being deleted
287
+ const firstProperty = parent.properties[0];
288
+ const isTransformPossible = ((_0 = parent.annotations) === null || _0 === void 0 ? void 0 : _0.length) === 1 &&
289
+ parent.properties.length === 1 &&
290
+ firstProperty.name.value === cds_annotation_parser_1.ReservedProperties.Value;
291
+ if (isTransformPossible && parent.range && ((_1 = firstProperty.value) === null || _1 === void 0 ? void 0 : _1.range)) {
292
+ // delete surrounding record parts, leaving property value only
293
+ this.edits.push(odata_annotation_core_1.TextEdit.del(odata_annotation_core_1.Range.create(parent.range.start, firstProperty.value.range.start)), odata_annotation_core_1.TextEdit.del(odata_annotation_core_1.Range.create(firstProperty.value.range.end, parent.range.end)));
294
+ }
295
+ else {
296
+ this.deleteNode(change.pointer, reversePath);
297
+ }
298
+ }
299
+ };
300
+ this[_r] = (change, reversePath) => {
301
+ this.deleteNode(change.pointer, reversePath);
302
+ };
303
+ this[_s] = (change, reversePath) => {
304
+ const [astNode] = reversePath;
305
+ if (!(astNode === null || astNode === void 0 ? void 0 : astNode.range)) {
306
+ return;
307
+ }
308
+ const range = (0, cds_annotation_parser_1.copyRange)(astNode.range);
309
+ range.start.character--; // also include # character
310
+ this.edits.push(odata_annotation_core_1.TextEdit.del(range));
311
+ };
312
+ //#endregion
313
+ //#region Modifications
314
+ this[_t] = (change, reversePath) => {
315
+ const [astNode] = reversePath;
316
+ if (!(astNode === null || astNode === void 0 ? void 0 : astNode.range)) {
317
+ return;
318
+ }
319
+ let indentLevel = this.getIndentLevel(change.pointer);
320
+ const parentPointer = change.pointer.split('/').slice(0, -1).join('/');
321
+ const insertEmbeddedAnnotationChange = this.changes.find((change) => change.pointer === parentPointer && change.type === change_1.INSERT_EMBEDDED_ANNOTATION_CHANGE_TYPE);
322
+ if (willTargetAnnotationIncreaseIndent(this.processedChanges, change.pointer)) {
323
+ indentLevel++;
324
+ }
325
+ if (insertEmbeddedAnnotationChange) {
326
+ indentLevel++;
327
+ }
328
+ const fragment = (0, cds_odata_annotation_converter_1.print)(change.newElement, printOptions);
329
+ const indentedFragment = (0, utils_1.increaseIndent)(fragment, indentLevel, true);
330
+ this.edits.push(odata_annotation_core_1.TextEdit.replace(astNode.range, indentedFragment));
331
+ };
332
+ this[_u] = (change, reversePath) => {
333
+ const [astNode] = reversePath;
334
+ const indentLevel = this.getIndentLevel(change.pointer);
335
+ if (!(astNode === null || astNode === void 0 ? void 0 : astNode.range)) {
336
+ return;
337
+ }
338
+ const text = (0, cds_odata_annotation_converter_1.indent)((0, cds_odata_annotation_converter_1.print)(change.newProperty, printOptions, false), {
339
+ level: indentLevel + 1,
340
+ skipFirstLine: true
341
+ });
342
+ this.edits.push(odata_annotation_core_1.TextEdit.replace(astNode.range, text));
343
+ };
344
+ this[_v] = (change, reversePath) => {
345
+ const [astNode] = reversePath;
346
+ if (!(astNode === null || astNode === void 0 ? void 0 : astNode.range)) {
347
+ return;
348
+ }
349
+ let expressionType = '';
350
+ if (astNode === null || astNode === void 0 ? void 0 : astNode.type) {
351
+ expressionType = convertCDSAstToEdmType(astNode.type);
352
+ }
353
+ if (expressionType === undefined) {
354
+ console.warn(`Could not determine expression type for ${JSON.stringify(change, undefined, 2)}. For changing enum flags "set-flags" change should be used.`);
355
+ return;
356
+ }
357
+ this.edits.push(odata_annotation_core_1.TextEdit.replace(astNode.range, (0, cds_odata_annotation_converter_1.printPrimitiveValue)(expressionType, change.newValue)));
358
+ };
359
+ this[_w] = (change, reversePath) => {
360
+ const [astNode] = reversePath;
361
+ if (!(astNode === null || astNode === void 0 ? void 0 : astNode.range)) {
362
+ return;
363
+ }
364
+ if (astNode.type === cds_annotation_parser_1.STRING_LITERAL_TYPE) {
365
+ const range = (0, cds_annotation_parser_1.copyRange)(astNode.range);
366
+ // string range includes quotes, but we only need to replace content.
367
+ range.start.character++;
368
+ range.end.character--;
369
+ this.edits.push(odata_annotation_core_1.TextEdit.replace(range, change.newValue));
370
+ }
371
+ else if (astNode.type === cds_annotation_parser_1.ENUM_TYPE && astNode.path.range) {
372
+ this.edits.push(odata_annotation_core_1.TextEdit.replace(astNode.range, (0, cds_odata_annotation_converter_1.printPrimitiveValue)("EnumMember" /* Edm.EnumMember */, change.newValue)));
373
+ }
374
+ else {
375
+ this.edits.push(odata_annotation_core_1.TextEdit.replace(astNode.range, change.newValue));
376
+ }
377
+ };
378
+ //#endregion
379
+ this[_x] = (change, reversePath) => {
380
+ const [astNode] = reversePath;
381
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.COLLECTION_TYPE && astNode.range) {
382
+ this.edits.push(odata_annotation_core_1.TextEdit.replace(astNode.range, (0, cds_odata_annotation_converter_1.printPrimitiveValue)("EnumMember" /* Edm.EnumMember */, change.value)));
383
+ }
384
+ else if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) === cds_annotation_parser_1.ENUM_TYPE) {
385
+ console.warn(`Could not apply change "set-flags". Expected target to be '${cds_annotation_parser_1.COLLECTION_TYPE}, but got ${cds_annotation_parser_1.ENUM_TYPE}. To replace single enum value 'replace-text-value' change should be used`);
386
+ }
387
+ };
388
+ this[_y] = (change, reversePath) => {
389
+ var _0, _1, _2, _3, _4, _5, _6;
390
+ const [astNode] = reversePath;
391
+ if (astNode.type !== cds_annotation_parser_1.COLLECTION_TYPE) {
392
+ return;
393
+ }
394
+ const content = getContainerContent(astNode, this.comments, this.tokens);
395
+ const anchor = findInsertPosition(content, astNode, change.index);
396
+ const indentLevel = this.getIndentLevel(change.pointer);
397
+ if (anchor) {
398
+ if (anchor.commaPosition) {
399
+ this.edits.push(odata_annotation_core_1.TextEdit.insert(anchor.commaPosition, ','));
400
+ }
401
+ const ranges = createElementRanges(this.document, this.tokens, change.fromPointers);
402
+ const moveEdits = getTextEditsForMove(this.textDocument, this.comments, this.tokens, anchor.position, ranges, indentLevel);
403
+ this.edits.push(...moveEdits);
404
+ if (((_0 = astNode.closeToken) === null || _0 === void 0 ? void 0 : _0.range) &&
405
+ ((_3 = (_2 = (_1 = astNode.closeToken) === null || _1 === void 0 ? void 0 : _1.range) === null || _2 === void 0 ? void 0 : _2.end) === null || _3 === void 0 ? void 0 : _3.line) === ((_6 = (_5 = (_4 = astNode.openToken) === null || _4 === void 0 ? void 0 : _4.range) === null || _5 === void 0 ? void 0 : _5.end) === null || _6 === void 0 ? void 0 : _6.line)) {
406
+ // [] is on the same line -> we need to expand this to multiple lines
407
+ const indent = ' '.repeat(indentLevel);
408
+ this.edits.push(odata_annotation_core_1.TextEdit.insert(astNode.closeToken.range.start, '\n' + indent));
409
+ }
410
+ }
411
+ };
412
+ this[_z] = (change, reversePath) => {
413
+ const [astNode] = reversePath;
414
+ if ((astNode === null || astNode === void 0 ? void 0 : astNode.type) !== cds_odata_annotation_converter_1.TARGET_TYPE) {
415
+ return;
416
+ }
417
+ const indentLevel = this.getIndentLevel(change.pointer);
418
+ const conversionSteps = convertToCompoundAnnotation(this.tokens, astNode, indentLevel, change.applyContentIndentation);
419
+ if (conversionSteps.length) {
420
+ this.edits.push(...conversionSteps);
421
+ }
422
+ };
423
+ for (const [, vocabulary] of this.vocabularyService.getVocabularies()) {
424
+ this.vocabularyAliases.add(vocabulary.defaultAlias);
425
+ }
426
+ }
427
+ /**
428
+ * Adds change to the stack.
429
+ *
430
+ * @param change - New change.
431
+ */
432
+ addChange(change) {
433
+ this.changes.push(change);
434
+ }
435
+ /**
436
+ * Creates text edits for the changes in the stack.
437
+ *
438
+ * @returns - Text edits.
439
+ */
440
+ getTextEdits() {
441
+ var _0;
442
+ return __awaiter(this, void 0, void 0, function* () {
443
+ this.resetState();
444
+ this.processedChanges = (0, preprocessor_1.preprocessChanges)(this.document, this.changes);
445
+ for (const change of this.processedChanges) {
446
+ const path = (0, pointer_1.getAstNodesFromPointer)(this.document, change.pointer).reverse();
447
+ const handler = this[change.type];
448
+ const result = handler(change, path);
449
+ if (result instanceof Promise) {
450
+ yield result;
451
+ }
452
+ }
453
+ for (const key of this.deletionRangesMapForTarget.keys()) {
454
+ const deletionRanges = (_0 = this.deletionRangesMapForTarget.get(key)) !== null && _0 !== void 0 ? _0 : [];
455
+ if (deletionRanges.length > 0) {
456
+ const targetDeletions = (0, deletion_1.getTextEditsForDeletionRanges)(deletionRanges, this.vocabularyAliases, this.tokens, this.annotationFile, false);
457
+ this.edits.push(...targetDeletions);
458
+ }
459
+ }
460
+ this.edits.sort(utils_1.compareTextEdits);
461
+ return this.edits;
462
+ });
463
+ }
464
+ /**
465
+ * Building text edits is a stateful process and needs to be cleared after each {@link CDSWriter.getTextEdits} call.
466
+ *
467
+ */
468
+ resetState() {
469
+ this.edits = [];
470
+ this.processedChanges = [];
471
+ this.indentLevelCache = {};
472
+ this.deletionRangesMapForTarget = new Map();
473
+ this.uniqueInserts = new Set();
474
+ }
475
+ getIndentLevel(pointer) {
476
+ const cachedValue = this.indentLevelCache[pointer];
477
+ if (cachedValue !== undefined) {
478
+ return cachedValue;
479
+ }
480
+ const level = (0, indent_1.getIndentLevelFromPointer)(this.document, this.tokens, pointer);
481
+ this.indentLevelCache[pointer] = level;
482
+ return level;
483
+ }
484
+ //#region Inserts
485
+ isFirstInsert(pointer, node, index = -1) {
486
+ const childCount = getChildCount(node);
487
+ const i = index > -1 ? Math.min(index, childCount) : childCount;
488
+ const insertPositionKey = `${pointer}/${i}`;
489
+ const firstInsert = !this.uniqueInserts.has(insertPositionKey);
490
+ this.uniqueInserts.add(insertPositionKey);
491
+ return firstInsert;
492
+ }
493
+ //#endregion
494
+ //#region Deletes
495
+ deleteNode(pointer, reversePath) {
496
+ const [astNode, parent] = reversePath;
497
+ const segments = pointer.split('/');
498
+ const lastIndex = segments.pop();
499
+ deleteValue(this.edits, pointer, astNode, parent, this.comments, this.tokens, lastIndex);
500
+ }
501
+ getDeletionRange(annotation, target, index) {
502
+ const { edmxPath } = this.facade.collectMetadataForAbsolutePath(target.name, target.kind, (0, ux_cds_compiler_facade_1.createMetadataCollector)(new Map(), this.facade));
503
+ return (0, deletion_1.getDeletionRangeForNode)(this.vocabularyService, this.vocabularyAliases, index, this.tokens, annotation, edmxPath);
504
+ }
505
+ }
506
+ exports.CDSWriter = CDSWriter;
507
+ _a = change_1.INSERT_TARGET_CHANGE_TYPE, _b = change_1.INSERT_RECORD_CHANGE_TYPE, _c = change_1.INSERT_COLLECTION_CHANGE_TYPE, _d = change_1.INSERT_ANNOTATION_CHANGE_TYPE, _e = change_1.INSERT_EMBEDDED_ANNOTATION_CHANGE_TYPE, _f = change_1.INSERT_RECORD_PROPERTY_CHANGE_TYPE, _g = change_1.INSERT_PRIMITIVE_VALUE_TYPE, _h = change_1.INSERT_QUALIFIER_CHANGE_TYPE, _j = change_1.INSERT_REFERENCE_CHANGE_TYPE, _k = change_1.DELETE_TARGET_CHANGE_TYPE, _l = change_1.DELETE_RECORD_CHANGE_TYPE, _m = change_1.DELETE_RECORD_PROPERTY_CHANGE_TYPE, _o = change_1.DELETE_ANNOTATION_GROUP_CHANGE_TYPE, _p = change_1.DELETE_ANNOTATION_CHANGE_TYPE, _q = change_1.DELETE_EMBEDDED_ANNOTATION_CHANGE_TYPE, _r = change_1.DELETE_PRIMITIVE_VALUE_CHANGE_TYPE, _s = change_1.DELETE_QUALIFIER_CHANGE_TYPE, _t = change_1.REPLACE_NODE_CHANGE_TYPE, _u = change_1.REPLACE_RECORD_PROPERTY_CHANGE_TYPE, _v = change_1.REPLACE_TEXT_VALUE_CHANGE_TYPE, _w = change_1.UPDATE_PRIMITIVE_VALUE_CHANGE_TYPE, _x = change_1.SET_FLAGS_CHANGE_TYPE, _y = change_1.MOVE_COLLECTION_VALUE_CHANGE_TYPE, _z = change_1.CONVERT_TO_COMPOUND_ANNOTATION_CHANGE_TYPE;
508
+ function convertInsertNodeToTextEdits(content, parent, changes, childIndentLevel, firstInsert) {
509
+ if (changes.length === 0) {
510
+ return [];
511
+ }
512
+ if (parent.type !== document_1.CDS_DOCUMENT_TYPE) {
513
+ return insertIntoNodeWithContent(content, parent, changes, childIndentLevel, firstInsert);
514
+ }
515
+ return [];
516
+ }
517
+ function willTargetAnnotationIncreaseIndent(changes, pointer) {
518
+ const targetPointer = pointer.split('/').slice(0, 3).join('/');
519
+ const conversionChange = changes.find((change) => change.pointer === targetPointer && change.type === change_1.CONVERT_TO_COMPOUND_ANNOTATION_CHANGE_TYPE);
520
+ return !!conversionChange;
521
+ }
522
+ function convertToCompoundAnnotation(tokens, node, indentLevel, indentContent) {
523
+ var _0, _1, _2, _3, _4, _5;
524
+ if (!node.range) {
525
+ return [];
526
+ }
527
+ // All assignments are grouped to the same target, but we only need to replace the last one.
528
+ const startPosition = (_2 = (_1 = (_0 = node.assignments[node.assignments.length - 1]) === null || _0 === void 0 ? void 0 : _0.range) === null || _1 === void 0 ? void 0 : _1.start) !== null && _2 !== void 0 ? _2 : (_3 = node.range) === null || _3 === void 0 ? void 0 : _3.start;
529
+ const startToken = (0, cds_compiler_tokens_1.findLastTokenBeforePosition)(ANNOTATION_START_PATTERN, tokens, startPosition);
530
+ if (!startToken) {
531
+ return [];
532
+ }
533
+ const nextToken = tokens[startToken.tokenIndex + 1];
534
+ if (!nextToken || nextToken.text === '(') {
535
+ return [];
536
+ }
537
+ // there can be trailing comma before the closing token ')' or ';'
538
+ const afterEndToken = (0, cds_compiler_tokens_1.findFirstTokenAfterPosition)(/[^,]/, tokens, (_4 = node.range) === null || _4 === void 0 ? void 0 : _4.end);
539
+ // there should always be an afterEndToken
540
+ // if it is an annotation on element then it would at least end with '}'
541
+ // if it is an annotation on entity then it would be ';'
542
+ if (!afterEndToken || afterEndToken.text === ')') {
543
+ // either its a compound annotation or there is something else wrong and we should create text edits
544
+ return [];
545
+ }
546
+ const endToken = (0, cds_compiler_tokens_1.findLastTokenBeforePosition)(undefined, tokens, (_5 = node.range) === null || _5 === void 0 ? void 0 : _5.end);
547
+ if (!endToken) {
548
+ return [];
549
+ }
550
+ const indentText = ' '.repeat(indentLevel + 1);
551
+ const closingIndent = ' '.repeat(indentLevel);
552
+ const contentIndent = [];
553
+ if (indentContent) {
554
+ for (let index = startToken.line; index < endToken.line; index++) {
555
+ contentIndent.push(odata_annotation_core_1.TextEdit.insert(odata_annotation_core_1.Position.create(index, 0), ' '));
556
+ }
557
+ }
558
+ // if annotation ends with ';' we need to insert closing ')' before ';' otherwise we need to insert it after the last token
559
+ // sometimes element annotations many end without ';'
560
+ const endCharacter = afterEndToken.text === ';' ? afterEndToken.column : endToken.column + endToken.text.length;
561
+ return [
562
+ odata_annotation_core_1.TextEdit.insert(odata_annotation_core_1.Position.create(startToken.line - 1, startToken.column + 1), `(\n${indentText}`),
563
+ ...contentIndent,
564
+ odata_annotation_core_1.TextEdit.insert(odata_annotation_core_1.Position.create(endToken.line - 1, endCharacter), `\n${closingIndent})`)
565
+ ];
566
+ }
567
+ function deleteBlock(edits, content, blockIndex) {
568
+ const block = content[blockIndex];
569
+ if ((block === null || block === void 0 ? void 0 : block.type) !== 'element') {
570
+ return;
571
+ }
572
+ edits.push(odata_annotation_core_1.TextEdit.del(block.range));
573
+ const previous = content[blockIndex - 1];
574
+ const next = content[blockIndex + 1];
575
+ if (next === null || next === void 0 ? void 0 : next.range) {
576
+ // there could be whitespace to the next element which should be removed
577
+ edits.push(odata_annotation_core_1.TextEdit.del(odata_annotation_core_1.Range.create(block.range.end, next.range.start)));
578
+ }
579
+ else if (previous === null || previous === void 0 ? void 0 : previous.range) {
580
+ // if the last child element is being deleted then white space between the last and previous should be removed as well
581
+ const edit = odata_annotation_core_1.TextEdit.del(odata_annotation_core_1.Range.create(previous.range.end, block.range.start));
582
+ // other deletion edits with the same range may already exist
583
+ if (!edits.some((item) => isRangesEqual(item.range, edit.range))) {
584
+ edits.push(edit);
585
+ }
586
+ }
587
+ }
588
+ function deleteValue(edits, pointer, astNode, parent, comments, tokens, lastIndex) {
589
+ var _0;
590
+ if (parent.type === cds_annotation_parser_1.COLLECTION_TYPE) {
591
+ if (!lastIndex) {
592
+ throw new error_1.ApiError(`${pointer} is not pointing to a collection element.`);
593
+ }
594
+ const content = getContainerContent(parent, comments, tokens);
595
+ const index = parseInt(lastIndex, 10);
596
+ const { startContentIndex } = findContentIndices(content, index);
597
+ deleteBlock(edits, content, startContentIndex);
598
+ }
599
+ else if (parent.type === cds_annotation_parser_1.RECORD_TYPE &&
600
+ (astNode.type === cds_annotation_parser_1.RECORD_PROPERTY_TYPE || astNode.type === cds_annotation_parser_1.ANNOTATION_TYPE)) {
601
+ if (!lastIndex) {
602
+ throw new error_1.ApiError(`${pointer} is not pointing to a record property.`);
603
+ }
604
+ const content = getContainerContent(parent, comments, tokens);
605
+ const index = parseInt(lastIndex, 10);
606
+ const { startContentIndex } = findContentIndices(content, index, index, astNode.type);
607
+ deleteBlock(edits, content, startContentIndex);
608
+ }
609
+ else if (parent.type === cds_annotation_parser_1.ANNOTATION_TYPE || parent.type === cds_annotation_parser_1.RECORD_PROPERTY_TYPE) {
610
+ // delete record value including ':'
611
+ if (((_0 = parent.colon) === null || _0 === void 0 ? void 0 : _0.range) && astNode.range) {
612
+ const range = odata_annotation_core_1.Range.create(parent.colon.range.start, astNode.range.end);
613
+ edits.push(odata_annotation_core_1.TextEdit.del(range));
614
+ }
615
+ }
616
+ else {
617
+ throw new error_1.ApiError(`Invalid ${pointer} for 'delete-record' change.`);
618
+ }
619
+ }
620
+ function insertIntoNodeWithContent(content, parent, changes, indentLevel, firstInsert) {
621
+ const edits = [];
622
+ const [indices, changesByIndex] = indexInserts(changes, getChildCount(parent));
623
+ for (const index of indices) {
624
+ // change.index should not be used in this scope, because changes with indices outside the container size are merged
625
+ // and change.index would not correctly reflect the place where a change needs to be inserted
626
+ const changeSet = changesByIndex.get(index);
627
+ if (!changeSet || !parent.range) {
628
+ continue;
629
+ }
630
+ const newElements = changeSet.map(printChange(parent)).join(',\n');
631
+ if (getChildCount(parent) === 0) {
632
+ const fragments = [];
633
+ const range = (0, cds_annotation_parser_1.copyRange)(parent.range);
634
+ // we need to adjust range to exclude boundary characters
635
+ range.start.character++; // range includes '{' or '[' characters
636
+ range.end.character--; // range includes '}' or ']' characters
637
+ fragments.push('\n');
638
+ fragments.push(newElements);
639
+ fragments.push(',');
640
+ const text = (0, cds_odata_annotation_converter_1.indent)(deIndent(fragments.join('')), {
641
+ level: indentLevel + 1,
642
+ skipFirstLine: true
643
+ });
644
+ edits.push(odata_annotation_core_1.TextEdit.replace(range, text + (0, cds_odata_annotation_converter_1.indent)('\n', { level: indentLevel, skipFirstLine: true })));
645
+ }
646
+ else {
647
+ const anchor = findInsertPosition(content, parent, index !== null && index !== void 0 ? index : -1);
648
+ if (!anchor) {
649
+ continue;
650
+ }
651
+ const fragments = [];
652
+ if (firstInsert && anchor.commaPosition) {
653
+ // for repeated inserts at the same spot we'll already have the trailing comma
654
+ edits.push(odata_annotation_core_1.TextEdit.insert(anchor.commaPosition, ','));
655
+ }
656
+ fragments.push('\n');
657
+ fragments.push(newElements);
658
+ fragments.push(',');
659
+ let finalText = fragments.join('');
660
+ finalText = deIndent(finalText);
661
+ const text = (0, cds_odata_annotation_converter_1.indent)(finalText, {
662
+ level: indentLevel + 1,
663
+ skipFirstLine: true
664
+ });
665
+ edits.push(odata_annotation_core_1.TextEdit.insert(anchor.position, text));
666
+ }
667
+ }
668
+ return edits;
669
+ }
670
+ function printChange(parent) {
671
+ return function (change) {
672
+ var _0;
673
+ if (change.type === change_1.INSERT_PRIMITIVE_VALUE_TYPE) {
674
+ const text = ((_0 = change.element.content[0]) === null || _0 === void 0 ? void 0 : _0.type) === odata_annotation_core_1.TEXT_TYPE ? change.element.content[0].text : '';
675
+ return (0, cds_odata_annotation_converter_1.printPrimitiveValue)(change.element.name, text);
676
+ }
677
+ else if ((parent === null || parent === void 0 ? void 0 : parent.type) === cds_annotation_parser_1.RECORD_TYPE && change.element.name === "Annotation" /* Edm.Annotation */) {
678
+ return (0, cds_odata_annotation_converter_1.printCsdlNode)(change.element, printOptions);
679
+ }
680
+ else {
681
+ return (0, cds_odata_annotation_converter_1.print)(change.element, printOptions);
682
+ }
683
+ };
684
+ }
685
+ function indexInserts(changes, containerSize) {
686
+ const changesByIndex = new Map();
687
+ const indices = [];
688
+ for (const change of changes) {
689
+ const index = change.index !== undefined && change.index > -1 ? Math.min(change.index, containerSize) : containerSize;
690
+ let changeSet = changesByIndex.get(index);
691
+ if (!changeSet) {
692
+ changeSet = [];
693
+ indices.push(index);
694
+ changesByIndex.set(index, changeSet);
695
+ }
696
+ changeSet.push(change);
697
+ }
698
+ return [indices, changesByIndex];
699
+ }
700
+ function findInsertPosition(content, parent, index = -1) {
701
+ const childCount = getChildCount(parent);
702
+ if (childCount === 0) {
703
+ if (!parent.range) {
704
+ return undefined;
705
+ }
706
+ const position = (0, cds_annotation_parser_1.copyPosition)(parent.range.start);
707
+ position.character++; // range includes '{' or '[' characters
708
+ return { position };
709
+ }
710
+ const i = index > -1 ? Math.min(index, childCount) : childCount;
711
+ const { previousContentIndex, startContentIndex } = findContentIndices(content, i);
712
+ const anchor = getStartAnchor(content, parent, previousContentIndex, startContentIndex);
713
+ const previousElement = content[previousContentIndex];
714
+ if (anchor) {
715
+ if ((previousElement === null || previousElement === void 0 ? void 0 : previousElement.type) === 'element' && !previousElement.trailingComma && previousElement.element.range) {
716
+ return {
717
+ position: anchor,
718
+ commaPosition: (0, cds_annotation_parser_1.copyPosition)(previousElement.element.range.end)
719
+ };
720
+ }
721
+ return { position: anchor };
722
+ }
723
+ return undefined;
724
+ }
725
+ function getChildCount(container) {
726
+ return getItems(container).length;
727
+ }
728
+ function getItems(container) {
729
+ var _0;
730
+ switch (container.type) {
731
+ case 'target':
732
+ return container.assignments;
733
+ case 'record':
734
+ return ((_0 = container.annotations) === null || _0 === void 0 ? void 0 : _0.length)
735
+ ? [...container.properties, ...container.annotations].sort(compareRange)
736
+ : container.properties;
737
+ case 'annotation-group-items':
738
+ case 'collection':
739
+ return container.items;
740
+ default:
741
+ return [];
742
+ }
743
+ }
744
+ function getCommas(container, tokens) {
745
+ if (container.type === cds_odata_annotation_converter_1.TARGET_TYPE) {
746
+ if (!container.range) {
747
+ return [];
748
+ }
749
+ return extractCommasFromCompilerTokens(container.range, container.assignments, tokens);
750
+ }
751
+ else {
752
+ return container.commas;
753
+ }
754
+ }
755
+ function extractCommasFromCompilerTokens(range, items, tokens) {
756
+ const result = [];
757
+ const { start, end } = range;
758
+ const startTokenIndex = tokens.findIndex((token) => token.line === start.line + 1 && token.column === start.character);
759
+ for (let i = startTokenIndex; i < tokens.length; i++) {
760
+ const token = tokens[i];
761
+ const tokenRange = odata_annotation_core_1.Range.create(token.line - 1, token.column, token.line - 1, token.column + token.text.length);
762
+ if (token.text === ',' && !items.some((item) => item.range && (0, odata_annotation_core_1.rangeContained)(item.range, tokenRange))) {
763
+ // we are only interested in commas separating items -> ignore commas that are inside an item
764
+ result.push({
765
+ type: 'token',
766
+ value: ',',
767
+ range: tokenRange
768
+ });
769
+ }
770
+ if ((token.line === end.line + 1 && token.column >= end.character) || token.line > end.line + 1) {
771
+ break;
772
+ }
773
+ }
774
+ return result;
775
+ }
776
+ function getStartAnchor(content, parent, previous, index) {
777
+ var _0, _1;
778
+ const previousElement = content[previous];
779
+ let startPosition = index === 0 ? (_0 = parent.range) === null || _0 === void 0 ? void 0 : _0.start : (_1 = previousElement === null || previousElement === void 0 ? void 0 : previousElement.range) === null || _1 === void 0 ? void 0 : _1.end;
780
+ const element = content[index];
781
+ if (startPosition) {
782
+ startPosition = (0, cds_annotation_parser_1.copyPosition)(startPosition);
783
+ if (index === 0) {
784
+ startPosition.character++;
785
+ }
786
+ }
787
+ if (!startPosition) {
788
+ return undefined;
789
+ }
790
+ if (!element && (previousElement === null || previousElement === void 0 ? void 0 : previousElement.type) === 'element') {
791
+ return startPosition;
792
+ }
793
+ const previousItem = content[index - 1];
794
+ if ((previousItem === null || previousItem === void 0 ? void 0 : previousItem.type) === 'comment') {
795
+ // multiple comments between previous item and starting item -> ignore them
796
+ updatePosition(startPosition, previousItem.range.end);
797
+ }
798
+ return startPosition;
799
+ }
800
+ function serializeReference(data) {
801
+ if (data.namespace) {
802
+ if (data.alias) {
803
+ return `using ${data.namespace} as ${data.alias} from '${data.referenceUri}';`;
804
+ }
805
+ else {
806
+ return `using ${data.namespace} from '${data.referenceUri}';`;
807
+ }
808
+ }
809
+ else {
810
+ return `using from '${data.referenceUri}';`;
811
+ }
812
+ }
813
+ function getTextEditForMissingRefs(missingReferences, fileUri, projectRoot) {
814
+ return __awaiter(this, void 0, void 0, function* () {
815
+ const missingReferencesTexts = [];
816
+ for (const missingRef of missingReferences) {
817
+ const relativePath = (0, path_1.relative)(projectRoot, (0, url_1.fileURLToPath)(fileUri));
818
+ const referenceUri = yield (0, project_access_1.toReferenceUri)(projectRoot, relativePath, missingRef);
819
+ missingReferencesTexts.push(serializeReference({ namespace: '', referenceUri }));
820
+ }
821
+ return missingReferencesTexts.join('\n') + '\n';
822
+ });
823
+ }
824
+ function isRangesEqual(range1, range2) {
825
+ if (!range1 || !range2) {
826
+ return false;
827
+ }
828
+ return (range1.start.line === range2.start.line &&
829
+ range1.start.character === range2.start.character &&
830
+ range1.end.line === range2.end.line &&
831
+ range1.end.character === range2.end.character);
832
+ }
833
+ function convertCDSAstToEdmType(kind) {
834
+ switch (kind) {
835
+ case 'time':
836
+ return "TimeOfDay" /* Edm.TimeOfDay */;
837
+ case 'date':
838
+ return "Date" /* Edm.Date */;
839
+ case 'timestamp':
840
+ return "DateTimeOffset" /* Edm.DateTimeOffset */;
841
+ case 'binary':
842
+ return "Binary" /* Edm.Binary */;
843
+ case 'path':
844
+ return "Path" /* Edm.Path */;
845
+ case 'boolean':
846
+ return "Bool" /* Edm.Bool */;
847
+ case 'string':
848
+ return "String" /* Edm.String */;
849
+ case 'enum':
850
+ return "EnumMember" /* Edm.EnumMember */;
851
+ case 'number':
852
+ return "Decimal" /* Edm.Decimal */;
853
+ default:
854
+ return undefined;
855
+ }
856
+ }
857
+ function deIndent(text) {
858
+ return text
859
+ .split('\n')
860
+ .map((val) => val.trimStart())
861
+ .join('\n');
862
+ }
863
+ function getInsertReferencePosition(references) {
864
+ var _0;
865
+ const range = (_0 = references[references.length - 1]) === null || _0 === void 0 ? void 0 : _0.uriRange;
866
+ if (!range) {
867
+ return { position: odata_annotation_core_1.Position.create(0, 0), prependNewLine: false };
868
+ }
869
+ const position = (0, cds_annotation_parser_1.copyPosition)(range.end);
870
+ // reference last position includes its last character, we need to insert after it.
871
+ position.character++;
872
+ return { position, prependNewLine: true };
873
+ }
874
+ function createElementRanges(document, tokens, pointers) {
875
+ const ranges = [];
876
+ const groups = pointers.reduce((acc, pointer) => {
877
+ const segments = pointer.split('/');
878
+ // remove /items/<index> suffix
879
+ const containerPath = segments.slice(0, -2).join('/');
880
+ const index = parseInt(segments.slice(-1)[0], 10);
881
+ const list = acc.get(containerPath);
882
+ if (list) {
883
+ list.push(index);
884
+ }
885
+ else {
886
+ acc.set(containerPath, [index]);
887
+ }
888
+ return acc;
889
+ }, new Map());
890
+ for (const [containerPath, indices] of groups) {
891
+ const [parent] = (0, pointer_1.getAstNodesFromPointer)(document, containerPath).reverse();
892
+ if ((parent === null || parent === void 0 ? void 0 : parent.type) === cds_annotation_parser_1.COLLECTION_TYPE) {
893
+ const indentLevel = (0, indent_1.getIndentLevelFromNode)(tokens, parent);
894
+ indices.sort((index1, index2) => index1 - index2);
895
+ for (let i = 1, start = indices[0], end = indices[0]; i <= indices.length; i++) {
896
+ const current = indices[i];
897
+ if (current === undefined) {
898
+ // end of collection
899
+ ranges.push({ parent, start, end, indentLevel });
900
+ }
901
+ else if (end + 1 === current) {
902
+ // indices are in sequence -> merge
903
+ end = current;
904
+ }
905
+ else {
906
+ // there is a gap between indices -> create a range
907
+ ranges.push({ parent, start, end, indentLevel });
908
+ start = end = current;
909
+ }
910
+ }
911
+ }
912
+ }
913
+ return ranges;
914
+ }
915
+ function compareRange(a, b) {
916
+ if (!a.range) {
917
+ return 1;
918
+ }
919
+ if (!b.range) {
920
+ return -1;
921
+ }
922
+ if ((0, odata_annotation_core_1.isBefore)(a.range.start, b.range.start)) {
923
+ return -1;
924
+ }
925
+ else if ((0, odata_annotation_core_1.isBefore)(b.range.start, a.range.start)) {
926
+ return 1;
927
+ }
928
+ return 0;
929
+ }
930
+ function getTextEditsForMove(document, comments, tokens, position, ranges, indentLevel) {
931
+ const edits = [];
932
+ const text = [];
933
+ for (const range of ranges) {
934
+ const sourceContent = getContainerContent(range.parent, comments, tokens);
935
+ cutRange(document, sourceContent, range, indentLevel, text, edits);
936
+ }
937
+ edits.push(odata_annotation_core_1.TextEdit.insert(position, ''.concat(...text)));
938
+ return edits;
939
+ }
940
+ function findContentIndices(content, start, end = start, nodeType) {
941
+ let previousContentIndex = -1;
942
+ let startContentIndex = -1;
943
+ let endContentIndex = -1;
944
+ for (let index = 0, contentIndex = 0; index < content.length; index++) {
945
+ const element = content[index];
946
+ if (element.type === 'element' && (!nodeType || element.element.type === nodeType)) {
947
+ if (start - 1 === contentIndex) {
948
+ previousContentIndex = index;
949
+ }
950
+ if (start === contentIndex) {
951
+ startContentIndex = index;
952
+ }
953
+ if (end === contentIndex) {
954
+ endContentIndex = index;
955
+ break;
956
+ }
957
+ contentIndex++;
958
+ }
959
+ }
960
+ return {
961
+ previousContentIndex,
962
+ startContentIndex,
963
+ endContentIndex
964
+ };
965
+ }
966
+ function isComma(token) {
967
+ return (token === null || token === void 0 ? void 0 : token.type) === 'token' && token.value === ',';
968
+ }
969
+ function cutRange(textDocument, content, cutRange, indentLevel, text, edits) {
970
+ var _0, _1, _2, _3;
971
+ const { parent, start, end } = cutRange;
972
+ const { previousContentIndex, endContentIndex } = findContentIndices(content, start, end);
973
+ const previousElement = content[previousContentIndex];
974
+ const endElement = content[endContentIndex];
975
+ let startPosition = start === 0 ? (_1 = (_0 = parent.openToken) === null || _0 === void 0 ? void 0 : _0.range) === null || _1 === void 0 ? void 0 : _1.end : (_2 = previousElement === null || previousElement === void 0 ? void 0 : previousElement.range) === null || _2 === void 0 ? void 0 : _2.end;
976
+ let endPosition = (_3 = endElement === null || endElement === void 0 ? void 0 : endElement.range) === null || _3 === void 0 ? void 0 : _3.end;
977
+ if (startPosition) {
978
+ startPosition = (0, cds_annotation_parser_1.copyPosition)(startPosition);
979
+ }
980
+ if (endPosition) {
981
+ endPosition = (0, cds_annotation_parser_1.copyPosition)(endPosition);
982
+ }
983
+ if (!startPosition || !endPosition) {
984
+ return;
985
+ }
986
+ let suffix;
987
+ if ((endElement === null || endElement === void 0 ? void 0 : endElement.type) === 'element') {
988
+ if (!endElement.trailingComma && endElement.trailingComment && endElement.element.range) {
989
+ // ...} // some comment
990
+ // ___|_______________|
991
+ // | |
992
+ // cut suffix range
993
+ const range = (0, cds_annotation_parser_1.copyRange)(odata_annotation_core_1.Range.create(endElement.element.range.end, endElement.trailingComment.range.end));
994
+ edits.push(odata_annotation_core_1.TextEdit.del(range));
995
+ suffix = ',' + textDocument.getText(range);
996
+ updatePosition(endPosition, endElement.element.range.end);
997
+ }
998
+ else if (!endElement.trailingComma) {
999
+ // ...}
1000
+ suffix = ',';
1001
+ }
1002
+ }
1003
+ const range = (0, cds_annotation_parser_1.copyRange)(odata_annotation_core_1.Range.create(startPosition, endPosition));
1004
+ const originalText = textDocument.getText(range);
1005
+ text.push(makeCut(originalText, suffix, cutRange, indentLevel));
1006
+ edits.push(odata_annotation_core_1.TextEdit.del(range));
1007
+ }
1008
+ function makeCut(originalText, suffix, cutRange, indentLevel) {
1009
+ let cut = originalText;
1010
+ const difference = indentLevel - cutRange.indentLevel;
1011
+ if (difference > 0) {
1012
+ const indent = ' '.repeat(difference);
1013
+ cut = cut.replaceAll('\n', '\n' + indent);
1014
+ }
1015
+ else if (difference < 0) {
1016
+ const indent = ' '.repeat(difference * -1);
1017
+ cut = cut.replaceAll('\n' + indent, '\n');
1018
+ }
1019
+ if (suffix !== undefined) {
1020
+ return cut + suffix;
1021
+ }
1022
+ else {
1023
+ return cut;
1024
+ }
1025
+ }
1026
+ function updatePosition(a, b) {
1027
+ a.line = b.line;
1028
+ a.character = b.character;
1029
+ }
1030
+ function getContainerContent(collection, comments, tokens) {
1031
+ if (!collection.range) {
1032
+ return [];
1033
+ }
1034
+ const items = getItems(collection);
1035
+ const commas = getCommas(collection, tokens);
1036
+ const commentsInContent = (collection.range !== undefined
1037
+ ? comments.filter((comment) => (0, odata_annotation_core_1.rangeContained)(collection.range, comment.range))
1038
+ : []).filter((comment) => !items.some((item) => item.range && (0, odata_annotation_core_1.rangeContained)(item.range, comment.range)));
1039
+ const source = [...commas, ...items, ...commentsInContent].sort(compareRange);
1040
+ const content = [];
1041
+ for (const node of source) {
1042
+ processNode(content, node);
1043
+ }
1044
+ return content;
1045
+ }
1046
+ function processNode(content, item) {
1047
+ const previousItem = content[content.length - 1];
1048
+ if (!item.range) {
1049
+ return;
1050
+ }
1051
+ if (isComma(item)) {
1052
+ if ((previousItem === null || previousItem === void 0 ? void 0 : previousItem.type) === 'element') {
1053
+ previousItem.trailingComma = item;
1054
+ updatePosition(previousItem.range.end, item.range.end);
1055
+ }
1056
+ else {
1057
+ content.push(item);
1058
+ }
1059
+ }
1060
+ else if (item.type === 'comment') {
1061
+ if ((previousItem === null || previousItem === void 0 ? void 0 : previousItem.type) === 'element' && item.range.start.line === previousItem.range.end.line) {
1062
+ previousItem.trailingComment = item;
1063
+ updatePosition(previousItem.range.end, item.range.end);
1064
+ }
1065
+ else {
1066
+ content.push(item);
1067
+ }
1068
+ }
1069
+ else {
1070
+ const element = {
1071
+ type: 'element',
1072
+ element: item,
1073
+ elementRange: (0, cds_annotation_parser_1.copyRange)(item.range),
1074
+ range: (0, cds_annotation_parser_1.copyRange)(item.range)
1075
+ };
1076
+ const previousLine = element.range.start.line - 1;
1077
+ if ((previousItem === null || previousItem === void 0 ? void 0 : previousItem.type) === 'comment' &&
1078
+ (previousItem.range.end.line === previousLine || previousItem.range.end.line === element.range.start.line)) {
1079
+ // typescript can't infer that content.pop() === previousItem
1080
+ element.leadingComment = content.pop();
1081
+ updatePosition(element.range.start, previousItem.range.start);
1082
+ }
1083
+ content.push(element);
1084
+ }
1085
+ }
1086
+ //# sourceMappingURL=writer.js.map