@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.
- package/LICENSE +201 -0
- package/Readme.md +35 -0
- package/dist/avt/annotations.d.ts +72 -0
- package/dist/avt/annotations.d.ts.map +1 -0
- package/dist/avt/annotations.js +508 -0
- package/dist/avt/annotations.js.map +1 -0
- package/dist/avt/expressions.d.ts +13 -0
- package/dist/avt/expressions.d.ts.map +1 -0
- package/dist/avt/expressions.js +34 -0
- package/dist/avt/expressions.js.map +1 -0
- package/dist/avt/find.d.ts +39 -0
- package/dist/avt/find.d.ts.map +1 -0
- package/dist/avt/find.js +130 -0
- package/dist/avt/find.js.map +1 -0
- package/dist/avt/index.d.ts +9 -0
- package/dist/avt/index.d.ts.map +1 -0
- package/dist/avt/index.js +32 -0
- package/dist/avt/index.js.map +1 -0
- package/dist/avt/metadata.d.ts +10 -0
- package/dist/avt/metadata.d.ts.map +1 -0
- package/dist/avt/metadata.js +346 -0
- package/dist/avt/metadata.js.map +1 -0
- package/dist/avt/pointer.d.ts +12 -0
- package/dist/avt/pointer.d.ts.map +1 -0
- package/dist/avt/pointer.js +188 -0
- package/dist/avt/pointer.js.map +1 -0
- package/dist/avt/to-internal.d.ts +81 -0
- package/dist/avt/to-internal.d.ts.map +1 -0
- package/dist/avt/to-internal.js +340 -0
- package/dist/avt/to-internal.js.map +1 -0
- package/dist/avt/types.d.ts +3 -0
- package/dist/avt/types.d.ts.map +1 -0
- package/dist/avt/types.js +3 -0
- package/dist/avt/types.js.map +1 -0
- package/dist/avt/utils.d.ts +61 -0
- package/dist/avt/utils.d.ts.map +1 -0
- package/dist/avt/utils.js +87 -0
- package/dist/avt/utils.js.map +1 -0
- package/dist/cds/adapter.d.ts +112 -0
- package/dist/cds/adapter.d.ts.map +1 -0
- package/dist/cds/adapter.js +703 -0
- package/dist/cds/adapter.js.map +1 -0
- package/dist/cds/cds-compiler-tokens.d.ts +30 -0
- package/dist/cds/cds-compiler-tokens.d.ts.map +1 -0
- package/dist/cds/cds-compiler-tokens.js +57 -0
- package/dist/cds/cds-compiler-tokens.js.map +1 -0
- package/dist/cds/change.d.ts +347 -0
- package/dist/cds/change.d.ts.map +1 -0
- package/dist/cds/change.js +232 -0
- package/dist/cds/change.js.map +1 -0
- package/dist/cds/comments.d.ts +15 -0
- package/dist/cds/comments.d.ts.map +1 -0
- package/dist/cds/comments.js +56 -0
- package/dist/cds/comments.js.map +1 -0
- package/dist/cds/deletion.d.ts +59 -0
- package/dist/cds/deletion.d.ts.map +1 -0
- package/dist/cds/deletion.js +821 -0
- package/dist/cds/deletion.js.map +1 -0
- package/dist/cds/document.d.ts +52 -0
- package/dist/cds/document.d.ts.map +1 -0
- package/dist/cds/document.js +98 -0
- package/dist/cds/document.js.map +1 -0
- package/dist/cds/indent.d.ts +20 -0
- package/dist/cds/indent.d.ts.map +1 -0
- package/dist/cds/indent.js +86 -0
- package/dist/cds/indent.js.map +1 -0
- package/dist/cds/index.d.ts +3 -0
- package/dist/cds/index.d.ts.map +1 -0
- package/dist/cds/index.js +21 -0
- package/dist/cds/index.js.map +1 -0
- package/dist/cds/pointer.d.ts +23 -0
- package/dist/cds/pointer.d.ts.map +1 -0
- package/dist/cds/pointer.js +438 -0
- package/dist/cds/pointer.js.map +1 -0
- package/dist/cds/preprocessor.d.ts +12 -0
- package/dist/cds/preprocessor.d.ts.map +1 -0
- package/dist/cds/preprocessor.js +338 -0
- package/dist/cds/preprocessor.js.map +1 -0
- package/dist/cds/references.d.ts +35 -0
- package/dist/cds/references.d.ts.map +1 -0
- package/dist/cds/references.js +413 -0
- package/dist/cds/references.js.map +1 -0
- package/dist/cds/service.d.ts +11 -0
- package/dist/cds/service.d.ts.map +1 -0
- package/dist/cds/service.js +37 -0
- package/dist/cds/service.js.map +1 -0
- package/dist/cds/utils.d.ts +35 -0
- package/dist/cds/utils.d.ts.map +1 -0
- package/dist/cds/utils.js +75 -0
- package/dist/cds/utils.js.map +1 -0
- package/dist/cds/writer.d.ts +104 -0
- package/dist/cds/writer.d.ts.map +1 -0
- package/dist/cds/writer.js +1086 -0
- package/dist/cds/writer.js.map +1 -0
- package/dist/change-converter.d.ts +54 -0
- package/dist/change-converter.d.ts.map +1 -0
- package/dist/change-converter.js +639 -0
- package/dist/change-converter.js.map +1 -0
- package/dist/error.d.ts +35 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +64 -0
- package/dist/error.js.map +1 -0
- package/dist/fiori-service.d.ts +130 -0
- package/dist/fiori-service.d.ts.map +1 -0
- package/dist/fiori-service.js +362 -0
- package/dist/fiori-service.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/protected.d.ts +3 -0
- package/dist/protected.d.ts.map +1 -0
- package/dist/protected.js +11 -0
- package/dist/protected.js.map +1 -0
- package/dist/types/adapter.d.ts +46 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +3 -0
- package/dist/types/adapter.js.map +1 -0
- package/dist/types/change.d.ts +187 -0
- package/dist/types/change.d.ts.map +1 -0
- package/dist/types/change.js +52 -0
- package/dist/types/change.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +33 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/internal-change.d.ts +98 -0
- package/dist/types/internal-change.d.ts.map +1 -0
- package/dist/types/internal-change.js +18 -0
- package/dist/types/internal-change.js.map +1 -0
- package/dist/types/project-info.d.ts +6 -0
- package/dist/types/project-info.d.ts.map +1 -0
- package/dist/types/project-info.js +3 -0
- package/dist/types/project-info.js.map +1 -0
- package/dist/types/service.d.ts +27 -0
- package/dist/types/service.d.ts.map +1 -0
- package/dist/types/service.js +3 -0
- package/dist/types/service.js.map +1 -0
- package/dist/types/text-file.d.ts +12 -0
- package/dist/types/text-file.d.ts.map +1 -0
- package/dist/types/text-file.js +3 -0
- package/dist/types/text-file.js.map +1 -0
- package/dist/utils/constants.d.ts +2 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +24 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/indent.d.ts +10 -0
- package/dist/utils/indent.d.ts.map +1 -0
- package/dist/utils/indent.js +36 -0
- package/dist/utils/indent.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +16 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/path.d.ts +8 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +31 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/pointer.d.ts +11 -0
- package/dist/utils/pointer.d.ts.map +1 -0
- package/dist/utils/pointer.js +26 -0
- package/dist/utils/pointer.js.map +1 -0
- package/dist/utils/reference.d.ts +10 -0
- package/dist/utils/reference.d.ts.map +1 -0
- package/dist/utils/reference.js +20 -0
- package/dist/utils/reference.js.map +1 -0
- package/dist/utils/text-edits.d.ts +12 -0
- package/dist/utils/text-edits.d.ts.map +1 -0
- package/dist/utils/text-edits.js +29 -0
- package/dist/utils/text-edits.js.map +1 -0
- package/dist/vocabularies.d.ts +11 -0
- package/dist/vocabularies.d.ts.map +1 -0
- package/dist/vocabularies.js +26 -0
- package/dist/vocabularies.js.map +1 -0
- package/dist/xml/adapter.d.ts +85 -0
- package/dist/xml/adapter.d.ts.map +1 -0
- package/dist/xml/adapter.js +579 -0
- package/dist/xml/adapter.js.map +1 -0
- package/dist/xml/changes.d.ts +117 -0
- package/dist/xml/changes.d.ts.map +1 -0
- package/dist/xml/changes.js +34 -0
- package/dist/xml/changes.js.map +1 -0
- package/dist/xml/comments.d.ts +17 -0
- package/dist/xml/comments.d.ts.map +1 -0
- package/dist/xml/comments.js +49 -0
- package/dist/xml/comments.js.map +1 -0
- package/dist/xml/document.d.ts +11 -0
- package/dist/xml/document.d.ts.map +1 -0
- package/dist/xml/document.js +3 -0
- package/dist/xml/document.js.map +1 -0
- package/dist/xml/index.d.ts +3 -0
- package/dist/xml/index.d.ts.map +1 -0
- package/dist/xml/index.js +8 -0
- package/dist/xml/index.js.map +1 -0
- package/dist/xml/pointer.d.ts +10 -0
- package/dist/xml/pointer.d.ts.map +1 -0
- package/dist/xml/pointer.js +29 -0
- package/dist/xml/pointer.js.map +1 -0
- package/dist/xml/references.d.ts +9 -0
- package/dist/xml/references.d.ts.map +1 -0
- package/dist/xml/references.js +87 -0
- package/dist/xml/references.js.map +1 -0
- package/dist/xml/service.d.ts +12 -0
- package/dist/xml/service.d.ts.map +1 -0
- package/dist/xml/service.js +55 -0
- package/dist/xml/service.js.map +1 -0
- package/dist/xml/writer.d.ts +39 -0
- package/dist/xml/writer.d.ts.map +1 -0
- package/dist/xml/writer.js +855 -0
- package/dist/xml/writer.js.map +1 -0
- 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
|