@sap-ux/fiori-annotation-api 0.11.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotation-provider.d.ts +1 -1
- package/dist/annotation-provider.d.ts.map +1 -1
- package/dist/annotation-provider.js +30 -36
- package/dist/annotation-provider.js.map +1 -1
- package/dist/avt/annotations.js +53 -56
- package/dist/avt/annotations.js.map +1 -1
- package/dist/avt/expressions.js +3 -7
- package/dist/avt/expressions.js.map +1 -1
- package/dist/avt/find.d.ts +2 -2
- package/dist/avt/find.d.ts.map +1 -1
- package/dist/avt/find.js +16 -20
- package/dist/avt/find.js.map +1 -1
- package/dist/avt/index.d.ts +8 -8
- package/dist/avt/index.d.ts.map +1 -1
- package/dist/avt/index.js +7 -31
- package/dist/avt/index.js.map +1 -1
- package/dist/avt/metadata.js +1 -4
- package/dist/avt/metadata.js.map +1 -1
- package/dist/avt/pointer.d.ts +1 -1
- package/dist/avt/pointer.d.ts.map +1 -1
- package/dist/avt/pointer.js +12 -15
- package/dist/avt/pointer.js.map +1 -1
- package/dist/avt/to-internal.d.ts +1 -1
- package/dist/avt/to-internal.d.ts.map +1 -1
- package/dist/avt/to-internal.js +53 -65
- package/dist/avt/to-internal.js.map +1 -1
- package/dist/avt/types.js +1 -2
- package/dist/avt/utils.d.ts +2 -2
- package/dist/avt/utils.d.ts.map +1 -1
- package/dist/avt/utils.js +11 -20
- package/dist/avt/utils.js.map +1 -1
- package/dist/cds/adapter.d.ts +5 -5
- package/dist/cds/adapter.d.ts.map +1 -1
- package/dist/cds/adapter.js +184 -187
- package/dist/cds/adapter.js.map +1 -1
- package/dist/cds/cds-compiler-tokens.js +11 -21
- package/dist/cds/cds-compiler-tokens.js.map +1 -1
- package/dist/cds/change.d.ts +1 -1
- package/dist/cds/change.d.ts.map +1 -1
- package/dist/cds/change.js +63 -79
- package/dist/cds/change.js.map +1 -1
- package/dist/cds/comments.d.ts +1 -1
- package/dist/cds/comments.d.ts.map +1 -1
- package/dist/cds/comments.js +4 -7
- package/dist/cds/comments.js.map +1 -1
- package/dist/cds/deletion.d.ts +1 -1
- package/dist/cds/deletion.d.ts.map +1 -1
- package/dist/cds/deletion.js +74 -61
- package/dist/cds/deletion.js.map +1 -1
- package/dist/cds/document.d.ts +4 -4
- package/dist/cds/document.d.ts.map +1 -1
- package/dist/cds/document.js +20 -27
- package/dist/cds/document.js.map +1 -1
- package/dist/cds/indent.d.ts +2 -2
- package/dist/cds/indent.d.ts.map +1 -1
- package/dist/cds/indent.js +16 -20
- package/dist/cds/indent.js.map +1 -1
- package/dist/cds/index.d.ts +2 -2
- package/dist/cds/index.d.ts.map +1 -1
- package/dist/cds/index.js +4 -22
- package/dist/cds/index.js.map +1 -1
- package/dist/cds/pointer.d.ts +1 -1
- package/dist/cds/pointer.d.ts.map +1 -1
- package/dist/cds/pointer.js +38 -42
- package/dist/cds/pointer.js.map +1 -1
- package/dist/cds/preprocessor.d.ts +3 -3
- package/dist/cds/preprocessor.d.ts.map +1 -1
- package/dist/cds/preprocessor.js +91 -95
- package/dist/cds/preprocessor.js.map +1 -1
- package/dist/cds/references.d.ts +2 -2
- package/dist/cds/references.d.ts.map +1 -1
- package/dist/cds/references.js +49 -55
- package/dist/cds/references.js.map +1 -1
- package/dist/cds/service.d.ts +1 -1
- package/dist/cds/service.d.ts.map +1 -1
- package/dist/cds/service.js +6 -8
- package/dist/cds/service.js.map +1 -1
- package/dist/cds/utils.d.ts +2 -2
- package/dist/cds/utils.d.ts.map +1 -1
- package/dist/cds/utils.js +20 -24
- package/dist/cds/utils.js.map +1 -1
- package/dist/cds/writer.d.ts +6 -6
- package/dist/cds/writer.d.ts.map +1 -1
- package/dist/cds/writer.js +199 -202
- package/dist/cds/writer.js.map +1 -1
- package/dist/change-converter.d.ts +1 -1
- package/dist/change-converter.d.ts.map +1 -1
- package/dist/change-converter.js +124 -128
- package/dist/change-converter.js.map +1 -1
- package/dist/error.js +3 -7
- package/dist/error.js.map +1 -1
- package/dist/external-services.d.ts +1 -1
- package/dist/external-services.d.ts.map +1 -1
- package/dist/external-services.js +10 -13
- package/dist/external-services.js.map +1 -1
- package/dist/fiori-service.d.ts +4 -4
- package/dist/fiori-service.d.ts.map +1 -1
- package/dist/fiori-service.js +41 -45
- package/dist/fiori-service.js.map +1 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -31
- package/dist/index.js.map +1 -1
- package/dist/logger.js +2 -5
- package/dist/logger.js.map +1 -1
- package/dist/protected.d.ts +2 -2
- package/dist/protected.d.ts.map +1 -1
- package/dist/protected.js +2 -8
- package/dist/protected.js.map +1 -1
- package/dist/sap/builders.d.ts +1 -1
- package/dist/sap/builders.d.ts.map +1 -1
- package/dist/sap/builders.js +22 -30
- package/dist/sap/builders.js.map +1 -1
- package/dist/sap/collector.d.ts +1 -1
- package/dist/sap/collector.d.ts.map +1 -1
- package/dist/sap/collector.js +50 -53
- package/dist/sap/collector.js.map +1 -1
- package/dist/sap/converter.d.ts +1 -1
- package/dist/sap/converter.d.ts.map +1 -1
- package/dist/sap/converter.js +37 -42
- package/dist/sap/converter.js.map +1 -1
- package/dist/sap/index.d.ts +1 -1
- package/dist/sap/index.d.ts.map +1 -1
- package/dist/sap/index.js +1 -6
- package/dist/sap/index.js.map +1 -1
- package/dist/sap/types.js +5 -8
- package/dist/sap/types.js.map +1 -1
- package/dist/types/adapter.d.ts +3 -3
- package/dist/types/adapter.d.ts.map +1 -1
- package/dist/types/adapter.js +1 -2
- package/dist/types/change.js +11 -14
- package/dist/types/change.js.map +1 -1
- package/dist/types/index.d.ts +6 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -32
- package/dist/types/index.js.map +1 -1
- package/dist/types/internal-change.d.ts +1 -1
- package/dist/types/internal-change.d.ts.map +1 -1
- package/dist/types/internal-change.js +13 -16
- package/dist/types/internal-change.js.map +1 -1
- package/dist/types/project-info.js +1 -2
- package/dist/types/service.d.ts +1 -1
- package/dist/types/service.d.ts.map +1 -1
- package/dist/types/service.js +1 -2
- package/dist/types/text-file.js +1 -2
- package/dist/utils/constants.js +20 -22
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/indent.js +1 -4
- package/dist/utils/indent.js.map +1 -1
- package/dist/utils/index.d.ts +7 -7
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +7 -17
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/metadata.js +1 -4
- package/dist/utils/metadata.js.map +1 -1
- package/dist/utils/path.js +5 -8
- package/dist/utils/path.js.map +1 -1
- package/dist/utils/pointer.d.ts +1 -1
- package/dist/utils/pointer.d.ts.map +1 -1
- package/dist/utils/pointer.js +1 -4
- package/dist/utils/pointer.js.map +1 -1
- package/dist/utils/range.js +1 -4
- package/dist/utils/range.js.map +1 -1
- package/dist/utils/reference.d.ts +1 -1
- package/dist/utils/reference.d.ts.map +1 -1
- package/dist/utils/reference.js +1 -4
- package/dist/utils/reference.js.map +1 -1
- package/dist/vocabularies.js +1 -4
- package/dist/vocabularies.js.map +1 -1
- package/dist/xml/adapter.d.ts +2 -2
- package/dist/xml/adapter.d.ts.map +1 -1
- package/dist/xml/adapter.js +92 -96
- package/dist/xml/adapter.js.map +1 -1
- package/dist/xml/changes.d.ts +1 -1
- package/dist/xml/changes.d.ts.map +1 -1
- package/dist/xml/changes.js +11 -15
- package/dist/xml/changes.js.map +1 -1
- package/dist/xml/comments.js +3 -6
- package/dist/xml/comments.js.map +1 -1
- package/dist/xml/document.d.ts +1 -1
- package/dist/xml/document.d.ts.map +1 -1
- package/dist/xml/document.js +1 -2
- package/dist/xml/index.d.ts +2 -2
- package/dist/xml/index.d.ts.map +1 -1
- package/dist/xml/index.js +4 -9
- package/dist/xml/index.js.map +1 -1
- package/dist/xml/pointer.js +1 -4
- package/dist/xml/pointer.js.map +1 -1
- package/dist/xml/references.js +15 -18
- package/dist/xml/references.js.map +1 -1
- package/dist/xml/service.d.ts +1 -1
- package/dist/xml/service.d.ts.map +1 -1
- package/dist/xml/service.js +4 -7
- package/dist/xml/service.js.map +1 -1
- package/dist/xml/writer.d.ts +2 -2
- package/dist/xml/writer.d.ts.map +1 -1
- package/dist/xml/writer.js +100 -104
- package/dist/xml/writer.js.map +1 -1
- package/package.json +13 -11
package/dist/cds/writer.js
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const indent_1 = require("./indent");
|
|
20
|
-
const printOptions = { ...odata_annotation_core_1.printOptions, useSnippetSyntax: false };
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { toReferenceUri } from '@sap-ux/project-access';
|
|
4
|
+
import { TextEdit, printOptions as defaultPrintOptions, Position, Edm, Range, rangeContained, TEXT_TYPE } from '@sap-ux/odata-annotation-core';
|
|
5
|
+
import cdsCompilerFacade from '@sap/ux-cds-compiler-facade';
|
|
6
|
+
const { createMetadataCollector } = cdsCompilerFacade;
|
|
7
|
+
import { copyRange, ENUM_TYPE, STRING_LITERAL_TYPE, ReservedProperties, ANNOTATION_TYPE, COLLECTION_TYPE, RECORD_PROPERTY_TYPE, RECORD_TYPE, copyPosition, ANNOTATION_GROUP_TYPE, ANNOTATION_GROUP_ITEMS_TYPE } from '@sap-ux/cds-annotation-parser';
|
|
8
|
+
import { TARGET_TYPE, printPrimitiveValue, indent, print, printTarget } from '@sap-ux/cds-odata-annotation-converter';
|
|
9
|
+
import { increaseIndent, compareByRange } from '../utils/index.js';
|
|
10
|
+
import { ApiError } from '../error.js';
|
|
11
|
+
import { CDS_DOCUMENT_TYPE, getChildCount, getItems } from './document.js';
|
|
12
|
+
import { getTextEditsForDeletionRanges, getDeletionRangeForNode } from './deletion.js';
|
|
13
|
+
import { getAstNodesFromPointer } from './pointer.js';
|
|
14
|
+
import { INSERT_PRIMITIVE_VALUE_TYPE, MOVE_COLLECTION_VALUE_CHANGE_TYPE, DELETE_ANNOTATION_CHANGE_TYPE, INSERT_ANNOTATION_CHANGE_TYPE, INSERT_TARGET_CHANGE_TYPE, INSERT_RECORD_CHANGE_TYPE, INSERT_COLLECTION_CHANGE_TYPE, INSERT_EMBEDDED_ANNOTATION_CHANGE_TYPE, INSERT_RECORD_PROPERTY_CHANGE_TYPE, INSERT_QUALIFIER_CHANGE_TYPE, INSERT_REFERENCE_CHANGE_TYPE, DELETE_EMBEDDED_ANNOTATION_CHANGE_TYPE, DELETE_PRIMITIVE_VALUE_CHANGE_TYPE, DELETE_QUALIFIER_CHANGE_TYPE, DELETE_RECORD_CHANGE_TYPE, DELETE_RECORD_PROPERTY_CHANGE_TYPE, REPLACE_NODE_CHANGE_TYPE, REPLACE_RECORD_PROPERTY_CHANGE_TYPE, SET_FLAGS_CHANGE_TYPE, UPDATE_PRIMITIVE_VALUE_CHANGE_TYPE, REPLACE_TEXT_VALUE_CHANGE_TYPE, DELETE_TARGET_CHANGE_TYPE, CONVERT_TO_COMPOUND_ANNOTATION_CHANGE_TYPE, DELETE_ANNOTATION_GROUP_CHANGE_TYPE, DELETE_ANNOTATION_GROUP_ITEMS_CHANGE_TYPE, createDeleteAnnotationChange } from './change.js';
|
|
15
|
+
import { createReferenceElement, preprocessChanges } from './preprocessor.js';
|
|
16
|
+
import { findLastTokenBeforePosition, findFirstTokenAfterPosition, matchTokenByStart, createTokenRange, isOldToken, tokenLine, tokenColumn } from './cds-compiler-tokens.js';
|
|
17
|
+
import { getIndentLevelFromPointer, getIndentLevelFromNode } from './indent.js';
|
|
18
|
+
const printOptions = { ...defaultPrintOptions, useSnippetSyntax: false };
|
|
21
19
|
const ANNOTATION_START_PATTERN = /^@/i;
|
|
22
20
|
/**
|
|
23
21
|
*
|
|
24
22
|
*/
|
|
25
|
-
class CDSWriter {
|
|
23
|
+
export class CDSWriter {
|
|
26
24
|
facade;
|
|
27
25
|
vocabularyService;
|
|
28
26
|
document;
|
|
@@ -84,9 +82,9 @@ class CDSWriter {
|
|
|
84
82
|
*/
|
|
85
83
|
async getTextEdits() {
|
|
86
84
|
this.resetState();
|
|
87
|
-
this.processedChanges =
|
|
85
|
+
this.processedChanges = preprocessChanges(this.document, this.changes, this.tokens);
|
|
88
86
|
for (const change of this.processedChanges) {
|
|
89
|
-
const path =
|
|
87
|
+
const path = getAstNodesFromPointer(this.document, change.pointer).reverse();
|
|
90
88
|
const handler = this[change.type];
|
|
91
89
|
const result = handler(change, path);
|
|
92
90
|
if (result instanceof Promise) {
|
|
@@ -96,12 +94,12 @@ class CDSWriter {
|
|
|
96
94
|
for (const key of this.deletionRangesMapForTarget.keys()) {
|
|
97
95
|
const deletionRanges = this.deletionRangesMapForTarget.get(key) ?? [];
|
|
98
96
|
if (deletionRanges.length > 0) {
|
|
99
|
-
const targetDeletions =
|
|
97
|
+
const targetDeletions = getTextEditsForDeletionRanges(deletionRanges, this.vocabularyAliases, this.tokens, this.annotationFile, false);
|
|
100
98
|
this.edits.push(...targetDeletions);
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
this.edits.unshift(...this.referenceEdits);
|
|
104
|
-
this.edits.sort(
|
|
102
|
+
this.edits.sort(compareByRange);
|
|
105
103
|
return this.edits;
|
|
106
104
|
}
|
|
107
105
|
/**
|
|
@@ -121,13 +119,13 @@ class CDSWriter {
|
|
|
121
119
|
if (cachedValue !== undefined) {
|
|
122
120
|
return cachedValue;
|
|
123
121
|
}
|
|
124
|
-
const level =
|
|
122
|
+
const level = getIndentLevelFromPointer(this.document, this.tokens, pointer);
|
|
125
123
|
this.indentLevelCache[pointer] = level;
|
|
126
124
|
return level;
|
|
127
125
|
}
|
|
128
126
|
//#region Inserts
|
|
129
127
|
isFirstInsert(pointer, node, index = -1) {
|
|
130
|
-
const childCount =
|
|
128
|
+
const childCount = getChildCount(node);
|
|
131
129
|
const i = index > -1 ? Math.min(index, childCount) : childCount;
|
|
132
130
|
const insertPositionKey = `${pointer}/${i}`;
|
|
133
131
|
const firstInsert = !this.uniqueInserts.has(insertPositionKey);
|
|
@@ -135,7 +133,7 @@ class CDSWriter {
|
|
|
135
133
|
return firstInsert;
|
|
136
134
|
}
|
|
137
135
|
// Change Handlers
|
|
138
|
-
[
|
|
136
|
+
[INSERT_TARGET_CHANGE_TYPE] = (change) => {
|
|
139
137
|
if (!this.document.range) {
|
|
140
138
|
return;
|
|
141
139
|
}
|
|
@@ -145,63 +143,63 @@ class CDSWriter {
|
|
|
145
143
|
prefix = '\n';
|
|
146
144
|
position.character = 0;
|
|
147
145
|
}
|
|
148
|
-
const newElements = prefix +
|
|
149
|
-
const edits = [
|
|
146
|
+
const newElements = prefix + printTarget(change.target, change.complexTypePathSegments) + '\n';
|
|
147
|
+
const edits = [TextEdit.insert(position, newElements)];
|
|
150
148
|
this.edits.push(...edits);
|
|
151
149
|
};
|
|
152
|
-
[
|
|
150
|
+
[INSERT_RECORD_CHANGE_TYPE] = (change, reversePath) => {
|
|
153
151
|
const [astNode] = reversePath;
|
|
154
152
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
155
153
|
// inserting new record as value eg: annotate IncidentService.Incidents with @UI.Chart;
|
|
156
|
-
if ((astNode?.type ===
|
|
154
|
+
if ((astNode?.type === ANNOTATION_TYPE || astNode?.type === RECORD_PROPERTY_TYPE) && astNode.range) {
|
|
157
155
|
const recordText = ' : ' +
|
|
158
|
-
|
|
156
|
+
indent(print(change.element, printOptions, { indentResult: false }), {
|
|
159
157
|
level: indentLevel,
|
|
160
158
|
skipFirstLine: true
|
|
161
159
|
});
|
|
162
160
|
const position = astNode.range.end;
|
|
163
|
-
this.edits.push(
|
|
161
|
+
this.edits.push(TextEdit.insert(position, recordText));
|
|
164
162
|
}
|
|
165
|
-
if (astNode?.type ===
|
|
163
|
+
if (astNode?.type === COLLECTION_TYPE) {
|
|
166
164
|
const content = getContainerContent(astNode, this.comments, this.tokens);
|
|
167
165
|
this.insertIntoNodeWithContent(content, astNode, change, indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
|
|
168
166
|
}
|
|
169
167
|
};
|
|
170
|
-
[
|
|
168
|
+
[INSERT_COLLECTION_CHANGE_TYPE] = (change, reversePath) => {
|
|
171
169
|
const [astNode] = reversePath;
|
|
172
170
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
173
|
-
const collectionText = change.element.content.length ?
|
|
171
|
+
const collectionText = change.element.content.length ? print(change.element) : '[]';
|
|
174
172
|
const textWithPrefix = ` : ${collectionText}`;
|
|
175
173
|
const position = astNode?.range?.end;
|
|
176
174
|
if (position) {
|
|
177
|
-
const text =
|
|
178
|
-
this.edits.push(
|
|
175
|
+
const text = indent(textWithPrefix, { level: indentLevel + 1, skipFirstLine: true });
|
|
176
|
+
this.edits.push(TextEdit.insert(position, text));
|
|
179
177
|
}
|
|
180
178
|
};
|
|
181
|
-
[
|
|
179
|
+
[INSERT_ANNOTATION_CHANGE_TYPE] = (change, reversePath) => {
|
|
182
180
|
const [astNode] = reversePath;
|
|
183
|
-
if (astNode?.type !==
|
|
181
|
+
if (astNode?.type !== TARGET_TYPE && astNode?.type !== ANNOTATION_GROUP_ITEMS_TYPE) {
|
|
184
182
|
return;
|
|
185
183
|
}
|
|
186
184
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
187
185
|
const content = getContainerContent(astNode, this.comments, this.tokens);
|
|
188
186
|
this.convertInsertNodeToTextEdits(content, astNode, change, indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
|
|
189
187
|
};
|
|
190
|
-
[
|
|
188
|
+
[INSERT_EMBEDDED_ANNOTATION_CHANGE_TYPE] = (change, reversePath) => {
|
|
191
189
|
const [astNode, parent] = reversePath;
|
|
192
190
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
193
|
-
if (astNode?.type ===
|
|
191
|
+
if (astNode?.type === RECORD_TYPE) {
|
|
194
192
|
const content = getContainerContent(astNode, this.comments, this.tokens);
|
|
195
193
|
this.insertIntoNodeWithContent(content, astNode, change, indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
|
|
196
194
|
}
|
|
197
|
-
else if (astNode?.type ===
|
|
195
|
+
else if (astNode?.type === ANNOTATION_TYPE) {
|
|
198
196
|
// we're inserting into the parent, not the value
|
|
199
197
|
let adjustedIndentLevel = indentLevel - 1;
|
|
200
198
|
if (willTargetAnnotationIncreaseIndent(this.processedChanges, change.pointer)) {
|
|
201
199
|
adjustedIndentLevel++;
|
|
202
200
|
}
|
|
203
201
|
if (astNode.value?.range &&
|
|
204
|
-
(parent?.type ===
|
|
202
|
+
(parent?.type === TARGET_TYPE || parent?.type === ANNOTATION_GROUP_ITEMS_TYPE)) {
|
|
205
203
|
const content = getContainerContent(parent, this.comments, this.tokens);
|
|
206
204
|
this.insertIntoNodeWithContent(content, parent, change, adjustedIndentLevel,
|
|
207
205
|
// adjust pointer to match the parent node, because we're inserting into the parent
|
|
@@ -209,40 +207,40 @@ class CDSWriter {
|
|
|
209
207
|
}
|
|
210
208
|
}
|
|
211
209
|
};
|
|
212
|
-
[
|
|
210
|
+
[INSERT_RECORD_PROPERTY_CHANGE_TYPE] = (change, reversePath) => {
|
|
213
211
|
const [astNode] = reversePath;
|
|
214
212
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
215
|
-
if (astNode?.type !==
|
|
213
|
+
if (astNode?.type !== RECORD_TYPE) {
|
|
216
214
|
return;
|
|
217
215
|
}
|
|
218
216
|
const content = getContainerContent(astNode, this.comments, this.tokens);
|
|
219
217
|
this.convertInsertNodeToTextEdits(content, astNode, change, indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
|
|
220
218
|
};
|
|
221
|
-
[
|
|
219
|
+
[INSERT_PRIMITIVE_VALUE_TYPE] = (change, reversePath) => {
|
|
222
220
|
const [astNode] = reversePath;
|
|
223
221
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
224
|
-
if (astNode?.type ===
|
|
222
|
+
if (astNode?.type === COLLECTION_TYPE) {
|
|
225
223
|
const content = getContainerContent(astNode, this.comments, this.tokens);
|
|
226
224
|
this.convertInsertNodeToTextEdits(content, astNode, change, indentLevel, this.isFirstInsert(change.pointer, astNode, change.index));
|
|
227
225
|
}
|
|
228
|
-
else if ((astNode?.type ===
|
|
226
|
+
else if ((astNode?.type === RECORD_PROPERTY_TYPE || astNode?.type === ANNOTATION_TYPE) && astNode.range) {
|
|
229
227
|
const textNode = change.element.content[0];
|
|
230
|
-
const value = textNode?.type ===
|
|
231
|
-
const recordText = ' : ' +
|
|
232
|
-
this.edits.push(
|
|
228
|
+
const value = textNode?.type === TEXT_TYPE ? textNode.text : '';
|
|
229
|
+
const recordText = ' : ' + indent(printPrimitiveValue(change.element.name, value));
|
|
230
|
+
this.edits.push(TextEdit.insert(astNode.range.end, recordText));
|
|
233
231
|
}
|
|
234
232
|
};
|
|
235
|
-
[
|
|
233
|
+
[INSERT_QUALIFIER_CHANGE_TYPE] = (change, reversePath) => {
|
|
236
234
|
const [astNode] = reversePath;
|
|
237
|
-
if (astNode?.type ===
|
|
238
|
-
this.edits.push(
|
|
235
|
+
if (astNode?.type === ANNOTATION_TYPE && astNode.term.range) {
|
|
236
|
+
this.edits.push(TextEdit.insert(astNode.term.range.end, ` #${change.value}`));
|
|
239
237
|
}
|
|
240
238
|
};
|
|
241
|
-
[
|
|
239
|
+
[INSERT_REFERENCE_CHANGE_TYPE] = async (change) => {
|
|
242
240
|
const { position, prependNewLine } = getInsertReferencePosition(this.document.references);
|
|
243
241
|
// TODO: this breaks memfs concept
|
|
244
242
|
const text = `${prependNewLine ? '\n' : ''}${await getTextEditForMissingRefs(change.references, this.document.uri, this.projectRoot)}`;
|
|
245
|
-
this.referenceEdits.push(
|
|
243
|
+
this.referenceEdits.push(TextEdit.insert(position, text));
|
|
246
244
|
};
|
|
247
245
|
//#endregion
|
|
248
246
|
//#region Deletes
|
|
@@ -252,57 +250,57 @@ class CDSWriter {
|
|
|
252
250
|
const lastIndex = segments.pop();
|
|
253
251
|
deleteValue(this.edits, pointer, astNode, parent, this.comments, this.tokens, lastIndex);
|
|
254
252
|
}
|
|
255
|
-
[
|
|
253
|
+
[DELETE_TARGET_CHANGE_TYPE] = (_change, reversePath) => {
|
|
256
254
|
const [astNode] = reversePath;
|
|
257
|
-
if (astNode?.type !==
|
|
255
|
+
if (astNode?.type !== TARGET_TYPE || !astNode.range || !astNode.nameRange) {
|
|
258
256
|
return;
|
|
259
257
|
}
|
|
260
258
|
// TODO: check if we can relay on target.kind instead of going through edmx conversions
|
|
261
|
-
const { edmxPath } = this.facade.collectMetadataForAbsolutePath(astNode.name, astNode.kind,
|
|
259
|
+
const { edmxPath } = this.facade.collectMetadataForAbsolutePath(astNode.name, astNode.kind, createMetadataCollector(new Map(), this.facade));
|
|
262
260
|
const ranges = astNode.assignments
|
|
263
|
-
.flatMap((assignment) => (assignment.type ===
|
|
261
|
+
.flatMap((assignment) => (assignment.type === ANNOTATION_TYPE ? [assignment] : assignment.items.items))
|
|
264
262
|
.map((annotation, i) => {
|
|
265
|
-
return
|
|
263
|
+
return getDeletionRangeForNode(this.vocabularyService, this.vocabularyAliases, i, this.tokens, annotation, edmxPath);
|
|
266
264
|
})
|
|
267
265
|
.filter((range) => !!range);
|
|
268
|
-
const targetDeletions =
|
|
266
|
+
const targetDeletions = getTextEditsForDeletionRanges(ranges, this.vocabularyAliases, this.tokens, this.annotationFile, true);
|
|
269
267
|
if (targetDeletions) {
|
|
270
268
|
this.edits.push(...targetDeletions);
|
|
271
269
|
}
|
|
272
270
|
};
|
|
273
|
-
[
|
|
271
|
+
[DELETE_RECORD_CHANGE_TYPE] = (change, reversePath) => {
|
|
274
272
|
this.deleteNode(change.pointer, reversePath);
|
|
275
273
|
};
|
|
276
|
-
[
|
|
274
|
+
[DELETE_RECORD_PROPERTY_CHANGE_TYPE] = (change, reversePath) => {
|
|
277
275
|
this.deleteNode(change.pointer, reversePath);
|
|
278
276
|
};
|
|
279
|
-
[
|
|
277
|
+
[DELETE_ANNOTATION_GROUP_CHANGE_TYPE] = (change, reversePath) => {
|
|
280
278
|
const [astNode, parent] = reversePath;
|
|
281
279
|
const segments = change.pointer.split('/');
|
|
282
280
|
const lastIndex = segments.pop() ?? '';
|
|
283
281
|
const index = Number.parseInt(lastIndex, 10);
|
|
284
|
-
if (Number.isNaN(index) || astNode?.type !==
|
|
282
|
+
if (Number.isNaN(index) || astNode?.type !== ANNOTATION_GROUP_TYPE || parent?.type !== TARGET_TYPE) {
|
|
285
283
|
return;
|
|
286
284
|
}
|
|
287
285
|
const content = getContainerContent(parent, this.comments, this.tokens);
|
|
288
286
|
const { startContentIndex } = findContentIndices(content, index, index);
|
|
289
287
|
deleteBlock(this.edits, content, startContentIndex);
|
|
290
288
|
};
|
|
291
|
-
[
|
|
289
|
+
[DELETE_ANNOTATION_GROUP_ITEMS_CHANGE_TYPE] = () => {
|
|
292
290
|
// should never be called
|
|
293
291
|
// preprocessor converts these changes to DeleteAnnotationGroup
|
|
294
292
|
};
|
|
295
293
|
getDeletionRange(annotation, target, index) {
|
|
296
|
-
const { edmxPath } = this.facade.collectMetadataForAbsolutePath(target.name, target.kind,
|
|
297
|
-
return
|
|
294
|
+
const { edmxPath } = this.facade.collectMetadataForAbsolutePath(target.name, target.kind, createMetadataCollector(new Map(), this.facade));
|
|
295
|
+
return getDeletionRangeForNode(this.vocabularyService, this.vocabularyAliases, index, this.tokens, annotation, edmxPath);
|
|
298
296
|
}
|
|
299
|
-
[
|
|
297
|
+
[DELETE_ANNOTATION_CHANGE_TYPE] = (change, reversePath) => {
|
|
300
298
|
const [annotation, parent, , greatGrandParent] = reversePath;
|
|
301
299
|
const segments = change.pointer.split('/');
|
|
302
300
|
const lastIndex = segments.pop();
|
|
303
|
-
const isInAnnotationGroup = greatGrandParent?.type ===
|
|
301
|
+
const isInAnnotationGroup = greatGrandParent?.type === TARGET_TYPE;
|
|
304
302
|
const target = isInAnnotationGroup ? greatGrandParent : parent;
|
|
305
|
-
if (!lastIndex || annotation?.type !==
|
|
303
|
+
if (!lastIndex || annotation?.type !== ANNOTATION_TYPE || target.type !== TARGET_TYPE) {
|
|
306
304
|
return;
|
|
307
305
|
}
|
|
308
306
|
const targetPointer = change.pointer.split('/').slice(0, 3).join('/');
|
|
@@ -314,7 +312,7 @@ class CDSWriter {
|
|
|
314
312
|
// We need to calculate correct indices for the annotations
|
|
315
313
|
for (let i = 0; i < target.assignments.length; i++) {
|
|
316
314
|
const assignment = target.assignments[i];
|
|
317
|
-
if (assignment.type ===
|
|
315
|
+
if (assignment.type === ANNOTATION_GROUP_TYPE) {
|
|
318
316
|
index += assignment.items.items.length;
|
|
319
317
|
}
|
|
320
318
|
else {
|
|
@@ -335,43 +333,43 @@ class CDSWriter {
|
|
|
335
333
|
}
|
|
336
334
|
targetDeletionRanges.push(range);
|
|
337
335
|
};
|
|
338
|
-
[
|
|
336
|
+
[DELETE_EMBEDDED_ANNOTATION_CHANGE_TYPE] = (change, reversePath) => {
|
|
339
337
|
const [astNode, parent] = reversePath;
|
|
340
|
-
if (astNode?.type ===
|
|
341
|
-
if (parent.type ===
|
|
338
|
+
if (astNode?.type === ANNOTATION_TYPE) {
|
|
339
|
+
if (parent.type === RECORD_TYPE) {
|
|
342
340
|
// Transform $value back to normal primitive value representation if last embedded annotation is being deleted
|
|
343
341
|
const firstProperty = parent.properties[0];
|
|
344
342
|
const isTransformPossible = parent.annotations?.length === 1 &&
|
|
345
343
|
parent.properties.length === 1 &&
|
|
346
|
-
firstProperty.name.value ===
|
|
344
|
+
firstProperty.name.value === ReservedProperties.Value;
|
|
347
345
|
if (isTransformPossible && parent.range && firstProperty.value?.range) {
|
|
348
346
|
// delete surrounding record parts, leaving property value only
|
|
349
|
-
this.edits.push(
|
|
347
|
+
this.edits.push(TextEdit.del(Range.create(parent.range.start, firstProperty.value.range.start)), TextEdit.del(Range.create(firstProperty.value.range.end, parent.range.end)));
|
|
350
348
|
}
|
|
351
349
|
else {
|
|
352
350
|
this.deleteNode(change.pointer, reversePath);
|
|
353
351
|
}
|
|
354
352
|
}
|
|
355
353
|
else {
|
|
356
|
-
this[
|
|
354
|
+
this[DELETE_ANNOTATION_CHANGE_TYPE](createDeleteAnnotationChange(change.pointer), reversePath);
|
|
357
355
|
}
|
|
358
356
|
}
|
|
359
357
|
};
|
|
360
|
-
[
|
|
358
|
+
[DELETE_PRIMITIVE_VALUE_CHANGE_TYPE] = (change, reversePath) => {
|
|
361
359
|
this.deleteNode(change.pointer, reversePath);
|
|
362
360
|
};
|
|
363
|
-
[
|
|
361
|
+
[DELETE_QUALIFIER_CHANGE_TYPE] = (change, reversePath) => {
|
|
364
362
|
const [astNode] = reversePath;
|
|
365
363
|
if (!astNode?.range) {
|
|
366
364
|
return;
|
|
367
365
|
}
|
|
368
|
-
const range =
|
|
366
|
+
const range = copyRange(astNode.range);
|
|
369
367
|
range.start.character--; // also include # character
|
|
370
|
-
this.edits.push(
|
|
368
|
+
this.edits.push(TextEdit.del(range));
|
|
371
369
|
};
|
|
372
370
|
//#endregion
|
|
373
371
|
//#region Modifications
|
|
374
|
-
[
|
|
372
|
+
[REPLACE_NODE_CHANGE_TYPE] = (change, reversePath) => {
|
|
375
373
|
const [astNode] = reversePath;
|
|
376
374
|
if (!astNode?.range) {
|
|
377
375
|
return;
|
|
@@ -380,23 +378,23 @@ class CDSWriter {
|
|
|
380
378
|
if (willTargetAnnotationIncreaseIndent(this.processedChanges, change.pointer)) {
|
|
381
379
|
indentLevel++;
|
|
382
380
|
}
|
|
383
|
-
const fragment =
|
|
384
|
-
const indentedFragment =
|
|
385
|
-
this.edits.push(
|
|
381
|
+
const fragment = print(change.newElement, printOptions);
|
|
382
|
+
const indentedFragment = increaseIndent(fragment, indentLevel, true);
|
|
383
|
+
this.edits.push(TextEdit.replace(astNode.range, indentedFragment));
|
|
386
384
|
};
|
|
387
|
-
[
|
|
385
|
+
[REPLACE_RECORD_PROPERTY_CHANGE_TYPE] = (change, reversePath) => {
|
|
388
386
|
const [astNode] = reversePath;
|
|
389
387
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
390
388
|
if (!astNode?.range) {
|
|
391
389
|
return;
|
|
392
390
|
}
|
|
393
|
-
const text =
|
|
391
|
+
const text = indent(print(change.newProperty, printOptions, { indentResult: false }), {
|
|
394
392
|
level: indentLevel + 1,
|
|
395
393
|
skipFirstLine: true
|
|
396
394
|
});
|
|
397
|
-
this.edits.push(
|
|
395
|
+
this.edits.push(TextEdit.replace(astNode.range, text));
|
|
398
396
|
};
|
|
399
|
-
[
|
|
397
|
+
[REPLACE_TEXT_VALUE_CHANGE_TYPE] = (change, reversePath) => {
|
|
400
398
|
const [astNode] = reversePath;
|
|
401
399
|
if (!astNode?.range) {
|
|
402
400
|
return;
|
|
@@ -409,40 +407,40 @@ class CDSWriter {
|
|
|
409
407
|
console.warn(`Could not determine expression type for ${JSON.stringify(change, undefined, 2)}. For changing enum flags "set-flags" change should be used.`);
|
|
410
408
|
return;
|
|
411
409
|
}
|
|
412
|
-
this.edits.push(
|
|
410
|
+
this.edits.push(TextEdit.replace(astNode.range, printPrimitiveValue(expressionType, change.newValue)));
|
|
413
411
|
};
|
|
414
|
-
[
|
|
412
|
+
[UPDATE_PRIMITIVE_VALUE_CHANGE_TYPE] = (change, reversePath) => {
|
|
415
413
|
const [astNode] = reversePath;
|
|
416
414
|
if (!astNode?.range) {
|
|
417
415
|
return;
|
|
418
416
|
}
|
|
419
|
-
if (astNode.type ===
|
|
420
|
-
const range =
|
|
417
|
+
if (astNode.type === STRING_LITERAL_TYPE) {
|
|
418
|
+
const range = copyRange(astNode.range);
|
|
421
419
|
// string range includes quotes, but we only need to replace content.
|
|
422
420
|
range.start.character++;
|
|
423
421
|
range.end.character--;
|
|
424
|
-
this.edits.push(
|
|
422
|
+
this.edits.push(TextEdit.replace(range, change.newValue));
|
|
425
423
|
}
|
|
426
|
-
else if (astNode.type ===
|
|
427
|
-
this.edits.push(
|
|
424
|
+
else if (astNode.type === ENUM_TYPE && astNode.path.range) {
|
|
425
|
+
this.edits.push(TextEdit.replace(astNode.range, printPrimitiveValue(Edm.EnumMember, change.newValue)));
|
|
428
426
|
}
|
|
429
427
|
else {
|
|
430
|
-
this.edits.push(
|
|
428
|
+
this.edits.push(TextEdit.replace(astNode.range, change.newValue));
|
|
431
429
|
}
|
|
432
430
|
};
|
|
433
431
|
//#endregion
|
|
434
|
-
[
|
|
432
|
+
[SET_FLAGS_CHANGE_TYPE] = (change, reversePath) => {
|
|
435
433
|
const [astNode] = reversePath;
|
|
436
|
-
if (astNode?.type ===
|
|
437
|
-
this.edits.push(
|
|
434
|
+
if (astNode?.type === COLLECTION_TYPE && astNode.range) {
|
|
435
|
+
this.edits.push(TextEdit.replace(astNode.range, printPrimitiveValue(Edm.EnumMember, change.value)));
|
|
438
436
|
}
|
|
439
|
-
else if (astNode?.type ===
|
|
440
|
-
console.warn(`Could not apply change "set-flags". Expected target to be '${
|
|
437
|
+
else if (astNode?.type === ENUM_TYPE) {
|
|
438
|
+
console.warn(`Could not apply change "set-flags". Expected target to be '${COLLECTION_TYPE}, but got ${ENUM_TYPE}. To replace single enum value 'replace-text-value' change should be used`);
|
|
441
439
|
}
|
|
442
440
|
};
|
|
443
|
-
[
|
|
441
|
+
[MOVE_COLLECTION_VALUE_CHANGE_TYPE] = (change, reversePath) => {
|
|
444
442
|
const [astNode] = reversePath;
|
|
445
|
-
if (astNode.type !==
|
|
443
|
+
if (astNode.type !== COLLECTION_TYPE) {
|
|
446
444
|
return;
|
|
447
445
|
}
|
|
448
446
|
const content = getContainerContent(astNode, this.comments, this.tokens);
|
|
@@ -450,7 +448,7 @@ class CDSWriter {
|
|
|
450
448
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
451
449
|
if (anchor) {
|
|
452
450
|
if (anchor.commaPosition) {
|
|
453
|
-
this.edits.push(
|
|
451
|
+
this.edits.push(TextEdit.insert(anchor.commaPosition, ','));
|
|
454
452
|
}
|
|
455
453
|
const ranges = createElementRanges(this.document, this.tokens, change.fromPointers);
|
|
456
454
|
const moveEdits = getTextEditsForMove({
|
|
@@ -465,13 +463,13 @@ class CDSWriter {
|
|
|
465
463
|
astNode.closeToken?.range?.end?.line === astNode.openToken?.range?.end?.line) {
|
|
466
464
|
// [] is on the same line -> we need to expand this to multiple lines
|
|
467
465
|
const indent = ' '.repeat(indentLevel);
|
|
468
|
-
this.edits.push(
|
|
466
|
+
this.edits.push(TextEdit.insert(astNode.closeToken.range.start, '\n' + indent));
|
|
469
467
|
}
|
|
470
468
|
}
|
|
471
469
|
};
|
|
472
|
-
[
|
|
470
|
+
[CONVERT_TO_COMPOUND_ANNOTATION_CHANGE_TYPE] = (change, reversePath) => {
|
|
473
471
|
const [astNode] = reversePath;
|
|
474
|
-
if (astNode?.type !==
|
|
472
|
+
if (astNode?.type !== TARGET_TYPE) {
|
|
475
473
|
return;
|
|
476
474
|
}
|
|
477
475
|
const indentLevel = this.getIndentLevel(change.pointer);
|
|
@@ -481,23 +479,23 @@ class CDSWriter {
|
|
|
481
479
|
}
|
|
482
480
|
};
|
|
483
481
|
insertIntoNodeWithContent(content, parent, change, indentLevel, firstInsert, referenceNode) {
|
|
484
|
-
const index = getIndexForInsertion(
|
|
482
|
+
const index = getIndexForInsertion(getChildCount(parent), change.index);
|
|
485
483
|
// change.index should not be used in this scope, because changes with indices outside the container size are merged
|
|
486
484
|
// and change.index would not correctly reflect the place where a change needs to be inserted
|
|
487
485
|
if (!change || !parent.range) {
|
|
488
486
|
return;
|
|
489
487
|
}
|
|
490
488
|
const newElements = printChange(parent, referenceNode)(change);
|
|
491
|
-
if (
|
|
489
|
+
if (getChildCount(parent) === 0) {
|
|
492
490
|
const fragments = [];
|
|
493
|
-
const range =
|
|
491
|
+
const range = copyRange(parent.range);
|
|
494
492
|
// we need to adjust range to exclude boundary characters
|
|
495
493
|
range.start.character++; // range includes '{' or '[' characters
|
|
496
494
|
range.end.character--; // range includes '}' or ']' characters
|
|
497
495
|
fragments.push('\n');
|
|
498
496
|
fragments.push(newElements);
|
|
499
497
|
fragments.push(',');
|
|
500
|
-
const text =
|
|
498
|
+
const text = indent(deIndent(fragments.join('')), {
|
|
501
499
|
level: indentLevel + 1,
|
|
502
500
|
skipFirstLine: true
|
|
503
501
|
});
|
|
@@ -516,22 +514,22 @@ class CDSWriter {
|
|
|
516
514
|
const fragments = [];
|
|
517
515
|
if (firstInsert && anchor.commaPosition) {
|
|
518
516
|
// for repeated inserts at the same spot we'll already have the trailing comma
|
|
519
|
-
this.edits.push(
|
|
517
|
+
this.edits.push(TextEdit.insert(anchor.commaPosition, ','));
|
|
520
518
|
}
|
|
521
519
|
fragments.push('\n');
|
|
522
520
|
fragments.push(newElements);
|
|
523
521
|
fragments.push(',');
|
|
524
522
|
let finalText = fragments.join('');
|
|
525
523
|
finalText = deIndent(finalText);
|
|
526
|
-
const text =
|
|
524
|
+
const text = indent(finalText, {
|
|
527
525
|
level: indentLevel + 1,
|
|
528
526
|
skipFirstLine: true
|
|
529
527
|
});
|
|
530
|
-
this.edits.push(
|
|
528
|
+
this.edits.push(TextEdit.insert(anchor.position, text));
|
|
531
529
|
}
|
|
532
530
|
}
|
|
533
531
|
insertFirstText(range, text, indentLevel) {
|
|
534
|
-
this.edits.push(
|
|
532
|
+
this.edits.push(TextEdit.replace(range, text + indent('\n', { level: indentLevel, skipFirstLine: true })));
|
|
535
533
|
}
|
|
536
534
|
insertAdditionalText(range, text) {
|
|
537
535
|
// Multiple inserts will have the same replacement range, we need to group them because
|
|
@@ -547,17 +545,17 @@ class CDSWriter {
|
|
|
547
545
|
if (!change) {
|
|
548
546
|
return;
|
|
549
547
|
}
|
|
550
|
-
if (parent.type !==
|
|
548
|
+
if (parent.type !== CDS_DOCUMENT_TYPE) {
|
|
551
549
|
this.insertIntoNodeWithContent(content, parent, change, childIndentLevel, firstInsert);
|
|
552
550
|
}
|
|
553
551
|
}
|
|
554
552
|
findInsertPosition(content, parent, insertionPointer, index = -1) {
|
|
555
|
-
const childCount =
|
|
553
|
+
const childCount = getChildCount(parent);
|
|
556
554
|
if (childCount === 0) {
|
|
557
555
|
if (!parent.range) {
|
|
558
556
|
return undefined;
|
|
559
557
|
}
|
|
560
|
-
const position =
|
|
558
|
+
const position = copyPosition(parent.range.start);
|
|
561
559
|
position.character++; // range includes '{' or '[' characters
|
|
562
560
|
return { position };
|
|
563
561
|
}
|
|
@@ -572,7 +570,7 @@ class CDSWriter {
|
|
|
572
570
|
if (!skipCommaInsertion(this.changes, content, this.document, previousContentIndex)) {
|
|
573
571
|
return {
|
|
574
572
|
position: anchor,
|
|
575
|
-
commaPosition:
|
|
573
|
+
commaPosition: copyPosition(previousElement.element.range.end)
|
|
576
574
|
};
|
|
577
575
|
}
|
|
578
576
|
}
|
|
@@ -581,10 +579,9 @@ class CDSWriter {
|
|
|
581
579
|
return undefined;
|
|
582
580
|
}
|
|
583
581
|
}
|
|
584
|
-
exports.CDSWriter = CDSWriter;
|
|
585
582
|
function willTargetAnnotationIncreaseIndent(changes, pointer) {
|
|
586
583
|
const targetPointer = pointer.split('/').slice(0, 3).join('/');
|
|
587
|
-
const conversionChange = changes.find((change) => change.pointer === targetPointer && change.type ===
|
|
584
|
+
const conversionChange = changes.find((change) => change.pointer === targetPointer && change.type === CONVERT_TO_COMPOUND_ANNOTATION_CHANGE_TYPE);
|
|
588
585
|
return !!conversionChange;
|
|
589
586
|
}
|
|
590
587
|
function convertToCompoundAnnotation(tokens, node, indentLevel, indentContent) {
|
|
@@ -593,7 +590,7 @@ function convertToCompoundAnnotation(tokens, node, indentLevel, indentContent) {
|
|
|
593
590
|
}
|
|
594
591
|
// All assignments are grouped to the same target, but we only need to replace the last one.
|
|
595
592
|
const startPosition = node.assignments[node.assignments.length - 1]?.range?.start ?? node.range?.start;
|
|
596
|
-
const startToken =
|
|
593
|
+
const startToken = findLastTokenBeforePosition(ANNOTATION_START_PATTERN, tokens, startPosition);
|
|
597
594
|
if (!startToken) {
|
|
598
595
|
return [];
|
|
599
596
|
}
|
|
@@ -602,7 +599,7 @@ function convertToCompoundAnnotation(tokens, node, indentLevel, indentContent) {
|
|
|
602
599
|
return [];
|
|
603
600
|
}
|
|
604
601
|
// there can be trailing comma before the closing token ')' or ';'
|
|
605
|
-
const afterEndToken =
|
|
602
|
+
const afterEndToken = findFirstTokenAfterPosition(/[^,]/, tokens, node.range?.end);
|
|
606
603
|
// there should always be an afterEndToken
|
|
607
604
|
// if it is an annotation on element then it would at least end with '}'
|
|
608
605
|
// if it is an annotation on entity then it would be ';'
|
|
@@ -610,37 +607,37 @@ function convertToCompoundAnnotation(tokens, node, indentLevel, indentContent) {
|
|
|
610
607
|
// either its a compound annotation or there is something else wrong and we should create text edits
|
|
611
608
|
return [];
|
|
612
609
|
}
|
|
613
|
-
const endToken =
|
|
610
|
+
const endToken = findLastTokenBeforePosition(undefined, tokens, node.range?.end);
|
|
614
611
|
if (!endToken) {
|
|
615
612
|
return [];
|
|
616
613
|
}
|
|
617
614
|
const indentText = ' '.repeat(indentLevel + 1);
|
|
618
615
|
const closingIndent = ' '.repeat(indentLevel);
|
|
619
616
|
const contentIndent = [];
|
|
620
|
-
const startTokenLine =
|
|
621
|
-
const startTokenCharacter =
|
|
622
|
-
const endTokenLine =
|
|
623
|
-
const endTokenCharacter =
|
|
617
|
+
const startTokenLine = tokenLine(startToken);
|
|
618
|
+
const startTokenCharacter = tokenColumn(startToken);
|
|
619
|
+
const endTokenLine = tokenLine(endToken);
|
|
620
|
+
const endTokenCharacter = tokenColumn(endToken);
|
|
624
621
|
if (indentContent) {
|
|
625
622
|
for (let index = startTokenLine + 1; index <= endTokenLine; index++) {
|
|
626
|
-
contentIndent.push(
|
|
623
|
+
contentIndent.push(TextEdit.insert(Position.create(index, 0), ' '));
|
|
627
624
|
}
|
|
628
625
|
}
|
|
629
626
|
// if annotation ends with ';' we need to insert closing ')' before ';' otherwise we need to insert it after the last token
|
|
630
627
|
// sometimes element annotations many end without ';'
|
|
631
|
-
const afterEndTokenCharacter =
|
|
632
|
-
const afterEndTokenLine =
|
|
628
|
+
const afterEndTokenCharacter = tokenColumn(afterEndToken);
|
|
629
|
+
const afterEndTokenLine = tokenLine(afterEndToken);
|
|
633
630
|
const endCharacter = afterEndToken.text === ';' ? afterEndTokenCharacter : endTokenCharacter + endToken.text.length;
|
|
634
|
-
const endReplaceRangeStart =
|
|
631
|
+
const endReplaceRangeStart = Position.create(endTokenLine, endCharacter);
|
|
635
632
|
// if the closing '}' for element container is in the same line as the value it is likely there will be whitespace between,
|
|
636
633
|
// we need to make sure that this whitespace is removed because `)` will be inserted in the same line
|
|
637
634
|
const endReplaceRangeEnd = endTokenLine === afterEndTokenLine
|
|
638
|
-
?
|
|
635
|
+
? Position.create(afterEndTokenLine, afterEndTokenCharacter)
|
|
639
636
|
: endReplaceRangeStart;
|
|
640
637
|
return [
|
|
641
|
-
|
|
638
|
+
TextEdit.insert(Position.create(startTokenLine, startTokenCharacter + 1), `(\n${indentText}`),
|
|
642
639
|
...contentIndent,
|
|
643
|
-
|
|
640
|
+
TextEdit.replace(Range.create(endReplaceRangeStart, endReplaceRangeEnd), `\n${closingIndent})`)
|
|
644
641
|
];
|
|
645
642
|
}
|
|
646
643
|
function deleteBlock(edits, content, blockIndex) {
|
|
@@ -648,7 +645,7 @@ function deleteBlock(edits, content, blockIndex) {
|
|
|
648
645
|
if (block?.type !== 'element') {
|
|
649
646
|
return;
|
|
650
647
|
}
|
|
651
|
-
edits.push(
|
|
648
|
+
edits.push(TextEdit.del(block.range));
|
|
652
649
|
const previous = content[blockIndex - 1];
|
|
653
650
|
const next = content[blockIndex + 1];
|
|
654
651
|
if (previous?.range) {
|
|
@@ -658,7 +655,7 @@ function deleteBlock(edits, content, blockIndex) {
|
|
|
658
655
|
}
|
|
659
656
|
else if (next?.range) {
|
|
660
657
|
// there could be whitespace to the next element which should be removed
|
|
661
|
-
edits.push(
|
|
658
|
+
edits.push(TextEdit.del(Range.create(block.range.end, next.range.start)));
|
|
662
659
|
}
|
|
663
660
|
}
|
|
664
661
|
function removeWhitespaceForConsequentDeletes(edits, content, blockIndex, block, next) {
|
|
@@ -671,7 +668,7 @@ function removeWhitespaceForConsequentDeletes(edits, content, blockIndex, block,
|
|
|
671
668
|
}
|
|
672
669
|
}
|
|
673
670
|
if (hasDeleteSequence && next?.range) {
|
|
674
|
-
const edit =
|
|
671
|
+
const edit = TextEdit.del(Range.create(block.range.end, next.range.start));
|
|
675
672
|
// other deletion edits with the same range may already exist
|
|
676
673
|
if (!edits.some((item) => isRangesEqual(item.range, edit.range))) {
|
|
677
674
|
edits.push(edit);
|
|
@@ -679,66 +676,66 @@ function removeWhitespaceForConsequentDeletes(edits, content, blockIndex, block,
|
|
|
679
676
|
}
|
|
680
677
|
}
|
|
681
678
|
function deletePreviousElementWhiteSpaces(previousElement, currentBlock, edits) {
|
|
682
|
-
const edit =
|
|
679
|
+
const edit = TextEdit.del(Range.create(previousElement.range.end, currentBlock.range.start));
|
|
683
680
|
// other deletion edits with the same range may already exist
|
|
684
681
|
if (!edits.some((item) => isRangesEqual(item.range, edit.range))) {
|
|
685
682
|
edits.push(edit);
|
|
686
683
|
}
|
|
687
684
|
}
|
|
688
685
|
function deleteValue(edits, pointer, astNode, parent, comments, tokens, lastIndex) {
|
|
689
|
-
if (parent.type ===
|
|
686
|
+
if (parent.type === COLLECTION_TYPE) {
|
|
690
687
|
if (!lastIndex) {
|
|
691
|
-
throw new
|
|
688
|
+
throw new ApiError(`${pointer} is not pointing to a collection element.`);
|
|
692
689
|
}
|
|
693
690
|
const content = getContainerContent(parent, comments, tokens);
|
|
694
691
|
const index = Number.parseInt(lastIndex, 10);
|
|
695
692
|
const { startContentIndex } = findContentIndices(content, index);
|
|
696
693
|
deleteBlock(edits, content, startContentIndex);
|
|
697
694
|
}
|
|
698
|
-
else if (parent.type ===
|
|
699
|
-
(astNode.type ===
|
|
695
|
+
else if (parent.type === RECORD_TYPE &&
|
|
696
|
+
(astNode.type === RECORD_PROPERTY_TYPE || astNode.type === ANNOTATION_TYPE)) {
|
|
700
697
|
if (!lastIndex) {
|
|
701
|
-
throw new
|
|
698
|
+
throw new ApiError(`${pointer} is not pointing to a record property.`);
|
|
702
699
|
}
|
|
703
700
|
const content = getContainerContent(parent, comments, tokens);
|
|
704
701
|
const index = Number.parseInt(lastIndex, 10);
|
|
705
702
|
const { startContentIndex } = findContentIndices(content, index, index, astNode.type);
|
|
706
703
|
deleteBlock(edits, content, startContentIndex);
|
|
707
704
|
}
|
|
708
|
-
else if (parent.type ===
|
|
705
|
+
else if (parent.type === ANNOTATION_TYPE || parent.type === RECORD_PROPERTY_TYPE) {
|
|
709
706
|
// delete record value including ':'
|
|
710
707
|
if (parent.colon?.range && astNode.range) {
|
|
711
|
-
const range =
|
|
712
|
-
edits.push(
|
|
708
|
+
const range = Range.create(parent.colon.range.start, astNode.range.end);
|
|
709
|
+
edits.push(TextEdit.del(range));
|
|
713
710
|
}
|
|
714
711
|
}
|
|
715
712
|
else {
|
|
716
|
-
throw new
|
|
713
|
+
throw new ApiError(`Invalid ${pointer} for 'delete-record' change.`);
|
|
717
714
|
}
|
|
718
715
|
}
|
|
719
716
|
function printChange(parent, referenceNode) {
|
|
720
717
|
return function (change) {
|
|
721
|
-
if (change.type ===
|
|
718
|
+
if (change.type === INSERT_EMBEDDED_ANNOTATION_CHANGE_TYPE) {
|
|
722
719
|
if (referenceNode) {
|
|
723
|
-
const element =
|
|
720
|
+
const element = createReferenceElement(referenceNode);
|
|
724
721
|
if (element) {
|
|
725
|
-
return
|
|
722
|
+
return print(change.element, printOptions, {
|
|
726
723
|
indentResult: false,
|
|
727
724
|
annotationContext: [element]
|
|
728
725
|
});
|
|
729
726
|
}
|
|
730
727
|
}
|
|
731
|
-
return '@' +
|
|
728
|
+
return '@' + print(change.element, printOptions);
|
|
732
729
|
}
|
|
733
|
-
else if (change.type ===
|
|
734
|
-
const text = change.element.content[0]?.type ===
|
|
735
|
-
return
|
|
730
|
+
else if (change.type === INSERT_PRIMITIVE_VALUE_TYPE) {
|
|
731
|
+
const text = change.element.content[0]?.type === TEXT_TYPE ? change.element.content[0].text : '';
|
|
732
|
+
return printPrimitiveValue(change.element.name, text);
|
|
736
733
|
}
|
|
737
|
-
else if (parent?.type ===
|
|
738
|
-
return
|
|
734
|
+
else if (parent?.type === RECORD_TYPE && change.element.name === Edm.Annotation) {
|
|
735
|
+
return print(change.element, printOptions);
|
|
739
736
|
}
|
|
740
737
|
else {
|
|
741
|
-
return
|
|
738
|
+
return print(change.element, printOptions);
|
|
742
739
|
}
|
|
743
740
|
};
|
|
744
741
|
}
|
|
@@ -748,7 +745,7 @@ function getIndexForInsertion(containerSize, insertionIndex) {
|
|
|
748
745
|
: containerSize;
|
|
749
746
|
}
|
|
750
747
|
function getCommas(container, tokens) {
|
|
751
|
-
if (container.type ===
|
|
748
|
+
if (container.type === TARGET_TYPE) {
|
|
752
749
|
if (!container.range) {
|
|
753
750
|
return [];
|
|
754
751
|
}
|
|
@@ -761,11 +758,11 @@ function getCommas(container, tokens) {
|
|
|
761
758
|
function extractCommasFromCompilerTokens(range, items, tokens) {
|
|
762
759
|
const result = [];
|
|
763
760
|
const { start, end } = range;
|
|
764
|
-
const startTokenIndex = tokens.findIndex(
|
|
761
|
+
const startTokenIndex = tokens.findIndex(matchTokenByStart(start));
|
|
765
762
|
for (let i = startTokenIndex; i < tokens.length; i++) {
|
|
766
763
|
const token = tokens[i];
|
|
767
|
-
const tokenRange =
|
|
768
|
-
if (token.text === ',' && !items.some((item) => item.range &&
|
|
764
|
+
const tokenRange = createTokenRange(token);
|
|
765
|
+
if (token.text === ',' && !items.some((item) => item.range && rangeContained(item.range, tokenRange))) {
|
|
769
766
|
// we are only interested in commas separating items -> ignore commas that are inside an item
|
|
770
767
|
result.push({
|
|
771
768
|
type: 'token',
|
|
@@ -773,7 +770,7 @@ function extractCommasFromCompilerTokens(range, items, tokens) {
|
|
|
773
770
|
range: tokenRange
|
|
774
771
|
});
|
|
775
772
|
}
|
|
776
|
-
if (
|
|
773
|
+
if (isOldToken(token)) {
|
|
777
774
|
if ((token.line === end.line + 1 && token.column >= end.character) || token.line > end.line + 1) {
|
|
778
775
|
break;
|
|
779
776
|
}
|
|
@@ -790,7 +787,7 @@ function getStartAnchor(content, parent, previous, index) {
|
|
|
790
787
|
let startPosition = index === 0 ? parent.range?.start : previousElement?.range?.end;
|
|
791
788
|
const element = content[index];
|
|
792
789
|
if (startPosition) {
|
|
793
|
-
startPosition =
|
|
790
|
+
startPosition = copyPosition(startPosition);
|
|
794
791
|
if (index === 0) {
|
|
795
792
|
startPosition.character++;
|
|
796
793
|
}
|
|
@@ -824,8 +821,8 @@ function serializeReference(data) {
|
|
|
824
821
|
async function getTextEditForMissingRefs(missingReferences, fileUri, projectRoot) {
|
|
825
822
|
const missingReferencesTexts = [];
|
|
826
823
|
for (const missingRef of missingReferences) {
|
|
827
|
-
const relativePath =
|
|
828
|
-
const referenceUri = await
|
|
824
|
+
const relativePath = relative(projectRoot, fileURLToPath(fileUri));
|
|
825
|
+
const referenceUri = await toReferenceUri(projectRoot, relativePath, missingRef);
|
|
829
826
|
missingReferencesTexts.push(serializeReference({ namespace: '', referenceUri }));
|
|
830
827
|
}
|
|
831
828
|
return missingReferencesTexts.join('\n') + '\n';
|
|
@@ -842,23 +839,23 @@ function isRangesEqual(range1, range2) {
|
|
|
842
839
|
function convertCDSAstToEdmType(kind) {
|
|
843
840
|
switch (kind) {
|
|
844
841
|
case 'time':
|
|
845
|
-
return
|
|
842
|
+
return Edm.TimeOfDay;
|
|
846
843
|
case 'date':
|
|
847
|
-
return
|
|
844
|
+
return Edm.Date;
|
|
848
845
|
case 'timestamp':
|
|
849
|
-
return
|
|
846
|
+
return Edm.DateTimeOffset;
|
|
850
847
|
case 'binary':
|
|
851
|
-
return
|
|
848
|
+
return Edm.Binary;
|
|
852
849
|
case 'path':
|
|
853
|
-
return
|
|
850
|
+
return Edm.Path;
|
|
854
851
|
case 'boolean':
|
|
855
|
-
return
|
|
852
|
+
return Edm.Bool;
|
|
856
853
|
case 'string':
|
|
857
|
-
return
|
|
854
|
+
return Edm.String;
|
|
858
855
|
case 'enum':
|
|
859
|
-
return
|
|
856
|
+
return Edm.EnumMember;
|
|
860
857
|
case 'number':
|
|
861
|
-
return
|
|
858
|
+
return Edm.Decimal;
|
|
862
859
|
default:
|
|
863
860
|
return undefined;
|
|
864
861
|
}
|
|
@@ -872,9 +869,9 @@ function deIndent(text) {
|
|
|
872
869
|
function getInsertReferencePosition(references) {
|
|
873
870
|
const range = references[references.length - 1]?.uriRange;
|
|
874
871
|
if (!range) {
|
|
875
|
-
return { position:
|
|
872
|
+
return { position: Position.create(0, 0), prependNewLine: false };
|
|
876
873
|
}
|
|
877
|
-
const position =
|
|
874
|
+
const position = copyPosition(range.end);
|
|
878
875
|
// reference last position includes its last character, we need to insert after it.
|
|
879
876
|
position.character++;
|
|
880
877
|
return { position, prependNewLine: true };
|
|
@@ -896,9 +893,9 @@ function createElementRanges(document, tokens, pointers) {
|
|
|
896
893
|
return acc;
|
|
897
894
|
}, new Map());
|
|
898
895
|
for (const [containerPath, indices] of groups) {
|
|
899
|
-
const [parent] =
|
|
900
|
-
if (parent?.type ===
|
|
901
|
-
const indentLevel =
|
|
896
|
+
const [parent] = getAstNodesFromPointer(document, containerPath).reverse();
|
|
897
|
+
if (parent?.type === COLLECTION_TYPE) {
|
|
898
|
+
const indentLevel = getIndentLevelFromNode(tokens, parent);
|
|
902
899
|
indices.sort((index1, index2) => index1 - index2);
|
|
903
900
|
for (let i = 1, start = indices[0], end = indices[0]; i <= indices.length; i++) {
|
|
904
901
|
const current = indices[i];
|
|
@@ -927,7 +924,7 @@ function getTextEditsForMove(writerContext, position, ranges, indentLevel) {
|
|
|
927
924
|
const sourceContent = getContainerContent(range.parent, writerContext.comments, writerContext.tokens);
|
|
928
925
|
cutRange(writerContext, sourceContent, range, indentLevel, text, edits);
|
|
929
926
|
}
|
|
930
|
-
edits.push(
|
|
927
|
+
edits.push(TextEdit.insert(position, ''.concat(...text)));
|
|
931
928
|
return edits;
|
|
932
929
|
}
|
|
933
930
|
function findContentIndices(content, start, end = start, nodeType) {
|
|
@@ -962,9 +959,9 @@ function isComma(token) {
|
|
|
962
959
|
function findDeletionChange(document, changes, range) {
|
|
963
960
|
return changes.some((change) => {
|
|
964
961
|
if (change.type.startsWith('delete-')) {
|
|
965
|
-
const [leaf] =
|
|
962
|
+
const [leaf] = getAstNodesFromPointer(document, change.pointer).reverse();
|
|
966
963
|
if (leaf?.range) {
|
|
967
|
-
return
|
|
964
|
+
return rangeContained(leaf.range, range);
|
|
968
965
|
}
|
|
969
966
|
}
|
|
970
967
|
return false;
|
|
@@ -978,10 +975,10 @@ function cutRange(writerContext, content, cutRange, indentLevel, text, edits) {
|
|
|
978
975
|
let startPosition = start === 0 ? parent.openToken?.range?.end : previousElement?.range?.end;
|
|
979
976
|
let endPosition = endElement?.range?.end;
|
|
980
977
|
if (startPosition) {
|
|
981
|
-
startPosition =
|
|
978
|
+
startPosition = copyPosition(startPosition);
|
|
982
979
|
}
|
|
983
980
|
if (endPosition) {
|
|
984
|
-
endPosition =
|
|
981
|
+
endPosition = copyPosition(endPosition);
|
|
985
982
|
}
|
|
986
983
|
if (!startPosition || !endPosition) {
|
|
987
984
|
return;
|
|
@@ -993,9 +990,9 @@ function cutRange(writerContext, content, cutRange, indentLevel, text, edits) {
|
|
|
993
990
|
// ___|_______________|
|
|
994
991
|
// | |
|
|
995
992
|
// cut suffix range
|
|
996
|
-
const range =
|
|
993
|
+
const range = copyRange(Range.create(endElement.element.range.end, endElement.trailingComment.range.end));
|
|
997
994
|
if (!findDeletionChange(writerContext.cdsDocument, writerContext.changes, range)) {
|
|
998
|
-
edits.push(
|
|
995
|
+
edits.push(TextEdit.del(range));
|
|
999
996
|
}
|
|
1000
997
|
suffix = ',' + writerContext.textDocument.getText(range);
|
|
1001
998
|
updatePosition(endPosition, endElement.element.range.end);
|
|
@@ -1005,11 +1002,11 @@ function cutRange(writerContext, content, cutRange, indentLevel, text, edits) {
|
|
|
1005
1002
|
suffix = ',';
|
|
1006
1003
|
}
|
|
1007
1004
|
}
|
|
1008
|
-
const range =
|
|
1005
|
+
const range = copyRange(Range.create(startPosition, endPosition));
|
|
1009
1006
|
const originalText = writerContext.textDocument.getText(range);
|
|
1010
1007
|
text.push(makeCut(originalText, suffix, cutRange, indentLevel));
|
|
1011
1008
|
if (!findDeletionChange(writerContext.cdsDocument, writerContext.changes, range)) {
|
|
1012
|
-
edits.push(
|
|
1009
|
+
edits.push(TextEdit.del(range));
|
|
1013
1010
|
}
|
|
1014
1011
|
}
|
|
1015
1012
|
function makeCut(originalText, suffix, cutRange, indentLevel) {
|
|
@@ -1038,12 +1035,12 @@ function getContainerContent(collection, comments, tokens) {
|
|
|
1038
1035
|
if (!collection.range) {
|
|
1039
1036
|
return [];
|
|
1040
1037
|
}
|
|
1041
|
-
const items =
|
|
1038
|
+
const items = getItems(collection);
|
|
1042
1039
|
const commas = getCommas(collection, tokens);
|
|
1043
1040
|
const commentsInContent = (collection.range !== undefined
|
|
1044
|
-
? comments.filter((comment) =>
|
|
1045
|
-
: []).filter((comment) => !items.some((item) => item.range &&
|
|
1046
|
-
const source = [...commas, ...items, ...commentsInContent].sort(
|
|
1041
|
+
? comments.filter((comment) => rangeContained(collection.range, comment.range))
|
|
1042
|
+
: []).filter((comment) => !items.some((item) => item.range && rangeContained(item.range, comment.range)));
|
|
1043
|
+
const source = [...commas, ...items, ...commentsInContent].sort(compareByRange);
|
|
1047
1044
|
const content = [];
|
|
1048
1045
|
for (const node of source) {
|
|
1049
1046
|
processNode(content, node);
|
|
@@ -1077,8 +1074,8 @@ function processNode(content, item) {
|
|
|
1077
1074
|
const element = {
|
|
1078
1075
|
type: 'element',
|
|
1079
1076
|
element: item,
|
|
1080
|
-
elementRange:
|
|
1081
|
-
range:
|
|
1077
|
+
elementRange: copyRange(item.range),
|
|
1078
|
+
range: copyRange(item.range)
|
|
1082
1079
|
};
|
|
1083
1080
|
const previousLine = element.range.start.line - 1;
|
|
1084
1081
|
if (previousItem?.type === 'comment' &&
|
|
@@ -1095,11 +1092,11 @@ function skipCommaInsertion(changes, content, document, insertAfterIndex) {
|
|
|
1095
1092
|
if (!change.type.startsWith('delete')) {
|
|
1096
1093
|
return false;
|
|
1097
1094
|
}
|
|
1098
|
-
const astNodes =
|
|
1095
|
+
const astNodes = getAstNodesFromPointer(document, change.pointer);
|
|
1099
1096
|
const toBeDeletedNodes = astNodes[astNodes.length - 1];
|
|
1100
1097
|
const node = content[insertAfterIndex];
|
|
1101
1098
|
if (node.type === 'element' && node?.element === toBeDeletedNodes) {
|
|
1102
|
-
if (toBeDeletedNodes.type ===
|
|
1099
|
+
if (toBeDeletedNodes.type === ANNOTATION_TYPE && content.length === 1) {
|
|
1103
1100
|
return false;
|
|
1104
1101
|
}
|
|
1105
1102
|
return true;
|